From 602d8eb449ccb452c256c920e1ed5d64a9fc8a9d Mon Sep 17 00:00:00 2001 From: Alec Grieser Date: Wed, 9 Nov 2022 16:16:56 +0000 Subject: [PATCH] Resolves #1430: Support non-idempotent target indexes while indexing by index This adds support for building a non-idempotent target index when indexing from a different source index. This works in a manner that is analogous to the way that this operation works for non-idempotent indexes built by a record scan, except that as the range set contains ranges of index entries from the source index, the maintainer needs to be updated to: (1) check the indexing type stamp and (2) modify the range set check to use the index key instead of the primary key. Then there are some updates to the indexers to adjust logic that assumed the target index type would always be idempotent. This resolves #1430. --- docs/ReleaseNotes.md | 2 +- .../provider/foundationdb/FDBRecordStore.java | 49 ++++++++----- .../foundationdb/IndexMaintainer.java | 4 ++ .../provider/foundationdb/IndexingBase.java | 51 +++++-------- .../foundationdb/IndexingByIndex.java | 7 +- .../indexes/NoOpIndexMaintainer.java | 6 ++ .../indexes/StandardIndexMaintainer.java | 72 +++++++++++++++++++ .../OnlineIndexerIndexFromIndexTest.java | 6 +- .../foundationdb/TerribleIndexMaintainer.java | 6 ++ 9 files changed, 146 insertions(+), 57 deletions(-) diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index 5a6e05d370..0d8ae71715 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -27,7 +27,7 @@ The Guava dependency version has been updated to 31.1. Projects may need to chec * **Performance** Improvement 5 [(Issue #NNN)](https://github.com/FoundationDB/fdb-record-layer/issues/NNN) * **Feature** Feature 1 [(Issue #NNN)](https://github.com/FoundationDB/fdb-record-layer/issues/NNN) * **Feature** Feature 2 [(Issue #NNN)](https://github.com/FoundationDB/fdb-record-layer/issues/NNN) -* **Feature** Feature 3 [(Issue #NNN)](https://github.com/FoundationDB/fdb-record-layer/issues/NNN) +* **Feature** Non-idempotent target indexes can now be built from an existing index [(Issue #1430)](https://github.com/FoundationDB/fdb-record-layer/issues/1430) * **Feature** Feature 4 [(Issue #NNN)](https://github.com/FoundationDB/fdb-record-layer/issues/NNN) * **Feature** Feature 5 [(Issue #NNN)](https://github.com/FoundationDB/fdb-record-layer/issues/NNN) * **Feature** Support planning aggregate indexes in Cascades. [(Issue #1885)](https://github.com/FoundationDB/fdb-record-layer/issues/1885) diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/provider/foundationdb/FDBRecordStore.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/provider/foundationdb/FDBRecordStore.java index a9e2efa0f9..d9714422a4 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/provider/foundationdb/FDBRecordStore.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/provider/foundationdb/FDBRecordStore.java @@ -42,6 +42,7 @@ import com.apple.foundationdb.record.ExecuteProperties; import com.apple.foundationdb.record.ExecuteState; import com.apple.foundationdb.record.FunctionNames; +import com.apple.foundationdb.record.IndexBuildProto; import com.apple.foundationdb.record.IndexEntry; import com.apple.foundationdb.record.IndexScanType; import com.apple.foundationdb.record.IndexState; @@ -605,22 +606,12 @@ private void updateSecondaryIndexes(@Nullable final FDBIndex for (Index index : indexes) { final IndexMaintainer maintainer = getIndexMaintainer(index); final CompletableFuture future; - if (!maintainer.isIdempotent() && isIndexWriteOnly(index)) { - // In this case, the index is still being built, so we are not - // going to update the record unless the rebuild job has already - // gotten to this range. - final Tuple primaryKey = newRecord == null ? oldRecord.getPrimaryKey() : newRecord.getPrimaryKey(); - future = maintainer.addedRangeWithKey(primaryKey) - .thenCompose(present -> { - if (present) { - return maintainer.update(oldRecord, newRecord); - } else { - return AsyncUtil.DONE; - } - }); - if (!MoreAsyncUtil.isCompletedNormally(future)) { - futures.add(future); - } + if (isIndexWriteOnly(index)) { + // In this case, the index is still being built. For some index + // types, the index update needs to check whether indexing + // process has already built the relevant ranges, and it + // may adjust the way the index is built in response. + future = maintainer.updateWhileWriteOnly(oldRecord, newRecord); } else { future = maintainer.update(oldRecord, newRecord); } @@ -3499,6 +3490,32 @@ public CompletableFuture getIndexBuildStateAsync(Index index) { return IndexBuildState.loadIndexBuildStateAsync(this, index); } + @API(API.Status.INTERNAL) + @Nonnull + public CompletableFuture loadIndexBuildStampAsync(Index index) { + byte[] stampKey = IndexingBase.indexBuildTypeSubspace(this, index).pack(); + return ensureContextActive().get(stampKey).thenApply(serializedStamp -> { + if (serializedStamp == null) { + return null; + } + try { + return IndexBuildProto.IndexBuildIndexingStamp.parseFrom(serializedStamp); + } catch (InvalidProtocolBufferException ex) { + RecordCoreException protoEx = new RecordCoreException("invalid indexing type stamp", + LogMessageKeys.INDEX_NAME, index.getName(), + LogMessageKeys.ACTUAL, ByteArrayUtil2.loggable(serializedStamp)); + protoEx.initCause(ex); + throw protoEx; + } + }); + } + + @API(API.Status.INTERNAL) + public void saveIndexBuildStamp(Index index, IndexBuildProto.IndexBuildIndexingStamp stamp) { + byte[] stampKey = IndexingBase.indexBuildTypeSubspace(this, index).pack(); + ensureContextActive().set(stampKey, stamp.toByteArray()); + } + // Remove any indexes that do not match the filter. // NOTE: This assumes that the filter will not filter out any indexes if all indexes are readable. private List sanitizeIndexes(@Nonnull List indexes, @Nonnull Predicate filter) { diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/provider/foundationdb/IndexMaintainer.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/provider/foundationdb/IndexMaintainer.java index 9b6924e5d5..cd5e1139be 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/provider/foundationdb/IndexMaintainer.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/provider/foundationdb/IndexMaintainer.java @@ -121,6 +121,10 @@ public RecordCursor scan(@Nonnull IndexScanBounds scanBounds, public abstract CompletableFuture update(@Nullable FDBIndexableRecord oldRecord, @Nullable FDBIndexableRecord newRecord); + @Nonnull + public abstract CompletableFuture updateWhileWriteOnly(@Nullable FDBIndexableRecord oldRecord, + @Nullable FDBIndexableRecord newRecord); + /** * Scans through the list of uniqueness violations within the database. diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/provider/foundationdb/IndexingBase.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/provider/foundationdb/IndexingBase.java index a878c56d79..8ecd9ae49e 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/provider/foundationdb/IndexingBase.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/provider/foundationdb/IndexingBase.java @@ -47,7 +47,6 @@ import com.apple.foundationdb.subspace.Subspace; import com.apple.foundationdb.tuple.ByteArrayUtil2; import com.apple.foundationdb.tuple.Tuple; -import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.Message; import org.apache.commons.lang3.tuple.Pair; import org.slf4j.Logger; @@ -401,35 +400,32 @@ public void enforceStampOverwrite() { @SuppressWarnings("PMD.CloseResource") private CompletableFuture setIndexingTypeOrThrow(FDBRecordStore store, boolean continuedBuild) { // continuedBuild is set if this session isn't a continuation of a previous indexing - Transaction transaction = store.getContext().ensureActive(); IndexBuildProto.IndexBuildIndexingStamp indexingTypeStamp = getIndexingTypeStamp(store); - return forEachTargetIndex(index -> setIndexingTypeOrThrow(store, continuedBuild, transaction, index, indexingTypeStamp)); + return forEachTargetIndex(index -> setIndexingTypeOrThrow(store, continuedBuild, index, indexingTypeStamp)); } @Nonnull - private CompletableFuture setIndexingTypeOrThrow(FDBRecordStore store, boolean continuedBuild, Transaction transaction, Index index, IndexBuildProto.IndexBuildIndexingStamp indexingTypeStamp) { - byte[] stampKey = indexBuildTypeSubspace(store, index).getKey(); + private CompletableFuture setIndexingTypeOrThrow(FDBRecordStore store, boolean continuedBuild, Index index, IndexBuildProto.IndexBuildIndexingStamp indexingTypeStamp) { if (forceStampOverwrite && !continuedBuild) { // Fresh session + overwrite = no questions asked - transaction.set(stampKey, indexingTypeStamp.toByteArray()); + store.saveIndexBuildStamp(index, indexingTypeStamp); return AsyncUtil.DONE; } - return transaction.get(stampKey) - .thenCompose(bytes -> { - if (bytes == null) { + return store.loadIndexBuildStampAsync(index) + .thenCompose(savedStamp -> { + if (savedStamp == null) { if (continuedBuild && indexingTypeStamp.getMethod() != IndexBuildProto.IndexBuildIndexingStamp.Method.BY_RECORDS) { // backward compatibility - maybe continuing an old BY_RECORD session return isWriteOnlyButNoRecordScanned(store, index) - .thenCompose(noRecordScanned -> throwAsByRecordsUnlessNoRecordWasScanned(noRecordScanned, transaction, index, stampKey, indexingTypeStamp)); + .thenCompose(noRecordScanned -> throwAsByRecordsUnlessNoRecordWasScanned(noRecordScanned, store, index, indexingTypeStamp)); } // Here: either not a continuedBuild (new session), or a BY_RECORD session (allowed to overwrite the null stamp) - transaction.set(stampKey, indexingTypeStamp.toByteArray()); + store.saveIndexBuildStamp(index, indexingTypeStamp); return AsyncUtil.DONE; } // Here: has non-null type stamp - IndexBuildProto.IndexBuildIndexingStamp savedStamp = parseTypeStampOrThrow(bytes); if (indexingTypeStamp.equals(savedStamp)) { // A matching stamp is already there - One less thing to worry about return AsyncUtil.DONE; @@ -438,14 +434,14 @@ private CompletableFuture setIndexingTypeOrThrow(FDBRecordStore store, boo indexingTypeStamp.getMethod() == IndexBuildProto.IndexBuildIndexingStamp.Method.BY_RECORDS && savedStamp.getMethod() == IndexBuildProto.IndexBuildIndexingStamp.Method.MULTI_TARGET_BY_RECORDS) { // Special case: partly built with multi target, but may be continued indexing on its own - transaction.set(stampKey, indexingTypeStamp.toByteArray()); + store.saveIndexBuildStamp(index, indexingTypeStamp); return AsyncUtil.DONE; } if (forceStampOverwrite) { // and a continued Build // check if partly built return isWriteOnlyButNoRecordScanned(store, index) .thenCompose(noRecordScanned -> - throwUnlessNoRecordWasScanned(noRecordScanned, transaction, index, stampKey, indexingTypeStamp, + throwUnlessNoRecordWasScanned(noRecordScanned, store, index, indexingTypeStamp, savedStamp, continuedBuild)); } // fall down to exception @@ -454,8 +450,9 @@ private CompletableFuture setIndexingTypeOrThrow(FDBRecordStore store, boo } @Nonnull - private CompletableFuture throwAsByRecordsUnlessNoRecordWasScanned(boolean noRecordScanned, Transaction transaction, - Index index, byte[] stampKey, + private CompletableFuture throwAsByRecordsUnlessNoRecordWasScanned(boolean noRecordScanned, + FDBRecordStore store, + Index index, IndexBuildProto.IndexBuildIndexingStamp indexingTypeStamp) { // A complicated way to reduce complexity. if (noRecordScanned) { @@ -465,7 +462,7 @@ private CompletableFuture throwAsByRecordsUnlessNoRecordWasScanned(boolean .addKeysAndValues(common.indexLogMessageKeyValues()) .toString()); } - transaction.set(stampKey, indexingTypeStamp.toByteArray()); + store.saveIndexBuildStamp(index, indexingTypeStamp); return AsyncUtil.DONE; } // Here: there is no type stamp, but indexing is ongoing. For backward compatibility reasons, we'll consider it a BY_RECORDS stamp @@ -479,15 +476,16 @@ private CompletableFuture throwAsByRecordsUnlessNoRecordWasScanned(boolean } @Nonnull - private CompletableFuture throwUnlessNoRecordWasScanned(boolean noRecordScanned, Transaction transaction, - Index index, byte[] stampKey, + private CompletableFuture throwUnlessNoRecordWasScanned(boolean noRecordScanned, + FDBRecordStore store, + Index index, IndexBuildProto.IndexBuildIndexingStamp indexingTypeStamp, IndexBuildProto.IndexBuildIndexingStamp savedStamp, boolean continuedBuild) { // Ditto (a complicated way to reduce complexity) if (noRecordScanned) { // we can safely overwrite the previous type stamp - transaction.set(stampKey, indexingTypeStamp.toByteArray()); + store.saveIndexBuildStamp(index, indexingTypeStamp); return AsyncUtil.DONE; } // A force overwrite cannot be allowed when partly built @@ -543,19 +541,6 @@ private CompletableFuture setScrubberTypeOrThrow(FDBRecordStore store) { abstract CompletableFuture buildIndexInternalAsync(); - private IndexBuildProto.IndexBuildIndexingStamp parseTypeStampOrThrow(byte[] bytes) { - try { - return IndexBuildProto.IndexBuildIndexingStamp.parseFrom(bytes); - } catch (InvalidProtocolBufferException ex) { - RecordCoreException protoEx = new RecordCoreException("invalid indexing type stamp", - LogMessageKeys.INDEX_NAME, common.getTargetIndexesNames(), - LogMessageKeys.INDEXER_ID, common.getUuid(), - LogMessageKeys.ACTUAL, bytes); - protoEx.initCause(ex); - throw protoEx; - } - } - private CompletableFuture isWriteOnlyButNoRecordScanned(FDBRecordStore store, Index index) { RangeSet rangeSet = new RangeSet(store.indexRangeSubspace(index)); AsyncIterator ranges = rangeSet.missingRanges(store.ensureContextActive()).iterator(); diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/provider/foundationdb/IndexingByIndex.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/provider/foundationdb/IndexingByIndex.java index c038e03df0..1fda3a3072 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/provider/foundationdb/IndexingByIndex.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/provider/foundationdb/IndexingByIndex.java @@ -151,7 +151,7 @@ private CompletableFuture buildRangeOnly(@Nonnull FDBRecordStore store, final IndexMaintainer maintainer = store.getIndexMaintainer(index); // idempotence - We could have verified it at the first iteration only, but the repeating checks seem harmless - validateOrThrowEx(maintainer.isIdempotent(), "target index is not idempotent"); + // validateOrThrowEx(maintainer.isIdempotent(), "target index is not idempotent"); // readability - This method shouldn't block if one has already opened the record store (as we did) Index srcIndex = getSourceIndex(store.getRecordMetaData()); validateOrThrowEx(store.isIndexScannable(srcIndex), "source index is not scannable"); @@ -160,7 +160,7 @@ private CompletableFuture buildRangeOnly(@Nonnull FDBRecordStore store, AsyncIterator ranges = rangeSet.missingRanges(store.ensureContextActive()).iterator(); final ExecuteProperties.Builder executeProperties = ExecuteProperties.newBuilder() - .setIsolationLevel(IsolationLevel.SNAPSHOT) + .setIsolationLevel(maintainer.isIdempotent() ? IsolationLevel.SNAPSHOT : IsolationLevel.SERIALIZABLE) .setReturnedRowLimit(getLimit() + 1); // respect limit in this path; +1 allows a continuation item final ScanProperties scanProperties = new ScanProperties(executeProperties.build()); @@ -179,10 +179,9 @@ private CompletableFuture buildRangeOnly(@Nonnull FDBRecordStore store, final AtomicReference>> lastResult = new AtomicReference<>(RecordCursorResult.exhausted()); final AtomicBoolean hasMore = new AtomicBoolean(true); - final boolean isIdempotent = true ; // Note that currently indexing by index is online implemented for idempotent indexes return iterateRangeOnly(store, cursor, this::getRecordIfTypeMatch, - lastResult, hasMore, recordsScanned, isIdempotent) + lastResult, hasMore, recordsScanned, maintainer.isIdempotent()) .thenApply(vignore -> hasMore.get() ? lastResult.get().get().getIndexEntry().getKey() : rangeEnd) diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/provider/foundationdb/indexes/NoOpIndexMaintainer.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/provider/foundationdb/indexes/NoOpIndexMaintainer.java index 9b24b5ac19..62b6fda9b7 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/provider/foundationdb/indexes/NoOpIndexMaintainer.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/provider/foundationdb/indexes/NoOpIndexMaintainer.java @@ -73,6 +73,12 @@ public CompletableFuture update(@Nullable FDBIndexable return AsyncUtil.DONE; } + @Nonnull + @Override + public CompletableFuture updateWhileWriteOnly(@Nullable final FDBIndexableRecord oldRecord, @Nullable final FDBIndexableRecord newRecord) { + return AsyncUtil.DONE; + } + @Nonnull @Override public RecordCursor scanUniquenessViolations(@Nonnull TupleRange range, @Nullable byte[] continuation, @Nonnull ScanProperties scanProperties) { diff --git a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/provider/foundationdb/indexes/StandardIndexMaintainer.java b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/provider/foundationdb/indexes/StandardIndexMaintainer.java index 94d04bd819..5ca3b5c955 100644 --- a/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/provider/foundationdb/indexes/StandardIndexMaintainer.java +++ b/fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/provider/foundationdb/indexes/StandardIndexMaintainer.java @@ -72,6 +72,7 @@ import com.apple.foundationdb.tuple.ByteArrayUtil; import com.apple.foundationdb.tuple.Tuple; import com.apple.foundationdb.tuple.TupleHelpers; +import com.google.common.base.Verify; import com.google.protobuf.Message; import org.apache.commons.lang3.tuple.Pair; import org.slf4j.Logger; @@ -251,6 +252,77 @@ public CompletableFuture update(@Nullable final FDBInd return future; } + @Override + public CompletableFuture updateWhileWriteOnly(@Nullable final FDBIndexableRecord oldRecord, @Nullable final FDBIndexableRecord newRecord) { + if (isIdempotent()) { + return update(oldRecord, newRecord); + } + return state.store.loadIndexBuildStampAsync(state.index).thenCompose(stamp -> { + if (stamp == null) { + // The index build has not begun, so the entire range is unbuilt, and thus + // the index update should be skipped + return updateWriteOnlyByRecords(oldRecord, newRecord); + } + switch (stamp.getMethod()) { + case BY_RECORDS: + return updateWriteOnlyByRecords(oldRecord, newRecord); + case BY_INDEX: + Object sourceIndexKey = Tuple.fromBytes(stamp.getSourceIndexSubspaceKey().toByteArray()).get(0); + Index sourceIndex = state.store.getRecordMetaData().getIndexFromSubspaceKey(sourceIndexKey); + return updateWriteOnlyByIndex(sourceIndex, oldRecord, newRecord); + default: + throw new RecordCoreException("unable to update write-only index with current type stamp") + .addLogInfo("stamp", stamp); + } + }); + } + + private CompletableFuture updateWriteOnlyByRecords(@Nullable final FDBIndexableRecord oldRecord, @Nullable final FDBIndexableRecord newRecord) { + Tuple primaryKey = oldRecord == null ? Verify.verifyNotNull(newRecord).getPrimaryKey() : oldRecord.getPrimaryKey(); + return addedRangeWithKey(primaryKey).thenCompose(inRange -> + inRange ? update(oldRecord, newRecord) : AsyncUtil.DONE); + } + + private CompletableFuture updateWriteOnlyByIndex(@Nonnull Index sourceIndex, @Nullable final FDBIndexableRecord oldRecord, @Nullable final FDBIndexableRecord newRecord) { + IndexMaintainer sourceIndexMaintainer = state.store.getIndexMaintainer(sourceIndex); + Tuple oldEntryKey = evaluateSingletonIndexKey(sourceIndex, sourceIndexMaintainer, oldRecord); + Tuple newEntryKey = evaluateSingletonIndexKey(sourceIndex, sourceIndexMaintainer, newRecord); + if (oldEntryKey != null && newEntryKey != null) { + if (oldEntryKey.equals(newEntryKey)) { + return addedRangeWithKey(oldEntryKey).thenCompose(inRange -> + inRange ? update(oldRecord, newRecord) : AsyncUtil.DONE); + } else { + return addedRangeWithKey(oldEntryKey) + .thenCompose(oldInRange -> oldInRange ? update(oldRecord, null) : AsyncUtil.DONE) + .thenCompose(ignore -> addedRangeWithKey(newEntryKey)) + .thenCompose(newInRange -> newInRange ? update(null, newRecord) : AsyncUtil.DONE); + } + } else { + Tuple entryKey = oldEntryKey == null ? newEntryKey : oldEntryKey; + if (entryKey == null) { + return AsyncUtil.DONE; + } else { + return addedRangeWithKey(entryKey).thenCompose(inRange -> + inRange ? update(oldRecord, newRecord) : AsyncUtil.DONE); + } + } + } + + @Nullable + private static Tuple evaluateSingletonIndexKey(Index index, IndexMaintainer maintainer, @Nullable FDBIndexableRecord record) { + if (record == null) { + return null; + } + List entries = maintainer.filteredIndexEntries(record); + if (entries == null || entries.isEmpty()) { + return null; + } else if (entries.size() != 1) { + throw new RecordCoreException("index produced incorrect number of entries for use as source index"); + } + IndexEntry entry = entries.get(0); + return FDBRecordStoreBase.indexEntryKey(index, entry.getKey(), record.getPrimaryKey()); + } + /** * Filter out index keys according to {@link IndexMaintenanceFilter}. * Keys that do not pass the filter will not be stored / removed from the index. diff --git a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/OnlineIndexerIndexFromIndexTest.java b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/OnlineIndexerIndexFromIndexTest.java index a72eec0603..445462a26c 100644 --- a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/OnlineIndexerIndexFromIndexTest.java +++ b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/OnlineIndexerIndexFromIndexTest.java @@ -224,10 +224,10 @@ public void testIndexFromIndexNoFallback() { .setTimer(timer) .build()) { - assertThrows(IndexingByIndex.ValidationException.class, indexBuilder::buildIndex); + indexBuilder.buildIndex(true); } - assertEquals(0, timer.getCount(FDBStoreTimer.Counts.ONLINE_INDEX_BUILDER_RECORDS_SCANNED)); - assertEquals(0, timer.getCount(FDBStoreTimer.Counts.ONLINE_INDEX_BUILDER_RECORDS_INDEXED)); + assertEquals(numRecords, timer.getCount(FDBStoreTimer.Counts.ONLINE_INDEX_BUILDER_RECORDS_SCANNED)); + assertEquals(numRecords, timer.getCount(FDBStoreTimer.Counts.ONLINE_INDEX_BUILDER_RECORDS_INDEXED)); } @Test diff --git a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/TerribleIndexMaintainer.java b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/TerribleIndexMaintainer.java index 65181a8ea8..130e84ebcc 100644 --- a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/TerribleIndexMaintainer.java +++ b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/TerribleIndexMaintainer.java @@ -94,6 +94,12 @@ public CompletableFuture update(@Nullable FDBIndexable } } + @Nonnull + @Override + public CompletableFuture updateWhileWriteOnly(@Nullable final FDBIndexableRecord oldRecord, @Nullable final FDBIndexableRecord newRecord) { + return AsyncUtil.DONE; + } + @Nonnull @Override public RecordCursor scanUniquenessViolations(@Nonnull TupleRange range, @Nullable byte[] continuation, @Nonnull ScanProperties scanProperties) {