use of com.apple.foundationdb.record.IndexEntry in project fdb-record-layer by FoundationDB.
the class PermutedMinMaxIndexMaintainer method updateIndexKeys.
@Override
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 int totalSize = state.index.getColumnSize();
final Subspace permutedSubspace = getSecondarySubspace();
for (IndexEntry indexEntry : indexEntries) {
final Tuple groupKey = TupleHelpers.subTuple(indexEntry.getKey(), 0, groupPrefixSize);
final Tuple value = TupleHelpers.subTuple(indexEntry.getKey(), groupPrefixSize, totalSize);
final int permutePosition = groupPrefixSize - permutedSize;
final Tuple groupPrefix = TupleHelpers.subTuple(groupKey, 0, permutePosition);
final Tuple groupSuffix = TupleHelpers.subTuple(groupKey, permutePosition, groupPrefixSize);
if (remove) {
// First remove from ordinary tree.
return updateOneKeyAsync(savedRecord, remove, indexEntry).thenCompose(vignore -> {
final byte[] permutedKeyToRemove = permutedSubspace.pack(groupPrefix.addAll(value).addAll(groupSuffix));
// See if value is the current minimum/maximum.
return state.store.ensureContextActive().get(permutedKeyToRemove).thenCompose(permutedValueExists -> {
if (permutedValueExists == null) {
// No, nothing more to do.
return AsyncUtil.DONE;
}
return getExtremum(groupKey).thenApply(extremum -> {
if (extremum == null) {
// No replacement, just remove.
state.store.ensureContextActive().clear(permutedKeyToRemove);
} else {
final Tuple remainingValue = TupleHelpers.subTuple(extremum, groupPrefixSize, totalSize);
if (!value.equals(remainingValue)) {
// New extremum: remove existing and store it.
final byte[] permutedKeyToAdd = permutedSubspace.pack(groupPrefix.addAll(remainingValue).addAll(groupSuffix));
final Transaction tr = state.store.ensureContextActive();
tr.clear(permutedKeyToRemove);
tr.set(permutedKeyToAdd, TupleHelpers.EMPTY.pack());
}
}
return null;
});
});
});
} else {
// Get existing minimum/maximum.
return getExtremum(groupKey).thenApply(extremum -> {
final boolean addPermuted;
if (extremum == null) {
// New group.
addPermuted = true;
} else {
final Tuple currentValue = TupleHelpers.subTuple(extremum, groupPrefixSize, totalSize);
int compare = value.compareTo(currentValue);
addPermuted = type == Type.MIN ? compare < 0 : compare > 0;
// Replace if new value is better.
if (addPermuted) {
final byte[] permutedKeyToRemove = permutedSubspace.pack(groupPrefix.addAll(currentValue).addAll(groupSuffix));
state.store.ensureContextActive().clear(permutedKeyToRemove);
}
}
if (addPermuted) {
final byte[] permutedKeyToAdd = permutedSubspace.pack(groupPrefix.addAll(value).addAll(groupSuffix));
state.store.ensureContextActive().set(permutedKeyToAdd, TupleHelpers.EMPTY.pack());
}
return null;
}).thenCompose(// Ordinary is second.
vignore -> updateOneKeyAsync(savedRecord, remove, indexEntry));
}
}
return AsyncUtil.DONE;
}
use of com.apple.foundationdb.record.IndexEntry in project fdb-record-layer by FoundationDB.
the class AtomicMutationIndexMaintainer method evaluateAggregateFunction.
@Override
@Nonnull
public CompletableFuture<Tuple> evaluateAggregateFunction(@Nonnull IndexAggregateFunction function, @Nonnull TupleRange range, @Nonnull IsolationLevel isolationveLevel) {
if (!matchesAggregateFunction(function)) {
throw new MetaDataException("this index does not support aggregate function: " + function);
}
final RecordCursor<IndexEntry> cursor = scan(IndexScanType.BY_GROUP, range, null, new ScanProperties(ExecuteProperties.newBuilder().setIsolationLevel(isolationveLevel).build()));
final BiFunction<Tuple, Tuple, Tuple> aggregator = mutation.getAggregator();
return cursor.reduce(mutation.getIdentity(), (accum, kv) -> aggregator.apply(accum, kv.getValue()));
}
use of com.apple.foundationdb.record.IndexEntry in project fdb-record-layer by FoundationDB.
the class AtomicMutationIndexMaintainer method updateIndexKeys.
@Override
protected <M extends Message> CompletableFuture<Void> updateIndexKeys(@Nonnull final FDBIndexableRecord<M> savedRecord, final boolean remove, @Nonnull final List<IndexEntry> indexEntries) {
final MutationType mutationType = mutation.getMutationType();
final int groupPrefixSize = getGroupingCount();
for (IndexEntry indexEntry : indexEntries) {
long startTime = System.nanoTime();
final Tuple groupKey;
final IndexEntry groupedValue;
if (groupPrefixSize <= 0) {
groupKey = TupleHelpers.EMPTY;
groupedValue = indexEntry;
} else if (groupPrefixSize == indexEntry.getKeySize()) {
groupKey = indexEntry.getKey();
groupedValue = indexEntry.subKey(0, 0);
} else {
groupKey = TupleHelpers.subTuple(indexEntry.getKey(), 0, groupPrefixSize);
groupedValue = indexEntry.subKey(groupPrefixSize, indexEntry.getKeySize());
}
final byte[] param = mutation.getMutationParam(groupedValue, remove);
if (param == null) {
continue;
}
// this is a bit of a hack here, but it's the minimally invasive way and most explicit way to do this
if (!mutation.allowsNegative()) {
Number numVal = (Number) groupedValue.getKeyValue(0);
if (numVal != null && numVal.longValue() < 0) {
throw new RecordCoreException("Attempted update of MAX_EVER_LONG or MIN_EVER_LONG index with negative value");
}
}
final byte[] key = state.indexSubspace.pack(groupKey);
if (AtomicMutation.Standard.MAX_EVER_VERSION.equals(mutation)) {
if (groupedValue.getKey().hasIncompleteVersionstamp()) {
// With an incomplete versionstamp, we need to call SET_VERSIONSTAMPED_VALUE.
// If multiple records (with possibly different local versions) are written with the same
// grouping key in the same context, we want to only write the one with the maximum
// local version. Choosing the one with the maximum byte representation will do this
// as all incomplete versionstamps are serialized with identical fake global versions.
state.context.updateVersionMutation(MutationType.SET_VERSIONSTAMPED_VALUE, key, param, (oldParam, newParam) -> ByteArrayUtil.compareUnsigned(oldParam, newParam) < 0 ? newParam : oldParam);
} else {
state.transaction.mutate(MutationType.BYTE_MAX, key, param);
}
} else {
state.transaction.mutate(mutationType, key, param);
final byte[] compareAndClear = mutation.getCompareAndClearParam();
if (compareAndClear != null) {
state.transaction.mutate(MutationType.COMPARE_AND_CLEAR, key, compareAndClear);
}
}
if (state.store.getTimer() != null) {
state.store.getTimer().recordSinceNanoTime(FDBStoreTimer.Events.MUTATE_INDEX_ENTRY, startTime);
}
}
return AsyncUtil.DONE;
}
use of com.apple.foundationdb.record.IndexEntry in project fdb-record-layer by FoundationDB.
the class TimeWindowLeaderboardIndexMaintainer method groupOrderedScoreIndexKeys.
/**
* Group the given <code>indexKeys</code> by group of size <code>groupPrefixSize</code>, ordering within each
* group by score, taking <code>highScoreFirst</code> into account from the directory
* or any sub-directory (if {@code includesGroup} is {@code true}).
* @param indexEntries index entries to be added to the index
* @param directory leaderboard directory used to decide whether higher scores are better (earlier in the list)
* @param includesGroup whether index entries also include the group key(s)
* @return a future that completes to index keys grouped by leaderboard
*/
protected CompletableFuture<Map<Tuple, Collection<OrderedScoreIndexKey>>> groupOrderedScoreIndexKeys(@Nonnull Iterable<IndexEntry> indexEntries, @Nonnull TimeWindowLeaderboardDirectory directory, boolean includesGroup) {
final int groupPrefixSize = getGroupingCount();
final Map<Tuple, CompletableFuture<Boolean>> groupDirections = new HashMap<>();
if (includesGroup) {
for (IndexEntry indexEntry : indexEntries) {
Tuple groupKey = TupleHelpers.subTuple(indexEntry.getKey(), 0, groupPrefixSize);
MapUtils.computeIfAbsent(groupDirections, groupKey, group -> isHighScoreFirst(directory, group));
}
}
return AsyncUtil.whenAll(groupDirections.values()).thenApply(vignore -> {
final Map<Tuple, Collection<OrderedScoreIndexKey>> grouped = new HashMap<>();
for (IndexEntry indexEntry : indexEntries) {
// group_keys[groupingPrefixSize], score, timestamp, other tiebreakers...
Tuple scoreKey = indexEntry.getKey();
Tuple groupKey = TupleHelpers.EMPTY;
if (includesGroup && groupPrefixSize > 0) {
groupKey = TupleHelpers.subTuple(scoreKey, 0, groupPrefixSize);
scoreKey = TupleHelpers.subTuple(scoreKey, groupPrefixSize, scoreKey.size());
}
if (includesGroup ? groupDirections.get(groupKey).join() : directory.isHighScoreFirst()) {
scoreKey = negateScoreForHighScoreFirst(scoreKey, 0);
}
final OrderedScoreIndexKey orderedScoreIndexKey = new OrderedScoreIndexKey(indexEntry, scoreKey);
grouped.compute(groupKey, (gignore, collection) -> {
if (collection == null) {
collection = new TreeSet<>();
}
collection.add(orderedScoreIndexKey);
return collection;
});
}
return grouped;
});
}
Aggregations