use of com.apple.foundationdb.record.metadata.Index in project fdb-record-layer by FoundationDB.
the class VersionIndexTest method withMetaDataRebuilds.
@ParameterizedTest(name = "withMetaDataRebuilds [formatVersion = {0}, splitLongRecords = {1}]")
@MethodSource("formatVersionArguments")
@SuppressWarnings("try")
public void withMetaDataRebuilds(int testFormatVersion, boolean testSplitLongRecords) {
formatVersion = testFormatVersion;
splitLongRecords = testSplitLongRecords;
RecordMetaDataHook firstHook = metaDataBuilder -> {
metaDataBuilder.setSplitLongRecords(splitLongRecords);
metaDataBuilder.addUniversalIndex(new Index("globalCount", new GroupingKeyExpression(EmptyKeyExpression.EMPTY, 0), IndexTypes.COUNT));
metaDataBuilder.addUniversalIndex(new Index("globalVersion", VersionKeyExpression.VERSION, IndexTypes.VERSION));
};
RecordMetaDataHook secondHook = metaDataBuilder -> {
firstHook.apply(metaDataBuilder);
metaDataBuilder.removeIndex("globalVersion");
metaDataBuilder.setStoreRecordVersions(false);
};
RecordMetaDataHook thirdHook = metaDataBuilder -> {
secondHook.apply(metaDataBuilder);
metaDataBuilder.setStoreRecordVersions(true);
metaDataBuilder.addUniversalIndex(new Index("globalVersion2", VersionKeyExpression.VERSION, IndexTypes.VERSION));
};
MySimpleRecord record1 = MySimpleRecord.newBuilder().setRecNo(1066L).build();
FDBRecordVersion version1;
MySimpleRecord record2 = MySimpleRecord.newBuilder().setRecNo(1776L).build();
MySimpleRecord record3 = MySimpleRecord.newBuilder().setRecNo(1955L).build();
FDBRecordVersion version3;
RecordQuery query = RecordQuery.newBuilder().setSort(VersionKeyExpression.VERSION).build();
// First with version on.
try (FDBRecordContext context = openContext(firstHook)) {
FDBStoredRecord<?> storedRecord = recordStore.saveRecord(record1);
assertTrue(storedRecord.hasVersion());
context.commit();
version1 = FDBRecordVersion.complete(context.getVersionStamp(), storedRecord.getVersion().getLocalVersion());
}
try (FDBRecordContext context = openContext(firstHook)) {
FDBStoredRecord<?> loadedRecord = recordStore.loadRecord(Tuple.from(1066L));
assertNotNull(loadedRecord);
assertTrue(loadedRecord.hasVersion());
assertEquals(version1, loadedRecord.getVersion());
RecordQueryPlan plan = planner.plan(query);
assertThat(plan, indexScan("globalVersion"));
List<FDBQueriedRecord<Message>> records = recordStore.executeQuery(plan).asList().join();
assertEquals(1, records.size());
FDBQueriedRecord<Message> queriedRecord = records.get(0);
assertEquals(Tuple.from(1066L), queriedRecord.getPrimaryKey());
assertTrue(queriedRecord.hasVersion());
assertEquals(version1, queriedRecord.getVersion());
}
// Now with version off.
try (FDBRecordContext context = openContext(secondHook)) {
FDBStoredRecord<?> storedRecord = recordStore.saveRecord(record2);
assertFalse(storedRecord.hasVersion());
context.commit();
}
try (FDBRecordContext context = openContext(secondHook)) {
FDBStoredRecord<?> loadedRecord1 = recordStore.loadRecord(Tuple.from(1066L));
assertNotNull(loadedRecord1);
assertEquals(testFormatVersion >= FDBRecordStore.SAVE_VERSION_WITH_RECORD_FORMAT_VERSION, loadedRecord1.hasVersion());
FDBStoredRecord<?> loadedRecord2 = recordStore.loadRecord(Tuple.from(1776L));
assertNotNull(loadedRecord2);
assertFalse(loadedRecord2.hasVersion());
assertThrows(RecordCoreException.class, () -> {
RecordQueryPlan plan = planner.plan(query);
fail("Came up with plan " + plan.toString() + " when it should be impossible");
});
}
// Now with version back on.
try (FDBRecordContext context = openContext(thirdHook)) {
FDBStoredRecord<?> storedRecord = recordStore.saveRecord(record3);
assertTrue(storedRecord.hasVersion());
context.commit();
version3 = FDBRecordVersion.complete(context.getVersionStamp(), storedRecord.getVersion().getLocalVersion());
}
try (FDBRecordContext context = openContext(thirdHook)) {
FDBStoredRecord<?> loadedRecord1 = recordStore.loadRecord(Tuple.from(1066L));
assertEquals(testFormatVersion >= FDBRecordStore.SAVE_VERSION_WITH_RECORD_FORMAT_VERSION, loadedRecord1.hasVersion());
FDBStoredRecord<?> loadedRecord2 = recordStore.loadRecord(Tuple.from(1776L));
assertFalse(loadedRecord2.hasVersion());
FDBStoredRecord<?> loadedRecord3 = recordStore.loadRecord(Tuple.from(1955L));
assertTrue(loadedRecord3.hasVersion());
assertEquals(version3, loadedRecord3.getVersion());
RecordQueryPlan plan = planner.plan(query);
assertThat(plan, indexScan("globalVersion2"));
List<FDBQueriedRecord<Message>> records = recordStore.executeQuery(plan).asList().join();
assertEquals(3, records.size());
if (testFormatVersion < FDBRecordStore.SAVE_VERSION_WITH_RECORD_FORMAT_VERSION) {
FDBQueriedRecord<Message> queriedRecord1 = records.get(0);
assertEquals(Tuple.from(1066L), queriedRecord1.getPrimaryKey());
assertFalse(queriedRecord1.hasVersion());
FDBQueriedRecord<Message> queriedRecord2 = records.get(1);
assertEquals(Tuple.from(1776L), queriedRecord2.getPrimaryKey());
assertFalse(queriedRecord2.hasVersion());
} else {
FDBQueriedRecord<Message> queriedRecord1 = records.get(0);
assertEquals(Tuple.from(1776L), queriedRecord1.getPrimaryKey());
assertFalse(queriedRecord1.hasVersion());
FDBQueriedRecord<Message> queriedRecord2 = records.get(1);
assertEquals(Tuple.from(1066L), queriedRecord2.getPrimaryKey());
assertTrue(queriedRecord2.hasVersion());
assertEquals(version1, queriedRecord2.getVersion());
}
FDBQueriedRecord<Message> queriedRecord3 = records.get(2);
assertEquals(Tuple.from(1955L), queriedRecord3.getPrimaryKey());
assertTrue(queriedRecord3.hasVersion());
assertEquals(version3, queriedRecord3.getVersion());
}
}
use of com.apple.foundationdb.record.metadata.Index in project fdb-record-layer by FoundationDB.
the class VersionIndexTest method upgradeFormatVersions.
@ParameterizedTest(name = "upgradeFormatVersions [formatVersion = {0}, splitLongRecords = {1}]")
@MethodSource("formatVersionArguments")
@SuppressWarnings("try")
public void upgradeFormatVersions(int testFormatVersion, boolean splitLongRecords) {
formatVersion = testFormatVersion;
final RecordMetaDataHook hook = metaDataBuilder -> {
simpleVersionHook.apply(metaDataBuilder);
metaDataBuilder.setSplitLongRecords(splitLongRecords);
};
final List<Message> records = Arrays.asList(MySimpleRecord.newBuilder().setRecNo(1066L).setNumValue2(1).build(), MySimpleRecord.newBuilder().setRecNo(1415L).setNumValue2(1).build(), MySimpleRecord.newBuilder().setRecNo(1776L).setNumValue2(2).build());
List<FDBStoredRecord<Message>> storedRecords;
try (FDBRecordContext context = openContext(hook)) {
List<FDBStoredRecord<Message>> storedRecordsWithIncompletes = records.stream().map(recordStore::saveRecord).collect(Collectors.toList());
context.commit();
byte[] globalVersion = context.getVersionStamp();
storedRecords = storedRecordsWithIncompletes.stream().map(record -> record.withCommittedVersion(globalVersion)).collect(Collectors.toList());
}
try (FDBRecordContext context = openContext(hook)) {
if (testFormatVersion < FDBRecordStore.SAVE_VERSION_WITH_RECORD_FORMAT_VERSION) {
validateUsingOlderVersionFormat(storedRecords);
} else {
validateUsingNewerVersionFormat(storedRecords);
}
}
// Update to the current format version
formatVersion = FDBRecordStore.MAX_SUPPORTED_FORMAT_VERSION;
if (testFormatVersion < FDBRecordStore.SAVE_VERSION_WITH_RECORD_FORMAT_VERSION && (splitLongRecords || testFormatVersion >= FDBRecordStore.SAVE_UNSPLIT_WITH_SUFFIX_FORMAT_VERSION)) {
// After format version upgrade, each record should now store that it has the version inlined
storedRecords = storedRecords.stream().map(record -> new FDBStoredRecord<>(record.getPrimaryKey(), record.getRecordType(), record.getRecord(), record.getKeyCount() + 1, record.getKeySize() * 2 + 1, record.getValueSize() + 1 + FDBRecordVersion.VERSION_LENGTH, record.isSplit(), true, record.getVersion())).collect(Collectors.toList());
}
try (FDBRecordContext context = openContext(hook)) {
if (!splitLongRecords && testFormatVersion < FDBRecordStore.SAVE_UNSPLIT_WITH_SUFFIX_FORMAT_VERSION) {
validateUsingOlderVersionFormat(storedRecords);
} else {
validateUsingNewerVersionFormat(storedRecords);
}
for (FDBStoredRecord<Message> storedRecord : storedRecords) {
Optional<FDBRecordVersion> loadedVersion = recordStore.loadRecordVersion(storedRecord.getPrimaryKey());
assertTrue(loadedVersion.isPresent());
assertEquals(storedRecord.getVersion(), loadedVersion.get());
RecordQuery query = RecordQuery.newBuilder().setFilter(Query.version().equalsValue(storedRecord.getVersion())).build();
RecordQueryPlan plan = planner.plan(query);
final String endpointString = "[" + storedRecord.getVersion().toVersionstamp(false).toString() + "]";
assertThat(plan, indexScan(allOf(indexName("globalVersion"), bounds(hasTupleString("[" + endpointString + "," + endpointString + "]")))));
List<FDBStoredRecord<Message>> queriedRecords = recordStore.executeQuery(plan).map(FDBQueriedRecord::getStoredRecord).asList().join();
assertEquals(Collections.singletonList(storedRecord), queriedRecords);
}
assertTrue(recordStore.deleteRecord(storedRecords.get(0).getPrimaryKey()));
final List<FDBStoredRecord<Message>> fewerRecords = storedRecords.subList(1, storedRecords.size());
if (!splitLongRecords && testFormatVersion < FDBRecordStore.SAVE_UNSPLIT_WITH_SUFFIX_FORMAT_VERSION) {
validateUsingOlderVersionFormat(fewerRecords);
} else {
validateUsingNewerVersionFormat(fewerRecords);
}
recordStore.saveRecord(storedRecords.get(0).getRecord());
if (!splitLongRecords && testFormatVersion < FDBRecordStore.SAVE_UNSPLIT_WITH_SUFFIX_FORMAT_VERSION) {
validateUsingOlderVersionFormat(fewerRecords);
} else {
validateUsingNewerVersionFormat(fewerRecords);
}
// do not commit (so we can do a second upgrade)
}
final Index newValueIndex = new Index("MySimpleRecord$num2", field("num_value_2"), IndexTypes.VALUE);
final Index newVersionIndex = new Index("MySimpleRecord$version-num2", concat(VersionKeyExpression.VERSION, field("num_value_2")), IndexTypes.VERSION);
final RecordMetaDataHook hookWithNewIndexes = metaDataBuilder -> {
hook.apply(metaDataBuilder);
metaDataBuilder.addIndex("MySimpleRecord", newValueIndex);
metaDataBuilder.addIndex("MySimpleRecord", newVersionIndex);
};
final List<FDBStoredRecord<Message>> newStoredRecords;
try (FDBRecordContext context = openContext(hookWithNewIndexes)) {
assertTrue(recordStore.getRecordStoreState().isReadable(newValueIndex));
boolean performedMigration = testFormatVersion < FDBRecordStore.SAVE_VERSION_WITH_RECORD_FORMAT_VERSION && (splitLongRecords || testFormatVersion >= FDBRecordStore.SAVE_UNSPLIT_WITH_SUFFIX_FORMAT_VERSION);
assertNotEquals(performedMigration, recordStore.getRecordStoreState().isReadable(newVersionIndex));
if (recordStore.getRecordStoreState().isReadable(newVersionIndex)) {
// Validate versions are the same for all records in index and in primary store
List<Pair<Tuple, FDBRecordVersion>> recordScannedValues = recordStore.scanRecords(null, ScanProperties.FORWARD_SCAN).map(record -> Pair.of(record.getPrimaryKey(), record.getVersion())).asList().join();
List<Pair<Tuple, FDBRecordVersion>> indexedScannedValues = recordStore.scanIndex(newVersionIndex, IndexScanType.BY_VALUE, TupleRange.ALL, null, ScanProperties.FORWARD_SCAN).map(indexEntry -> Pair.of(TupleHelpers.subTuple(indexEntry.getKey(), 2, indexEntry.getKey().size()), FDBRecordVersion.fromVersionstamp(indexEntry.getKey().getVersionstamp(0), false))).asList().join();
assertEquals(recordScannedValues, indexedScannedValues);
}
// Save record at newer version
assertTrue(recordStore.deleteRecord(storedRecords.get(0).getPrimaryKey()));
FDBStoredRecord<Message> newRecord0 = recordStore.saveRecord(storedRecords.get(0).getRecord());
FDBStoredRecord<Message> newRecord2 = recordStore.saveRecord(storedRecords.get(2).getRecord());
assertEquals(newRecord0.getVersion(), recordStore.loadRecordVersion(newRecord0.getPrimaryKey()).get());
assertEquals(newRecord2.getVersion(), recordStore.loadRecordVersion(newRecord2.getPrimaryKey()).get());
context.commit();
byte[] versionstamp = context.getVersionStamp();
newStoredRecords = Arrays.asList(newRecord0.withVersion(FDBRecordVersion.complete(versionstamp, 0)), storedRecords.get(1), newRecord2.withVersion(FDBRecordVersion.complete(versionstamp, 1)));
}
try (FDBRecordContext context = openContext(hookWithNewIndexes)) {
if (!splitLongRecords && testFormatVersion < FDBRecordStore.SAVE_UNSPLIT_WITH_SUFFIX_FORMAT_VERSION) {
validateUsingOlderVersionFormat(newStoredRecords);
} else {
validateUsingNewerVersionFormat(newStoredRecords);
}
}
}
use of com.apple.foundationdb.record.metadata.Index in project fdb-record-layer by FoundationDB.
the class TextIndexTest method saveComplexWithAggressiveConflictRanges.
@Test
public void saveComplexWithAggressiveConflictRanges() throws Exception {
// These two documents are in different groups, so even with aggressive conflict
// ranges, they should be able to be committed concurrently.
final ComplexDocument zeroGroupDocument = ComplexDocument.newBuilder().setGroup(0).setDocId(1623L).setText(TextSamples.ROMEO_AND_JULIET_PROLOGUE).build();
final ComplexDocument oneGroupDocument = ComplexDocument.newBuilder().setGroup(1).setDocId(1623L).setText(TextSamples.ROMEO_AND_JULIET_PROLOGUE).build();
final RecordMetaDataHook hook = metaDataBuilder -> {
final Index newIndex = new Index(COMPLEX_TEXT_BY_GROUP.getName(), COMBINED_TEXT_BY_GROUP.getRootExpression(), IndexTypes.TEXT, ImmutableMap.of(IndexOptions.TEXT_ADD_AGGRESSIVE_CONFLICT_RANGES_OPTION, "true"));
metaDataBuilder.addIndex(COMPLEX_DOC, newIndex);
};
saveTwoRecordsConcurrently(hook, zeroGroupDocument, oneGroupDocument, true);
}
use of com.apple.foundationdb.record.metadata.Index 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())));
}
}
use of com.apple.foundationdb.record.metadata.Index in project fdb-record-layer by FoundationDB.
the class TextIndexTest method querySimpleDocumentsWithoutPositions.
@Test
public void querySimpleDocumentsWithoutPositions() throws Exception {
final List<SimpleDocument> documents = TextIndexTestUtils.toSimpleDocuments(Arrays.asList(TextSamples.ANGSTROM, TextSamples.AETHELRED, TextSamples.ROMEO_AND_JULIET_PROLOGUE, TextSamples.FRENCH));
// Query but make sure
try (FDBRecordContext context = openContext()) {
openRecordStore(context, metaDataBuilder -> {
metaDataBuilder.removeIndex(TextIndexTestUtils.SIMPLE_DEFAULT_NAME);
metaDataBuilder.addIndex(SIMPLE_DOC, SIMPLE_TEXT_NO_POSITIONS);
});
documents.forEach(recordStore::saveRecord);
// Queries that *don't* require position information should be planned to use the index
assertEquals(Arrays.asList(1L, 2L, 3L), querySimpleDocumentsWithIndex(Query.field("text").text().containsAny("king civil récu"), SIMPLE_TEXT_NO_POSITIONS.getName(), 0, true));
assertEquals(Collections.singletonList(2L), querySimpleDocumentsWithIndex(Query.field("text").text().containsAll("unclean verona"), SIMPLE_TEXT_NO_POSITIONS.getName(), 0, true));
assertEquals(Arrays.asList(0L, 1L, 2L, 3L), querySimpleDocumentsWithIndex(Query.field("text").text().containsPrefix("th"), SIMPLE_TEXT_NO_POSITIONS.getName(), 0, true));
// Queries that *do* require position information must be planned as scans
assertEquals(Collections.singletonList(2L), querySimpleDocumentsWithScan(Query.field("text").text().containsPhrase("civil blood makes civil hands unclean"), 0));
assertEquals(Collections.singletonList(3L), querySimpleDocumentsWithScan(Query.field("text").text().containsAll("France Napoleons", 3), 0));
commit(context);
}
final List<SimpleDocument> newDocuments = documents.stream().map(doc -> doc.toBuilder().setDocId(doc.getDocId() + documents.size()).build()).collect(Collectors.toList());
// Upgrade to writing position information
try (FDBRecordContext context = openContext()) {
openRecordStore(context, metaDataBuilder -> {
metaDataBuilder.removeIndex(TextIndexTestUtils.SIMPLE_DEFAULT_NAME);
metaDataBuilder.addIndex(SIMPLE_DOC, new Index(SIMPLE_TEXT_NO_POSITIONS.getName(), SIMPLE_TEXT_NO_POSITIONS.getRootExpression(), IndexTypes.TEXT));
});
newDocuments.forEach(recordStore::saveRecord);
// Queries that *don't* require position information produce the same plan
assertEquals(Arrays.asList(1L, 2L, 3L, 5L, 6L, 7L), querySimpleDocumentsWithIndex(Query.field("text").text().containsAny("king civil récu"), SIMPLE_TEXT_NO_POSITIONS.getName(), 0, true));
assertEquals(Arrays.asList(2L, 6L), querySimpleDocumentsWithIndex(Query.field("text").text().containsAll("unclean verona"), SIMPLE_TEXT_NO_POSITIONS.getName(), 0, true));
assertEquals(Arrays.asList(0L, 1L, 2L, 4L, 5L, 6L, 3L, 7L), querySimpleDocumentsWithIndex(Query.field("text").text().containsPrefix("th"), SIMPLE_TEXT_NO_POSITIONS.getName(), 0, true));
// Queries that *do* require position information now use the index, but previously written documents show up in the
// query spuriously
assertEquals(Arrays.asList(2L, 6L), querySimpleDocumentsWithIndex(Query.field("text").text().containsPhrase("civil blood makes civil hands unclean"), SIMPLE_TEXT_NO_POSITIONS.getName(), 0, true));
assertEquals(Collections.singletonList(2L), querySimpleDocumentsWithIndex(Query.field("text").text().containsPhrase("unclean verona"), SIMPLE_TEXT_NO_POSITIONS.getName(), 0, true));
assertEquals(Arrays.asList(3L, 7L), querySimpleDocumentsWithIndex(Query.field("text").text().containsAll("France Napoleons", 3), SIMPLE_TEXT_NO_POSITIONS.getName(), 0, true));
assertEquals(Collections.singletonList(3L), querySimpleDocumentsWithIndex(Query.field("text").text().containsAll("Thiers Napoleons", 3), SIMPLE_TEXT_NO_POSITIONS.getName(), 0, true));
commit(context);
}
}
Aggregations