private void StartCommit(long sizeInBytes, IDictionary<string, string> commitUserData)
{
System.Diagnostics.Debug.Assert(TestPoint("startStartCommit"));
// TODO: as of LUCENE-2095, we can simplify this method,
// since only 1 thread can be in here at once
if (hitOOM)
{
throw new System.SystemException("this writer hit an OutOfMemoryError; cannot commit");
}
try
{
if (infoStream != null)
Message("startCommit(): start sizeInBytes=" + sizeInBytes);
SegmentInfos toSync = null;
long myChangeCount;
lock (this)
{
// Wait for any running addIndexes to complete
// first, then block any from running until we've
// copied the segmentInfos we intend to sync:
BlockAddIndexes(false);
// On commit the segmentInfos must never
// reference a segment in another directory:
System.Diagnostics.Debug.Assert(!HasExternalSegments());
try
{
System.Diagnostics.Debug.Assert(lastCommitChangeCount <= changeCount);
myChangeCount = changeCount;
if (changeCount == lastCommitChangeCount)
{
if (infoStream != null)
Message(" skip startCommit(): no changes pending");
return ;
}
// First, we clone & incref the segmentInfos we intend
// to sync, then, without locking, we sync() each file
// referenced by toSync, in the background. Multiple
// threads can be doing this at once, if say a large
// merge and a small merge finish at the same time:
if (infoStream != null)
Message("startCommit index=" + SegString(segmentInfos) + " changeCount=" + changeCount);
readerPool.Commit();
// It's possible another flush (that did not close
// the open do stores) snuck in after the flush we
// just did, so we remove any tail segments
// referencing the open doc store from the
// SegmentInfos we are about to sync (the main
// SegmentInfos will keep them):
toSync = (SegmentInfos) segmentInfos.Clone();
string dss = docWriter.DocStoreSegment;
if (dss != null)
{
while (true)
{
String dss2 = toSync.Info(toSync.Count - 1).DocStoreSegment;
if (dss2 == null || !dss2.Equals(dss))
{
break;
}
toSync.RemoveAt(toSync.Count - 1);
changeCount++;
}
}
if (commitUserData != null)
toSync.UserData = commitUserData;
deleter.IncRef(toSync, false);
ICollection<string> files = toSync.Files(directory, false);
foreach(string fileName in files)
{
System.Diagnostics.Debug.Assert(directory.FileExists(fileName), "file " + fileName + " does not exist");
// If this trips it means we are missing a call to
// .checkpoint somewhere, because by the time we
// are called, deleter should know about every
// file referenced by the current head
// segmentInfos:
System.Diagnostics.Debug.Assert(deleter.Exists(fileName));
}
}
finally
{
ResumeAddIndexes();
}
}
System.Diagnostics.Debug.Assert(TestPoint("midStartCommit"));
bool setPending = false;
try
{
// Loop until all files toSync references are sync'd:
while (true)
{
ICollection<string> pending = new List<string>();
IEnumerator<string> it = toSync.Files(directory, false).GetEnumerator();
while (it.MoveNext())
{
string fileName = it.Current;
if (StartSync(fileName, pending))
{
bool success = false;
try
{
// Because we incRef'd this commit point, above,
// the file had better exist:
System.Diagnostics.Debug.Assert(directory.FileExists(fileName), "file '" + fileName + "' does not exist dir=" + directory);
if (infoStream != null)
Message("now sync " + fileName);
directory.Sync(fileName);
success = true;
}
finally
{
FinishSync(fileName, success);
}
}
}
// All files that I require are either synced or being
// synced by other threads. If they are being synced,
// we must at this point block until they are done.
// If this returns false, that means an error in
// another thread resulted in failing to actually
// sync one of our files, so we repeat:
if (WaitForAllSynced(pending))
break;
}
System.Diagnostics.Debug.Assert(TestPoint("midStartCommit2"));
lock (this)
{
// If someone saved a newer version of segments file
// since I first started syncing my version, I can
// safely skip saving myself since I've been
// superseded:
while (true)
{
if (myChangeCount <= lastCommitChangeCount)
{
if (infoStream != null)
{
Message("sync superseded by newer infos");
}
break;
}
else if (pendingCommit == null)
{
// My turn to commit
if (segmentInfos.Generation > toSync.Generation)
toSync.UpdateGeneration(segmentInfos);
bool success = false;
try
{
// Exception here means nothing is prepared
// (this method unwinds everything it did on
// an exception)
try
{
toSync.PrepareCommit(directory);
}
finally
{
// Have our master segmentInfos record the
// generations we just prepared. We do this
// on error or success so we don't
// double-write a segments_N file.
segmentInfos.UpdateGeneration(toSync);
}
System.Diagnostics.Debug.Assert(pendingCommit == null);
setPending = true;
pendingCommit = toSync;
pendingCommitChangeCount = (uint) myChangeCount;
success = true;
}
finally
{
if (!success && infoStream != null)
Message("hit exception committing segments file");
}
break;
}
else
{
// Must wait for other commit to complete
DoWait();
}
}
}
if (infoStream != null)
Message("done all syncs");
System.Diagnostics.Debug.Assert(TestPoint("midStartCommitSuccess"));
}
finally
{
lock (this)
{
if (!setPending)
deleter.DecRef(toSync);
}
}
}
catch (System.OutOfMemoryException oom)
{
HandleOOM(oom, "startCommit");
}
System.Diagnostics.Debug.Assert(TestPoint("finishStartCommit"));
}