use of com.apple.foundationdb.subspace.Subspace in project fdb-record-layer by FoundationDB.
the class RankIndexMaintainer method scan.
@Nonnull
@Override
public RecordCursor<IndexEntry> scan(@Nonnull IndexScanType scanType, @Nonnull TupleRange rankRange, @Nullable byte[] continuation, @Nonnull ScanProperties scanProperties) {
if (scanType == IndexScanType.BY_VALUE) {
return scan(rankRange, continuation, scanProperties);
} else if (scanType != IndexScanType.BY_RANK) {
throw new RecordCoreException("Can only scan rank index by rank or by value.");
}
final Subspace extraSubspace = getSecondarySubspace();
final CompletableFuture<TupleRange> scoreRangeFuture = RankedSetIndexHelper.rankRangeToScoreRange(state, getGroupingCount(), extraSubspace, config, rankRange);
return RecordCursor.mapFuture(getExecutor(), scoreRangeFuture, continuation, (scoreRange, scoreContinuation) -> {
if (scoreRange == null) {
return RecordCursor.empty(getExecutor());
} else {
return scan(scoreRange, scoreContinuation, scanProperties);
}
});
}
use of com.apple.foundationdb.subspace.Subspace in project fdb-record-layer by FoundationDB.
the class BitmapValueIndexMaintainer method updateIndexKeys.
@Override
@Nonnull
protected <M extends Message> CompletableFuture<Void> updateIndexKeys(@Nonnull final FDBIndexableRecord<M> savedRecord, final boolean remove, @Nonnull final List<IndexEntry> indexEntries) {
final int groupPrefixSize = getGroupingCount();
final List<CompletableFuture<Void>> futures = unique && !remove ? new ArrayList<>(indexEntries.size()) : null;
for (IndexEntry indexEntry : indexEntries) {
final long startTime = System.nanoTime();
final Tuple groupKey = TupleHelpers.subTuple(indexEntry.getKey(), 0, groupPrefixSize);
Object positionObject = indexEntry.getKey().get(groupPrefixSize);
if (positionObject == null) {
continue;
}
if (!(positionObject instanceof Number)) {
// This should be prevented by the checks in the meta-data builder, but just in case
throw new RecordCoreException("position field in index entry is not a number").addLogInfo(LogMessageKeys.KEY, indexEntry.getKey(), LogMessageKeys.INDEX_NAME, state.index.getName(), LogMessageKeys.INDEX_TYPE, state.index.getType());
}
long position = ((Number) positionObject).longValue();
final int offset = (int) Math.floorMod(position, (long) entrySize);
position -= offset;
final byte[] key = state.indexSubspace.pack(groupKey.add(position));
// This has to be the same size every time, with all the unset bits, or else it gets truncated.
// We really could use a new mutation that took a linear bit position to set / clear and only did length extension or something like that.
final byte[] bitmap = new byte[(entrySize + 7) / 8];
if (remove) {
if (state.store.isIndexWriteOnly(state.index)) {
// If the index isn't built, it's possible this key wasn't reached.
// So initialize it to zeros (or leave it alone).
state.transaction.mutate(MutationType.BIT_OR, key, bitmap);
}
// Otherwise the entry must already exist for us to be removing it,
// so there is no danger that this will store all (but one) ones in a new key.
Arrays.fill(bitmap, (byte) 0xFF);
bitmap[offset / 8] &= ~(byte) (1 << (offset % 8));
state.transaction.mutate(MutationType.BIT_AND, key, bitmap);
Arrays.fill(bitmap, (byte) 0x00);
state.transaction.mutate(MutationType.COMPARE_AND_CLEAR, key, bitmap);
} else {
if (unique) {
// Snapshot read to see if the bit is already set.
CompletableFuture<Void> future = state.transaction.snapshot().get(key).thenAccept(existing -> {
if (existing != null && (existing[offset / 8] & (byte) (1 << (offset % 8))) != 0) {
throw new RecordIndexUniquenessViolation(state.index, indexEntry, savedRecord.getPrimaryKey(), // Unfortunately, we don't know the other key.
null);
}
});
futures.add(future);
// Then arrange to conflict for the position with any concurrent update.
final byte[] conflictKey = new Subspace(key).pack(offset);
state.transaction.addReadConflictKey(conflictKey);
state.transaction.addWriteConflictKey(conflictKey);
}
bitmap[offset / 8] |= (byte) (1 << (offset % 8));
state.transaction.mutate(MutationType.BIT_OR, key, bitmap);
}
if (state.store.getTimer() != null) {
state.store.getTimer().recordSinceNanoTime(FDBStoreTimer.Events.MUTATE_INDEX_ENTRY, startTime);
}
}
return futures != null ? AsyncUtil.whenAll(futures) : AsyncUtil.DONE;
}
use of com.apple.foundationdb.subspace.Subspace in project fdb-record-layer by FoundationDB.
the class SplitHelper method writeSplitRecord.
private static void writeSplitRecord(@Nonnull final FDBRecordContext context, @Nonnull final Subspace subspace, @Nonnull final Tuple key, @Nonnull final byte[] serialized, final boolean clearBasedOnPreviousSizeInfo, @Nullable final FDBStoredSizes previousSizeInfo, @Nullable SizeInfo sizeInfo) {
final Transaction tr = context.ensureActive();
final Subspace keySplitSubspace = subspace.subspace(key);
clearPreviousSplitRecord(context, subspace, key, clearBasedOnPreviousSizeInfo, previousSizeInfo);
long index = SplitHelper.START_SPLIT_RECORD;
int offset = 0;
while (offset < serialized.length) {
int nextOffset = offset + SplitHelper.SPLIT_RECORD_SIZE;
if (nextOffset > serialized.length) {
nextOffset = serialized.length;
}
final byte[] keyBytes = keySplitSubspace.pack(index);
final byte[] valueBytes = Arrays.copyOfRange(serialized, offset, nextOffset);
tr.set(keyBytes, valueBytes);
if (sizeInfo != null) {
if (offset == 0) {
sizeInfo.set(keyBytes, valueBytes);
sizeInfo.setSplit(true);
} else {
sizeInfo.add(keyBytes, valueBytes);
}
}
index++;
offset = nextOffset;
}
}
use of com.apple.foundationdb.subspace.Subspace in project fdb-record-layer by FoundationDB.
the class FDBRecordStore method loadIndexStatesAsync.
@Nonnull
private CompletableFuture<Map<String, IndexState>> loadIndexStatesAsync(@Nonnull IsolationLevel isolationLevel) {
Subspace isSubspace = getSubspace().subspace(Tuple.from(INDEX_STATE_SPACE_KEY));
KeyValueCursor cursor = KeyValueCursor.Builder.withSubspace(isSubspace).setContext(getContext()).setRange(TupleRange.ALL).setContinuation(null).setScanProperties(new ScanProperties(ExecuteProperties.newBuilder().setIsolationLevel(isolationLevel).setDefaultCursorStreamingMode(CursorStreamingMode.WANT_ALL).build())).build();
FDBStoreTimer timer = getTimer();
CompletableFuture<Map<String, IndexState>> result = cursor.asList().thenApply(list -> {
Map<String, IndexState> indexStateMap;
if (list.isEmpty()) {
indexStateMap = Collections.emptyMap();
} else {
ImmutableMap.Builder<String, IndexState> indexStateMapBuilder = ImmutableMap.builder();
for (KeyValue kv : list) {
String indexName = isSubspace.unpack(kv.getKey()).getString(0);
Object code = Tuple.fromBytes(kv.getValue()).get(0);
indexStateMapBuilder.put(indexName, IndexState.fromCode(code));
if (timer != null) {
timer.increment(FDBStoreTimer.Counts.LOAD_STORE_STATE_KEY);
timer.increment(FDBStoreTimer.Counts.LOAD_STORE_STATE_KEY_BYTES, kv.getKey().length);
timer.increment(FDBStoreTimer.Counts.LOAD_STORE_STATE_VALUE_BYTES, kv.getValue().length);
}
}
indexStateMap = indexStateMapBuilder.build();
}
return indexStateMap;
});
if (timer != null) {
result = timer.instrument(FDBStoreTimer.Events.LOAD_RECORD_STORE_INDEX_META_DATA, result, context.getExecutor());
}
return result;
}
use of com.apple.foundationdb.subspace.Subspace 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());
}
Aggregations