Author: shalin
Date: Fri Apr  3 09:08:37 2009
New Revision: 761577

URL: http://svn.apache.org/viewvc?rev=761577&view=rev
Log:
SOLR-1068 --  Use a single threaded executor to fsync replicated index and 
configuration files before moving them

Modified:
    lucene/solr/trunk/CHANGES.txt
    lucene/solr/trunk/src/common/org/apache/solr/common/util/FileUtils.java
    lucene/solr/trunk/src/java/org/apache/solr/handler/SnapPuller.java

Modified: lucene/solr/trunk/CHANGES.txt
URL: 
http://svn.apache.org/viewvc/lucene/solr/trunk/CHANGES.txt?rev=761577&r1=761576&r2=761577&view=diff
==============================================================================
--- lucene/solr/trunk/CHANGES.txt (original)
+++ lucene/solr/trunk/CHANGES.txt Fri Apr  3 09:08:37 2009
@@ -369,7 +369,7 @@
 
 21. Upgraded to Lucene 2.9-dev r752164 (shalin)
 
-22. SOLR-1068: Use fsync on replicated index and configuration files (yonik, 
shalin)
+22. SOLR-1068: Use fsync on replicated index and configuration files (yonik, 
Noble Paul, shalin)
 
 23. SOLR-952: Cleanup duplicated code in deprecated HighlightingUtils (hossman)
 

Modified: 
lucene/solr/trunk/src/common/org/apache/solr/common/util/FileUtils.java
URL: 
http://svn.apache.org/viewvc/lucene/solr/trunk/src/common/org/apache/solr/common/util/FileUtils.java?rev=761577&r1=761576&r2=761577&view=diff
==============================================================================
--- lucene/solr/trunk/src/common/org/apache/solr/common/util/FileUtils.java 
(original)
+++ lucene/solr/trunk/src/common/org/apache/solr/common/util/FileUtils.java Fri 
Apr  3 09:08:37 2009
@@ -20,6 +20,7 @@
 import java.io.File;
 import java.io.IOException;
 import java.io.RandomAccessFile;
+import java.io.FileNotFoundException;
 
 /**
  * @version $Id$
@@ -50,6 +51,9 @@
    * @throws IOException if the file could not be synced
    */
   public static void sync(File fullFile) throws IOException  {
+    if (fullFile == null || !fullFile.exists())
+      throw new FileNotFoundException("File does not exist " + fullFile);
+
     boolean success = false;
     int retryCount = 0;
     IOException exc = null;

Modified: lucene/solr/trunk/src/java/org/apache/solr/handler/SnapPuller.java
URL: 
http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/handler/SnapPuller.java?rev=761577&r1=761576&r2=761577&view=diff
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/handler/SnapPuller.java 
(original)
+++ lucene/solr/trunk/src/java/org/apache/solr/handler/SnapPuller.java Fri Apr  
3 09:08:37 2009
@@ -38,9 +38,7 @@
 import java.nio.channels.FileChannel;
 import java.text.SimpleDateFormat;
 import java.util.*;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
+import java.util.concurrent.*;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -87,6 +85,8 @@
 
   private volatile FileFetcher fileFetcher;
 
+  private volatile ExecutorService fsyncService;
+
   private volatile boolean stop = false;
 
   private boolean useInternal = false;
@@ -247,6 +247,8 @@
       fetchFileList(latestVersion);
       LOG.info("Number of files in latest snapshot in master: " + 
filesToDownload.size());
 
+      // Create the sync service
+      fsyncService = Executors.newSingleThreadExecutor();
       // use a synchronized list because the list is read by other threads (to 
show details)
       filesDownloaded = Collections.synchronizedList(new ArrayList<Map<String, 
Object>>());
       // if the generateion of master is older than that of the slave , it 
means they are not compatible to be copied
@@ -261,7 +263,7 @@
         downloadIndexFiles(isSnapNeeded, tmpIndexDir, latestVersion);
         LOG.info("Total time taken for download : " + 
((System.currentTimeMillis() - replicationStartTime) / 1000) + " secs");
         Collection<Map<String, Object>> modifiedConfFiles = 
