use of com.apple.foundationdb.tuple.Tuple in project fdb-record-layer by FoundationDB.
the class TimeWindowLeaderboardIndexMaintainer method timeWindowRankAndEntry.
@Nonnull
public <M extends Message> CompletableFuture<Pair<Long, Tuple>> timeWindowRankAndEntry(@Nonnull FDBRecord<M> record, int type, long timestamp) {
final List<IndexEntry> indexEntries = evaluateIndex(record);
final CompletableFuture<TimeWindowLeaderboard> leaderboardFuture = oldestLeaderboardMatching(type, timestamp);
return leaderboardFuture.thenCompose(leaderboard -> {
if (leaderboard == null) {
return CompletableFuture.completedFuture(null);
}
return groupOrderedScoreIndexKeys(indexEntries, leaderboard.getDirectory(), true).thenCompose(groupedScores -> {
if (groupedScores.isEmpty()) {
return CompletableFuture.completedFuture(null);
}
if (groupedScores.size() > 1) {
throw new RecordCoreException("Record has more than one group of scores");
}
Map.Entry<Tuple, Collection<OrderedScoreIndexKey>> groupEntry = groupedScores.entrySet().iterator().next();
Optional<OrderedScoreIndexKey> bestContainedScore = groupEntry.getValue().stream().filter(score -> leaderboard.containsTimestamp(score.timestamp)).findFirst();
if (!bestContainedScore.isPresent()) {
return CompletableFuture.completedFuture(null);
}
// bestContainedScore should be the one stored in the leaderboard's ranked set; get its rank there.
final Tuple groupKey = groupEntry.getKey();
return isHighScoreFirst(leaderboard.getDirectory(), groupKey).thenCompose(highScoreFirst -> {
final OrderedScoreIndexKey indexKey = bestContainedScore.get();
final Tuple leaderboardGroupKey = leaderboard.getSubspaceKey().addAll(groupKey);
final Subspace extraSubspace = getSecondarySubspace();
final Subspace rankSubspace = extraSubspace.subspace(leaderboardGroupKey);
final RankedSet.Config leaderboardConfig = config.toBuilder().setNLevels(leaderboard.getNLevels()).build();
final RankedSet rankedSet = new RankedSetIndexHelper.InstrumentedRankedSet(state, rankSubspace, leaderboardConfig);
// Undo any negation needed to find entry.
final Tuple entry = highScoreFirst ? negateScoreForHighScoreFirst(indexKey.scoreKey, 0) : indexKey.scoreKey;
return RankedSetIndexHelper.rankForScore(state, rankedSet, indexKey.scoreKey, true).thenApply(rank -> Pair.of(rank, entry));
});
});
});
}
use of com.apple.foundationdb.tuple.Tuple in project fdb-record-layer by FoundationDB.
the class TimeWindowLeaderboardIndexMaintainer method updateIndexKeys.
@Override
protected <M extends Message> CompletableFuture<Void> updateIndexKeys(@Nonnull final FDBIndexableRecord<M> savedRecord, final boolean remove, @Nonnull final List<IndexEntry> indexEntries) {
final Subspace extraSubspace = getSecondarySubspace();
// The value for the index key cannot vary from entry-to-entry, so get the value only from the first entry.
final Tuple entryValue = indexEntries.isEmpty() ? TupleHelpers.EMPTY : indexEntries.get(0).getValue();
return loadDirectory().thenCompose(directory -> {
if (directory == null) {
return AsyncUtil.DONE;
}
return groupOrderedScoreIndexKeys(indexEntries, directory, true).thenCompose(groupedScores -> {
final List<CompletableFuture<Void>> futures = new ArrayList<>();
for (Iterable<TimeWindowLeaderboard> directoryEntry : directory.getLeaderboards().values()) {
for (TimeWindowLeaderboard leaderboard : directoryEntry) {
for (Map.Entry<Tuple, Collection<OrderedScoreIndexKey>> groupEntry : groupedScores.entrySet()) {
final Optional<OrderedScoreIndexKey> bestContainedScore = groupEntry.getValue().stream().filter(score -> leaderboard.containsTimestamp(score.timestamp)).findFirst();
if (bestContainedScore.isPresent()) {
final Tuple groupKey = groupEntry.getKey();
final OrderedScoreIndexKey indexKey = bestContainedScore.get();
final Tuple leaderboardGroupKey = leaderboard.getSubspaceKey().addAll(groupKey);
// Update the ordinary B-tree for this leaderboard.
final Tuple entryKey = leaderboardGroupKey.addAll(indexKey.scoreKey);
CompletableFuture<Void> updateOrdinaryIndex = updateOneKeyAsync(savedRecord, remove, new IndexEntry(state.index, entryKey, entryValue));
if (!MoreAsyncUtil.isCompletedNormally(updateOrdinaryIndex)) {
futures.add(updateOrdinaryIndex);
}
// Update the corresponding rankset for this leaderboard.
// Notice that as each leaderboard has its own subspace key and at most one score
// per record is chosen per leaderboard, this is the only time this record will be
// indexed in this rankSubspace. Compare/contrast: RankIndexMaintainer::updateIndexKeys
final Subspace rankSubspace = extraSubspace.subspace(leaderboardGroupKey);
final RankedSet.Config leaderboardConfig = config.toBuilder().setNLevels(leaderboard.getNLevels()).build();
futures.add(RankedSetIndexHelper.updateRankedSet(state, rankSubspace, leaderboardConfig, entryKey, indexKey.scoreKey, remove));
}
}
}
}
Optional<Long> latestTimestamp = groupedScores.values().stream().flatMap(Collection::stream).map(OrderedScoreIndexKey::getTimestamp).max(Long::compareTo);
if (latestTimestamp.isPresent()) {
// Keep track of the latest timestamp for any indexed entry.
// Then, if time window update adds an index that starts before then, we have to index existing records.
state.transaction.mutate(MutationType.MAX, state.indexSubspace.getKey(), AtomicMutation.Standard.encodeSignedLong(latestTimestamp.get()));
}
return AsyncUtil.whenAll(futures);
});
});
}
use of com.apple.foundationdb.tuple.Tuple in project fdb-record-layer by FoundationDB.
the class TimeWindowLeaderboardIndexMaintainer method deleteWhere.
@Override
public CompletableFuture<Void> deleteWhere(Transaction tr, @Nonnull Tuple prefix) {
return loadDirectory().thenApply(directory -> {
if (directory != null) {
final Subspace indexSubspace = getIndexSubspace();
final Subspace extraSubspace = getSecondarySubspace();
for (Iterable<TimeWindowLeaderboard> directoryEntry : directory.getLeaderboards().values()) {
for (TimeWindowLeaderboard leaderboard : directoryEntry) {
// Range deletes are used for the more common operation of deleting a time window, so we need
// to do each one here to get to its grouping.
final Tuple leaderboardGroupKey = leaderboard.getSubspaceKey().addAll(prefix);
// NOTE: Range.startsWith(), Subspace.range() and so on cover keys *strictly* within the range, but we
// may store something at the group root as well.
final byte[] indexKey = indexSubspace.pack(leaderboardGroupKey);
tr.clear(indexKey, ByteArrayUtil.strinc(indexKey));
final byte[] ranksetKey = extraSubspace.pack(leaderboardGroupKey);
tr.clear(ranksetKey, ByteArrayUtil.strinc(ranksetKey));
}
}
}
return null;
});
}
use of com.apple.foundationdb.tuple.Tuple in project fdb-record-layer by FoundationDB.
the class KeySpaceDirectory method listSubdirectoryAsync.
@Nonnull
// SonarQube doesn't realize that the cursor is wrapped and returned
@SuppressWarnings("squid:S2095")
protected RecordCursor<ResolvedKeySpacePath> listSubdirectoryAsync(@Nullable KeySpacePath listFrom, @Nonnull FDBRecordContext context, @Nonnull String subdirName, @Nullable ValueRange<?> valueRange, @Nullable byte[] continuation, @Nonnull ScanProperties scanProperties) {
if (listFrom != null && listFrom.getDirectory() != this) {
throw new RecordCoreException("Provided path does not belong to this directory").addLogInfo("path", listFrom, "directory", this.getName());
}
final KeySpaceDirectory subdir = getSubdirectory(subdirName);
final CompletableFuture<ResolvedKeySpacePath> resolvedFromFuture = listFrom == null ? CompletableFuture.completedFuture(null) : listFrom.toResolvedPathAsync(context);
// The chained cursor cannot implement reverse scan, so we implement it by having the
// inner key value cursor do the reversing but telling the chained cursor we are moving
// forward.
final ScanProperties chainedCursorScanProperties;
if (scanProperties.isReverse()) {
chainedCursorScanProperties = scanProperties.setReverse(false);
} else {
chainedCursorScanProperties = scanProperties;
}
// For the read of the individual row keys, we only want to read a single key. In addition,
// the ChainedCursor is going to do counting of our reads to apply any limits that were specified
// on the ScanProperties. We don't want the inner KeyValueCursor in nextTuple() to ALSO count those
// same reads so we clear out its limits.
final ScanProperties keyReadScanProperties = scanProperties.with(props -> props.clearState().setReturnedRowLimit(1));
return new LazyCursor<>(resolvedFromFuture.thenCompose(resolvedFrom -> {
final Subspace subspace = resolvedFrom == null ? new Subspace() : resolvedFrom.toSubspace();
return subdir.getValueRange(context, valueRange, subspace).thenApply(range -> {
final RecordCursor<Tuple> cursor = new ChainedCursor<>(context, lastKey -> nextTuple(context, subspace, range, lastKey, keyReadScanProperties), Tuple::pack, Tuple::fromBytes, continuation, chainedCursorScanProperties);
return cursor.mapPipelined(tuple -> {
final Tuple key = Tuple.fromList(tuple.getItems());
return findChildForKey(context, resolvedFrom, key, 1, 0);
}, 1);
});
}), context.getExecutor());
}
use of com.apple.foundationdb.tuple.Tuple in project fdb-record-layer by FoundationDB.
the class BunchedMapScanTest method testScan.
private void testScan(int limit, boolean reverse, @Nonnull BiFunction<Transaction, byte[], BunchedMapIterator<Tuple, Tuple>> iteratorFunction) {
try (Transaction tr = db.createTransaction()) {
byte[] continuation = null;
List<Tuple> readKeys = new ArrayList<>();
Tuple lastKey = null;
do {
int returned = 0;
BunchedMapIterator<Tuple, Tuple> bunchedMapIterator = iteratorFunction.apply(tr, continuation);
while (bunchedMapIterator.hasNext()) {
Tuple toAdd = bunchedMapIterator.peek().getKey();
readKeys.add(toAdd);
assertEquals(toAdd, bunchedMapIterator.next().getKey());
if (lastKey != null) {
assertEquals(reverse ? 1 : -1, lastKey.compareTo(toAdd));
}
lastKey = toAdd;
returned += 1;
}
assertFalse(bunchedMapIterator.hasNext());
assertThrows(NoSuchElementException.class, bunchedMapIterator::peek);
assertThrows(NoSuchElementException.class, bunchedMapIterator::next);
continuation = bunchedMapIterator.getContinuation();
if (limit == ReadTransaction.ROW_LIMIT_UNLIMITED || returned < limit) {
assertNull(continuation);
} else {
assertNotNull(continuation);
}
} while (continuation != null);
if (reverse) {
readKeys = Lists.reverse(readKeys);
}
assertEquals(keys, readKeys);
tr.cancel();
}
}
Aggregations