Search in sources :

Example 41 with TupleRange

use of com.apple.foundationdb.record.TupleRange in project fdb-record-layer by FoundationDB.

the class IndexingMultiTargetByRecords method rebuildRangeOnly.

@Nonnull
private CompletableFuture<Tuple> rebuildRangeOnly(@Nonnull FDBRecordStore store, Tuple cont, @Nonnull AtomicLong recordsScanned) {
    validateSameMetadataOrThrow(store);
    final boolean isIdempotent = areTheyAllIdempotent(store, common.getTargetIndexes());
    final IsolationLevel isolationLevel = isIdempotent ? IsolationLevel.SNAPSHOT : IsolationLevel.SERIALIZABLE;
    final ExecuteProperties.Builder executeProperties = ExecuteProperties.newBuilder().setIsolationLevel(isolationLevel);
    final ScanProperties scanProperties = new ScanProperties(executeProperties.build());
    final TupleRange tupleRange = TupleRange.between(cont, null);
    RecordCursor<FDBStoredRecord<Message>> cursor = store.scanRecords(tupleRange, null, scanProperties);
    final AtomicReference<RecordCursorResult<FDBStoredRecord<Message>>> lastResult = new AtomicReference<>(RecordCursorResult.exhausted());
    final AtomicBoolean hasMore = new AtomicBoolean(true);
    return iterateRangeOnly(store, cursor, this::getRecordIfTypeMatch, lastResult, hasMore, recordsScanned, isIdempotent).thenApply(vignore -> hasMore.get() ? lastResult.get().get().getPrimaryKey() : null);
}
Also used : IsolationLevel(com.apple.foundationdb.record.IsolationLevel) Message(com.google.protobuf.Message) RecordCursorResult(com.apple.foundationdb.record.RecordCursorResult) AtomicReference(java.util.concurrent.atomic.AtomicReference) ExecuteProperties(com.apple.foundationdb.record.ExecuteProperties) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) ScanProperties(com.apple.foundationdb.record.ScanProperties) TupleRange(com.apple.foundationdb.record.TupleRange) Nonnull(javax.annotation.Nonnull)

Example 42 with TupleRange

use of com.apple.foundationdb.record.TupleRange in project fdb-record-layer by FoundationDB.

the class IndexingScrubDangling method scrubIndexRangeOnly.

