diff --git a/CHANGES.txt b/CHANGES.txt index c65acf4a2a7..1dc582a10ce 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -365,6 +365,8 @@ Other Changes 21. Upgraded to Lucene 2.9-dev r752164 (shalin) +22. SOLR-1068: Use fsync on replicated index and configuration files (yonik, shalin) + Build ---------------------- 1. SOLR-776: Added in ability to sign artifacts via Ant for releases (gsingers) diff --git a/src/common/org/apache/solr/common/util/FileUtils.java b/src/common/org/apache/solr/common/util/FileUtils.java index 1ecb807c833..a9bd887d4d1 100644 --- a/src/common/org/apache/solr/common/util/FileUtils.java +++ b/src/common/org/apache/solr/common/util/FileUtils.java @@ -19,6 +19,7 @@ package org.apache.solr.common.util; import java.io.File; import java.io.IOException; +import java.io.RandomAccessFile; /** * @version $Id$ @@ -29,10 +30,10 @@ public class FileUtils { * Resolves a path relative a base directory. * *

- * This method does what "new File(base,path)" Should do, it wasn't + * This method does what "new File(base,path)" Should do, it wasn't * completely lame: If path is absolute, then a File for that path is returned; * if it's not absoluve, then a File is returnd using "path" as a child - * of "base") + * of "base") *

*/ public static File resolvePath(File base, String path) throws IOException { @@ -40,4 +41,44 @@ public class FileUtils { return r.isAbsolute() ? r : new File(base, path); } + /** + * Copied from Lucene's {@link org.apache.lucene.store.FSDirectory#sync(String)} + * + * @see org.apache.lucene.store.FSDirectory#sync(String) + * + * @param fullFile the File to be synced to disk + * @throws IOException if the file could not be synced + */ + public static void sync(File fullFile) throws IOException { + boolean success = false; + int retryCount = 0; + IOException exc = null; + while(!success && retryCount < 5) { + retryCount++; + RandomAccessFile file = null; + try { + try { + file = new RandomAccessFile(fullFile, "rw"); + file.getFD().sync(); + success = true; + } finally { + if (file != null) + file.close(); + } + } catch (IOException ioe) { + if (exc == null) + exc = ioe; + try { + // Pause 5 msec + Thread.sleep(5); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + } + } + } + if (!success) + // Throw original exception + throw exc; + } + } diff --git a/src/java/org/apache/solr/handler/SnapPuller.java b/src/java/org/apache/solr/handler/SnapPuller.java index 9b57f9eb0d1..15aeca80aaa 100644 --- a/src/java/org/apache/solr/handler/SnapPuller.java +++ b/src/java/org/apache/solr/handler/SnapPuller.java @@ -23,6 +23,7 @@ import org.apache.solr.common.SolrException; import org.apache.solr.common.util.FastInputStream; import org.apache.solr.common.util.JavaBinCodec; import org.apache.solr.common.util.NamedList; +import org.apache.solr.common.util.FileUtils; import org.apache.solr.core.SolrCore; import static org.apache.solr.handler.ReplicationHandler.*; import org.apache.solr.search.SolrIndexSearcher; @@ -475,7 +476,7 @@ public class SnapPuller { /** * Copy all index files from the temp index dir to the actual index. The segments_N file is copied last. */ - private boolean copyIndexFiles(File snapDir, File indexDir) { + private boolean copyIndexFiles(File snapDir, File indexDir) throws IOException { String segmentsFile = null; List copiedfiles = new ArrayList(); for (Map f : filesDownloaded) { @@ -490,11 +491,13 @@ public class SnapPuller { continue; } if (!copyAFile(snapDir, indexDir, fname, copiedfiles)) return false; + FileUtils.sync(new File(indexDir, fname)); copiedfiles.add(fname); } //copy the segments file last if (segmentsFile != null) { if (!copyAFile(snapDir, indexDir, segmentsFile, copiedfiles)) return false; + FileUtils.sync(new File(indexDir, segmentsFile)); } return true; } @@ -516,7 +519,9 @@ public class SnapPuller { } } boolean status = file.renameTo(oldFile); - if (!status) { + if (status) { + FileUtils.sync(oldFile); + } else { throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unable to rename: " + file + " to: " + oldFile); }