use of com.apple.foundationdb.Transaction in project fdb-record-layer by FoundationDB.
the class FDBRecordStore method deleteStore.
/**
* Delete the record store at the given {@link Subspace}. In addition to the store's
* data this will delete the store's header and therefore will remove any evidence that
* the store existed.
*
* <p>
* This method does not read the underlying record store, so it does not validate
* that a record store exists in the given subspace. As it might be the case that
* this record store has a cacheable store state (see {@link #setStateCacheability(boolean)}),
* this method resets the database's
* {@linkplain FDBRecordContext#getMetaDataVersionStamp(IsolationLevel) meta-data version-stamp}.
* As a result, calling this method may cause other clients to invalidate their caches needlessly.
* </p>
*
* @param context the transactional context in which to delete the record store
* @param subspace the subspace containing the record store
*/
public static void deleteStore(FDBRecordContext context, Subspace subspace) {
// In theory, we only need to set the meta-data version stamp if the record store's
// meta-data is cacheable, but we can't know that from here.
context.setMetaDataVersionStamp();
context.setDirtyStoreState(true);
final Transaction transaction = context.ensureActive();
transaction.clear(subspace.range());
}
use of com.apple.foundationdb.Transaction in project fdb-record-layer by FoundationDB.
the class FDBRecordStore method addRecordsReadConflict.
/**
* Add a read conflict key for all records.
*/
private void addRecordsReadConflict() {
if (recordsReadConflict) {
return;
}
recordsReadConflict = true;
Transaction tr = ensureContextActive();
byte[] recordKey = getSubspace().pack(Tuple.from(RECORD_KEY));
tr.addReadConflictRange(recordKey, ByteArrayUtil.strinc(recordKey));
}
use of com.apple.foundationdb.Transaction in project fdb-record-layer by FoundationDB.
the class FDBRecordStore method firstUnbuiltRange.
/**
* Returns the first unbuilt range of an index that is currently being bulit.
* If there is no range that is currently unbuilt, it will return an
* empty {@link Optional}. If there is one, it will return an {@link Optional}
* set to the first unbuilt range it finds.
* @param index the index to check built state
* @return a future that will contain the first unbuilt range if any
*/
@Nonnull
public CompletableFuture<Optional<Range>> firstUnbuiltRange(@Nonnull Index index) {
if (!getRecordMetaData().hasIndex(index.getName())) {
throw new MetaDataException("Index " + index.getName() + " does not exist in meta-data.");
}
Transaction tr = ensureContextActive();
RangeSet rangeSet = new RangeSet(indexRangeSubspace(index));
AsyncIterator<Range> missingRangeIterator = rangeSet.missingRanges(tr, null, null, 1).iterator();
return missingRangeIterator.onHasNext().thenApply(hasFirst -> {
if (hasFirst) {
return Optional.of(missingRangeIterator.next());
} else {
return Optional.empty();
}
});
}
use of com.apple.foundationdb.Transaction in project fdb-record-layer by FoundationDB.
the class IndexingBase method setIndexingTypeOrThrow.
@Nonnull
private CompletableFuture<Void> 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));
}
use of com.apple.foundationdb.Transaction in project fdb-record-layer by FoundationDB.
the class IndexingBase method iterateRangeOnly.
/**
* iterate cursor's items and index them.
*
* @param store the record store.
* @param cursor iteration items.
* @param getRecordToIndex function to convert cursor's item to a record that should be indexed (or null, if inapplicable)
* @param nextResultCont when return, if hasMore is true, holds the last cursored result - unprocessed - as a
* continuation item.
* @param hasMore when return, true if the cursor's source is not exhausted (not more items in range).
* @param recordsScanned when return, number of scanned records.
* @param isIdempotent are all the built indexes idempotent
* @param <T> cursor result's type.
*
* @return hasMore, nextResultCont, and recordsScanned.
*/
protected <T> CompletableFuture<Void> iterateRangeOnly(@Nonnull FDBRecordStore store, @Nonnull RecordCursor<T> cursor, @Nonnull BiFunction<FDBRecordStore, RecordCursorResult<T>, CompletableFuture<FDBStoredRecord<Message>>> getRecordToIndex, @Nonnull AtomicReference<RecordCursorResult<T>> nextResultCont, @Nonnull AtomicBoolean hasMore, @Nullable AtomicLong recordsScanned, final boolean isIdempotent) {
final FDBStoreTimer timer = getRunner().getTimer();
final FDBRecordContext context = store.getContext();
// Need to do this each transaction because other index enabled state might have changed. Could cache based on that.
// Copying the state also guards against changes made by other online building from check version.
AtomicLong recordsScannedCounter = new AtomicLong();
final AtomicReference<RecordCursorResult<T>> nextResult = new AtomicReference<>(null);
return AsyncUtil.whileTrue(() -> cursor.onNext().thenCompose(result -> {
RecordCursorResult<T> currResult;
final boolean isExhausted;
if (result.hasNext()) {
// has next, process one previous item (if exists)
currResult = nextResult.get();
nextResult.set(result);
if (currResult == null) {
// that was the first item, nothing to process
return AsyncUtil.READY_TRUE;
}
isExhausted = false;
} else {
// end of the cursor list
timerIncrement(timer, FDBStoreTimer.Counts.ONLINE_INDEX_BUILDER_RANGES_BY_COUNT);
if (!result.getNoNextReason().isSourceExhausted()) {
nextResultCont.set(nextResult.get());
hasMore.set(true);
return AsyncUtil.READY_FALSE;
}
// source is exhausted, fall down to handle the last item and return with hasMore=false
currResult = nextResult.get();
if (currResult == null) {
// there was no data
hasMore.set(false);
return AsyncUtil.READY_FALSE;
}
// here, process the last item and return
nextResult.set(null);
isExhausted = true;
}
// here: currResult must have value
timerIncrement(timer, FDBStoreTimer.Counts.ONLINE_INDEX_BUILDER_RECORDS_SCANNED);
recordsScannedCounter.incrementAndGet();
return getRecordToIndex.apply(store, currResult).thenCompose(rec -> {
if (null == rec) {
if (isExhausted) {
hasMore.set(false);
return AsyncUtil.READY_FALSE;
}
return AsyncUtil.READY_TRUE;
}
// This record should be indexed. Add it to the transaction.
if (isIdempotent) {
store.addRecordReadConflict(rec.getPrimaryKey());
}
timerIncrement(timer, FDBStoreTimer.Counts.ONLINE_INDEX_BUILDER_RECORDS_INDEXED);
final CompletableFuture<Void> updateMaintainer = updateMaintainerBuilder(store, rec);
if (isExhausted) {
// we've just processed the last item
hasMore.set(false);
return updateMaintainer.thenApply(vignore -> false);
}
return updateMaintainer.thenCompose(vignore -> context.getApproximateTransactionSize().thenApply(size -> {
if (size >= common.config.getMaxWriteLimitBytes()) {
// the transaction becomes too big - stop iterating
timerIncrement(timer, FDBStoreTimer.Counts.ONLINE_INDEX_BUILDER_RANGES_BY_SIZE);
nextResultCont.set(nextResult.get());
hasMore.set(true);
return false;
}
return true;
}));
});
}), cursor.getExecutor()).thenApply(vignore -> {
long recordsScannedInTransaction = recordsScannedCounter.get();
if (recordsScanned != null) {
recordsScanned.addAndGet(recordsScannedInTransaction);
}
if (common.isTrackProgress()) {
for (Index index : common.getTargetIndexes()) {
final Subspace scannedRecordsSubspace = indexBuildScannedRecordsSubspace(store, index);
store.context.ensureActive().mutate(MutationType.ADD, scannedRecordsSubspace.getKey(), FDBRecordStore.encodeRecordCount(recordsScannedInTransaction));
}
}
return null;
});
}
Aggregations