-
Notifications
You must be signed in to change notification settings - Fork 103
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Have indexing by record scan by key ranges instead of by tuple ranges #2655
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,6 +30,7 @@ | |
import com.apple.foundationdb.record.IndexScanType; | ||
import com.apple.foundationdb.record.IndexState; | ||
import com.apple.foundationdb.record.IsolationLevel; | ||
import com.apple.foundationdb.record.KeyRange; | ||
import com.apple.foundationdb.record.PipelineOperation; | ||
import com.apple.foundationdb.record.RecordCoreArgumentException; | ||
import com.apple.foundationdb.record.RecordCoreException; | ||
|
@@ -866,6 +867,20 @@ RecordCursor<FDBStoredRecord<M>> scanRecords(@Nullable Tuple low, @Nullable Tupl | |
@Nullable byte[] continuation, | ||
@Nonnull ScanProperties scanProperties); | ||
|
||
/** | ||
* Scan the records in the database in a key range. | ||
* | ||
* @param range key range | ||
* @param continuation any continuation from a previous scan | ||
* @param scanProperties skip, limit and other scan properties | ||
* | ||
* @return a cursor that will scan everything in the range, picking up at continuation, and honoring the given scan properties | ||
*/ | ||
@Nonnull | ||
RecordCursor<FDBStoredRecord<M>> scanRecordsKeyRange(@Nonnull KeyRange range, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you think this API should be |
||
@Nullable byte[] continuation, | ||
@Nonnull ScanProperties scanProperties); | ||
|
||
/** | ||
* Count the number of records in the database in a range. | ||
* | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,10 +23,10 @@ | |
import com.apple.foundationdb.Range; | ||
import com.apple.foundationdb.annotation.API; | ||
import com.apple.foundationdb.async.AsyncUtil; | ||
import com.apple.foundationdb.async.RangeSet; | ||
import com.apple.foundationdb.record.ExecuteProperties; | ||
import com.apple.foundationdb.record.IndexBuildProto; | ||
import com.apple.foundationdb.record.IsolationLevel; | ||
import com.apple.foundationdb.record.KeyRange; | ||
import com.apple.foundationdb.record.RecordCursor; | ||
import com.apple.foundationdb.record.RecordCursorResult; | ||
import com.apple.foundationdb.record.ScanProperties; | ||
|
@@ -125,9 +125,7 @@ private CompletableFuture<Void> buildMultiTargetIndex(@Nonnull SubspaceProvider | |
} else { | ||
final Range range = tupleRange.toRange(); | ||
rangeStart = range.begin; | ||
// tupleRange has an inclusive high endpoint, so end isn't a valid tuple. | ||
// But buildRangeOnly needs to convert missing Ranges back to TupleRanges, so round up. | ||
rangeEnd = ByteArrayUtil.strinc(range.end); | ||
rangeEnd = range.end; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Setting up this particular test may be a pain, so it may not be worth it, but I think if we wanted to validate that this fixed anything, we could add a test that used non-integer record type keys. Or a test that used a record type key with what would have been problematic value for its record type key, like 255 |
||
} | ||
|
||
final CompletableFuture<FDBRecordStore> maybePresetRangeFuture = | ||
|
@@ -172,12 +170,13 @@ private CompletableFuture<Boolean> buildRangeOnly(@Nonnull FDBRecordStore store, | |
if (range == null) { | ||
return AsyncUtil.READY_FALSE; // no more missing ranges - all done | ||
} | ||
final Tuple rangeStart = RangeSet.isFirstKey(range.begin) ? null : Tuple.fromBytes(range.begin); | ||
final Tuple rangeEnd = RangeSet.isFinalKey(range.end) ? null : Tuple.fromBytes(range.end); | ||
final TupleRange tupleRange = TupleRange.between(rangeStart, rangeEnd); | ||
final byte[] keyPrefix = store.recordsSubspace().pack(); | ||
final byte[] rangeStart = ByteArrayUtil.join(keyPrefix, range.begin); | ||
final byte[] rangeEnd = ByteArrayUtil.join(keyPrefix, range.end); | ||
final KeyRange keyRange = new KeyRange(rangeStart, rangeEnd); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm trying to wrap my head around the That feels fair enough. |
||
|
||
RecordCursor<FDBStoredRecord<Message>> cursor = | ||
store.scanRecords(tupleRange, null, scanProperties); | ||
store.scanRecordsKeyRange(keyRange, null, scanProperties); | ||
|
||
final AtomicReference<RecordCursorResult<FDBStoredRecord<Message>>> lastResult = new AtomicReference<>(RecordCursorResult.exhausted()); | ||
final AtomicBoolean hasMore = new AtomicBoolean(true); | ||
|
@@ -189,20 +188,20 @@ private CompletableFuture<Boolean> buildRangeOnly(@Nonnull FDBRecordStore store, | |
this::getRecordIfTypeMatch, | ||
lastResult, hasMore, recordsScanned, isIdempotent) | ||
.thenCompose(ignore -> postIterateRangeOnly(targetRangeSets, hasMore.get(), lastResult, | ||
rangeStart, rangeEnd, scanProperties.isReverse())); | ||
range.begin, range.end, scanProperties.isReverse())); | ||
}); | ||
} | ||
|
||
private CompletableFuture<Boolean> postIterateRangeOnly(List<IndexingRangeSet> targetRangeSets, boolean hasMore, | ||
AtomicReference<RecordCursorResult<FDBStoredRecord<Message>>> lastResult, | ||
Tuple rangeStart, Tuple rangeEnd, boolean isReverse) { | ||
byte[] rangeStart, byte[] rangeEnd, boolean isReverse) { | ||
if (isReverse) { | ||
Tuple continuation = hasMore ? lastResult.get().get().getPrimaryKey() : rangeStart; | ||
return insertRanges(targetRangeSets, packOrNull(continuation), packOrNull(rangeEnd)) | ||
byte[] continuation = hasMore ? packOrNull(lastResult.get().get().getPrimaryKey()) : rangeStart; | ||
return insertRanges(targetRangeSets, continuation, rangeEnd) | ||
.thenApply(ignore -> hasMore || rangeStart != null); | ||
} else { | ||
Tuple continuation = hasMore ? lastResult.get().get().getPrimaryKey() : rangeEnd; | ||
return insertRanges(targetRangeSets, packOrNull(rangeStart), packOrNull(continuation)) | ||
byte[] continuation = hasMore ? packOrNull(lastResult.get().get().getPrimaryKey()) : rangeEnd; | ||
return insertRanges(targetRangeSets, rangeStart, continuation) | ||
.thenApply(ignore -> hasMore || rangeEnd != null); | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor: I was trying to think if there was a better way to structure this than a
Consumer
. The answer may be "no". The best alternatives I could think of would be to either have this take aKeyValueCursor.Builder
than callers would pre-fill with the range already set, or something more exotic like adding a.setRange(KeyValueCursor.Builder)
to theTupleRange
andKeyValueRange
abstractions, or makingTupleRange
andKeyValueRange
implement some common interface that could set the low and high endpoints on aKeyValueCursor.Builder
.