Search in sources :

Example 71 with IndexEntry

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;
}
Also used : IndexEntry(com.apple.foundationdb.record.IndexEntry) LogMessageKeys(com.apple.foundationdb.record.logging.LogMessageKeys) IndexMaintainerState(com.apple.foundationdb.record.provider.foundationdb.IndexMaintainerState) CompletableFuture(java.util.concurrent.CompletableFuture) AsyncUtil(com.apple.foundationdb.async.AsyncUtil) MetaDataException(com.apple.foundationdb.record.metadata.MetaDataException) Subspace(com.apple.foundationdb.subspace.Subspace) Key(com.apple.foundationdb.record.metadata.Key) Transaction(com.apple.foundationdb.Transaction) IndexScanType(com.apple.foundationdb.record.IndexScanType) Tuple(com.apple.foundationdb.tuple.Tuple) RecordCoreException(com.apple.foundationdb.record.RecordCoreException) ScanProperties(com.apple.foundationdb.record.ScanProperties) Nonnull(javax.annotation.Nonnull) Nullable(javax.annotation.Nullable) QueryToKeyMatcher(com.apple.foundationdb.record.query.QueryToKeyMatcher) FDBIndexableRecord(com.apple.foundationdb.record.provider.foundationdb.FDBIndexableRecord) KeyValue(com.apple.foundationdb.KeyValue) IndexOptions(com.apple.foundationdb.record.metadata.IndexOptions) FDBStoreTimer(com.apple.foundationdb.record.provider.foundationdb.FDBStoreTimer) TupleRange(com.apple.foundationdb.record.TupleRange) KeyValueCursor(com.apple.foundationdb.record.provider.foundationdb.KeyValueCursor) List(java.util.List) Index(com.apple.foundationdb.record.metadata.Index) TupleHelpers(com.apple.foundationdb.tuple.TupleHelpers) Message(com.google.protobuf.Message) RecordCursor(com.apple.foundationdb.record.RecordCursor) IndexTypes(com.apple.foundationdb.record.metadata.IndexTypes) API(com.apple.foundationdb.annotation.API) Transaction(com.apple.foundationdb.Transaction) Subspace(com.apple.foundationdb.subspace.Subspace) IndexEntry(com.apple.foundationdb.record.IndexEntry) Tuple(com.apple.foundationdb.tuple.Tuple)

Example 72 with IndexEntry

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()));
}
Also used : ScanProperties(com.apple.foundationdb.record.ScanProperties) IndexEntry(com.apple.foundationdb.record.IndexEntry) MetaDataException(com.apple.foundationdb.record.metadata.MetaDataException) Tuple(com.apple.foundationdb.tuple.Tuple) Nonnull(javax.annotation.Nonnull)

Example 73 with IndexEntry

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;
}
Also used : RecordCoreException(com.apple.foundationdb.record.RecordCoreException) MutationType(com.apple.foundationdb.MutationType) IndexEntry(com.apple.foundationdb.record.IndexEntry) Tuple(com.apple.foundationdb.tuple.Tuple)

Example 74 with IndexEntry

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;
    });
}
Also used : CompletableFuture(java.util.concurrent.CompletableFuture) HashMap(java.util.HashMap) IndexEntry(com.apple.foundationdb.record.IndexEntry) Collection(java.util.Collection) Tuple(com.apple.foundationdb.tuple.Tuple)

Aggregations

IndexEntry (com.apple.foundationdb.record.IndexEntry)74 Tuple (com.apple.foundationdb.tuple.Tuple)41 Nonnull (javax.annotation.Nonnull)37 Index (com.apple.foundationdb.record.metadata.Index)34 Test (org.junit.jupiter.api.Test)34 FDBRecordContext (com.apple.foundationdb.record.provider.foundationdb.FDBRecordContext)32 Message (com.google.protobuf.Message)32 ScanProperties (com.apple.foundationdb.record.ScanProperties)29 ArrayList (java.util.ArrayList)29 RecordCoreException (com.apple.foundationdb.record.RecordCoreException)28 TupleRange (com.apple.foundationdb.record.TupleRange)28 List (java.util.List)28 Nullable (javax.annotation.Nullable)27 IndexScanType (com.apple.foundationdb.record.IndexScanType)24 RecordCursor (com.apple.foundationdb.record.RecordCursor)22 CompletableFuture (java.util.concurrent.CompletableFuture)21 Collectors (java.util.stream.Collectors)21 ExecuteProperties (com.apple.foundationdb.record.ExecuteProperties)20 TupleHelpers (com.apple.foundationdb.tuple.TupleHelpers)20 FDBStoreTimer (com.apple.foundationdb.record.provider.foundationdb.FDBStoreTimer)19