mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-24 17:32:48 +00:00
WIP update
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2999 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -26,18 +26,29 @@ import java.nio.MappedByteBuffer;
|
|||||||
import java.nio.channels.FileChannel;
|
import java.nio.channels.FileChannel;
|
||||||
import java.nio.channels.FileLock;
|
import java.nio.channels.FileLock;
|
||||||
import java.nio.channels.FileChannel.MapMode;
|
import java.nio.channels.FileChannel.MapMode;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.EnumMap;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
import java.util.zip.CRC32;
|
import java.util.zip.CRC32;
|
||||||
|
|
||||||
|
import javax.swing.plaf.multi.MultiInternalFrameUI;
|
||||||
|
|
||||||
import org.alfresco.error.AlfrescoRuntimeException;
|
import org.alfresco.error.AlfrescoRuntimeException;
|
||||||
import org.alfresco.repo.search.impl.lucene.LuceneAnalyser;
|
import org.alfresco.repo.search.IndexerException;
|
||||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
import org.alfresco.repo.search.impl.lucene.ClosingIndexSearcher;
|
||||||
|
import org.alfresco.repo.search.impl.lucene.FilterIndexReaderByNodeRefs;
|
||||||
|
import org.alfresco.repo.search.impl.lucene.LuceneIndexer;
|
||||||
import org.alfresco.util.GUID;
|
import org.alfresco.util.GUID;
|
||||||
|
import org.apache.lucene.analysis.Analyzer;
|
||||||
|
import org.apache.lucene.analysis.standard.StandardAnalyzer;
|
||||||
import org.apache.lucene.index.IndexReader;
|
import org.apache.lucene.index.IndexReader;
|
||||||
import org.apache.lucene.index.IndexWriter;
|
import org.apache.lucene.index.IndexWriter;
|
||||||
|
import org.apache.lucene.index.MultiReader;
|
||||||
import org.apache.lucene.index.Term;
|
import org.apache.lucene.index.Term;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -62,6 +73,8 @@ import org.apache.lucene.index.Term;
|
|||||||
* Incomplete delete merging does not matter - the overlay would still exist and be treated as such. So a document may be deleted in the index as well as in the applied overlay. It
|
* Incomplete delete merging does not matter - the overlay would still exist and be treated as such. So a document may be deleted in the index as well as in the applied overlay. It
|
||||||
* is still correctly deleted.
|
* is still correctly deleted.
|
||||||
*
|
*
|
||||||
|
* NOTE: Public methods locak as required, the private methods assume that the appropriate locks have been obtained.
|
||||||
|
*
|
||||||
* TODO: Write element status into individual directories. This would be enough for recovery if both index files are lost or corrupted.
|
* TODO: Write element status into individual directories. This would be enough for recovery if both index files are lost or corrupted.
|
||||||
*
|
*
|
||||||
* TODO: Tidy up index status at start up or after some time. How long would you leave a merge to run?
|
* TODO: Tidy up index status at start up or after some time. How long would you leave a merge to run?
|
||||||
@@ -83,6 +96,11 @@ public class IndexInfo
|
|||||||
*/
|
*/
|
||||||
private static String INDEX_INFO_BACKUP = "IndexInfoBackup";
|
private static String INDEX_INFO_BACKUP = "IndexInfoBackup";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this index shared by more than one repository? We can make many lock optimisations if the index is not shared.
|
||||||
|
*/
|
||||||
|
private boolean indexIsShared;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The directory that holds the index
|
* The directory that holds the index
|
||||||
*/
|
*/
|
||||||
@@ -122,38 +140,50 @@ public class IndexInfo
|
|||||||
/**
|
/**
|
||||||
* Lock for the index entries
|
* Lock for the index entries
|
||||||
*/
|
*/
|
||||||
ReentrantReadWriteLock entriesReadWriteLock = new ReentrantReadWriteLock();
|
private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
|
||||||
|
|
||||||
/**
|
|
||||||
* Lock for switching over the main cached reader
|
|
||||||
*/
|
|
||||||
ReentrantReadWriteLock mainIndexReaderReadWriteLock = new ReentrantReadWriteLock();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read only index readers that also do reference counting.
|
* Read only index readers that also do reference counting.
|
||||||
*/
|
*/
|
||||||
private HashMap<String, IndexWriter> referenceCountingReadOnlyIndexReaders = new HashMap<String, IndexWriter>();
|
private HashMap<String, IndexReader> referenceCountingReadOnlyIndexReaders = new HashMap<String, IndexReader>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Index writers
|
* Main index reader
|
||||||
|
*/
|
||||||
|
private IndexReader mainIndexReader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Index writers for deltas
|
||||||
*/
|
*/
|
||||||
private HashMap<String, IndexWriter> indexWriters = new HashMap<String, IndexWriter>();
|
private HashMap<String, IndexWriter> indexWriters = new HashMap<String, IndexWriter>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Index Readers
|
* Index Readers for deltas
|
||||||
*/
|
*/
|
||||||
private HashMap<String, IndexReader> indexReaders = new HashMap<String, IndexReader>();
|
private HashMap<String, IndexReader> indexReaders = new HashMap<String, IndexReader>();
|
||||||
|
|
||||||
private DictionaryService dictionaryService;
|
/**
|
||||||
|
* Map of state transitions
|
||||||
|
*/
|
||||||
|
private EnumMap<TransactionStatus, Transition> transitions = new EnumMap<TransactionStatus, Transition>(
|
||||||
|
TransactionStatus.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct an index in the given directory.
|
||||||
|
*
|
||||||
|
* @param indexDirectory
|
||||||
|
*/
|
||||||
public IndexInfo(File indexDirectory)
|
public IndexInfo(File indexDirectory)
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
|
initialiseTransitions();
|
||||||
|
|
||||||
this.indexDirectory = indexDirectory;
|
this.indexDirectory = indexDirectory;
|
||||||
|
|
||||||
|
// Make sure the directory exists
|
||||||
if (!this.indexDirectory.exists())
|
if (!this.indexDirectory.exists())
|
||||||
{
|
{
|
||||||
if (this.indexDirectory.mkdirs())
|
if (!this.indexDirectory.mkdirs())
|
||||||
{
|
{
|
||||||
throw new AlfrescoRuntimeException("Failed to create index directory");
|
throw new AlfrescoRuntimeException("Failed to create index directory");
|
||||||
}
|
}
|
||||||
@@ -163,29 +193,33 @@ public class IndexInfo
|
|||||||
throw new AlfrescoRuntimeException("The index must be held in a directory");
|
throw new AlfrescoRuntimeException("The index must be held in a directory");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create the info files.
|
||||||
File indexInfoFile = new File(this.indexDirectory, INDEX_INFO);
|
File indexInfoFile = new File(this.indexDirectory, INDEX_INFO);
|
||||||
File indexInfoBackupFile = new File(this.indexDirectory, INDEX_INFO_BACKUP);
|
File indexInfoBackupFile = new File(this.indexDirectory, INDEX_INFO_BACKUP);
|
||||||
if (createFile(indexInfoFile) && createFile(indexInfoBackupFile))
|
if (createFile(indexInfoFile) && createFile(indexInfoBackupFile))
|
||||||
{
|
{
|
||||||
|
// a spanking new index
|
||||||
version = 0;
|
version = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Open the files and channels
|
||||||
this.indexInfoRAF = openFile(indexInfoFile);
|
this.indexInfoRAF = openFile(indexInfoFile);
|
||||||
this.indexInfoChannel = this.indexInfoRAF.getChannel();
|
this.indexInfoChannel = this.indexInfoRAF.getChannel();
|
||||||
|
|
||||||
this.indexInfoBackupRAF = openFile(indexInfoBackupFile);
|
this.indexInfoBackupRAF = openFile(indexInfoBackupFile);
|
||||||
this.indexInfoBackupChannel = this.indexInfoBackupRAF.getChannel();
|
this.indexInfoBackupChannel = this.indexInfoBackupRAF.getChannel();
|
||||||
|
|
||||||
|
// Read info from disk if this is not a new index.
|
||||||
if (version == -1)
|
if (version == -1)
|
||||||
{
|
{
|
||||||
entriesReadWriteLock.writeLock().lock();
|
readWriteLock.writeLock().lock();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
doWithFileLock(new LockWork<Object>()
|
doWithFileLock(new LockWork<Object>()
|
||||||
{
|
{
|
||||||
public Object doWork() throws Exception
|
public Object doWork() throws Exception
|
||||||
{
|
{
|
||||||
checkStatus();
|
setStatusFromFile();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,21 +227,11 @@ public class IndexInfo
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
entriesReadWriteLock.writeLock().unlock();
|
readWriteLock.writeLock().unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDictionaryService(DictionaryService dictionaryService)
|
|
||||||
{
|
|
||||||
this.dictionaryService = dictionaryService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DictionaryService getDictionaryService()
|
|
||||||
{
|
|
||||||
return dictionaryService;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method should only be called from one thread.
|
* This method should only be called from one thread.
|
||||||
*
|
*
|
||||||
@@ -229,39 +253,53 @@ public class IndexInfo
|
|||||||
return reader;
|
return reader;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The delta information does not need to be saved to disk.
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* @return
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
private File ensureDeltaExistsAndIsRegistered(String id) throws IOException
|
private File ensureDeltaExistsAndIsRegistered(String id) throws IOException
|
||||||
{
|
{
|
||||||
File location = new File(indexDirectory, id);
|
File location = new File(indexDirectory, id);
|
||||||
if (!IndexReader.indexExists(location))
|
if (!IndexReader.indexExists(location))
|
||||||
{
|
{
|
||||||
IndexWriter creator = new IndexWriter(location, new LuceneAnalyser(dictionaryService), true);
|
IndexWriter creator = new IndexWriter(location, new StandardAnalyzer(), true);
|
||||||
creator.setUseCompoundFile(true);
|
creator.setUseCompoundFile(true);
|
||||||
creator.close();
|
creator.close();
|
||||||
}
|
}
|
||||||
entriesReadWriteLock.readLock().lock();
|
readWriteLock.readLock().lock();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!indexEntries.containsKey(id))
|
if (!indexEntries.containsKey(id))
|
||||||
{
|
{
|
||||||
entriesReadWriteLock.writeLock().lock();
|
readWriteLock.readLock().unlock();
|
||||||
|
// release to upgrade to write lock
|
||||||
|
readWriteLock.writeLock().lock();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
indexEntries.put(id, new IndexEntry(IndexType.DELTA, id, "", TransactionStatus.ACTIVE, ""));
|
if (!indexEntries.containsKey(id))
|
||||||
|
{
|
||||||
|
indexEntries.put(id, new IndexEntry(IndexType.DELTA, id, "", TransactionStatus.ACTIVE, ""));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
entriesReadWriteLock.writeLock().unlock();
|
// Downgrade
|
||||||
|
readWriteLock.readLock().lock();
|
||||||
|
readWriteLock.writeLock().unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
entriesReadWriteLock.readLock().unlock();
|
readWriteLock.readLock().unlock();
|
||||||
}
|
}
|
||||||
return location;
|
return location;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IndexWriter getDeltaIndexWriter(String id) throws IOException
|
public IndexWriter getDeltaIndexWriter(String id, Analyzer analyzer) throws IOException
|
||||||
{
|
{
|
||||||
IndexWriter writer = indexWriters.get(id);
|
IndexWriter writer = indexWriters.get(id);
|
||||||
if (writer == null)
|
if (writer == null)
|
||||||
@@ -269,7 +307,7 @@ public class IndexInfo
|
|||||||
// close index writer if required
|
// close index writer if required
|
||||||
closeDeltaIndexReader(id);
|
closeDeltaIndexReader(id);
|
||||||
File location = ensureDeltaExistsAndIsRegistered(id);
|
File location = ensureDeltaExistsAndIsRegistered(id);
|
||||||
writer = new IndexWriter(location, new LuceneAnalyser(dictionaryService), false);
|
writer = new IndexWriter(location, analyzer, false);
|
||||||
indexWriters.put(id, writer);
|
indexWriters.put(id, writer);
|
||||||
}
|
}
|
||||||
return writer;
|
return writer;
|
||||||
@@ -295,21 +333,299 @@ public class IndexInfo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IndexReader getMainIndexReferenceCountingReadOnlyIndexReader()
|
public IndexReader getMainIndexReferenceCountingReadOnlyIndexReader() throws IOException
|
||||||
{
|
{
|
||||||
return null;
|
readWriteLock.readLock().lock();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (mainIndexReader == null)
|
||||||
|
{
|
||||||
|
readWriteLock.readLock().unlock();
|
||||||
|
readWriteLock.writeLock().lock();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (mainIndexReader == null)
|
||||||
|
{
|
||||||
|
// Sync with disk image if required
|
||||||
|
doWithFileLock(new LockWork<Object>()
|
||||||
|
{
|
||||||
|
public Object doWork() throws Exception
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
mainIndexReader = createMainIndexReader();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
readWriteLock.readLock();
|
||||||
|
readWriteLock.writeLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mainIndexReader;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
readWriteLock.readLock().unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IndexReader getMainIndexReferenceCountingReadOnlyIndexReader(String id)
|
public IndexReader getMainIndexReferenceCountingReadOnlyIndexReader(LuceneIndexer luceneIndexer) throws IOException
|
||||||
{
|
{
|
||||||
return null;
|
readWriteLock.readLock().lock();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (mainIndexReader == null)
|
||||||
|
{
|
||||||
|
readWriteLock.readLock().unlock();
|
||||||
|
readWriteLock.writeLock().lock();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (mainIndexReader == null)
|
||||||
|
{
|
||||||
|
// Sync with disk image if required
|
||||||
|
doWithFileLock(new LockWork<Object>()
|
||||||
|
{
|
||||||
|
public Object doWork() throws Exception
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
mainIndexReader = createMainIndexReader();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
readWriteLock.readLock();
|
||||||
|
readWriteLock.writeLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Combine the index delta with the main index
|
||||||
|
// Make sure the index is written to disk
|
||||||
|
// TODO: Should use the in memory index but we often end up forcing to disk anyway.
|
||||||
|
// Is it worth it?
|
||||||
|
luceneIndexer.flushPending();
|
||||||
|
IndexReader deltaReader = getDeltaIndexReader(luceneIndexer.getDeltaId());
|
||||||
|
IndexReader reader = new MultiReader(new IndexReader[] {
|
||||||
|
new FilterIndexReaderByNodeRefs(mainIndexReader, luceneIndexer.getDeletions()), deltaReader });
|
||||||
|
return reader;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
readWriteLock.readLock().unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setStatus(String id, TransactionStatus status, Set<Term> toDelete)
|
public void setStatus(final String id, final TransactionStatus state, final Set<Term> toDelete, final Set<Term> read)
|
||||||
|
throws IOException
|
||||||
{
|
{
|
||||||
|
final Transition transition = getTransition(state);
|
||||||
|
readWriteLock.writeLock().lock();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
doWithFileLock(new LockWork<Object>()
|
||||||
|
{
|
||||||
|
public Object doWork() throws Exception
|
||||||
|
{
|
||||||
|
transition.transition(id, toDelete, read);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
readWriteLock.writeLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Internal support for status management
|
||||||
|
//
|
||||||
|
|
||||||
|
private Transition getTransition(TransactionStatus state)
|
||||||
|
{
|
||||||
|
Transition transition = transitions.get(state);
|
||||||
|
if (transition != null)
|
||||||
|
{
|
||||||
|
return transition;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new IndexerException("Invalid state " + state);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initialiseTransitions()
|
||||||
|
{
|
||||||
|
|
||||||
|
transitions.put(TransactionStatus.PREPARING, new PreparingTransition());
|
||||||
|
transitions.put(TransactionStatus.PREPARED, new PreparedTransition());
|
||||||
|
transitions.put(TransactionStatus.COMMITTING, new CommittingTransition());
|
||||||
|
transitions.put(TransactionStatus.COMMITTED, new CommittedTransition());
|
||||||
|
transitions.put(TransactionStatus.ROLLINGBACK, new RollingBackTransition());
|
||||||
|
transitions.put(TransactionStatus.ROLLEDBACK, new RolledBackTransition());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface Transition
|
||||||
|
{
|
||||||
|
void transition(String id, Set<Term> toDelete, Set<Term> read) throws IOException;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class PreparingTransition implements Transition
|
||||||
|
{
|
||||||
|
public void transition(String id, Set<Term> toDelete, Set<Term> read) throws IOException
|
||||||
|
{
|
||||||
|
IndexEntry entry = indexEntries.get(id);
|
||||||
|
if (entry == null)
|
||||||
|
{
|
||||||
|
throw new IndexerException("Unknown transaction " + id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TransactionStatus.PREPARING.follows(entry.getStatus()))
|
||||||
|
{
|
||||||
|
entry.setStatus(TransactionStatus.PREPARING);
|
||||||
|
writeStatus();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new IndexerException("Invalid transition for "
|
||||||
|
+ id + " from " + entry.getStatus() + " to " + TransactionStatus.PREPARING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class PreparedTransition implements Transition
|
||||||
|
{
|
||||||
|
public void transition(String id, Set<Term> toDelete, Set<Term> read) throws IOException
|
||||||
|
{
|
||||||
|
IndexEntry entry = indexEntries.get(id);
|
||||||
|
if (entry == null)
|
||||||
|
{
|
||||||
|
throw new IndexerException("Unknown transaction " + id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TransactionStatus.PREPARED.follows(entry.getStatus()))
|
||||||
|
{
|
||||||
|
entry.setStatus(TransactionStatus.PREPARED);
|
||||||
|
writeStatus();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new IndexerException("Invalid transition for "
|
||||||
|
+ id + " from " + entry.getStatus() + " to " + TransactionStatus.PREPARED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CommittingTransition implements Transition
|
||||||
|
{
|
||||||
|
public void transition(String id, Set<Term> toDelete, Set<Term> read) throws IOException
|
||||||
|
{
|
||||||
|
IndexEntry entry = indexEntries.get(id);
|
||||||
|
if (entry == null)
|
||||||
|
{
|
||||||
|
throw new IndexerException("Unknown transaction " + id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TransactionStatus.COMMITTING.follows(entry.getStatus()))
|
||||||
|
{
|
||||||
|
entry.setStatus(TransactionStatus.COMMITTING);
|
||||||
|
writeStatus();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new IndexerException("Invalid transition for "
|
||||||
|
+ id + " from " + entry.getStatus() + " to " + TransactionStatus.COMMITTING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CommittedTransition implements Transition
|
||||||
|
{
|
||||||
|
public void transition(String id, Set<Term> toDelete, Set<Term> read) throws IOException
|
||||||
|
{
|
||||||
|
IndexEntry entry = indexEntries.get(id);
|
||||||
|
if (entry == null)
|
||||||
|
{
|
||||||
|
throw new IndexerException("Unknown transaction " + id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TransactionStatus.COMMITTED.follows(entry.getStatus()))
|
||||||
|
{
|
||||||
|
// Do the deletions
|
||||||
|
|
||||||
|
entry.setStatus(TransactionStatus.COMMITTED);
|
||||||
|
writeStatus();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new IndexerException("Invalid transition for "
|
||||||
|
+ id + " from " + entry.getStatus() + " to " + TransactionStatus.COMMITTED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RollingBackTransition implements Transition
|
||||||
|
{
|
||||||
|
public void transition(String id, Set<Term> toDelete, Set<Term> read) throws IOException
|
||||||
|
{
|
||||||
|
IndexEntry entry = indexEntries.get(id);
|
||||||
|
if (entry == null)
|
||||||
|
{
|
||||||
|
throw new IndexerException("Unknown transaction " + id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TransactionStatus.ROLLINGBACK.follows(entry.getStatus()))
|
||||||
|
{
|
||||||
|
entry.setStatus(TransactionStatus.ROLLINGBACK);
|
||||||
|
writeStatus();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new IndexerException("Invalid transition for "
|
||||||
|
+ id + " from " + entry.getStatus() + " to " + TransactionStatus.ROLLINGBACK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RolledBackTransition implements Transition
|
||||||
|
{
|
||||||
|
public void transition(String id, Set<Term> toDelete, Set<Term> read) throws IOException
|
||||||
|
{
|
||||||
|
IndexEntry entry = indexEntries.get(id);
|
||||||
|
if (entry == null)
|
||||||
|
{
|
||||||
|
throw new IndexerException("Unknown transaction " + id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TransactionStatus.ROLLEDBACK.follows(entry.getStatus()))
|
||||||
|
{
|
||||||
|
entry.setStatus(TransactionStatus.ROLLEDBACK);
|
||||||
|
writeStatus();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new IndexerException("Invalid transition for "
|
||||||
|
+ id + " from " + entry.getStatus() + " to " + TransactionStatus.ROLLEDBACK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Internal methods for implementation support
|
||||||
|
// ===========================================
|
||||||
|
//
|
||||||
|
// These methods should all be called with the appropriate locks.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
private static boolean createFile(File file)
|
private static boolean createFile(File file)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -353,7 +669,7 @@ public class IndexInfo
|
|||||||
* @param raf
|
* @param raf
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
private void checkStatus() throws IOException
|
private void setStatusFromFile() throws IOException
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -362,9 +678,119 @@ public class IndexInfo
|
|||||||
catch (IOException e)
|
catch (IOException e)
|
||||||
{
|
{
|
||||||
// The first data file is corrupt so we fall back to the back up
|
// The first data file is corrupt so we fall back to the back up
|
||||||
System.out.println("BACKUP");
|
|
||||||
setStatusFromFile(indexInfoBackupChannel);
|
setStatusFromFile(indexInfoBackupChannel);
|
||||||
}
|
}
|
||||||
|
clearOldReaders();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearOldReaders() throws IOException
|
||||||
|
{
|
||||||
|
// Find valid
|
||||||
|
HashSet<String> valid = new HashSet<String>();
|
||||||
|
for (String id : indexEntries.keySet())
|
||||||
|
{
|
||||||
|
IndexEntry entry = indexEntries.get(id);
|
||||||
|
if (entry.getStatus().isCommitted())
|
||||||
|
{
|
||||||
|
valid.add(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Find current invalid
|
||||||
|
HashSet<String> inValid = new HashSet<String>();
|
||||||
|
for (String id : referenceCountingReadOnlyIndexReaders.keySet())
|
||||||
|
{
|
||||||
|
if (!valid.contains(id))
|
||||||
|
{
|
||||||
|
inValid.add(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Clear invalid
|
||||||
|
boolean hasInvalid = false;
|
||||||
|
for (String id : inValid)
|
||||||
|
{
|
||||||
|
IndexReader reader = referenceCountingReadOnlyIndexReaders.remove(id);
|
||||||
|
ReferenceCounting referenceCounting = (ReferenceCounting) reader;
|
||||||
|
referenceCounting.setInvalidForReuse();
|
||||||
|
hasInvalid = true;
|
||||||
|
}
|
||||||
|
if (hasInvalid)
|
||||||
|
{
|
||||||
|
mainIndexReader = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IndexReader createMainIndexReader() throws IOException
|
||||||
|
{
|
||||||
|
IndexReader reader = null;
|
||||||
|
for (String id : indexEntries.keySet())
|
||||||
|
{
|
||||||
|
IndexEntry entry = indexEntries.get(id);
|
||||||
|
if (entry.getStatus().isCommitted())
|
||||||
|
{
|
||||||
|
IndexReader subReader = getReferenceCountingIndexReader(id);
|
||||||
|
if (reader == null)
|
||||||
|
{
|
||||||
|
reader = subReader;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
reader = new MultiReader(new IndexReader[] { reader, subReader });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return reader;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IndexReader getReferenceCountingIndexReader(String id) throws IOException
|
||||||
|
{
|
||||||
|
IndexReader reader = referenceCountingReadOnlyIndexReaders.get(id);
|
||||||
|
if (reader == null)
|
||||||
|
{
|
||||||
|
File location = new File(indexDirectory, id);
|
||||||
|
reader = IndexReader.open(location);
|
||||||
|
reader = ReferenceCountingReadOnlyIndexReaderFactory.createReader(reader);
|
||||||
|
}
|
||||||
|
return reader;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkVersion() throws IOException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return checkVersion(indexInfoChannel);
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
// The first data file is corrupt so we fall back to the back up
|
||||||
|
return checkVersion(indexInfoBackupChannel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkVersion(FileChannel channel) throws IOException
|
||||||
|
{
|
||||||
|
if (channel.size() > 0)
|
||||||
|
{
|
||||||
|
channel.position(0);
|
||||||
|
ByteBuffer buffer;
|
||||||
|
|
||||||
|
if (useNIOMemoryMapping)
|
||||||
|
{
|
||||||
|
MappedByteBuffer mbb = channel.map(MapMode.READ_ONLY, 0, 8);
|
||||||
|
mbb.load();
|
||||||
|
buffer = mbb;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buffer = ByteBuffer.wrap(new byte[8]);
|
||||||
|
channel.read(buffer);
|
||||||
|
buffer.position(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.position(0);
|
||||||
|
long onDiskVersion = buffer.getLong();
|
||||||
|
return (version == onDiskVersion);
|
||||||
|
}
|
||||||
|
return (version == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setStatusFromFile(FileChannel channel) throws IOException
|
private void setStatusFromFile(FileChannel channel) throws IOException
|
||||||
@@ -551,18 +977,26 @@ public class IndexInfo
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface LockWork<Result>
|
private interface LockWork<Result>
|
||||||
{
|
{
|
||||||
public Result doWork() throws Exception;
|
public Result doWork() throws Exception;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <R> R doWithFileLock(LockWork<R> lockWork)
|
private <R> R doWithFileLock(LockWork<R> lockWork)
|
||||||
{
|
{
|
||||||
FileLock fileLock = null;
|
FileLock fileLock = null;
|
||||||
R result = null;
|
R result = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
fileLock = indexInfoChannel.lock();
|
if (indexIsShared)
|
||||||
|
{
|
||||||
|
fileLock = indexInfoChannel.lock();
|
||||||
|
if (!checkVersion())
|
||||||
|
{
|
||||||
|
setStatusFromFile();
|
||||||
|
clearOldReaders();
|
||||||
|
}
|
||||||
|
}
|
||||||
result = lockWork.doWork();
|
result = lockWork.doWork();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -594,54 +1028,37 @@ public class IndexInfo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args)
|
public static void main(String[] args) throws IOException
|
||||||
{
|
{
|
||||||
|
System.setProperty("disableLuceneLocks", "true");
|
||||||
|
|
||||||
int repeat = 100;
|
int repeat = 100;
|
||||||
final IndexInfo ii = new IndexInfo(new File("c:\\indexTest"));
|
final IndexInfo ii = new IndexInfo(new File("c:\\indexTest"));
|
||||||
ii.indexEntries.clear();
|
|
||||||
|
|
||||||
for (int i = 0; i < 100; i++)
|
long totalTimeA = 0;
|
||||||
{
|
long countA = 0;
|
||||||
String guid = GUID.generate();
|
|
||||||
ii.indexEntries.put(guid, new IndexEntry(IndexType.INDEX, guid, GUID.generate(),
|
|
||||||
TransactionStatus.COMMITTED, ""));
|
|
||||||
}
|
|
||||||
|
|
||||||
long totalTime = 0;
|
|
||||||
long count = 0;
|
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
long start = System.nanoTime();
|
long start = System.nanoTime();
|
||||||
|
ii.indexEntries.clear();
|
||||||
for (int i = 0; i < repeat; i++)
|
for (int i = 0; i < 100; i++)
|
||||||
{
|
{
|
||||||
ii.entriesReadWriteLock.writeLock().lock();
|
String guid = GUID.generate();
|
||||||
try
|
ii.indexEntries.put(guid, new IndexEntry(IndexType.DELTA, guid, GUID.generate(),
|
||||||
{
|
TransactionStatus.ACTIVE, ""));
|
||||||
ii.doWithFileLock(new LockWork<Object>()
|
ii.getDeltaIndexReader(guid);
|
||||||
{
|
ii.setStatus(guid, TransactionStatus.PREPARING, null, null);
|
||||||
public Object doWork() throws Exception
|
ii.setStatus(guid, TransactionStatus.PREPARED, null, null);
|
||||||
{
|
ii.setStatus(guid, TransactionStatus.COMMITTING, null, null);
|
||||||
|
ii.setStatus(guid, TransactionStatus.COMMITTED, null, null);
|
||||||
ii.version = 0;
|
|
||||||
ii.checkStatus();
|
|
||||||
ii.writeStatus();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
ii.entriesReadWriteLock.writeLock().unlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
long end = System.nanoTime();
|
long end = System.nanoTime();
|
||||||
|
|
||||||
totalTime += (end - start);
|
totalTimeA += (end - start);
|
||||||
count += repeat;
|
countA += repeat;
|
||||||
float average = count * 1000000000f / totalTime;
|
float average = countA * 1000000000f / totalTimeA;
|
||||||
|
|
||||||
System.out.println("Repeated "
|
System.out.println("Repeated "
|
||||||
+ repeat + " in " + ((end - start) / 1000000000.0) + " average = " + average);
|
+ repeat + " in " + ((end - start) / 1000000000.0) + " average = " + average);
|
||||||
|
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005 Alfresco, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Mozilla Public License version 1.1
|
||||||
|
* with a permitted attribution clause. You may obtain a
|
||||||
|
* copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.alfresco.org/legal/license.txt
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||||
|
* either express or implied. See the License for the specific
|
||||||
|
* language governing permissions and limitations under the
|
||||||
|
* License.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.search.impl.lucene.index;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public interface ReferenceCounting
|
||||||
|
{
|
||||||
|
public void incrementReferenceCount();
|
||||||
|
|
||||||
|
public void decrementReferenceCount() throws IOException;
|
||||||
|
|
||||||
|
public boolean isUsed();
|
||||||
|
|
||||||
|
public void setInvalidForReuse() throws IOException;
|
||||||
|
}
|
@@ -28,17 +28,6 @@ import org.springframework.aop.target.SingletonTargetSource;
|
|||||||
|
|
||||||
public class ReferenceCountingReadOnlyIndexReaderFactory
|
public class ReferenceCountingReadOnlyIndexReaderFactory
|
||||||
{
|
{
|
||||||
public interface RefCounting
|
|
||||||
{
|
|
||||||
public void incrementRefCount();
|
|
||||||
|
|
||||||
public void decrementRefCount() throws IOException;
|
|
||||||
|
|
||||||
public boolean isUsed();
|
|
||||||
|
|
||||||
public void setClosable() throws IOException;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IndexReader createReader(IndexReader indexReader)
|
public static IndexReader createReader(IndexReader indexReader)
|
||||||
{
|
{
|
||||||
AdvisorAdapterRegistry advisorAdapterRegistry = GlobalAdvisorAdapterRegistry.getInstance();
|
AdvisorAdapterRegistry advisorAdapterRegistry = GlobalAdvisorAdapterRegistry.getInstance();
|
||||||
@@ -49,7 +38,7 @@ public class ReferenceCountingReadOnlyIndexReaderFactory
|
|||||||
return proxy;
|
return proxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Interceptor extends DelegatingIntroductionInterceptor implements RefCounting
|
public static class Interceptor extends DelegatingIntroductionInterceptor implements ReferenceCounting
|
||||||
{
|
{
|
||||||
|
|
||||||
private static final long serialVersionUID = 7693185658022810428L;
|
private static final long serialVersionUID = 7693185658022810428L;
|
||||||
@@ -58,7 +47,7 @@ public class ReferenceCountingReadOnlyIndexReaderFactory
|
|||||||
|
|
||||||
int refCount = 0;
|
int refCount = 0;
|
||||||
|
|
||||||
boolean shouldClose = false;
|
boolean invalidForReuse = false;
|
||||||
|
|
||||||
Interceptor(IndexReader indexReader)
|
Interceptor(IndexReader indexReader)
|
||||||
{
|
{
|
||||||
@@ -76,7 +65,7 @@ public class ReferenceCountingReadOnlyIndexReaderFactory
|
|||||||
// Close
|
// Close
|
||||||
else if (methodName.equals("close"))
|
else if (methodName.equals("close"))
|
||||||
{
|
{
|
||||||
decrementRefCount();
|
decrementReferenceCount();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -85,12 +74,12 @@ public class ReferenceCountingReadOnlyIndexReaderFactory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void incrementRefCount()
|
public synchronized void incrementReferenceCount()
|
||||||
{
|
{
|
||||||
refCount++;
|
refCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void decrementRefCount() throws IOException
|
public synchronized void decrementReferenceCount() throws IOException
|
||||||
{
|
{
|
||||||
refCount--;
|
refCount--;
|
||||||
closeIfRequired();
|
closeIfRequired();
|
||||||
@@ -98,7 +87,7 @@ public class ReferenceCountingReadOnlyIndexReaderFactory
|
|||||||
|
|
||||||
private void closeIfRequired() throws IOException
|
private void closeIfRequired() throws IOException
|
||||||
{
|
{
|
||||||
if ((refCount == 0) && shouldClose)
|
if ((refCount == 0) && invalidForReuse)
|
||||||
{
|
{
|
||||||
indexReader.close();
|
indexReader.close();
|
||||||
}
|
}
|
||||||
@@ -109,9 +98,9 @@ public class ReferenceCountingReadOnlyIndexReaderFactory
|
|||||||
return (refCount > 0);
|
return (refCount > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void setClosable() throws IOException
|
public synchronized void setInvalidForReuse() throws IOException
|
||||||
{
|
{
|
||||||
shouldClose = true;
|
invalidForReuse = true;
|
||||||
closeIfRequired();
|
closeIfRequired();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,27 +1,20 @@
|
|||||||
package org.alfresco.repo.search.impl.lucene.index;
|
package org.alfresco.repo.search.impl.lucene.index;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Status of indexes that make up the whole index.
|
* Status of indexes that make up the whole index. This starts with the value from javax.transaction.Status.
|
||||||
* This starts with the value from javax.transaction.Status.
|
|
||||||
*
|
*
|
||||||
* Lifecycle
|
* Lifecycle ---------
|
||||||
* ---------
|
|
||||||
*
|
*
|
||||||
* As a transaction starts, the delta is ACTIVE
|
* As a transaction starts, the delta is ACTIVE It may be MARKED_ROLLBACK -> ROLLED BACK -> PREPARING -> PREPARED -> COMMITTING -> COMMITTED... with roll back at any time
|
||||||
* It may be MARKED_ROLLBACK -> ROLLED BACK
|
|
||||||
* -> PREPARING -> PREPARED -> COMMITTING -> COMMITTED...
|
|
||||||
* with roll back at any time
|
|
||||||
*
|
*
|
||||||
* If the index has any delayed indexing it commits to
|
* If the index has any delayed indexing it commits to COMMITTED_REQUIRES_REINDEX and then the overlay can go from -> COMMITTED_REINDEXING -> COMMITTED_REINDEXED
|
||||||
* COMMITTED_REQUIRES_REINDEX
|
|
||||||
* and then the overlay can go from -> COMMITTED_REINDEXING -> COMMITTED_REINDEXED
|
|
||||||
*
|
*
|
||||||
* If there was no reindexing required the delat commits as COMMITTED
|
* If there was no reindexing required the delat commits as COMMITTED
|
||||||
*
|
*
|
||||||
* A delta changes to an index overlay as it is committed.
|
* A delta changes to an index overlay as it is committed.
|
||||||
*
|
*
|
||||||
* For an overlay in COMMITTED or COMMITTED_REINDEXED it can have its delete list applied
|
* For an overlay in COMMITTED or COMMITTED_REINDEXED it can have its delete list applied to sub indexes. At this point it becomes a sub index.
|
||||||
* to sub indexes. At this point it becomes a sub index.
|
|
||||||
*
|
*
|
||||||
* @author Andy Hind
|
* @author Andy Hind
|
||||||
*/
|
*/
|
||||||
@@ -29,45 +22,229 @@ package org.alfresco.repo.search.impl.lucene.index;
|
|||||||
public enum TransactionStatus
|
public enum TransactionStatus
|
||||||
{
|
{
|
||||||
// Match the order in javax.transaction.Status so ordinal values are correct
|
// Match the order in javax.transaction.Status so ordinal values are correct
|
||||||
ACTIVE,
|
ACTIVE
|
||||||
MARKED_ROLLBACK,
|
{
|
||||||
PREPARED,
|
public boolean follows(TransactionStatus previous)
|
||||||
COMMITTED,
|
{
|
||||||
ROLLEDBACK,
|
return false;
|
||||||
UNKNOWN,
|
}
|
||||||
NO_TRANSACTION,
|
},
|
||||||
PREPARING,
|
|
||||||
COMMITTING,
|
MARKED_ROLLBACK
|
||||||
ROLLINGBACK,
|
{
|
||||||
|
public boolean follows(TransactionStatus previous)
|
||||||
|
{
|
||||||
|
return previous.allowsRollbackOrMark(previous);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
PREPARED
|
||||||
|
{
|
||||||
|
public boolean follows(TransactionStatus previous)
|
||||||
|
{
|
||||||
|
return previous == TransactionStatus.PREPARING;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
COMMITTED
|
||||||
|
{
|
||||||
|
public boolean isCommitted()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean follows(TransactionStatus previous)
|
||||||
|
{
|
||||||
|
return previous == TransactionStatus.COMMITTING;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
ROLLEDBACK
|
||||||
|
{
|
||||||
|
public boolean follows(TransactionStatus previous)
|
||||||
|
{
|
||||||
|
return previous == TransactionStatus.ROLLINGBACK;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
UNKNOWN
|
||||||
|
{
|
||||||
|
public boolean follows(TransactionStatus previous)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
NO_TRANSACTION
|
||||||
|
{
|
||||||
|
public boolean follows(TransactionStatus previous)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
PREPARING
|
||||||
|
{
|
||||||
|
public boolean follows(TransactionStatus previous)
|
||||||
|
{
|
||||||
|
return previous == TransactionStatus.ACTIVE;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
COMMITTING
|
||||||
|
{
|
||||||
|
public boolean follows(TransactionStatus previous)
|
||||||
|
{
|
||||||
|
return previous == TransactionStatus.PREPARED;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
ROLLINGBACK
|
||||||
|
{
|
||||||
|
public boolean follows(TransactionStatus previous)
|
||||||
|
{
|
||||||
|
return previous.allowsRollbackOrMark(previous);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This entry is the source for an active merge.
|
* This entry is the source for an active merge. The result will be in a new index.
|
||||||
* The result will be in a new index.
|
|
||||||
*/
|
*/
|
||||||
MERGING,
|
MERGE
|
||||||
|
{
|
||||||
|
public boolean isCommitted()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean follows(TransactionStatus previous)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A new index element that is being made by a merge.
|
* A new index element that is being made by a merge.
|
||||||
*/
|
*/
|
||||||
MERGING_TARGET,
|
MERGE_TARGET
|
||||||
|
{
|
||||||
|
public boolean follows(TransactionStatus previous)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These index overlays require reindexing
|
* These index overlays require reindexing
|
||||||
*/
|
*/
|
||||||
COMMITTED_REQUIRES_REINDEX,
|
COMMITTED_REQUIRES_REINDEX
|
||||||
|
{
|
||||||
|
public boolean isCommitted()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean follows(TransactionStatus previous)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These index overlays are reindexing
|
* These index overlays are reindexing
|
||||||
*/
|
*/
|
||||||
COMMITTED_REINDEXING,
|
COMMITTED_REINDEXING
|
||||||
|
{
|
||||||
|
public boolean isCommitted()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean follows(TransactionStatus previous)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These index overlays have ben reindexed.
|
* These index overlays have ben reindexed.
|
||||||
*/
|
*/
|
||||||
COMMITTED_REINDEXED,
|
COMMITTED_REINDEXED
|
||||||
|
{
|
||||||
|
public boolean isCommitted()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean follows(TransactionStatus previous)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Committed but the index still has deletions
|
||||||
|
*/
|
||||||
|
|
||||||
|
COMMITTED_WITH_DELETIONS
|
||||||
|
{
|
||||||
|
public boolean isCommitted()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean follows(TransactionStatus previous)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Pending deleted are being committed to for the delta.
|
||||||
|
*/
|
||||||
|
COMMITTED_DELETING
|
||||||
|
{
|
||||||
|
public boolean isCommitted()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean follows(TransactionStatus previous)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* An entry that may be deleted
|
* An entry that may be deleted
|
||||||
*/
|
*/
|
||||||
DELETABLE;
|
DELETABLE
|
||||||
|
{
|
||||||
|
public boolean follows(TransactionStatus previous)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public boolean isCommitted()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract boolean follows(TransactionStatus previous);
|
||||||
|
|
||||||
|
private boolean allowsRollbackOrMark(TransactionStatus previous)
|
||||||
|
{
|
||||||
|
switch (previous)
|
||||||
|
{
|
||||||
|
case ACTIVE:
|
||||||
|
case MARKED_ROLLBACK:
|
||||||
|
case PREPARED:
|
||||||
|
case PREPARING:
|
||||||
|
case COMMITTING:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
Reference in New Issue
Block a user