Save This Page
Home » lucene-2.4.1-src » org.apache » lucene » index » [javadoc | source]
    1   package org.apache.lucene.index;
    2   
    3   /**
    4    * Licensed to the Apache Software Foundation (ASF) under one or more
    5    * contributor license agreements.  See the NOTICE file distributed with
    6    * this work for additional information regarding copyright ownership.
    7    * The ASF licenses this file to You under the Apache License, Version 2.0
    8    * (the "License"); you may not use this file except in compliance with
    9    * the License.  You may obtain a copy of the License at
   10    *
   11    *     http://www.apache.org/licenses/LICENSE-2.0
   12    *
   13    * Unless required by applicable law or agreed to in writing, software
   14    * distributed under the License is distributed on an "AS IS" BASIS,
   15    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   16    * See the License for the specific language governing permissions and
   17    * limitations under the License.
   18    */
   19   
   20   import org.apache.lucene.store.Directory;
   21   import org.apache.lucene.store.IndexInput;
   22   import org.apache.lucene.store.IndexOutput;
   23   import org.apache.lucene.store.ChecksumIndexOutput;
   24   import org.apache.lucene.store.ChecksumIndexInput;
   25   
   26   import java.io.File;
   27   import java.io.FileNotFoundException;
   28   import java.io.IOException;
   29   import java.io.PrintStream;
   30   import java.util.Vector;
   31   
   32   final class SegmentInfos extends Vector {
   33   
   34     /** The file format version, a negative number. */
   35     /* Works since counter, the old 1st entry, is always >= 0 */
   36     public static final int FORMAT = -1;
   37   
   38     /** This format adds details used for lockless commits.  It differs
   39      * slightly from the previous format in that file names
   40      * are never re-used (write once).  Instead, each file is
   41      * written to the next generation.  For example,
   42      * segments_1, segments_2, etc.  This allows us to not use
   43      * a commit lock.  See <a
   44      * href="http://lucene.apache.org/java/docs/fileformats.html">file
   45      * formats</a> for details.
   46      */
   47     public static final int FORMAT_LOCKLESS = -2;
   48   
   49     /** This format adds a "hasSingleNormFile" flag into each segment info.
   50      * See <a href="http://issues.apache.org/jira/browse/LUCENE-756">LUCENE-756</a>
   51      * for details.
   52      */
   53     public static final int FORMAT_SINGLE_NORM_FILE = -3;
   54   
   55     /** This format allows multiple segments to share a single
   56      * vectors and stored fields file. */
   57     public static final int FORMAT_SHARED_DOC_STORE = -4;
   58   
   59     /** This format adds a checksum at the end of the file to
   60      *  ensure all bytes were successfully written. */
   61     public static final int FORMAT_CHECKSUM = -5;
   62   
   63     /** This format adds the deletion count for each segment.
   64      *  This way IndexWriter can efficiently report numDocs(). */
   65     public static final int FORMAT_DEL_COUNT = -6;
   66   
   67     /** This format adds the boolean hasProx to record if any
   68      *  fields in the segment store prox information (ie, have
   69      *  omitTf==false) */
   70     public static final int FORMAT_HAS_PROX = -7;
   71   
   72     /* This must always point to the most recent file format. */
   73     static final int CURRENT_FORMAT = FORMAT_HAS_PROX;
   74     
   75     public int counter = 0;    // used to name new segments
   76     /**
   77      * counts how often the index has been changed by adding or deleting docs.
   78      * starting with the current time in milliseconds forces to create unique version numbers.
   79      */
   80     private long version = System.currentTimeMillis();
   81   
   82     private long generation = 0;     // generation of the "segments_N" for the next commit
   83     private long lastGeneration = 0; // generation of the "segments_N" file we last successfully read
   84                                      // or wrote; this is normally the same as generation except if
   85                                      // there was an IOException that had interrupted a commit
   86   
   87     /**
   88      * If non-null, information about loading segments_N files
   89      * will be printed here.  @see #setInfoStream.
   90      */
   91     private static PrintStream infoStream;
   92   
   93     public final SegmentInfo info(int i) {
   94       return (SegmentInfo) get(i);
   95     }
   96   
   97     /**
   98      * Get the generation (N) of the current segments_N file
   99      * from a list of files.
  100      *
  101      * @param files -- array of file names to check
  102      */
  103     public static long getCurrentSegmentGeneration(String[] files) {
  104       if (files == null) {
  105         return -1;
  106       }
  107       long max = -1;
  108       for (int i = 0; i < files.length; i++) {
  109         String file = files[i];
  110         if (file.startsWith(IndexFileNames.SEGMENTS) && !file.equals(IndexFileNames.SEGMENTS_GEN)) {
  111           long gen = generationFromSegmentsFileName(file);
  112           if (gen > max) {
  113             max = gen;
  114           }
  115         }
  116       }
  117       return max;
  118     }
  119   
  120     /**
  121      * Get the generation (N) of the current segments_N file
  122      * in the directory.
  123      *
  124      * @param directory -- directory to search for the latest segments_N file
  125      */
  126     public static long getCurrentSegmentGeneration(Directory directory) throws IOException {
  127       String[] files = directory.list();
  128       if (files == null)
  129         throw new IOException("cannot read directory " + directory + ": list() returned null");
  130       return getCurrentSegmentGeneration(files);
  131     }
  132   
  133     /**
  134      * Get the filename of the current segments_N file
  135      * from a list of files.
  136      *
  137      * @param files -- array of file names to check
  138      */
  139   
  140     public static String getCurrentSegmentFileName(String[] files) throws IOException {
  141       return IndexFileNames.fileNameFromGeneration(IndexFileNames.SEGMENTS,
  142                                                    "",
  143                                                    getCurrentSegmentGeneration(files));
  144     }
  145   
  146     /**
  147      * Get the filename of the current segments_N file
  148      * in the directory.
  149      *
  150      * @param directory -- directory to search for the latest segments_N file
  151      */
  152     public static String getCurrentSegmentFileName(Directory directory) throws IOException {
  153       return IndexFileNames.fileNameFromGeneration(IndexFileNames.SEGMENTS,
  154                                                    "",
  155                                                    getCurrentSegmentGeneration(directory));
  156     }
  157   
  158     /**
  159      * Get the segments_N filename in use by this segment infos.
  160      */
  161     public String getCurrentSegmentFileName() {
  162       return IndexFileNames.fileNameFromGeneration(IndexFileNames.SEGMENTS,
  163                                                    "",
  164                                                    lastGeneration);
  165     }
  166   
  167     /**
  168      * Parse the generation off the segments file name and
  169      * return it.
  170      */
  171     public static long generationFromSegmentsFileName(String fileName) {
  172       if (fileName.equals(IndexFileNames.SEGMENTS)) {
  173         return 0;
  174       } else if (fileName.startsWith(IndexFileNames.SEGMENTS)) {
  175         return Long.parseLong(fileName.substring(1+IndexFileNames.SEGMENTS.length()),
  176                               Character.MAX_RADIX);
  177       } else {
  178         throw new IllegalArgumentException("fileName \"" + fileName + "\" is not a segments file");
  179       }
  180     }
  181   
  182   
  183     /**
  184      * Get the next segments_N filename that will be written.
  185      */
  186     public String getNextSegmentFileName() {
  187       long nextGeneration;
  188   
  189       if (generation == -1) {
  190         nextGeneration = 1;
  191       } else {
  192         nextGeneration = generation+1;
  193       }
  194       return IndexFileNames.fileNameFromGeneration(IndexFileNames.SEGMENTS,
  195                                                    "",
  196                                                    nextGeneration);
  197     }
  198   
  199     /**
  200      * Read a particular segmentFileName.  Note that this may
  201      * throw an IOException if a commit is in process.
  202      *
  203      * @param directory -- directory containing the segments file
  204      * @param segmentFileName -- segment file to load
  205      * @throws CorruptIndexException if the index is corrupt
  206      * @throws IOException if there is a low-level IO error
  207      */
  208     public final void read(Directory directory, String segmentFileName) throws CorruptIndexException, IOException {
  209       boolean success = false;
  210   
  211       // Clear any previous segments:
  212       clear();
  213   
  214       ChecksumIndexInput input = new ChecksumIndexInput(directory.openInput(segmentFileName));
  215   
  216       generation = generationFromSegmentsFileName(segmentFileName);
  217   
  218       lastGeneration = generation;
  219   
  220       try {
  221         int format = input.readInt();
  222         if(format < 0){     // file contains explicit format info
  223           // check that it is a format we can understand
  224           if (format < CURRENT_FORMAT)
  225             throw new CorruptIndexException("Unknown format version: " + format);
  226           version = input.readLong(); // read version
  227           counter = input.readInt(); // read counter
  228         }
  229         else{     // file is in old format without explicit format info
  230           counter = format;
  231         }
  232         
  233         for (int i = input.readInt(); i > 0; i--) { // read segmentInfos
  234           add(new SegmentInfo(directory, format, input));
  235         }
  236         
  237         if(format >= 0){    // in old format the version number may be at the end of the file
  238           if (input.getFilePointer() >= input.length())
  239             version = System.currentTimeMillis(); // old file format without version number
  240           else
  241             version = input.readLong(); // read version
  242         }
  243   
  244         if (format <= FORMAT_CHECKSUM) {
  245           final long checksumNow = input.getChecksum();
  246           final long checksumThen = input.readLong();
  247           if (checksumNow != checksumThen)
  248             throw new CorruptIndexException("checksum mismatch in segments file");
  249         }
  250         success = true;
  251       }
  252       finally {
  253         input.close();
  254         if (!success) {
  255           // Clear any segment infos we had loaded so we
  256           // have a clean slate on retry:
  257           clear();
  258         }
  259       }
  260     }
  261   
  262     /**
  263      * This version of read uses the retry logic (for lock-less
  264      * commits) to find the right segments file to load.
  265      * @throws CorruptIndexException if the index is corrupt
  266      * @throws IOException if there is a low-level IO error
  267      */
  268     public final void read(Directory directory) throws CorruptIndexException, IOException {
  269   
  270       generation = lastGeneration = -1;
  271   
  272       new FindSegmentsFile(directory) {
  273   
  274         protected Object doBody(String segmentFileName) throws CorruptIndexException, IOException {
  275           read(directory, segmentFileName);
  276           return null;
  277         }
  278       }.run();
  279     }
  280   
  281     // Only non-null after prepareCommit has been called and
  282     // before finishCommit is called
  283     ChecksumIndexOutput pendingOutput;
  284   
  285     private final void write(Directory directory) throws IOException {
  286   
  287       String segmentFileName = getNextSegmentFileName();
  288   
  289       // Always advance the generation on write:
  290       if (generation == -1) {
  291         generation = 1;
  292       } else {
  293         generation++;
  294       }
  295   
  296       ChecksumIndexOutput output = new ChecksumIndexOutput(directory.createOutput(segmentFileName));
  297   
  298       boolean success = false;
  299   
  300       try {
  301         output.writeInt(CURRENT_FORMAT); // write FORMAT
  302         output.writeLong(++version); // every write changes
  303                                      // the index
  304         output.writeInt(counter); // write counter
  305         output.writeInt(size()); // write infos
  306         for (int i = 0; i < size(); i++) {
  307           info(i).write(output);
  308         }
  309         output.prepareCommit();
  310         success = true;
  311         pendingOutput = output;
  312       } finally {
  313         if (!success) {
  314           // We hit an exception above; try to close the file
  315           // but suppress any exception:
  316           try {
  317             output.close();
  318           } catch (Throwable t) {
  319             // Suppress so we keep throwing the original exception
  320           }
  321           try {
  322             // Try not to leave a truncated segments_N file in
  323             // the index:
  324             directory.deleteFile(segmentFileName);
  325           } catch (Throwable t) {
  326             // Suppress so we keep throwing the original exception
  327           }
  328         }
  329       }
  330     }
  331   
  332     /**
  333      * Returns a copy of this instance, also copying each
  334      * SegmentInfo.
  335      */
  336     
  337     public Object clone() {
  338       SegmentInfos sis = (SegmentInfos) super.clone();
  339       for(int i=0;i<sis.size();i++) {
  340         sis.set(i, sis.info(i).clone());
  341       }
  342       return sis;
  343     }
  344   
  345     /**
  346      * version number when this SegmentInfos was generated.
  347      */
  348     public long getVersion() {
  349       return version;
  350     }
  351     public long getGeneration() {
  352       return generation;
  353     }
  354     public long getLastGeneration() {
  355       return lastGeneration;
  356     }
  357   
  358     /**
  359      * Current version number from segments file.
  360      * @throws CorruptIndexException if the index is corrupt
  361      * @throws IOException if there is a low-level IO error
  362      */
  363     public static long readCurrentVersion(Directory directory)
  364       throws CorruptIndexException, IOException {
  365   
  366       return ((Long) new FindSegmentsFile(directory) {
  367           protected Object doBody(String segmentFileName) throws CorruptIndexException, IOException {
  368   
  369             IndexInput input = directory.openInput(segmentFileName);
  370   
  371             int format = 0;
  372             long version = 0;
  373             try {
  374               format = input.readInt();
  375               if(format < 0){
  376                 if (format < CURRENT_FORMAT)
  377                   throw new CorruptIndexException("Unknown format version: " + format);
  378                 version = input.readLong(); // read version
  379               }
  380             }
  381             finally {
  382               input.close();
  383             }
  384        
  385             if(format < 0)
  386               return new Long(version);
  387   
  388             // We cannot be sure about the format of the file.
  389             // Therefore we have to read the whole file and cannot simply seek to the version entry.
  390             SegmentInfos sis = new SegmentInfos();
  391             sis.read(directory, segmentFileName);
  392             return new Long(sis.getVersion());
  393           }
  394         }.run()).longValue();
  395     }
  396   
  397     /** If non-null, information about retries when loading
  398      * the segments file will be printed to this.
  399      */
  400     public static void setInfoStream(PrintStream infoStream) {
  401       SegmentInfos.infoStream = infoStream;
  402     }
  403   
  404     /* Advanced configuration of retry logic in loading
  405        segments_N file */
  406     private static int defaultGenFileRetryCount = 10;
  407     private static int defaultGenFileRetryPauseMsec = 50;
  408     private static int defaultGenLookaheadCount = 10;
  409   
  410     /**
  411      * Advanced: set how many times to try loading the
  412      * segments.gen file contents to determine current segment
  413      * generation.  This file is only referenced when the
  414      * primary method (listing the directory) fails.
  415      */
  416     public static void setDefaultGenFileRetryCount(int count) {
  417       defaultGenFileRetryCount = count;
  418     }
  419   
  420     /**
  421      * @see #setDefaultGenFileRetryCount
  422      */
  423     public static int getDefaultGenFileRetryCount() {
  424       return defaultGenFileRetryCount;
  425     }
  426   
  427     /**
  428      * Advanced: set how many milliseconds to pause in between
  429      * attempts to load the segments.gen file.
  430      */
  431     public static void setDefaultGenFileRetryPauseMsec(int msec) {
  432       defaultGenFileRetryPauseMsec = msec;
  433     }
  434   
  435     /**
  436      * @see #setDefaultGenFileRetryPauseMsec
  437      */
  438     public static int getDefaultGenFileRetryPauseMsec() {
  439       return defaultGenFileRetryPauseMsec;
  440     }
  441   
  442     /**
  443      * Advanced: set how many times to try incrementing the
  444      * gen when loading the segments file.  This only runs if
  445      * the primary (listing directory) and secondary (opening
  446      * segments.gen file) methods fail to find the segments
  447      * file.
  448      */
  449     public static void setDefaultGenLookaheadCount(int count) {
  450       defaultGenLookaheadCount = count;
  451     }
  452     /**
  453      * @see #setDefaultGenLookaheadCount
  454      */
  455     public static int getDefaultGenLookahedCount() {
  456       return defaultGenLookaheadCount;
  457     }
  458   
  459     /**
  460      * @see #setInfoStream
  461      */
  462     public static PrintStream getInfoStream() {
  463       return infoStream;
  464     }
  465   
  466     private static void message(String message) {
  467       if (infoStream != null) {
  468         infoStream.println("SIS [" + Thread.currentThread().getName() + "]: " + message);
  469       }
  470     }
  471   
  472     /**
  473      * Utility class for executing code that needs to do
  474      * something with the current segments file.  This is
  475      * necessary with lock-less commits because from the time
  476      * you locate the current segments file name, until you
  477      * actually open it, read its contents, or check modified
  478      * time, etc., it could have been deleted due to a writer
  479      * commit finishing.
  480      */
  481     public abstract static class FindSegmentsFile {
  482       
  483       File fileDirectory;
  484       Directory directory;
  485   
  486       public FindSegmentsFile(File directory) {
  487         this.fileDirectory = directory;
  488       }
  489   
  490       public FindSegmentsFile(Directory directory) {
  491         this.directory = directory;
  492       }
  493   
  494       public Object run() throws CorruptIndexException, IOException {
  495         String segmentFileName = null;
  496         long lastGen = -1;
  497         long gen = 0;
  498         int genLookaheadCount = 0;
  499         IOException exc = null;
  500         boolean retry = false;
  501   
  502         int method = 0;
  503   
  504         // Loop until we succeed in calling doBody() without
  505         // hitting an IOException.  An IOException most likely
  506         // means a commit was in process and has finished, in
  507         // the time it took us to load the now-old infos files
  508         // (and segments files).  It's also possible it's a
  509         // true error (corrupt index).  To distinguish these,
  510         // on each retry we must see "forward progress" on
  511         // which generation we are trying to load.  If we
  512         // don't, then the original error is real and we throw
  513         // it.
  514         
  515         // We have three methods for determining the current
  516         // generation.  We try the first two in parallel, and
  517         // fall back to the third when necessary.
  518   
  519         while(true) {
  520   
  521           if (0 == method) {
  522   
  523             // Method 1: list the directory and use the highest
  524             // segments_N file.  This method works well as long
  525             // as there is no stale caching on the directory
  526             // contents (NOTE: NFS clients often have such stale
  527             // caching):
  528             String[] files = null;
  529   
  530             long genA = -1;
  531   
  532             if (directory != null)
  533               files = directory.list();
  534             else
  535               files = fileDirectory.list();
  536             
  537             if (files != null)
  538               genA = getCurrentSegmentGeneration(files);
  539   
  540             message("directory listing genA=" + genA);
  541   
  542             // Method 2: open segments.gen and read its
  543             // contents.  Then we take the larger of the two
  544             // gen's.  This way, if either approach is hitting
  545             // a stale cache (NFS) we have a better chance of
  546             // getting the right generation.
  547             long genB = -1;
  548             if (directory != null) {
  549               for(int i=0;i<defaultGenFileRetryCount;i++) {
  550                 IndexInput genInput = null;
  551                 try {
  552                   genInput = directory.openInput(IndexFileNames.SEGMENTS_GEN);
  553                 } catch (FileNotFoundException e) {
  554                   message("segments.gen open: FileNotFoundException " + e);
  555                   break;
  556                 } catch (IOException e) {
  557                   message("segments.gen open: IOException " + e);
  558                 }
  559   
  560                 if (genInput != null) {
  561                   try {
  562                     int version = genInput.readInt();
  563                     if (version == FORMAT_LOCKLESS) {
  564                       long gen0 = genInput.readLong();
  565                       long gen1 = genInput.readLong();
  566                       message("fallback check: " + gen0 + "; " + gen1);
  567                       if (gen0 == gen1) {
  568                         // The file is consistent.
  569                         genB = gen0;
  570                         break;
  571                       }
  572                     }
  573                   } catch (IOException err2) {
  574                     // will retry
  575                   } finally {
  576                     genInput.close();
  577                   }
  578                 }
  579                 try {
  580                   Thread.sleep(defaultGenFileRetryPauseMsec);
  581                 } catch (InterruptedException e) {
  582                   // will retry
  583                 }
  584               }
  585             }
  586   
  587             message(IndexFileNames.SEGMENTS_GEN + " check: genB=" + genB);
  588   
  589             // Pick the larger of the two gen's:
  590             if (genA > genB)
  591               gen = genA;
  592             else
  593               gen = genB;
  594             
  595             if (gen == -1) {
  596               // Neither approach found a generation
  597               String s;
  598               if (files != null) {
  599                 s = "";
  600                 for(int i=0;i<files.length;i++)
  601                   s += " " + files[i];
  602               } else
  603                 s = " null";
  604               throw new FileNotFoundException("no segments* file found in " + directory + ": files:" + s);
  605             }
  606           }
  607   
  608           // Third method (fallback if first & second methods
  609           // are not reliable): since both directory cache and
  610           // file contents cache seem to be stale, just
  611           // advance the generation.
  612           if (1 == method || (0 == method && lastGen == gen && retry)) {
  613   
  614             method = 1;
  615   
  616             if (genLookaheadCount < defaultGenLookaheadCount) {
  617               gen++;
  618               genLookaheadCount++;
  619               message("look ahead increment gen to " + gen);
  620             }
  621           }
  622   
  623           if (lastGen == gen) {
  624   
  625             // This means we're about to try the same
  626             // segments_N last tried.  This is allowed,
  627             // exactly once, because writer could have been in
  628             // the process of writing segments_N last time.
  629   
  630             if (retry) {
  631               // OK, we've tried the same segments_N file
  632               // twice in a row, so this must be a real
  633               // error.  We throw the original exception we
  634               // got.
  635               throw exc;
  636             } else {
  637               retry = true;
  638             }
  639   
  640           } else if (0 == method) {
  641             // Segment file has advanced since our last loop, so
  642             // reset retry:
  643             retry = false;
  644           }
  645   
  646           lastGen = gen;
  647   
  648           segmentFileName = IndexFileNames.fileNameFromGeneration(IndexFileNames.SEGMENTS,
  649                                                                   "",
  650                                                                   gen);
  651   
  652           try {
  653             Object v = doBody(segmentFileName);
  654             if (exc != null) {
  655               message("success on " + segmentFileName);
  656             }
  657             return v;
  658           } catch (IOException err) {
  659   
  660             // Save the original root cause:
  661             if (exc == null) {
  662               exc = err;
  663             }
  664   
  665             message("primary Exception on '" + segmentFileName + "': " + err + "'; will retry: retry=" + retry + "; gen = " + gen);
  666   
  667             if (!retry && gen > 1) {
  668   
  669               // This is our first time trying this segments
  670               // file (because retry is false), and, there is
  671               // possibly a segments_(N-1) (because gen > 1).
  672               // So, check if the segments_(N-1) exists and
  673               // try it if so:
  674               String prevSegmentFileName = IndexFileNames.fileNameFromGeneration(IndexFileNames.SEGMENTS,
  675                                                                                  "",
  676                                                                                  gen-1);
  677   
  678               final boolean prevExists;
  679               if (directory != null)
  680                 prevExists = directory.fileExists(prevSegmentFileName);
  681               else
  682                 prevExists = new File(fileDirectory, prevSegmentFileName).exists();
  683   
  684               if (prevExists) {
  685                 message("fallback to prior segment file '" + prevSegmentFileName + "'");
  686                 try {
  687                   Object v = doBody(prevSegmentFileName);
  688                   if (exc != null) {
  689                     message("success on fallback " + prevSegmentFileName);
  690                   }
  691                   return v;
  692                 } catch (IOException err2) {
  693                   message("secondary Exception on '" + prevSegmentFileName + "': " + err2 + "'; will retry");
  694                 }
  695               }
  696             }
  697           }
  698         }
  699       }
  700   
  701       /**
  702        * Subclass must implement this.  The assumption is an
  703        * IOException will be thrown if something goes wrong
  704        * during the processing that could have been caused by
  705        * a writer committing.
  706        */
  707       protected abstract Object doBody(String segmentFileName) throws CorruptIndexException, IOException;
  708     }
  709   
  710     /**
  711      * Returns a new SegmentInfos containg the SegmentInfo
  712      * instances in the specified range first (inclusive) to
  713      * last (exclusive), so total number of segments returned
  714      * is last-first.
  715      */
  716     public SegmentInfos range(int first, int last) {
  717       SegmentInfos infos = new SegmentInfos();
  718       infos.addAll(super.subList(first, last));
  719       return infos;
  720     }
  721   
  722     // Carry over generation numbers from another SegmentInfos
  723     void updateGeneration(SegmentInfos other) {
  724       lastGeneration = other.lastGeneration;
  725       generation = other.generation;
  726       version = other.version;
  727     }
  728   
  729     public final void rollbackCommit(Directory dir) throws IOException {
  730       if (pendingOutput != null) {
  731         try {
  732           pendingOutput.close();
  733         } catch (Throwable t) {
  734           // Suppress so we keep throwing the original exception
  735           // in our caller
  736         }
  737   
  738         // Must carefully compute fileName from "generation"
  739         // since lastGeneration isn't incremented:
  740         try {
  741           final String segmentFileName = IndexFileNames.fileNameFromGeneration(IndexFileNames.SEGMENTS,
  742                                                                                "",
  743                                                                                generation);
  744           dir.deleteFile(segmentFileName);
  745         } catch (Throwable t) {
  746           // Suppress so we keep throwing the original exception
  747           // in our caller
  748         }
  749         pendingOutput = null;
  750       }
  751     }
  752   
  753     /** Call this to start a commit.  This writes the new
  754      *  segments file, but writes an invalid checksum at the
  755      *  end, so that it is not visible to readers.  Once this
  756      *  is called you must call {@link #finishCommit} to complete
  757      *  the commit or {@link #rollbackCommit} to abort it. */
  758     public final void prepareCommit(Directory dir) throws IOException {
  759       if (pendingOutput != null)
  760         throw new IllegalStateException("prepareCommit was already called");
  761       write(dir);
  762     }
  763   
  764     public final void finishCommit(Directory dir) throws IOException {
  765       if (pendingOutput == null)
  766         throw new IllegalStateException("prepareCommit was not called");
  767       boolean success = false;
  768       try {
  769         pendingOutput.finishCommit();
  770         pendingOutput.close();
  771         pendingOutput = null;
  772         success = true;
  773       } finally {
  774         if (!success)
  775           rollbackCommit(dir);
  776       }
  777   
  778       // NOTE: if we crash here, we have left a segments_N
  779       // file in the directory in a possibly corrupt state (if
  780       // some bytes made it to stable storage and others
  781       // didn't).  But, the segments_N file includes checksum
  782       // at the end, which should catch this case.  So when a
  783       // reader tries to read it, it will throw a
  784       // CorruptIndexException, which should cause the retry
  785       // logic in SegmentInfos to kick in and load the last
  786       // good (previous) segments_N-1 file.
  787   
  788       final String fileName = IndexFileNames.fileNameFromGeneration(IndexFileNames.SEGMENTS,
  789                                                                     "",
  790                                                                     generation);
  791       success = false;
  792       try {
  793         dir.sync(fileName);
  794         success = true;
  795       } finally {
  796         if (!success) {
  797           try {
  798             dir.deleteFile(fileName);
  799           } catch (Throwable t) {
  800             // Suppress so we keep throwing the original exception
  801           }
  802         }
  803       }
  804   
  805       lastGeneration = generation;
  806   
  807       try {
  808         IndexOutput genOutput = dir.createOutput(IndexFileNames.SEGMENTS_GEN);
  809         try {
  810           genOutput.writeInt(FORMAT_LOCKLESS);
  811           genOutput.writeLong(generation);
  812           genOutput.writeLong(generation);
  813         } finally {
  814           genOutput.close();
  815         }
  816       } catch (Throwable t) {
  817         // It's OK if we fail to write this file since it's
  818         // used only as one of the retry fallbacks.
  819       }
  820     }
  821   
  822     /** Writes & syncs to the Directory dir, taking care to
  823      *  remove the segments file on exception */
  824     public final void commit(Directory dir) throws IOException {
  825       prepareCommit(dir);
  826       finishCommit(dir);
  827     }
  828   
  829     synchronized String segString(Directory directory) {
  830       StringBuffer buffer = new StringBuffer();
  831       final int count = size();
  832       for(int i = 0; i < count; i++) {
  833         if (i > 0) {
  834           buffer.append(' ');
  835         }
  836         final SegmentInfo info = info(i);
  837         buffer.append(info.segString(directory));
  838         if (info.dir != directory)
  839           buffer.append("**");
  840       }
  841       return buffer.toString();
  842     }
  843   }

Save This Page
Home » lucene-2.4.1-src » org.apache » lucene » index » [javadoc | source]