use of com.apple.foundationdb.Range in project fdb-record-layer by FoundationDB.
the class IndexingByRecords method buildRange.
// Builds a range within a single transaction. It will look for the missing ranges within the given range and build those while
// updating the range set.
@Nonnull
private CompletableFuture<Void> buildRange(@Nonnull FDBRecordStore store, @Nullable Tuple start, @Nullable Tuple end, @Nullable AtomicLong recordsScanned) {
RangeSet rangeSet = new RangeSet(store.indexRangeSubspace(common.getIndex()));
AsyncIterator<Range> ranges = rangeSet.missingRanges(store.ensureContextActive(), packOrNull(start), packOrNull(end)).iterator();
return ranges.onHasNext().thenCompose(hasAny -> {
if (hasAny) {
return AsyncUtil.whileTrue(() -> {
Range range = ranges.next();
Tuple rangeStart = RangeSet.isFirstKey(range.begin) ? null : Tuple.fromBytes(range.begin);
Tuple rangeEnd = RangeSet.isFinalKey(range.end) ? null : Tuple.fromBytes(range.end);
return CompletableFuture.allOf(// one long, respectively.
buildRangeOnly(store, rangeStart, rangeEnd, false, recordsScanned), rangeSet.insertRange(store.ensureContextActive(), range, true)).thenCompose(vignore -> ranges.onHasNext());
}, store.getExecutor());
} else {
return AsyncUtil.DONE;
}
});
}
use of com.apple.foundationdb.Range in project fdb-record-layer by FoundationDB.
the class IndexingByRecords method buildRanges.
@Nonnull
private CompletableFuture<Void> buildRanges(SubspaceProvider subspaceProvider, @Nonnull Subspace subspace, RangeSet rangeSet, Queue<Range> rangeDeque) {
return AsyncUtil.whileTrue(() -> {
if (rangeDeque.isEmpty()) {
// We're done.
return CompletableFuture.completedFuture(false);
}
Range toBuild = rangeDeque.remove();
// This only works if the things included within the rangeSet are serialized Tuples.
Tuple startTuple = Tuple.fromBytes(toBuild.begin);
Tuple endTuple = RangeSet.isFinalKey(toBuild.end) ? null : Tuple.fromBytes(toBuild.end);
return buildUnbuiltRange(startTuple, endTuple).handle((realEnd, ex) -> handleBuiltRange(subspaceProvider, subspace, rangeSet, rangeDeque, startTuple, endTuple, realEnd, ex)).thenCompose(Function.identity());
}, getRunner().getExecutor());
}
use of com.apple.foundationdb.Range in project fdb-record-layer by FoundationDB.
the class IndexingByRecords method buildRange.
@Nonnull
private CompletableFuture<Void> buildRange(@Nonnull SubspaceProvider subspaceProvider, @Nullable Key.Evaluated start, @Nullable Key.Evaluated end) {
return getRunner().runAsync(context -> context.getReadVersionAsync().thenCompose(vignore -> subspaceProvider.getSubspaceAsync(context).thenCompose(subspace -> {
RangeSet rangeSet = new RangeSet(subspace.subspace(Tuple.from(FDBRecordStore.INDEX_RANGE_SPACE_KEY, common.getIndex().getSubspaceKey())));
byte[] startBytes = packOrNull(convertOrNull(start));
byte[] endBytes = packOrNull(convertOrNull(end));
Queue<Range> rangeDeque = new ArrayDeque<>();
ReadTransactionContext rtc = context.ensureActive();
return rangeSet.missingRanges(rtc, startBytes, endBytes).thenAccept(rangeDeque::addAll).thenCompose(vignore2 -> buildRanges(subspaceProvider, subspace, rangeSet, rangeDeque));
})));
}
use of com.apple.foundationdb.Range in project fdb-record-layer by FoundationDB.
the class IndexingByRecords method buildRange.
/**
* Builds (transactionally) the index by adding records with primary keys within the given range.
* This will look for gaps of keys within the given range that haven't yet been rebuilt and then
* rebuild only those ranges. As a result, if this method is called twice, the first time, it will
* build whatever needs to be built, and then the second time, it will notice that there are no ranges
* that need to be built, so it will do nothing. In this way, it is idempotent and thus safe to
* use in retry loops.
*
* This method will fail if there is too much work to be done in a single transaction. If one wants
* to handle building a range that does not fit in a single transaction, one should use the
* {@link #buildRange(Key.Evaluated, Key.Evaluated) buildRange()}
* function that takes an {@link FDBDatabase} as its first parameter.
*
* @param store the record store in which to rebuild the range
* @param start the (inclusive) beginning primary key of the range to build (or <code>null</code> to go to the end)
* @param end the (exclusive) end primary key of the range to build (or <code>null</code> to go to the end)
* @return a future that will be ready when the build has completed
*/
@Nonnull
public CompletableFuture<Void> buildRange(@Nonnull FDBRecordStore store, @Nullable Key.Evaluated start, @Nullable Key.Evaluated end) {
RangeSet rangeSet = new RangeSet(store.indexRangeSubspace(common.getIndex()));
byte[] startBytes = packOrNull(convertOrNull(start));
byte[] endBytes = packOrNull(convertOrNull(end));
AsyncIterator<Range> ranges = rangeSet.missingRanges(store.ensureContextActive(), startBytes, endBytes).iterator();
return ranges.onHasNext().thenCompose(hasNext -> {
if (hasNext) {
return AsyncUtil.whileTrue(() -> {
Range toBuild = ranges.next();
Tuple startTuple = Tuple.fromBytes(toBuild.begin);
Tuple endTuple = RangeSet.isFinalKey(toBuild.end) ? null : Tuple.fromBytes(toBuild.end);
AtomicReference<Tuple> currStart = new AtomicReference<>(startTuple);
return AsyncUtil.whileTrue(() -> buildUnbuiltRange(store, currStart.get(), endTuple, null).thenApply(realEnd -> {
if (realEnd != null && !realEnd.equals(endTuple)) {
currStart.set(realEnd);
return true;
} else {
return false;
}
}), store.getExecutor()).thenCompose(vignore -> ranges.onHasNext());
}, store.getExecutor());
} else {
return AsyncUtil.DONE;
}
});
}
use of com.apple.foundationdb.Range in project fdb-record-layer by FoundationDB.
the class IndexingBase method setScrubberTypeOrThrow.
@Nonnull
private CompletableFuture<Void> setScrubberTypeOrThrow(FDBRecordStore store) {
// HERE: The index must be readable, checked by the caller
// if scrubber had already run and still have missing ranges, do nothing
// else: clear ranges and overwrite type-stamp
Transaction tr = store.getContext().ensureActive();
IndexBuildProto.IndexBuildIndexingStamp indexingTypeStamp = getIndexingTypeStamp(store);
validateOrThrowEx(indexingTypeStamp.getMethod().equals(IndexBuildProto.IndexBuildIndexingStamp.Method.SCRUB_REPAIR), "Not a scrubber type-stamp");
// Note: the scrubbers do not support multi target (yet)
final Index index = common.getIndex();
final Subspace indexesRangeSubspace = indexScrubIndexRangeSubspace(store, index);
final Subspace recordsRangeSubspace = indexScrubRecordsRangeSubspace(store, index);
RangeSet indexRangeSet = new RangeSet(indexesRangeSubspace);
RangeSet recordsRangeSet = new RangeSet(recordsRangeSubspace);
AsyncIterator<Range> indexRanges = indexRangeSet.missingRanges(tr).iterator();
AsyncIterator<Range> recordsRanges = recordsRangeSet.missingRanges(tr).iterator();
return indexRanges.onHasNext().thenCompose(hasNextIndex -> {
if (Boolean.FALSE.equals(hasNextIndex)) {
// erase the 'ranges' data to allow a fresh index re-scrubbing.
if (LOGGER.isInfoEnabled()) {
LOGGER.info(KeyValueLogMessage.build("Reset scrubber's index range").addKeysAndValues(common.indexLogMessageKeyValues()).toString());
}
tr.clear(indexesRangeSubspace.range());
}
return recordsRanges.onHasNext().thenAccept(hasNextRecord -> {
if (Boolean.FALSE.equals(hasNextRecord)) {
// erase the 'ranges' data to allow a fresh records re-scrubbing.
if (LOGGER.isInfoEnabled()) {
LOGGER.info(KeyValueLogMessage.build("Reset scrubber's records range").addKeysAndValues(common.indexLogMessageKeyValues()).toString());
}
tr.clear(recordsRangeSubspace.range());
}
});
});
}
Aggregations