store the translog id in the commit point data of a lucene commit point. work done towards better concurrency on flush operation

This commit is contained in:
kimchy 2011-05-19 18:04:22 +03:00
parent 0d63fd68a8
commit 1911368feb
3 changed files with 59 additions and 29 deletions

View File

@ -26,8 +26,8 @@ import org.elasticsearch.ElasticSearchException;
import org.elasticsearch.common.Preconditions; import org.elasticsearch.common.Preconditions;
import org.elasticsearch.common.Unicode; import org.elasticsearch.common.Unicode;
import org.elasticsearch.common.bloom.BloomFilter; import org.elasticsearch.common.bloom.BloomFilter;
import org.elasticsearch.common.collect.MapBuilder;
import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.lucene.IndexWriters;
import org.elasticsearch.common.lucene.Lucene; import org.elasticsearch.common.lucene.Lucene;
import org.elasticsearch.common.lucene.ReaderSearcherHolder; import org.elasticsearch.common.lucene.ReaderSearcherHolder;
import org.elasticsearch.common.lucene.uid.UidField; import org.elasticsearch.common.lucene.uid.UidField;
@ -62,6 +62,7 @@ import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
@ -122,6 +123,10 @@ public class RobinEngine extends AbstractIndexShardComponent implements Engine {
private volatile boolean possibleMergeNeeded = false; private volatile boolean possibleMergeNeeded = false;
// we use flushNeeded here, since if there are no changes, then the commit won't write
// will not really happen, and then the commitUserData and the new translog will not be reflected
private volatile boolean flushNeeded = false;
private volatile int disableFlushCounter = 0; private volatile int disableFlushCounter = 0;
// indexing searcher is initialized // indexing searcher is initialized
@ -141,6 +146,8 @@ public class RobinEngine extends AbstractIndexShardComponent implements Engine {
private final Object failedEngineMutex = new Object(); private final Object failedEngineMutex = new Object();
private final CopyOnWriteArrayList<FailedEngineListener> failedEngineListeners = new CopyOnWriteArrayList<FailedEngineListener>(); private final CopyOnWriteArrayList<FailedEngineListener> failedEngineListeners = new CopyOnWriteArrayList<FailedEngineListener>();
private final AtomicLong translogIdGenerator = new AtomicLong();
@Inject public RobinEngine(ShardId shardId, @IndexSettings Settings indexSettings, ThreadPool threadPool, @Inject public RobinEngine(ShardId shardId, @IndexSettings Settings indexSettings, ThreadPool threadPool,
IndexSettingsService indexSettingsService, IndexSettingsService indexSettingsService,
Store store, SnapshotDeletionPolicy deletionPolicy, Translog translog, Store store, SnapshotDeletionPolicy deletionPolicy, Translog translog,
@ -227,11 +234,23 @@ public class RobinEngine extends AbstractIndexShardComponent implements Engine {
} }
try { try {
translog.newTranslog(newTransactionLogId()); if (IndexReader.indexExists(store.directory())) {
Map<String, String> commitUserData = IndexReader.getCommitUserData(store.directory());
if (commitUserData.containsKey(Translog.TRANSLOG_ID_KEY)) {
translogIdGenerator.set(Long.parseLong(commitUserData.get(Translog.TRANSLOG_ID_KEY)));
} else {
translogIdGenerator.set(System.currentTimeMillis());
indexWriter.commit(MapBuilder.<String, String>newMapBuilder().put(Translog.TRANSLOG_ID_KEY, Long.toString(translogIdGenerator.get())).map());
}
} else {
translogIdGenerator.set(System.currentTimeMillis());
indexWriter.commit(MapBuilder.<String, String>newMapBuilder().put(Translog.TRANSLOG_ID_KEY, Long.toString(translogIdGenerator.get())).map());
}
translog.newTranslog(translogIdGenerator.get());
this.nrtResource = buildNrtResource(indexWriter); this.nrtResource = buildNrtResource(indexWriter);
if (indexingSearcher.get() != null) { if (indexingSearcher.get() != null) {
indexingSearcher.get().release();
indexingSearcher.set(null); indexingSearcher.set(null);
indexingSearcher.get().release();
} }
} catch (IOException e) { } catch (IOException e) {
try { try {
@ -266,6 +285,7 @@ public class RobinEngine extends AbstractIndexShardComponent implements Engine {
innerCreate(create, writer); innerCreate(create, writer);
dirty = true; dirty = true;
possibleMergeNeeded = true; possibleMergeNeeded = true;
flushNeeded = true;
} catch (IOException e) { } catch (IOException e) {
throw new CreateFailedEngineException(shardId, create, e); throw new CreateFailedEngineException(shardId, create, e);
} catch (OutOfMemoryError e) { } catch (OutOfMemoryError e) {
@ -374,6 +394,7 @@ public class RobinEngine extends AbstractIndexShardComponent implements Engine {
innerIndex(index, writer); innerIndex(index, writer);
dirty = true; dirty = true;
possibleMergeNeeded = true; possibleMergeNeeded = true;
flushNeeded = true;
} catch (IOException e) { } catch (IOException e) {
throw new IndexFailedEngineException(shardId, index, e); throw new IndexFailedEngineException(shardId, index, e);
} catch (OutOfMemoryError e) { } catch (OutOfMemoryError e) {
@ -475,6 +496,7 @@ public class RobinEngine extends AbstractIndexShardComponent implements Engine {
innerDelete(delete, writer); innerDelete(delete, writer);
dirty = true; dirty = true;
possibleMergeNeeded = true; possibleMergeNeeded = true;
flushNeeded = true;
} catch (IOException e) { } catch (IOException e) {
throw new DeleteFailedEngineException(shardId, delete, e); throw new DeleteFailedEngineException(shardId, delete, e);
} catch (OutOfMemoryError e) { } catch (OutOfMemoryError e) {
@ -572,6 +594,7 @@ public class RobinEngine extends AbstractIndexShardComponent implements Engine {
translog.add(new Translog.DeleteByQuery(delete)); translog.add(new Translog.DeleteByQuery(delete));
dirty = true; dirty = true;
possibleMergeNeeded = true; possibleMergeNeeded = true;
flushNeeded = true;
} catch (IOException e) { } catch (IOException e) {
throw new DeleteByQueryFailedEngineException(shardId, delete, e); throw new DeleteByQueryFailedEngineException(shardId, delete, e);
} finally { } finally {
@ -681,10 +704,17 @@ public class RobinEngine extends AbstractIndexShardComponent implements Engine {
// to be allocated to a different node // to be allocated to a different node
indexWriter.close(); indexWriter.close();
indexWriter = createWriter(); indexWriter = createWriter();
if (flushNeeded) {
flushNeeded = false;
long translogId = translogIdGenerator.incrementAndGet();
indexWriter.commit(MapBuilder.<String, String>newMapBuilder().put(Translog.TRANSLOG_ID_KEY, Long.toString(translogId)).map());
translog.newTranslog(translogId);
}
AcquirableResource<ReaderSearcherHolder> current = nrtResource; AcquirableResource<ReaderSearcherHolder> current = nrtResource;
nrtResource = buildNrtResource(indexWriter); nrtResource = buildNrtResource(indexWriter);
current.markForClose(); current.markForClose();
translog.newTranslog(newTransactionLogId());
} catch (Exception e) { } catch (Exception e) {
throw new FlushFailedEngineException(shardId, e); throw new FlushFailedEngineException(shardId, e);
} catch (OutOfMemoryError e) { } catch (OutOfMemoryError e) {
@ -692,14 +722,18 @@ public class RobinEngine extends AbstractIndexShardComponent implements Engine {
throw new FlushFailedEngineException(shardId, e); throw new FlushFailedEngineException(shardId, e);
} }
} else { } else {
try { if (flushNeeded) {
indexWriter.commit(); flushNeeded = false;
translog.newTranslog(newTransactionLogId()); try {
} catch (Exception e) { long translogId = translogIdGenerator.incrementAndGet();
throw new FlushFailedEngineException(shardId, e); indexWriter.commit(MapBuilder.<String, String>newMapBuilder().put(Translog.TRANSLOG_ID_KEY, Long.toString(translogId)).map());
} catch (OutOfMemoryError e) { translog.newTranslog(translogId);
failEngine(e); } catch (Exception e) {
throw new FlushFailedEngineException(shardId, e); throw new FlushFailedEngineException(shardId, e);
} catch (OutOfMemoryError e) {
failEngine(e);
throw new FlushFailedEngineException(shardId, e);
}
} }
} }
} finally { } finally {
@ -1013,12 +1047,6 @@ public class RobinEngine extends AbstractIndexShardComponent implements Engine {
config.setMaxThreadStates(indexConcurrency); config.setMaxThreadStates(indexConcurrency);
indexWriter = new IndexWriter(store.directory(), config); indexWriter = new IndexWriter(store.directory(), config);
// we commit here on a fresh index since we want to have a commit point to support snapshotting
if (create) {
indexWriter.commit();
}
} catch (IOException e) { } catch (IOException e) {
safeClose(indexWriter); safeClose(indexWriter);
throw e; throw e;
@ -1070,14 +1098,6 @@ public class RobinEngine extends AbstractIndexShardComponent implements Engine {
return newAcquirableResource(new ReaderSearcherHolder(indexSearcher)); return newAcquirableResource(new ReaderSearcherHolder(indexSearcher));
} }
private long newTransactionLogId() throws IOException {
try {
return IndexWriters.rollbackSegmentInfos(indexWriter).getVersion();
} catch (Exception e) {
return IndexReader.getCurrentVersion(store.directory());
}
}
private static class RobinSearchResult implements Searcher { private static class RobinSearchResult implements Searcher {
private final AcquirableResource<ReaderSearcherHolder> nrtHolder; private final AcquirableResource<ReaderSearcherHolder> nrtHolder;

View File

@ -43,6 +43,7 @@ import java.io.EOFException;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
/** /**
@ -84,9 +85,16 @@ public class LocalIndexShardGateway extends AbstractIndexShardComponent implemen
@Override public void recover(RecoveryStatus recoveryStatus) throws IndexShardGatewayRecoveryException { @Override public void recover(RecoveryStatus recoveryStatus) throws IndexShardGatewayRecoveryException {
recoveryStatus.index().startTime(System.currentTimeMillis()); recoveryStatus.index().startTime(System.currentTimeMillis());
long version = -1; long version = -1;
long translogId = -1;
try { try {
if (IndexReader.indexExists(indexShard.store().directory())) { if (IndexReader.indexExists(indexShard.store().directory())) {
version = IndexReader.getCurrentVersion(indexShard.store().directory()); version = IndexReader.getCurrentVersion(indexShard.store().directory());
Map<String, String> commitUserData = IndexReader.getCommitUserData(indexShard.store().directory());
if (commitUserData.containsKey(Translog.TRANSLOG_ID_KEY)) {
translogId = Long.parseLong(commitUserData.get(Translog.TRANSLOG_ID_KEY));
} else {
translogId = version;
}
} }
} catch (IOException e) { } catch (IOException e) {
throw new IndexShardGatewayRecoveryException(shardId(), "Failed to fetch index version after copying it over", e); throw new IndexShardGatewayRecoveryException(shardId(), "Failed to fetch index version after copying it over", e);
@ -108,7 +116,7 @@ public class LocalIndexShardGateway extends AbstractIndexShardComponent implemen
} }
recoveryStatus.translog().startTime(System.currentTimeMillis()); recoveryStatus.translog().startTime(System.currentTimeMillis());
if (version == -1) { if (translogId == -1) {
// no translog files, bail // no translog files, bail
indexShard.start("post recovery from gateway, no translog"); indexShard.start("post recovery from gateway, no translog");
// no index, just start the shard and bail // no index, just start the shard and bail
@ -118,9 +126,9 @@ public class LocalIndexShardGateway extends AbstractIndexShardComponent implemen
// move an existing translog, if exists, to "recovering" state, and start reading from it // move an existing translog, if exists, to "recovering" state, and start reading from it
FsTranslog translog = (FsTranslog) indexShard.translog(); FsTranslog translog = (FsTranslog) indexShard.translog();
File recoveringTranslogFile = new File(translog.location(), "translog-" + version + ".recovering"); File recoveringTranslogFile = new File(translog.location(), "translog-" + translogId + ".recovering");
if (!recoveringTranslogFile.exists()) { if (!recoveringTranslogFile.exists()) {
File translogFile = new File(translog.location(), "translog-" + version); File translogFile = new File(translog.location(), "translog-" + translogId);
if (translogFile.exists()) { if (translogFile.exists()) {
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
if (translogFile.renameTo(recoveringTranslogFile)) { if (translogFile.renameTo(recoveringTranslogFile)) {

View File

@ -40,6 +40,8 @@ import java.io.InputStream;
@ThreadSafe @ThreadSafe
public interface Translog extends IndexShardComponent { public interface Translog extends IndexShardComponent {
public static final String TRANSLOG_ID_KEY = "translog_id";
/** /**
* Returns the id of the current transaction log. * Returns the id of the current transaction log.
*/ */