use of com.apple.foundationdb.record.EndpointType in project fdb-record-layer by FoundationDB.
the class KeySpaceDirectory method getValueRange.
@Nonnull
private CompletableFuture<KeyRange> getValueRange(@Nonnull FDBRecordContext context, @Nullable ValueRange<?> valueRange, @Nonnull Subspace subspace) {
final byte[] startKey;
final byte[] stopKey;
final EndpointType startType;
final EndpointType stopType;
if (getValue() == KeySpaceDirectory.ANY_VALUE) {
if (valueRange != null && valueRange.getLowEndpoint() != EndpointType.TREE_START) {
if (KeyType.typeOf(valueRange.getLow()) != getKeyType()) {
throw invalidValueTypeException(KeyType.typeOf(valueRange.getLow()), getKeyType(), getName(), valueRange);
}
startKey = subspace.pack(valueRange.getLow());
startType = valueRange.getLowEndpoint();
if (startType != EndpointType.RANGE_INCLUSIVE && startType != EndpointType.RANGE_EXCLUSIVE) {
throw new RecordCoreArgumentException("Endpoint type not supported for directory list", LogMessageKeys.ENDPOINT_TYPE, startType);
}
} else {
final byte[] subspaceBytes = subspace.pack();
startKey = new byte[subspaceBytes.length + 1];
System.arraycopy(subspaceBytes, 0, startKey, 0, subspaceBytes.length);
startKey[subspaceBytes.length] = getKeyType().getTypeLowBounds();
startType = EndpointType.RANGE_INCLUSIVE;
}
if (valueRange != null && valueRange.getHighEndpoint() != EndpointType.TREE_END) {
if (KeyType.typeOf(valueRange.getHigh()) != getKeyType()) {
throw invalidValueTypeException(KeyType.typeOf(valueRange.getHigh()), getKeyType(), getName(), valueRange);
}
stopKey = subspace.pack(valueRange.getHigh());
stopType = valueRange.getHighEndpoint();
if (stopType != EndpointType.RANGE_INCLUSIVE && stopType != EndpointType.RANGE_EXCLUSIVE) {
throw new RecordCoreArgumentException("Endpoint type not supported for directory list", LogMessageKeys.ENDPOINT_TYPE, stopType);
}
} else {
final byte[] subspaceBytes = subspace.pack();
stopKey = new byte[subspaceBytes.length + 1];
System.arraycopy(subspaceBytes, 0, stopKey, 0, subspaceBytes.length);
stopKey[subspaceBytes.length] = getKeyType().getTypeHighBounds();
stopType = EndpointType.RANGE_EXCLUSIVE;
}
return CompletableFuture.completedFuture(new KeyRange(startKey, startType, stopKey, stopType));
} else {
if (valueRange != null) {
throw new RecordCoreArgumentException("range is not applicable when the subdirectory has a value.", LogMessageKeys.DIR_NAME, getName(), LogMessageKeys.RANGE, valueRange);
}
return toTupleValueAsync(context, this.value).thenApply(resolvedValue -> {
final byte[] key = subspace.pack(Tuple.from(resolvedValue.getResolvedValue()));
return new KeyRange(key, EndpointType.RANGE_INCLUSIVE, ByteArrayUtil.strinc(key), EndpointType.RANGE_EXCLUSIVE);
});
}
}
use of com.apple.foundationdb.record.EndpointType in project fdb-record-layer by FoundationDB.
the class KeySpaceDirectoryTest method listRangeTestCases.
private List<Pair<ValueRange<Object>, List<Tuple>>> listRangeTestCases(List<Tuple> values) {
values.sort(null);
// Size >= 1. It is very likely to be 5 (but can be smaller) expect when the key type is NULL or BOOLEAN.
int size = values.size();
List<Pair<ValueRange<Object>, List<Tuple>>> testCases = new LinkedList<>();
testCases.add(Pair.of(null, new ArrayList<>(values)));
testCases.add(newTestCase(values.get(0), null, EndpointType.RANGE_INCLUSIVE, EndpointType.TREE_END, new ArrayList<>(values)));
testCases.add(newTestCase(values.get(0), null, EndpointType.RANGE_EXCLUSIVE, EndpointType.TREE_END, new ArrayList<>(values.subList(1, size))));
testCases.add(newTestCase(null, values.get(size - 1), EndpointType.TREE_START, EndpointType.RANGE_INCLUSIVE, new ArrayList<>(values)));
testCases.add(newTestCase(null, values.get(size - 1), EndpointType.TREE_START, EndpointType.RANGE_EXCLUSIVE, new ArrayList<>(values.subList(0, size - 1))));
testCases.add(newTestCase(null, null, EndpointType.TREE_START, EndpointType.TREE_END, new ArrayList<>(values)));
testCases.add(newTestCase(values.get(0), values.get(0), EndpointType.RANGE_INCLUSIVE, EndpointType.RANGE_INCLUSIVE, new ArrayList<>(values.subList(0, 1))));
// Only test this for LONG because it might be tricky to modify values for some other types.
if (KeyType.LONG.isMatch(values.get(0))) {
Tuple first = values.get(0);
Tuple justBeforeFirst = Tuple.from((Long) first.get(0) - 1);
Tuple justAfterFirst = Tuple.from((Long) first.get(0) + 1);
Tuple last = values.get(size - 1);
Tuple justBeforeLast = Tuple.from((Long) last.get(0) - 1);
Tuple justAfterLast = Tuple.from((Long) last.get(0) + 1);
// Endpoint type does not matters to the values who are not in the collection.
for (EndpointType endpointType : Arrays.asList(EndpointType.RANGE_INCLUSIVE, EndpointType.RANGE_EXCLUSIVE)) {
testCases.add(newTestCase(justBeforeFirst, null, endpointType, EndpointType.TREE_END, new ArrayList<>(values.subList(0, size))));
testCases.add(newTestCase(justAfterFirst, null, endpointType, EndpointType.TREE_END, new ArrayList<>(values.subList(1, size))));
testCases.add(newTestCase(null, justBeforeLast, EndpointType.TREE_START, endpointType, new ArrayList<>(values.subList(0, size - 1))));
testCases.add(newTestCase(null, justAfterLast, EndpointType.TREE_START, endpointType, new ArrayList<>(values.subList(0, size))));
}
}
if (size >= 2) {
Tuple first = values.get(0);
Tuple last = values.get(size - 1);
testCases.add(newTestCase(first, last, EndpointType.RANGE_INCLUSIVE, EndpointType.RANGE_INCLUSIVE, new ArrayList<>(values.subList(0, size))));
testCases.add(newTestCase(first, last, EndpointType.RANGE_INCLUSIVE, EndpointType.RANGE_EXCLUSIVE, new ArrayList<>(values.subList(0, size - 1))));
testCases.add(newTestCase(first, last, EndpointType.RANGE_EXCLUSIVE, EndpointType.RANGE_INCLUSIVE, new ArrayList<>(values.subList(1, size))));
testCases.add(newTestCase(first, last, EndpointType.RANGE_EXCLUSIVE, EndpointType.RANGE_EXCLUSIVE, new ArrayList<>(values.subList(1, size - 1))));
}
if (size >= 4) {
Tuple second = values.get(1);
Tuple secondLast = values.get(size - 2);
testCases.add(newTestCase(second, secondLast, EndpointType.RANGE_INCLUSIVE, EndpointType.RANGE_INCLUSIVE, new ArrayList<>(values.subList(1, size - 1))));
testCases.add(newTestCase(second, secondLast, EndpointType.RANGE_INCLUSIVE, EndpointType.RANGE_EXCLUSIVE, new ArrayList<>(values.subList(1, size - 2))));
testCases.add(newTestCase(second, secondLast, EndpointType.RANGE_EXCLUSIVE, EndpointType.RANGE_INCLUSIVE, new ArrayList<>(values.subList(2, size - 1))));
testCases.add(newTestCase(second, secondLast, EndpointType.RANGE_EXCLUSIVE, EndpointType.RANGE_EXCLUSIVE, new ArrayList<>(values.subList(2, size - 2))));
}
return testCases;
}
use of com.apple.foundationdb.record.EndpointType in project fdb-record-layer by FoundationDB.
the class FDBRecordStore method scanTypedRecords.
@Nonnull
public <M extends Message> RecordCursor<FDBStoredRecord<M>> scanTypedRecords(@Nonnull RecordSerializer<M> typedSerializer, @Nullable final Tuple low, @Nullable final Tuple high, @Nonnull final EndpointType lowEndpoint, @Nonnull final EndpointType highEndpoint, @Nullable byte[] continuation, @Nonnull ScanProperties scanProperties) {
final RecordMetaData metaData = metaDataProvider.getRecordMetaData();
final Subspace recordsSubspace = recordsSubspace();
final SplitHelper.SizeInfo sizeInfo = new SplitHelper.SizeInfo();
final RecordCursor<FDBRawRecord> rawRecords;
if (metaData.isSplitLongRecords()) {
RecordCursor<KeyValue> keyValues = KeyValueCursor.Builder.withSubspace(recordsSubspace).setContext(context).setContinuation(continuation).setLow(low, lowEndpoint).setHigh(high, highEndpoint).setScanProperties(scanProperties.with(ExecuteProperties::clearRowAndTimeLimits).with(ExecuteProperties::clearState)).build();
rawRecords = new SplitHelper.KeyValueUnsplitter(context, recordsSubspace, keyValues, useOldVersionFormat(), sizeInfo, scanProperties.isReverse(), new CursorLimitManager(context, scanProperties.with(ExecuteProperties::clearReturnedRowLimit))).skip(scanProperties.getExecuteProperties().getSkip()).limitRowsTo(scanProperties.getExecuteProperties().getReturnedRowLimit());
} else {
KeyValueCursor.Builder keyValuesBuilder = KeyValueCursor.Builder.withSubspace(recordsSubspace).setContext(context).setContinuation(continuation).setLow(low, lowEndpoint).setHigh(high, highEndpoint);
if (omitUnsplitRecordSuffix) {
rawRecords = keyValuesBuilder.setScanProperties(scanProperties).build().map(kv -> {
sizeInfo.set(kv);
Tuple primaryKey = SplitHelper.unpackKey(recordsSubspace, kv);
return new FDBRawRecord(primaryKey, kv.getValue(), null, sizeInfo);
});
} else {
final ScanProperties finalScanProperties = scanProperties.with(executeProperties -> {
final ExecuteProperties.Builder builder = executeProperties.toBuilder().clearTimeLimit().clearSkipAndAdjustLimit().clearState();
int returnedRowLimit = builder.getReturnedRowLimitOrMax();
if (returnedRowLimit != Integer.MAX_VALUE) {
// Adjust limit to twice the supplied limit in case there are versions in the records
builder.setReturnedRowLimit(2 * returnedRowLimit);
}
return builder.build();
});
rawRecords = new SplitHelper.KeyValueUnsplitter(context, recordsSubspace, keyValuesBuilder.setScanProperties(finalScanProperties).build(), useOldVersionFormat(), sizeInfo, scanProperties.isReverse(), new CursorLimitManager(context, scanProperties.with(ExecuteProperties::clearReturnedRowLimit))).skip(scanProperties.getExecuteProperties().getSkip()).limitRowsTo(scanProperties.getExecuteProperties().getReturnedRowLimit());
}
}
RecordCursor<FDBStoredRecord<M>> result = rawRecords.mapPipelined(rawRecord -> {
final Optional<CompletableFuture<FDBRecordVersion>> versionFutureOptional;
if (useOldVersionFormat()) {
// Older format versions: do a separate read to get the version.
versionFutureOptional = loadRecordVersionAsync(rawRecord.getPrimaryKey(), scanProperties.getExecuteProperties().getIsolationLevel().isSnapshot());
} else {
// Newer format versions: the version is either in the record or it is not -- do not do another read.
versionFutureOptional = Optional.empty();
}
return deserializeRecord(typedSerializer, rawRecord, metaData, versionFutureOptional);
}, pipelineSizer.getPipelineSize(PipelineOperation.KEY_TO_RECORD));
return context.instrument(FDBStoreTimer.Events.SCAN_RECORDS, result);
}
use of com.apple.foundationdb.record.EndpointType 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.record.EndpointType in project fdb-record-layer by FoundationDB.
the class TimeWindowLeaderboardIndexMaintainer method negateScoreRange.
/**
* Negate the score part of the given range, which is after the group prefix and before the timestamp and any
* other tiebreakers.
* @param range the range of scores in normal order
* @return a range with scores negated so that they sort in reverse order
*/
protected TupleRange negateScoreRange(@Nonnull TupleRange range) {
final int groupPrefixSize = getGroupingCount();
Tuple low = range.getLow();
Tuple high = range.getHigh();
EndpointType lowEndpoint = range.getLowEndpoint();
EndpointType highEndpoint = range.getHighEndpoint();
if (low == null || low.size() < groupPrefixSize) {
if (lowEndpoint == EndpointType.TREE_START) {
lowEndpoint = EndpointType.TREE_END;
}
} else {
low = negateScoreForHighScoreFirst(low, groupPrefixSize);
}
if (high == null || high.size() < groupPrefixSize) {
if (lowEndpoint == EndpointType.TREE_END) {
lowEndpoint = EndpointType.TREE_START;
}
} else {
high = negateScoreForHighScoreFirst(high, groupPrefixSize);
}
return new TupleRange(high, low, highEndpoint, lowEndpoint);
}
Aggregations