@Nonnull
private CompletableFuture<Boolean> scrubIndexRangeOnly(@Nonnull FDBRecordStore store, byte[] startBytes, byte[] endBytes, @Nonnull AtomicLong recordsScanned) {
    // return false when done
    Index index = common.getIndex();
    final RecordMetaData metaData = store.getRecordMetaData();
    final RecordMetaDataProvider recordMetaDataProvider = common.getRecordStoreBuilder().getMetaDataProvider();
    if (recordMetaDataProvider == null || !metaData.equals(recordMetaDataProvider.getRecordMetaData())) {
        throw new MetaDataException("Store does not have the same metadata");
    }
    final IndexMaintainer maintainer = store.getIndexMaintainer(index);
    // scrubbing only readable, VALUE, idempotence indexes (at least for now)
    validateOrThrowEx(maintainer.isIdempotent(), "scrubbed index is not idempotent");
    validateOrThrowEx(index.getType().equals(IndexTypes.VALUE) || scrubbingPolicy.ignoreIndexTypeCheck(), "scrubbed index is not a VALUE index");
    validateOrThrowEx(store.getIndexState(index) == IndexState.READABLE, "scrubbed index is not readable");
    RangeSet rangeSet = new RangeSet(indexScrubIndexRangeSubspace(store, index));
    AsyncIterator<Range> ranges = rangeSet.missingRanges(store.ensureContextActive(), startBytes, endBytes).iterator();
    final ExecuteProperties.Builder executeProperties = ExecuteProperties.newBuilder().setIsolationLevel(IsolationLevel.SNAPSHOT).setReturnedRowLimit(// always respectLimit in this path; +1 allows a continuation item
    getLimit() + 1);
    final ScanProperties scanProperties = new ScanProperties(executeProperties.build());
    return ranges.onHasNext().thenCompose(hasNext -> {
        if (Boolean.FALSE.equals(hasNext)) {
            // Here: no more missing ranges - all done
            // To avoid stale metadata, we'll keep the scrubbed-ranges indicator empty until the next scrub call.
            Transaction tr = store.getContext().ensureActive();
            tr.clear(indexScrubIndexRangeSubspace(store, index).range());
            return AsyncUtil.READY_FALSE;
        }
        final Range range = ranges.next();
        final Tuple rangeStart = RangeSet.isFirstKey(range.begin) ? null : Tuple.fromBytes(range.begin);
        final Tuple rangeEnd = RangeSet.isFinalKey(range.end) ? null : Tuple.fromBytes(range.end);
        final TupleRange tupleRange = TupleRange.between(rangeStart, rangeEnd);
        RecordCursor<FDBIndexedRecord<Message>> cursor = store.scanIndexRecords(index.getName(), IndexScanType.BY_VALUE, tupleRange, null, IndexOrphanBehavior.RETURN, scanProperties);
        final AtomicBoolean hasMore = new AtomicBoolean(true);
        final AtomicReference<RecordCursorResult<FDBIndexedRecord<Message>>> lastResult = new AtomicReference<>(RecordCursorResult.exhausted());
        final long scanLimit = scrubbingPolicy.getEntriesScanLimit();
        return iterateRangeOnly(store, cursor, this::deleteIndexIfDangling, lastResult, hasMore, recordsScanned, true).thenApply(vignore -> hasMore.get() ? lastResult.get().get().getIndexEntry().getKey() : rangeEnd).thenCompose(cont -> rangeSet.insertRange(store.ensureContextActive(), packOrNull(rangeStart), packOrNull(cont), true).thenApply(ignore -> {
            if (scanLimit > 0) {
                scanCounter += recordsScanned.get();
                if (scanLimit <= scanCounter) {
                    return false;
                }
            }
            return !Objects.equals(cont, rangeEnd);
        }));
    });
}
Also used : IndexEntry(com.apple.foundationdb.record.IndexEntry) RecordMetaData(com.apple.foundationdb.record.RecordMetaData) Arrays(java.util.Arrays) LogMessageKeys(com.apple.foundationdb.record.logging.LogMessageKeys) LoggerFactory(org.slf4j.LoggerFactory) AsyncIterator(com.apple.foundationdb.async.AsyncIterator) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) CompletableFuture(java.util.concurrent.CompletableFuture) AsyncUtil(com.apple.foundationdb.async.AsyncUtil) MetaDataException(com.apple.foundationdb.record.metadata.MetaDataException) RangeSet(com.apple.foundationdb.async.RangeSet) AtomicReference(java.util.concurrent.atomic.AtomicReference) Subspace(com.apple.foundationdb.subspace.Subspace) Transaction(com.apple.foundationdb.Transaction) ExecuteProperties(com.apple.foundationdb.record.ExecuteProperties) IndexScanType(com.apple.foundationdb.record.IndexScanType) Tuple(com.apple.foundationdb.tuple.Tuple) Range(com.apple.foundationdb.Range) RecordCursorResult(com.apple.foundationdb.record.RecordCursorResult) KeyValueLogMessage(com.apple.foundationdb.record.logging.KeyValueLogMessage) ScanProperties(com.apple.foundationdb.record.ScanProperties) IndexBuildProto(com.apple.foundationdb.record.IndexBuildProto) Nonnull(javax.annotation.Nonnull) Nullable(javax.annotation.Nullable) IsolationLevel(com.apple.foundationdb.record.IsolationLevel) Logger(org.slf4j.Logger) IndexState(com.apple.foundationdb.record.IndexState) TupleRange(com.apple.foundationdb.record.TupleRange) Objects(java.util.Objects) AtomicLong(java.util.concurrent.atomic.AtomicLong) List(java.util.List) RecordMetaDataProvider(com.apple.foundationdb.record.RecordMetaDataProvider) Index(com.apple.foundationdb.record.metadata.Index) Message(com.google.protobuf.Message) RecordCursor(com.apple.foundationdb.record.RecordCursor) IndexTypes(com.apple.foundationdb.record.metadata.IndexTypes) API(com.apple.foundationdb.annotation.API) RecordMetaData(com.apple.foundationdb.record.RecordMetaData) KeyValueLogMessage(com.apple.foundationdb.record.logging.KeyValueLogMessage) Message(com.google.protobuf.Message) RecordMetaDataProvider(com.apple.foundationdb.record.RecordMetaDataProvider) RangeSet(com.apple.foundationdb.async.RangeSet) Index(com.apple.foundationdb.record.metadata.Index) RecordCursorResult(com.apple.foundationdb.record.RecordCursorResult) AtomicReference(java.util.concurrent.atomic.AtomicReference) Range(com.apple.foundationdb.Range) TupleRange(com.apple.foundationdb.record.TupleRange) MetaDataException(com.apple.foundationdb.record.metadata.MetaDataException) ExecuteProperties(com.apple.foundationdb.record.ExecuteProperties) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) Transaction(com.apple.foundationdb.Transaction) ScanProperties(com.apple.foundationdb.record.ScanProperties) TupleRange(com.apple.foundationdb.record.TupleRange) Tuple(com.apple.foundationdb.tuple.Tuple) Nonnull(javax.annotation.Nonnull)

