use of com.apple.foundationdb.async.RankedSet 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.async.RankedSet 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.async.RankedSet in project fdb-record-layer by FoundationDB.
the class RankedSetIndexHelper method updateRankedSet.
@Nonnull
public static CompletableFuture<Void> updateRankedSet(@Nonnull IndexMaintainerState state, @Nonnull Subspace rankSubspace, @Nonnull RankedSet.Config config, @Nonnull Tuple valueKey, @Nonnull Tuple scoreKey, boolean remove) {
final RankedSet rankedSet = new InstrumentedRankedSet(state, rankSubspace, config);
final byte[] score = scoreKey.pack();
CompletableFuture<Void> result = init(state, rankedSet).thenCompose(v -> {
if (remove) {
if (config.isCountDuplicates()) {
// Decrement count and possibly remove.
return removeFromRankedSet(state, rankedSet, score);
} else {
// If no one else has this score, remove from ranked set.
return state.transaction.getRange(state.indexSubspace.range(valueKey)).iterator().onHasNext().thenCompose(hasNext -> hasNext ? AsyncUtil.DONE : removeFromRankedSet(state, rankedSet, score));
}
} else {
return rankedSet.add(state.transaction, score).thenApply(added -> null);
}
});
return state.store.instrument(Events.RANKED_SET_UPDATE, result);
}
use of com.apple.foundationdb.async.RankedSet in project fdb-record-layer by FoundationDB.
the class RankedSetIndexHelper method rankRangeToScoreRange.
@Nonnull
public static CompletableFuture<TupleRange> rankRangeToScoreRange(@Nonnull IndexMaintainerState state, int groupPrefixSize, @Nonnull Subspace rankSubspace, @Nonnull RankedSet.Config config, @Nonnull TupleRange rankRange) {
final Tuple prefix = groupPrefix(groupPrefixSize, rankRange, rankSubspace);
if (prefix != null) {
rankSubspace = rankSubspace.subspace(prefix);
}
// Map low and high ranks to scores and scan main index with those.
Number lowRankNum = extractRank(groupPrefixSize, rankRange.getLow());
boolean startFromBeginning = lowRankNum == null || lowRankNum.longValue() < 0L;
EndpointType lowEndpoint = startFromBeginning ? EndpointType.RANGE_INCLUSIVE : rankRange.getLowEndpoint();
Number highRankNum = extractRank(groupPrefixSize, rankRange.getHigh());
EndpointType highEndpoint = rankRange.getHighEndpoint();
if (highRankNum != null && (highRankNum.longValue() < 0L || highEndpoint == EndpointType.RANGE_EXCLUSIVE && highRankNum.longValue() == 0L)) {
// This range is below 0, so we know the range is empty without having to look.
return CompletableFuture.completedFuture(null);
}
if (highRankNum != null && highEndpoint == EndpointType.RANGE_EXCLUSIVE && lowEndpoint == EndpointType.RANGE_EXCLUSIVE && highRankNum.equals(lowRankNum)) {
// This range is exclusively empty.
return CompletableFuture.completedFuture(null);
}
if (startFromBeginning && highRankNum == null) {
// Scanning whole range, no need to convert any ranks.
return CompletableFuture.completedFuture(TupleRange.allOf(prefix));
}
final RankedSet rankedSet = new InstrumentedRankedSet(state, rankSubspace, config);
return init(state, rankedSet).thenCompose(v -> {
CompletableFuture<Tuple> lowScoreFuture = scoreForRank(state, rankedSet, startFromBeginning ? 0L : lowRankNum, null);
CompletableFuture<Tuple> highScoreFuture = scoreForRank(state, rankedSet, highRankNum, null);
return lowScoreFuture.thenCombine(highScoreFuture, (lowScore, highScore) -> {
// from low until the end.
if (lowScore == null) {
return null;
}
EndpointType adjustedHighEndpoint = highScore != null ? highEndpoint : prefix != null ? EndpointType.RANGE_INCLUSIVE : EndpointType.TREE_END;
TupleRange scoreRange = new TupleRange(lowScore, highScore, lowEndpoint, adjustedHighEndpoint);
if (prefix != null) {
scoreRange = scoreRange.prepend(prefix);
}
return scoreRange;
});
});
}
use of com.apple.foundationdb.async.RankedSet in project fdb-record-layer by FoundationDB.
the class RankIndexMaintainer method rank.
protected <M extends Message> CompletableFuture<Long> rank(@Nonnull EvaluationContext context, @Nullable IndexRecordFunction<Long> function, @Nonnull FDBRecord<M> record) {
final int groupPrefixSize = getGroupingCount();
Key.Evaluated indexKey = IndexFunctionHelper.recordFunctionIndexEntry(state.store, state.index, context, function, record, groupPrefixSize);
if (indexKey == null) {
return CompletableFuture.completedFuture(null);
}
Tuple scoreValue = indexKey.toTuple();
Subspace rankSubspace = getSecondarySubspace();
if (groupPrefixSize > 0) {
Tuple prefix = Tuple.fromList(scoreValue.getItems().subList(0, groupPrefixSize));
rankSubspace = rankSubspace.subspace(prefix);
scoreValue = Tuple.fromList(scoreValue.getItems().subList(groupPrefixSize, scoreValue.size()));
}
RankedSet rankedSet = new RankedSetIndexHelper.InstrumentedRankedSet(state, rankSubspace, config);
return RankedSetIndexHelper.rankForScore(state, rankedSet, scoreValue, true);
}
Aggregations