getModifiedConfFiles(confFilesToDownload);
-        if (modifiedConfFiles != null && !modifiedConfFiles.isEmpty()) {
+        if (!modifiedConfFiles.isEmpty()) {
           downloadConfFiles(confFilesToDownload, latestVersion);
           if (isSnapNeeded) {
             modifyIndexProps(tmpIndexDir.getName());
@@ -274,6 +276,7 @@
             reloadCore();
           }
         } else {
+          terminateAndWaitFsyncService();
           LOG.info("Conf files are not downloaded or are in sync");
           if (isSnapNeeded) {
             modifyIndexProps(tmpIndexDir.getName());
@@ -302,10 +305,29 @@
       filesToDownload = filesDownloaded = confFilesDownloaded = 
confFilesToDownload = null;
       replicationStartTime = 0;
       fileFetcher = null;
+      fsyncService = null;
       stop = false;
+      fsyncException = null;
     }
   }
 
+  private volatile Exception fsyncException;
+
+  /**
+   * terminate the fsync service and wait for all the tasks to complete. If it 
is already terminated
+   *
+   * @throws Exception
+   */
+  private void terminateAndWaitFsyncService() throws Exception {
+    if (fsyncService.isTerminated()) return;
+    fsyncService.shutdown();
+     // give a long wait say 1 hr
+    fsyncService.awaitTermination(3600, TimeUnit.SECONDS);
+    // if any fsync failed, throw that exception back
+    Exception fsyncExceptionCopy = fsyncException;
+    if (fsyncExceptionCopy != null) throw fsyncExceptionCopy;
+  }
+
   /**
    * Helper method to record the last replication's details so that we can 
show them on the statistics page across
    * restarts.
@@ -394,19 +416,26 @@
     LOG.info("Starting download of configuration files from master: " + 
confFilesToDownload);
     confFilesDownloaded = Collections.synchronizedList(new 
ArrayList<Map<String, Object>>());
     File tmpconfDir = new File(solrCore.getResourceLoader().getConfigDir(), 
"conf." + getDateAsStr(new Date()));
-    boolean status = tmpconfDir.mkdirs();
-    if (!status) {
-      throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
-              "Failed to create temporary config folder: " + 
tmpconfDir.getName());
-    }
-    for (Map<String, Object> file : confFilesToDownload) {
-      String saveAs = (String) (file.get(ALIAS) == null ? file.get(NAME) : 
file.get(ALIAS));
-      fileFetcher = new FileFetcher(tmpconfDir, file, saveAs, true, 
latestVersion);
-      currentFile = file;
-      fileFetcher.fetchFile();
-      confFilesDownloaded.add(new HashMap<String, Object>(file));
+    try {
+      boolean status = tmpconfDir.mkdirs();
+      if (!status) {
+        throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
+                "Failed to create temporary config folder: " + 
tmpconfDir.getName());
+      }
+      for (Map<String, Object> file : confFilesToDownload) {
+        String saveAs = (String) (file.get(ALIAS) == null ? file.get(NAME) : 
file.get(ALIAS));
+        fileFetcher = new FileFetcher(tmpconfDir, file, saveAs, true, 
latestVersion);
+        currentFile = file;
+        fileFetcher.fetchFile();
+        confFilesDownloaded.add(new HashMap<String, Object>(file));
+      }
+      // this is called before copying the files to the original conf dir
+      // so that if there is an exception avoid corrupting the original files.
+      terminateAndWaitFsyncService();
+      copyTmpConfFiles2Conf(tmpconfDir);
+    } finally {
+      delTree(tmpconfDir);
     }
-    copyTmpConfFiles2Conf(tmpconfDir);
   }
 
   /**
@@ -491,13 +520,11 @@
         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;
   }
@@ -507,27 +534,22 @@
    */
   private void copyTmpConfFiles2Conf(File tmpconfDir) throws IOException {
     File confDir = new File(solrCore.getResourceLoader().getConfigDir());
-    try {
-      for (File file : tmpconfDir.listFiles()) {
-        File oldFile = new File(confDir, file.getName());
-        if (oldFile.exists()) {
-          File backupFile = new File(confDir, oldFile.getName() + "." + 
getDateAsStr(new Date(oldFile.lastModified())));
-          boolean status = oldFile.renameTo(backupFile);
-          if (!status) {
-            throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
-                    "Unable to rename: " + oldFile + " to: " + backupFile);
-          }
-        }
-        boolean status = file.renameTo(oldFile);
-        if (status) {
-          FileUtils.sync(oldFile);
-        } else {
+    for (File file : tmpconfDir.listFiles()) {
+      File oldFile = new File(confDir, file.getName());
+      if (oldFile.exists()) {
+        File backupFile = new File(confDir, oldFile.getName() + "." + 
getDateAsStr(new Date(oldFile.lastModified())));
+        boolean status = oldFile.renameTo(backupFile);
+        if (!status) {
           throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
-                  "Unable to rename: " + file + " to: " + oldFile);
+                  "Unable to rename: " + oldFile + " to: " + backupFile);
         }
       }
-    } finally {
-      delTree(tmpconfDir);
+      boolean status = file.renameTo(oldFile);
+      if (status) {
+      } else {
+        throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
+                "Unable to rename: " + file + " to: " + oldFile);
+      }
     }
   }
 
@@ -786,6 +808,16 @@
         }
       } finally {
         cleanup();
+        //if cleanup suceeds . The file is downloaded fully. do an fsync
+        fsyncService.submit(new Runnable(){
+          public void run() {
+            try {
+              FileUtils.sync(file);
+            } catch (IOException e) {
+              fsyncException = e;
+            }
+          }
+        });
       }
     }
 


Reply via email to