Example 43 with TupleRange

use of com.apple.foundationdb.record.TupleRange in project fdb-record-layer by FoundationDB.

the class IndexScanComparisons method getPlannerGraphDetails.

@Override
public void getPlannerGraphDetails(@Nonnull ImmutableList.Builder<String> detailsBuilder, @Nonnull ImmutableMap.Builder<String, Attribute> attributeMapBuilder) {
    if (scanType != IndexScanType.BY_VALUE) {
        detailsBuilder.add("scan type: {{scanType}}");
        attributeMapBuilder.put("scanType", Attribute.gml(scanType.toString()));
    }
    @Nullable final TupleRange tupleRange = scanComparisons.toTupleRangeWithoutContext();
    if (tupleRange != null) {
        detailsBuilder.add("range: " + tupleRange.getLowEndpoint().toString(false) + "{{low}}, {{high}}" + tupleRange.getHighEndpoint().toString(true));
        attributeMapBuilder.put("low", Attribute.gml(tupleRange.getLow() == null ? "-∞" : tupleRange.getLow().toString()));
        attributeMapBuilder.put("high", Attribute.gml(tupleRange.getHigh() == null ? "∞" : tupleRange.getHigh().toString()));
    } else {
        detailsBuilder.add("comparisons: {{comparisons}}");
        attributeMapBuilder.put("comparisons", Attribute.gml(scanComparisons.toString()));
    }
}
Also used : TupleRange(com.apple.foundationdb.record.TupleRange) Nullable(javax.annotation.Nullable)

Example 44 with TupleRange

use of com.apple.foundationdb.record.TupleRange in project fdb-record-layer by FoundationDB.

the class TextScan method scan.

