27
27
public sealed class SegmentInfos : System.Collections.ArrayList
29
private class AnonymousClassFindSegmentsFile:FindSegmentsFile
31
private void InitBlock(SegmentInfos enclosingInstance)
33
this.enclosingInstance = enclosingInstance;
35
private SegmentInfos enclosingInstance;
36
public SegmentInfos Enclosing_Instance
40
return enclosingInstance;
44
internal AnonymousClassFindSegmentsFile(SegmentInfos enclosingInstance, Lucene.Net.Store.Directory Param1):base(Param1)
46
InitBlock(enclosingInstance);
49
public override System.Object DoBody(System.String segmentFileName)
51
Enclosing_Instance.Read(directory, segmentFileName);
55
private class AnonymousClassFindSegmentsFile1:FindSegmentsFile
57
internal AnonymousClassFindSegmentsFile1(Lucene.Net.Store.Directory Param1):base(Param1)
60
public override System.Object DoBody(System.String segmentFileName)
63
IndexInput input = directory.OpenInput(segmentFileName);
69
format = input.ReadInt();
72
if (format < Lucene.Net.Index.SegmentInfos.FORMAT_SINGLE_NORM_FILE)
73
throw new System.IO.IOException("Unknown format version: " + format);
74
version = input.ReadLong(); // read version
83
return (long) version;
85
// We cannot be sure about the format of the file.
86
// Therefore we have to read the whole file and cannot simply seek to the version entry.
87
SegmentInfos sis = new SegmentInfos();
88
sis.Read(directory, segmentFileName);
89
return (long) sis.GetVersion();
30
93
/// <summary>The file format version, a negative number. </summary>
31
94
/* Works since counter, the old 1st entry, is always >= 0 */
32
95
public const int FORMAT = - 1;
97
/// <summary>This format adds details used for lockless commits. It differs
98
/// slightly from the previous format in that file names
99
/// are never re-used (write once). Instead, each file is
100
/// written to the next generation. For example,
101
/// segments_1, segments_2, etc. This allows us to not use
102
/// a commit lock. See <a
103
/// href="http://lucene.apache.org/java/docs/fileformats.html">file
104
/// formats</a> for details.
106
public const int FORMAT_LOCKLESS = - 2;
108
/// <summary>This is the current file format written. It adds a
109
/// "hasSingleNormFile" flag into each segment info.
110
/// See <a href="http://issues.apache.org/jira/browse/LUCENE-756">LUCENE-756</a>
113
public const int FORMAT_SINGLE_NORM_FILE = - 3;
34
115
public int counter = 0; // used to name new segments
35
116
/// <summary> counts how often the index has been changed by adding or deleting docs.
36
117
/// starting with the current time in milliseconds forces to create unique version numbers.
38
private long version = System.DateTime.Now.Ticks;
119
private long version = System.DateTime.Now.Millisecond;
121
private long generation = 0; // generation of the "segments_N" for the next commit
122
private long lastGeneration = 0; // generation of the "segments_N" file we last successfully read
123
// or wrote; this is normally the same as generation except if
124
// there was an IOException that had interrupted a commit
126
/// <summary> If non-null, information about loading segments_N files</summary>
127
/// <seealso cref="#setInfoStream.">
129
private static System.IO.TextWriter infoStream;
40
131
public SegmentInfo Info(int i)
42
133
return (SegmentInfo) this[i];
45
public void Read(Directory directory)
48
IndexInput input = directory.OpenInput(IndexFileNames.SEGMENTS);
136
/// <summary> Get the generation (N) of the current segments_N file
137
/// from a list of files.
140
/// <param name="files">-- array of file names to check
142
public static long GetCurrentSegmentGeneration(System.String[] files)
149
int prefixLen = IndexFileNames.SEGMENTS.Length + 1;
150
for (int i = 0; i < files.Length; i++)
152
System.String file = System.IO.Path.GetFileName(files[i]);
153
if (file.StartsWith(IndexFileNames.SEGMENTS) && !file.Equals(IndexFileNames.SEGMENTS_GEN))
155
if (file.Equals(IndexFileNames.SEGMENTS))
157
// Pre lock-less commits:
165
#if !PRE_LUCENE_NET_2_0_0_COMPATIBLE
166
long v = Lucene.Net.Documents.NumberTools.ToLong(file.Substring(prefixLen));
168
long v = System.Convert.ToInt64(file.Substring(prefixLen), 16);
180
/// <summary> Get the generation (N) of the current segments_N file
181
/// in the directory.
184
/// <param name="directory">-- directory to search for the latest segments_N file
186
public static long GetCurrentSegmentGeneration(Directory directory)
188
System.String[] files = directory.List();
191
throw new System.IO.IOException("Cannot read directory " + directory);
193
return GetCurrentSegmentGeneration(files);
196
/// <summary> Get the filename of the current segments_N file
197
/// from a list of files.
200
/// <param name="files">-- array of file names to check
203
public static System.String GetCurrentSegmentFileName(System.String[] files)
205
return IndexFileNames.FileNameFromGeneration(IndexFileNames.SEGMENTS, "", GetCurrentSegmentGeneration(files));
208
/// <summary> Get the filename of the current segments_N file
209
/// in the directory.
212
/// <param name="directory">-- directory to search for the latest segments_N file
214
public static System.String GetCurrentSegmentFileName(Directory directory)
216
return IndexFileNames.FileNameFromGeneration(IndexFileNames.SEGMENTS, "", GetCurrentSegmentGeneration(directory));
219
/// <summary> Get the segments_N filename in use by this segment infos.</summary>
220
public System.String GetCurrentSegmentFileName()
222
return IndexFileNames.FileNameFromGeneration(IndexFileNames.SEGMENTS, "", lastGeneration);
225
/// <summary> Get the next segments_N filename that will be written.</summary>
226
public System.String GetNextSegmentFileName()
230
if (generation == - 1)
236
nextGeneration = generation + 1;
238
return IndexFileNames.FileNameFromGeneration(IndexFileNames.SEGMENTS, "", nextGeneration);
241
/// <summary> Read a particular segmentFileName. Note that this may
242
/// throw an IOException if a commit is in process.
245
/// <param name="directory">-- directory containing the segments file
247
/// <param name="segmentFileName">-- segment file to load
249
public void Read(Directory directory, System.String segmentFileName)
251
bool success = false;
253
IndexInput input = directory.OpenInput(segmentFileName);
255
if (segmentFileName.Equals(IndexFileNames.SEGMENTS))
261
#if !PRE_LUCENE_NET_2_0_0_COMPATIBLE
262
generation = Lucene.Net.Documents.NumberTools.ToLong(segmentFileName.Substring(1 + IndexFileNames.SEGMENTS.Length));
264
generation = System.Convert.ToInt64(segmentFileName.Substring(1 + IndexFileNames.SEGMENTS.Length), 16);
267
lastGeneration = generation;
51
271
int format = input.ReadInt();
67
287
for (int i = input.ReadInt(); i > 0; i--)
69
289
// read segmentInfos
70
SegmentInfo si = new SegmentInfo(input.ReadString(), input.ReadInt(), directory);
290
Add(new SegmentInfo(directory, format, input));
76
295
// in old format the version number may be at the end of the file
77
296
if (input.GetFilePointer() >= input.Length())
78
version = (System.DateTime.Now.Ticks - 621355968000000000) / 10000;
297
version = System.DateTime.Now.Millisecond;
79
298
// old file format without version number
81
300
version = input.ReadLong(); // read version
309
// Clear any segment infos we had loaded so we
310
// have a clean slate on retry:
315
/// <summary> This version of read uses the retry logic (for lock-less
316
/// commits) to find the right segments file to load.
318
public void Read(Directory directory)
321
generation = lastGeneration = - 1;
323
new AnonymousClassFindSegmentsFile(this, directory).run();
90
326
public void Write(Directory directory)
92
IndexOutput output = directory.CreateOutput("segments.new");
329
System.String segmentFileName = GetNextSegmentFileName();
331
// Always advance the generation on write:
332
if (generation == - 1)
341
IndexOutput output = directory.CreateOutput(segmentFileName);
95
output.WriteInt(FORMAT); // write FORMAT
96
output.WriteLong(++version); // every write changes the index
345
output.WriteInt(FORMAT_SINGLE_NORM_FILE); // write FORMAT
346
output.WriteLong(++version); // every write changes
97
348
output.WriteInt(counter); // write counter
98
349
output.WriteInt(Count); // write infos
99
350
for (int i = 0; i < Count; i++)
101
SegmentInfo si = Info(i);
102
output.WriteString(si.name);
103
output.WriteInt(si.docCount);
352
Info(i).Write(output);
122
411
public static long ReadCurrentVersion(Directory directory)
125
IndexInput input = directory.OpenInput(IndexFileNames.SEGMENTS);
130
format = input.ReadInt();
414
return (long) ((System.Int64) new AnonymousClassFindSegmentsFile1(directory).run());
417
/// <summary>If non-null, information about retries when loading
418
/// the segments file will be printed to this.
420
public static void SetInfoStream(System.IO.TextWriter infoStream)
422
SegmentInfos.infoStream = infoStream;
425
/* Advanced configuration of retry logic in loading
427
private static int defaultGenFileRetryCount = 10;
428
private static int defaultGenFileRetryPauseMsec = 50;
429
private static int defaultGenLookaheadCount = 10;
431
/// <summary> Advanced: set how many times to try loading the
432
/// segments.gen file contents to determine current segment
433
/// generation. This file is only referenced when the
434
/// primary method (listing the directory) fails.
436
public static void SetDefaultGenFileRetryCount(int count)
438
defaultGenFileRetryCount = count;
441
/// <seealso cref="#setDefaultGenFileRetryCount">
443
public static int GetDefaultGenFileRetryCount()
445
return defaultGenFileRetryCount;
448
/// <summary> Advanced: set how many milliseconds to pause in between
449
/// attempts to load the segments.gen file.
451
public static void SetDefaultGenFileRetryPauseMsec(int msec)
453
defaultGenFileRetryPauseMsec = msec;
456
/// <seealso cref="#setDefaultGenFileRetryPauseMsec">
458
public static int GetDefaultGenFileRetryPauseMsec()
460
return defaultGenFileRetryPauseMsec;
463
/// <summary> Advanced: set how many times to try incrementing the
464
/// gen when loading the segments file. This only runs if
465
/// the primary (listing directory) and secondary (opening
466
/// segments.gen file) methods fail to find the segments
469
public static void SetDefaultGenLookaheadCount(int count)
471
defaultGenLookaheadCount = count;
473
/// <seealso cref="#setDefaultGenLookaheadCount">
475
public static int GetDefaultGenLookahedCount()
477
return defaultGenLookaheadCount;
480
/// <seealso cref="#setInfoStream">
482
public static System.IO.TextWriter GetInfoStream()
487
private static void Message(System.String message)
489
if (infoStream != null)
491
infoStream.WriteLine(SupportClass.ThreadClass.Current().Name + ": " + message);
495
/// <summary> Utility class for executing code that needs to do
496
/// something with the current segments file. This is
497
/// necessary with lock-less commits because from the time
498
/// you locate the current segments file name, until you
499
/// actually open it, read its contents, or check modified
500
/// time, etc., it could have been deleted due to a writer
501
/// commit finishing.
503
public abstract class FindSegmentsFile
506
internal System.IO.FileInfo fileDirectory;
507
internal Directory directory;
509
public FindSegmentsFile(System.IO.FileInfo directory)
511
this.fileDirectory = directory;
514
public FindSegmentsFile(Directory directory)
516
this.directory = directory;
519
public System.Object run()
521
System.String segmentFileName = null;
524
int genLookaheadCount = 0;
525
System.IO.IOException exc = null;
530
// Loop until we succeed in calling doBody() without
531
// hitting an IOException. An IOException most likely
532
// means a commit was in process and has finished, in
533
// the time it took us to load the now-old infos files
534
// (and segments files). It's also possible it's a
535
// true error (corrupt index). To distinguish these,
536
// on each retry we must see "forward progress" on
537
// which generation we are trying to load. If we
538
// don't, then the original error is real and we throw
541
// We have three methods for determining the current
542
// generation. We try each in sequence.
134
throw new System.IO.IOException("Unknown format version: " + format);
135
version = input.ReadLong(); // read version
547
// Method 1: list the directory and use the highest
548
// segments_N file. This method works well as long
549
// as there is no stale caching on the directory
551
System.String[] files = null;
555
if (directory != null)
557
files = directory.List();
561
files = System.IO.Directory.GetFileSystemEntries(fileDirectory.FullName);
562
for (int i = 0; i < files.Length; i++)
564
files[i] = System.IO.Path.GetFileName (files[i]);
568
gen = Lucene.Net.Index.SegmentInfos.GetCurrentSegmentGeneration(files);
572
System.String s = "";
573
for (int i = 0; i < files.Length; i++)
575
s += (" " + files[i]);
577
throw new System.IO.FileNotFoundException("no segments* file found: files:" + s);
581
// Method 2 (fallback if Method 1 isn't reliable):
582
// if the directory listing seems to be stale, then
583
// try loading the "segments.gen" file.
584
if (1 == method || (0 == method && lastGen == gen && retry))
589
for (int i = 0; i < Lucene.Net.Index.SegmentInfos.defaultGenFileRetryCount; i++)
591
IndexInput genInput = null;
594
genInput = directory.OpenInput(IndexFileNames.SEGMENTS_GEN);
596
catch (System.IO.IOException e)
598
Lucene.Net.Index.SegmentInfos.Message("segments.gen open: IOException " + e);
600
if (genInput != null)
605
int version = genInput.ReadInt();
606
if (version == Lucene.Net.Index.SegmentInfos.FORMAT_LOCKLESS)
608
long gen0 = genInput.ReadLong();
609
long gen1 = genInput.ReadLong();
610
Lucene.Net.Index.SegmentInfos.Message("fallback check: " + gen0 + "; " + gen1);
613
// The file is consistent.
616
Lucene.Net.Index.SegmentInfos.Message("fallback to '" + IndexFileNames.SEGMENTS_GEN + "' check: now try generation " + gen0 + " > " + gen);
623
catch (System.IO.IOException err2)
634
System.Threading.Thread.Sleep(new System.TimeSpan((System.Int64) 10000 * Lucene.Net.Index.SegmentInfos.defaultGenFileRetryPauseMsec));
636
catch (System.Threading.ThreadInterruptedException e)
643
// Method 3 (fallback if Methods 2 & 3 are not
644
// reliable): since both directory cache and file
645
// contents cache seem to be stale, just advance the
647
if (2 == method || (1 == method && lastGen == gen && retry))
652
if (genLookaheadCount < Lucene.Net.Index.SegmentInfos.defaultGenLookaheadCount)
656
Lucene.Net.Index.SegmentInfos.Message("look ahead increment gen to " + gen);
663
// This means we're about to try the same
664
// segments_N last tried. This is allowed,
665
// exactly once, because writer could have been in
666
// the process of writing segments_N last time.
670
// OK, we've tried the same segments_N file
671
// twice in a row, so this must be a real
672
// error. We throw the original exception we
683
// Segment file has advanced since our last loop, so
690
segmentFileName = IndexFileNames.FileNameFromGeneration(IndexFileNames.SEGMENTS, "", gen);
694
System.Object v = DoBody(segmentFileName);
697
Lucene.Net.Index.SegmentInfos.Message("success on " + segmentFileName);
701
catch (System.IO.IOException err)
704
// Save the original root cause:
710
Lucene.Net.Index.SegmentInfos.Message("primary Exception on '" + segmentFileName + "': " + err + "'; will retry: retry=" + retry + "; gen = " + gen);
712
if (!retry && gen > 1)
715
// This is our first time trying this segments
716
// file (because retry is false), and, there is
717
// possibly a segments_(N-1) (because gen > 1).
718
// So, check if the segments_(N-1) exists and
720
System.String prevSegmentFileName = IndexFileNames.FileNameFromGeneration(IndexFileNames.SEGMENTS, "", gen - 1);
722
if (directory.FileExists(prevSegmentFileName))
724
Lucene.Net.Index.SegmentInfos.Message("fallback to prior segment file '" + prevSegmentFileName + "'");
727
System.Object v = DoBody(prevSegmentFileName);
730
Lucene.Net.Index.SegmentInfos.Message("success on fallback " + prevSegmentFileName);
734
catch (System.IO.IOException err2)
736
Lucene.Net.Index.SegmentInfos.Message("secondary Exception on '" + prevSegmentFileName + "': " + err2 + "'; will retry");
146
// We cannot be sure about the format of the file.
147
// Therefore we have to read the whole file and cannot simply seek to the version entry.
149
SegmentInfos sis = new SegmentInfos();
151
return sis.GetVersion();
744
/// <summary> Subclass must implement this. The assumption is an
745
/// IOException will be thrown if something goes wrong
746
/// during the processing that could have been caused by
747
/// a writer committing.
749
public abstract System.Object DoBody(System.String segmentFileName);
154
752
public void Optimize(Directory directory)