use of com.apple.foundationdb.record.RecordCursorResult in project fdb-record-layer by FoundationDB.
the class MapPipelinedCursor method cancelPendingFutures.
@Nonnull
private RecordCursorContinuation cancelPendingFutures() {
Iterator<CompletableFuture<RecordCursorResult<V>>> iter = pipeline.iterator();
// The earliest continuation we could need to start with is the one from the last returned result.
// We may, however, return more results if they are already completed.
RecordCursorContinuation continuation = nextResult.getContinuation();
while (iter.hasNext()) {
CompletableFuture<RecordCursorResult<V>> pendingEntry = iter.next();
if (!pendingEntry.isDone()) {
// futures, remove them from the pipeline, and do *not* update the continuation.
while (true) {
iter.remove();
pendingEntry.cancel(false);
if (!iter.hasNext()) {
return continuation;
}
pendingEntry = iter.next();
}
} else {
// Entry is done, so this cursor will return this result. Keep the entry
// in the pipeline, and update the continuation.
continuation = pendingEntry.join().getContinuation();
}
}
return continuation;
}
use of com.apple.foundationdb.record.RecordCursorResult in project fdb-record-layer by FoundationDB.
the class OrElseCursor method onNext.
@Nonnull
@Override
public CompletableFuture<RecordCursorResult<T>> onNext() {
if (nextResult != null && !nextResult.hasNext()) {
return CompletableFuture.completedFuture(nextResult);
}
final CompletableFuture<RecordCursorResult<T>> innerFuture;
switch(state) {
case USE_INNER:
innerFuture = inner.onNext();
break;
case USE_OTHER:
innerFuture = other.onNext();
break;
case UNDECIDED:
innerFuture = inner.onNext().thenCompose(result -> {
if (result.hasNext()) {
// Inner cursor has produced a value, so we take the inner branch.
state = RecordCursorProto.OrElseContinuation.State.USE_INNER;
return CompletableFuture.completedFuture(result);
} else if (result.getNoNextReason().isOutOfBand()) {
// Not sure if the inner cursor will ever produce a value.
return CompletableFuture.completedFuture(result);
} else {
// Inner cursor will never produce a value.
state = RecordCursorProto.OrElseContinuation.State.USE_OTHER;
other = func.apply(getExecutor());
return other.onNext();
}
});
break;
default:
throw new UnknownOrElseCursorStateException();
}
return innerFuture.thenApply(result -> result.withContinuation(new Continuation(state, result.getContinuation()))).thenApply(this::postProcess);
}
use of com.apple.foundationdb.record.RecordCursorResult in project fdb-record-layer by FoundationDB.
the class FDBInQueryTest method testInWithContinuation.
/**
* Verify that an IN join is executed correctly when continuations are used.
*/
@DualPlannerTest
void testInWithContinuation() throws Exception {
final RecordMetaDataHook recordMetaDataHook = metaData -> {
metaData.getRecordType("MyRecord").setPrimaryKey(field("str_value"));
metaData.addIndex("MyRecord", "ind", field("header").nest(field("rec_no"), field("path")));
};
setupRecordsWithHeader(recordMetaDataHook, (i, record) -> {
record.setStrValue("_" + i);
record.getHeaderBuilder().setRecNo(i % 5).setPath("String" + i % 50).setNum(i);
});
List<String> ls = asList("String1", "String6", "String25", "String11");
RecordQuery query = RecordQuery.newBuilder().setRecordType("MyRecord").setFilter(Query.field("header").matches(Query.and(Query.field("rec_no").equalsValue(1L), Query.field("path").in(ls)))).build();
// Index(ind [EQUALS 1, EQUALS $__in_path__0]) WHERE __in_path__0 IN [String1, String6, String25, String11]
RecordQueryPlan plan = planner.plan(query);
if (planner instanceof RecordQueryPlanner) {
assertMatchesExactly(plan, inValuesJoinPlan(indexPlan().where(indexName("ind")).and(scanComparisons(range("[EQUALS 1, EQUALS $__in_path__0]")))).where(inValuesList(equalsObject(ls))));
assertEquals(1075745133, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
assertEquals(1864571255, plan.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
assertEquals(-847163347, plan.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
} else {
assertMatchesExactly(plan, fetchFromPartialRecordPlan(inValuesJoinPlan(coveringIndexPlan().where(indexPlanOf(indexPlan().where(indexName("ind")).and(scanComparisons(equalities(exactly(equalsObject(new Comparisons.SimpleComparison(Comparisons.Type.EQUALS, 1L)), anyParameterComparison()))))))).where(inValuesList(equalsObject(ls)))));
assertEquals(559717093, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
assertEquals(-744622543, plan.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
assertEquals(-1523769992, plan.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
}
// result: [ "_1", "_51", "_56", "_6", "_11", "_61"]
final Holder<byte[]> continuation = new Holder<>();
queryRecordsWithHeader(recordMetaDataHook, plan, null, 10, cursor -> {
RecordCursorResult<TestRecordsWithHeaderProto.MyRecord.Builder> result = cursor.getNext();
assertEquals("_1", Objects.requireNonNull(result.get()).getStrValue());
continuation.value = result.getContinuation().toBytes();
}, TestHelpers::assertDiscardedNone);
queryRecordsWithHeader(recordMetaDataHook, planner.plan(query), continuation.value, 10, cursor -> {
RecordCursorResult<TestRecordsWithHeaderProto.MyRecord.Builder> result = cursor.getNext();
assertEquals("_51", Objects.requireNonNull(result.get()).getStrValue());
result = cursor.getNext();
assertEquals("_56", Objects.requireNonNull(result.get()).getStrValue());
continuation.value = result.getContinuation().toBytes();
}, TestHelpers::assertDiscardedNone);
RecordQuery query2 = RecordQuery.newBuilder().setRecordType("MyRecord").setFilter(Query.field("header").matches(Query.and(Query.field("rec_no").equalsValue(1L), Query.field("path").in(asList("String6", "String11"))))).build();
// we miss _6
// Note, Since we have two equals operands, the continuation ends up being relative to that
// and is just the id, so we want the id of the continuation point from before ("_56") to be greater than the
// first id of the new continuation ("_11")
queryRecordsWithHeader(recordMetaDataHook, planner.plan(query2), continuation.value, 10, cursor -> {
RecordCursorResult<TestRecordsWithHeaderProto.MyRecord.Builder> result = cursor.getNext();
assertEquals("_11", Objects.requireNonNull(result.get()).getStrValue());
result = cursor.getNext();
assertEquals("_61", Objects.requireNonNull(result.get()).getStrValue());
result = cursor.getNext();
assertFalse(result.hasNext());
}, TestHelpers::assertDiscardedNone);
}
use of com.apple.foundationdb.record.RecordCursorResult in project fdb-record-layer by FoundationDB.
the class TextIndexTest method performQueryWithRecordStoreScan.
@Nonnull
private Set<Long> performQueryWithRecordStoreScan(@Nonnull RecordMetaDataHook hook, @Nonnull QueryComponent filter) throws Exception {
final ScanProperties scanProperties = new ScanProperties(ExecuteProperties.newBuilder().setTimeLimit(3000).build());
Set<Long> results = new HashSet<>();
byte[] continuation = null;
do {
try (FDBRecordContext context = openContext()) {
openRecordStore(context);
try (RecordCursor<Long> cursor = recordStore.scanRecords(continuation, scanProperties).filter(record -> record.getRecordType().getName().equals(SIMPLE_DOC)).filter(record -> filter.eval(recordStore, EvaluationContext.EMPTY, record) == Boolean.TRUE).map(record -> record.getPrimaryKey().getLong(0))) {
cursor.forEach(results::add).get();
RecordCursorResult<Long> noNextResult = cursor.getNext();
continuation = noNextResult.getContinuation().toBytes();
}
}
} while (continuation != null);
return results;
}
use of com.apple.foundationdb.record.RecordCursorResult in project fdb-record-layer by FoundationDB.
the class TextIndexTest method scanMultipleWithScanRecordLimits.
private void scanMultipleWithScanRecordLimits(@Nonnull Index index, @Nonnull List<String> tokens, int scanRecordLimit, boolean reverse) throws Exception {
try (FDBRecordContext context = openContext()) {
openRecordStore(context);
ScanProperties scanProperties = ExecuteProperties.newBuilder().setScannedRecordsLimit(scanRecordLimit).build().asScanProperties(reverse);
List<RecordCursor<IndexEntry>> cursors = tokens.stream().map(token -> recordStore.scanIndex(index, BY_TEXT_TOKEN, TupleRange.allOf(Tuple.from(token)), null, scanProperties)).collect(Collectors.toList());
int cursorIndex = 0;
int retrieved = 0;
while (!cursors.isEmpty()) {
RecordCursor<IndexEntry> cursor = cursors.get(cursorIndex);
RecordCursorResult<IndexEntry> result = cursor.getNext();
if (result.hasNext()) {
retrieved++;
cursorIndex = (cursorIndex + 1) % cursors.size();
} else {
if (!result.getNoNextReason().isSourceExhausted()) {
assertEquals(SCAN_LIMIT_REACHED, result.getNoNextReason());
}
cursors.remove(cursorIndex);
if (cursorIndex == cursors.size()) {
cursorIndex = 0;
}
}
}
// With the order that they are retrieved, the maximum value is the scanRecordLimit
// or the number of tokens.
assertThat(retrieved, lessThanOrEqualTo(Math.max(scanRecordLimit, tokens.size())));
}
}
Aggregations