@Nonnull
// try-with-resources - the two cursors returned cannot be closed because they are wrapped and returned
@SuppressWarnings("squid:S2095")
private <M extends Message> RecordCursor<IndexEntry> scan(@Nonnull FDBRecordStoreBase<M> store, @Nonnull EvaluationContext context, @Nullable Tuple prefix, @Nullable TupleRange suffix, @Nonnull Index index, @Nonnull List<String> tokenList, @Nullable byte[] continuation, @Nonnull ScanProperties scanProperties) {
    if (tokenList.isEmpty()) {
        return RecordCursor.empty();
    }
    final int prefixEntries = 1 + (prefix != null ? prefix.size() : 0);
    final Comparisons.Type comparisonType = textComparison.getType();
    if (comparisonType.equals(Comparisons.Type.TEXT_CONTAINS_PREFIX) || (tokenList.size() == 1 && (comparisonType.equals(Comparisons.Type.TEXT_CONTAINS_ALL_PREFIXES) || comparisonType.equals(Comparisons.Type.TEXT_CONTAINS_ANY_PREFIX)))) {
        if (tokenList.size() != 1) {
            throw new RecordCoreException("text prefix comparison included " + tokenList.size() + " comparands instead of one");
        }
        return scanTokenPrefix(store, tokenList.get(0), prefix, suffix, index, scanProperties).apply(continuation);
    } else if (tokenList.size() == 1) {
        // is necessary, not just nice to have.
        return scanToken(store, tokenList.get(0), prefix, suffix, index, scanProperties).apply(continuation);
    } else if (comparisonType.equals(Comparisons.Type.TEXT_CONTAINS_ALL)) {
        // Take the intersection of all children. Note that to handle skip and the returned row limit correctly,
        // the skip and limit are both removed and then applied later.
        final ScanProperties childScanProperties = scanProperties.with(ExecuteProperties::clearSkipAndLimit);
        List<Function<byte[], RecordCursor<IndexEntry>>> intersectionChildren = tokenList.stream().map(token -> scanToken(store, token, prefix, suffix, index, childScanProperties)).collect(Collectors.toList());
        return IntersectionCursor.create(suffixComparisonKeyFunction(prefixEntries), scanProperties.isReverse(), intersectionChildren, continuation, store.getTimer()).skip(scanProperties.getExecuteProperties().getSkip()).limitRowsTo(scanProperties.getExecuteProperties().getReturnedRowLimit());
    } else if (comparisonType.equals(Comparisons.Type.TEXT_CONTAINS_ALL_PREFIXES)) {
        final Comparisons.TextContainsAllPrefixesComparison allPrefixesComparison = (Comparisons.TextContainsAllPrefixesComparison) textComparison;
        final ScanProperties childScanProperties = scanProperties.with(ExecuteProperties::clearSkipAndLimit);
        List<Function<byte[], RecordCursor<IndexEntry>>> intersectionChildren = tokenList.stream().map(token -> scanTokenPrefix(store, token, prefix, suffix, index, childScanProperties)).collect(Collectors.toList());
        return ProbableIntersectionCursor.create(suffixComparisonKeyFunction(prefixEntries), intersectionChildren, allPrefixesComparison.getExpectedRecords(), allPrefixesComparison.getFalsePositivePercentage(), continuation, store.getTimer()).skip(scanProperties.getExecuteProperties().getSkip()).limitRowsTo(scanProperties.getExecuteProperties().getReturnedRowLimit());
    } else if (comparisonType.equals(Comparisons.Type.TEXT_CONTAINS_ANY)) {
        // Take the union of all children. Note that to handle skip and the returned row limit correctly,
        // the skip is removed from the children and applied to the returned cursor. Also, the limit
        // is adjusted upwards and then must be applied again to returned union.
        final ScanProperties childScanProperties = scanProperties.with(ExecuteProperties::clearSkipAndAdjustLimit);
        List<Function<byte[], RecordCursor<IndexEntry>>> unionChildren = tokenList.stream().map(token -> scanToken(store, token, prefix, suffix, index, childScanProperties)).collect(Collectors.toList());
        return UnionCursor.create(suffixComparisonKeyFunction(prefixEntries), scanProperties.isReverse(), unionChildren, continuation, store.getTimer()).skip(scanProperties.getExecuteProperties().getSkip()).limitRowsTo(scanProperties.getExecuteProperties().getReturnedRowLimit());
    } else if (comparisonType.equals(Comparisons.Type.TEXT_CONTAINS_ANY_PREFIX)) {
        final ScanProperties childScanProperties = scanProperties.with(ExecuteProperties::clearSkipAndAdjustLimit);
        List<Function<byte[], RecordCursor<IndexEntry>>> unionChildren = tokenList.stream().map(token -> scanTokenPrefix(store, token, prefix, suffix, index, childScanProperties)).collect(Collectors.toList());
        return UnorderedUnionCursor.create(unionChildren, continuation, store.getTimer()).skip(scanProperties.getExecuteProperties().getSkip()).limitRowsTo(scanProperties.getExecuteProperties().getReturnedRowLimit());
    } else {
        // Apply the filter based on the position lists
        final Function<List<IndexEntry>, Boolean> predicate;
        if (comparisonType.equals(Comparisons.Type.TEXT_CONTAINS_ALL_WITHIN) && textComparison instanceof Comparisons.TextWithMaxDistanceComparison) {
            int maxDistance = ((Comparisons.TextWithMaxDistanceComparison) textComparison).getMaxDistance();
            predicate = entries -> entriesContainAllWithin(entries, maxDistance);
        } else if (comparisonType.equals(Comparisons.Type.TEXT_CONTAINS_PHRASE)) {
            List<String> tokensWithStopWords = getTokenList(store, context, false);
            predicate = entries -> entriesContainPhrase(entries, tokensWithStopWords);
        } else {
            throw new RecordCoreException("unsupported comparison type for text query: " + comparisonType);
        }
        // It's either TEXT_CONTAINS_ALL_WITHIN_DISTANCE or TEXT_CONTAINS_PHRASE. In any case, we need to scan
        // all tokens, intersect, and then apply a filter on the returned list.
        final ScanProperties childScanProperties = scanProperties.with(ExecuteProperties::clearSkipAndLimit);
        List<Function<byte[], RecordCursor<IndexEntry>>> intersectionChildren = tokenList.stream().map(token -> scanToken(store, token, prefix, suffix, index, childScanProperties)).collect(Collectors.toList());
        final RecordCursor<List<IndexEntry>> intersectionCursor = IntersectionMultiCursor.create(suffixComparisonKeyFunction(prefixEntries), scanProperties.isReverse(), intersectionChildren, continuation, store.getTimer());
        return intersectionCursor.filterInstrumented(predicate, store.getTimer(), inCounts, duringEvents, successCounts, failureCounts).map(indexEntries -> indexEntries.get(0)).skip(scanProperties.getExecuteProperties().getSkip()).limitRowsTo(scanProperties.getExecuteProperties().getReturnedRowLimit());
    }
}
Also used : IndexEntry(com.apple.foundationdb.record.IndexEntry) StoreTimer(com.apple.foundationdb.record.provider.common.StoreTimer) PriorityQueue(java.util.PriorityQueue) Function(java.util.function.Function) PlanHashable(com.apple.foundationdb.record.PlanHashable) ArrayList(java.util.ArrayList) UnorderedUnionCursor(com.apple.foundationdb.record.provider.foundationdb.cursors.UnorderedUnionCursor) ExecuteProperties(com.apple.foundationdb.record.ExecuteProperties) IndexScanType(com.apple.foundationdb.record.IndexScanType) Tuple(com.apple.foundationdb.tuple.Tuple) TextIndexMaintainer(com.apple.foundationdb.record.provider.foundationdb.indexes.TextIndexMaintainer) Pair(org.apache.commons.lang3.tuple.Pair) RecordCoreException(com.apple.foundationdb.record.RecordCoreException) ScanProperties(com.apple.foundationdb.record.ScanProperties) UnionCursor(com.apple.foundationdb.record.provider.foundationdb.cursors.UnionCursor) Nonnull(javax.annotation.Nonnull) Nullable(javax.annotation.Nullable) FDBRecordStoreBase(com.apple.foundationdb.record.provider.foundationdb.FDBRecordStoreBase) ImmutableSet(com.google.common.collect.ImmutableSet) Iterator(java.util.Iterator) FDBStoreTimer(com.apple.foundationdb.record.provider.foundationdb.FDBStoreTimer) Set(java.util.Set) Collectors(java.util.stream.Collectors) IntersectionCursor(com.apple.foundationdb.record.provider.foundationdb.cursors.IntersectionCursor) TupleRange(com.apple.foundationdb.record.TupleRange) Objects(java.util.Objects) TextTokenizer(com.apple.foundationdb.record.provider.common.text.TextTokenizer) Comparisons(com.apple.foundationdb.record.query.expressions.Comparisons) List(java.util.List) Index(com.apple.foundationdb.record.metadata.Index) EvaluationContext(com.apple.foundationdb.record.EvaluationContext) ObjectPlanHash(com.apple.foundationdb.record.ObjectPlanHash) TupleHelpers(com.apple.foundationdb.tuple.TupleHelpers) IntersectionMultiCursor(com.apple.foundationdb.record.provider.foundationdb.cursors.IntersectionMultiCursor) Message(com.google.protobuf.Message) RecordCursor(com.apple.foundationdb.record.RecordCursor) API(com.apple.foundationdb.annotation.API) ProbableIntersectionCursor(com.apple.foundationdb.record.provider.foundationdb.cursors.ProbableIntersectionCursor) Comparator(java.util.Comparator) Collections(java.util.Collections) RecordCursor(com.apple.foundationdb.record.RecordCursor) IndexEntry(com.apple.foundationdb.record.IndexEntry) RecordCoreException(com.apple.foundationdb.record.RecordCoreException) ExecuteProperties(com.apple.foundationdb.record.ExecuteProperties) Function(java.util.function.Function) Comparisons(com.apple.foundationdb.record.query.expressions.Comparisons) ScanProperties(com.apple.foundationdb.record.ScanProperties) ArrayList(java.util.ArrayList) List(java.util.List) Nonnull(javax.annotation.Nonnull)

