use of com.apple.foundationdb.subspace.Subspace in project fdb-record-layer by FoundationDB.
the class TimeWindowLeaderboardIndexMaintainer method saveSubDirectory.
protected void saveSubDirectory(@Nonnull TimeWindowLeaderboardSubDirectory subdirectory) {
final Subspace extraSubspace = getSecondarySubspace();
state.transaction.set(extraSubspace.pack(SUB_DIRECTORY_PREFIX.addAll(subdirectory.getGroup())), subdirectory.toProto().toByteArray());
}
use of com.apple.foundationdb.subspace.Subspace in project fdb-record-layer by FoundationDB.
the class TimeWindowLeaderboardIndexMaintainer method evaluateEqualRange.
private CompletableFuture<Tuple> evaluateEqualRange(@Nonnull TupleRange range, @Nonnull EvaluateEqualRange function) {
final Tuple tuple = range.getLow();
final int type = (int) tuple.getLong(0);
final long timestamp = tuple.getLong(1);
final int groupingCount = getGroupingCount();
final Tuple groupKey = TupleHelpers.subTuple(tuple, 2, 2 + groupingCount);
final Tuple values = TupleHelpers.subTuple(tuple, 2 + groupingCount, tuple.size());
final CompletableFuture<TimeWindowLeaderboard> leaderboardFuture = oldestLeaderboardMatching(type, timestamp);
return leaderboardFuture.thenCompose(leaderboard -> {
if (leaderboard == null) {
return CompletableFuture.completedFuture(null);
}
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);
return function.apply(leaderboard, rankedSet, groupKey, values);
});
}
use of com.apple.foundationdb.subspace.Subspace 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.subspace.Subspace 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.subspace.Subspace in project fdb-record-layer by FoundationDB.
the class TimeWindowLeaderboardIndexMaintainer method loadSubDirectory.
@Nonnull
protected CompletableFuture<TimeWindowLeaderboardSubDirectory> loadSubDirectory(@Nonnull TimeWindowLeaderboardDirectory directory, @Nonnull Tuple group) {
TimeWindowLeaderboardSubDirectory subdirectory = directory.getSubDirectory(group);
if (subdirectory != null) {
return CompletableFuture.completedFuture(subdirectory);
}
final Subspace extraSubspace = getSecondarySubspace();
return state.transaction.get(extraSubspace.pack(SUB_DIRECTORY_PREFIX.addAll(group))).thenApply(bytes -> {
final TimeWindowLeaderboardSubDirectory newsub;
if (bytes == null) {
newsub = new TimeWindowLeaderboardSubDirectory(group, directory.isHighScoreFirst());
} else {
TimeWindowLeaderboardProto.TimeWindowLeaderboardSubDirectory.Builder builder = TimeWindowLeaderboardProto.TimeWindowLeaderboardSubDirectory.newBuilder();
try {
builder.mergeFrom(bytes);
} catch (InvalidProtocolBufferException ex) {
throw new RecordCoreStorageException("error decoding leaderboard sub-directory", ex);
}
newsub = new TimeWindowLeaderboardSubDirectory(group, builder.build());
}
directory.addSubDirectory(newsub);
return newsub;
});
}
Aggregations