Example 45 with TupleRange

use of com.apple.foundationdb.record.TupleRange in project fdb-record-layer by FoundationDB.

the class TextScan method scan.

/**
 * Scan the store to produce a cursor of index entries that all satisfy the comparison.
 *
 * @param store the record store for the query
 * @param context the query evaluation context
 * @param continuation a continuation from a previous scan to resume query execution
 * @param scanProperties execution properties of this scan
 * @param <M> message type associated with the store and evaluation context
 * @return a cursor of index entries from the given scan
 */
@Nonnull
public <M extends Message> RecordCursor<IndexEntry> scan(@Nonnull FDBRecordStoreBase<M> store, @Nonnull EvaluationContext context, @Nullable byte[] continuation, @Nonnull ScanProperties scanProperties) {
    final Tuple prefix = groupingComparisons != null ? groupingComparisons.toTupleRange(store, context).getHigh() : null;
    final TupleRange suffix = suffixComparisons != null ? suffixComparisons.toTupleRange(store, context) : null;
    final List<String> tokenList = getTokenList(store, context);
    return scan(store, context, prefix, suffix, index, tokenList, continuation, scanProperties);
}
Also used : TupleRange(com.apple.foundationdb.record.TupleRange) Tuple(com.apple.foundationdb.tuple.Tuple) Nonnull(javax.annotation.Nonnull)

Aggregations

TupleRange (com.apple.foundationdb.record.TupleRange)48 Tuple (com.apple.foundationdb.tuple.Tuple)29 Nonnull (javax.annotation.Nonnull)28 Message (com.google.protobuf.Message)21 Index (com.apple.foundationdb.record.metadata.Index)16 Test (org.junit.jupiter.api.Test)16 ScanProperties (com.apple.foundationdb.record.ScanProperties)15 FDBRecordContext (com.apple.foundationdb.record.provider.foundationdb.FDBRecordContext)14 Nullable (javax.annotation.Nullable)14 ExecuteProperties (com.apple.foundationdb.record.ExecuteProperties)11 IndexEntry (com.apple.foundationdb.record.IndexEntry)11 List (java.util.List)11 IsolationLevel (com.apple.foundationdb.record.IsolationLevel)10 RecordCoreException (com.apple.foundationdb.record.RecordCoreException)10 RecordCursor (com.apple.foundationdb.record.RecordCursor)10 IndexScanType (com.apple.foundationdb.record.IndexScanType)9 CompletableFuture (java.util.concurrent.CompletableFuture)9 Range (com.apple.foundationdb.Range)8 API (com.apple.foundationdb.annotation.API)8 AsyncUtil (com.apple.foundationdb.async.AsyncUtil)8