use of com.apple.foundationdb.record.RecordIndexUniquenessViolation in project fdb-record-layer by FoundationDB.
the class OnlineIndexerUniqueIndexTest method uniquenessViolations.
@Test
public void uniquenessViolations() {
List<TestRecords1Proto.MySimpleRecord> records = LongStream.range(0, 10).mapToObj(val -> TestRecords1Proto.MySimpleRecord.newBuilder().setRecNo(val).setNumValue2(((int) val) % 5).build()).collect(Collectors.toList());
Index index = new Index("simple$value_2", field("num_value_2"), EmptyKeyExpression.EMPTY, IndexTypes.VALUE, IndexOptions.UNIQUE_OPTIONS);
FDBRecordStoreTestBase.RecordMetaDataHook hook = metaDataBuilder -> metaDataBuilder.addIndex("MySimpleRecord", index);
// Case 1: Entirely in build.
openSimpleMetaData();
try (FDBRecordContext context = openContext()) {
records.forEach(recordStore::saveRecord);
context.commit();
}
openSimpleMetaData(hook);
try (FDBRecordContext context = openContext()) {
recordStore.markIndexWriteOnly(index).join();
context.commit();
}
try (OnlineIndexer indexBuilder = OnlineIndexer.newBuilder().setDatabase(fdb).setMetaData(metaData).setIndex(index).setSubspace(subspace).build()) {
indexBuilder.buildIndexAsync().handle((ignore, e) -> {
assertNotNull(e);
RuntimeException runE = FDBExceptions.wrapException(e);
assertNotNull(runE);
assertThat(runE, instanceOf(RecordIndexUniquenessViolation.class));
return null;
}).join();
// Case 2: While in write-only mode.
try (FDBRecordContext context = openContext()) {
recordStore.deleteAllRecords();
recordStore.markIndexWriteOnly(index).join();
context.commit();
}
try (FDBRecordContext context = openContext()) {
records.forEach(recordStore::saveRecord);
context.commit();
}
try (FDBRecordContext context = openContext()) {
assertEquals(10, (int) recordStore.scanUniquenessViolations(index).getCount().join());
context.commit();
}
indexBuilder.buildIndexAsync().handle((ignore, e) -> {
assertNotNull(e);
RuntimeException runE = FDBExceptions.wrapException(e);
assertNotNull(runE);
assertThat(runE, instanceOf(RecordIndexUniquenessViolation.class));
return null;
}).join();
}
// Case 3: Some in write-only mode.
fdb.run(context -> {
FDBRecordStore.deleteStore(context, subspace);
return null;
});
openSimpleMetaData();
try (FDBRecordContext context = openContext()) {
for (int i = 0; i < 5; i++) {
recordStore.saveRecord(records.get(i));
}
context.commit();
}
openSimpleMetaData(hook);
try (FDBRecordContext context = openContext()) {
recordStore.markIndexWriteOnly(index).join();
for (int i = 5; i < records.size(); i++) {
recordStore.saveRecord(records.get(i));
}
context.commit();
}
try (OnlineIndexer indexBuilder = OnlineIndexer.newBuilder().setDatabase(fdb).setMetaData(metaData).setIndex(index).setSubspace(subspace).build()) {
indexBuilder.buildIndexAsync().handle((ignore, e) -> {
assertNotNull(e);
RuntimeException runE = FDBExceptions.wrapException(e);
assertNotNull(runE);
assertThat(runE, instanceOf(RecordIndexUniquenessViolation.class));
return null;
}).join();
}
// Case 4: Some in write-only mode with an initial range build that shouldn't affect anything.
fdb.run(context -> {
FDBRecordStore.deleteStore(context, subspace);
return null;
});
openSimpleMetaData();
try (FDBRecordContext context = openContext()) {
for (int i = 5; i < records.size(); i++) {
recordStore.saveRecord(records.get(i));
}
context.commit();
}
openSimpleMetaData(hook);
try (FDBRecordContext context = openContext()) {
recordStore.markIndexWriteOnly(index).join();
for (int i = 0; i < 5; i++) {
recordStore.saveRecord(records.get(i));
}
context.commit();
}
try (OnlineIndexer indexBuilder = OnlineIndexer.newBuilder().setDatabase(fdb).setMetaData(metaData).setIndex(index).setSubspace(subspace).build()) {
indexBuilder.buildUnbuiltRange(Key.Evaluated.scalar(0L), Key.Evaluated.scalar(5L)).join();
try (FDBRecordContext context = openContext()) {
assertEquals(0, (int) recordStore.scanUniquenessViolations(index).getCount().join());
context.commit();
}
indexBuilder.buildUnbuiltRange(Key.Evaluated.scalar(5L), Key.Evaluated.scalar(10L)).join();
try (FDBRecordContext context = openContext()) {
assertEquals(10, (int) recordStore.scanUniquenessViolations(index).getCount().join());
context.commit();
}
indexBuilder.buildIndexAsync().handle((ignore, e) -> {
assertNotNull(e);
RuntimeException runE = FDBExceptions.wrapException(e);
assertNotNull(runE);
assertThat(runE, instanceOf(RecordIndexUniquenessViolation.class));
return null;
}).join();
}
// Case 5: Should be caught by write-only writes after build.
fdb.run(context -> {
FDBRecordStore.deleteStore(context, subspace);
return null;
});
openSimpleMetaData();
try (FDBRecordContext context = openContext()) {
for (int i = 0; i < 5; i++) {
recordStore.saveRecord(records.get(i));
}
context.commit();
}
openSimpleMetaData(hook);
try (FDBRecordContext context = openContext()) {
recordStore.markIndexWriteOnly(index).join();
context.commit();
}
try (OnlineIndexer indexBuilder = OnlineIndexer.newBuilder().setDatabase(fdb).setMetaData(metaData).setIndex(index).setSubspace(subspace).build()) {
indexBuilder.buildIndex();
}
try (FDBRecordContext context = openContext()) {
for (int i = 5; i < records.size(); i++) {
recordStore.saveRecord(records.get(i));
}
context.commit();
fail("Did not catch uniqueness violation when done after build by write-only writes");
} catch (RecordIndexUniquenessViolation e) {
// passed.
}
// Case 6: Should be caught by write-only writes after partial build.
fdb.run(context -> {
FDBRecordStore.deleteStore(context, subspace);
return null;
});
openSimpleMetaData();
try (FDBRecordContext context = openContext()) {
for (int i = 0; i < 5; i++) {
recordStore.saveRecord(records.get(i));
}
context.commit();
}
openSimpleMetaData(hook);
try (FDBRecordContext context = openContext()) {
recordStore.markIndexWriteOnly(index).join();
context.commit();
}
try (OnlineIndexer indexBuilder = OnlineIndexer.newBuilder().setDatabase(fdb).setMetaData(metaData).setIndex(index).setSubspace(subspace).build()) {
indexBuilder.buildUnbuiltRange(Key.Evaluated.scalar(0L), Key.Evaluated.scalar(5L)).join();
try (FDBRecordContext context = openContext()) {
for (int i = 5; i < records.size(); i++) {
recordStore.saveRecord(records.get(i));
}
context.commit();
}
try (FDBRecordContext context = openContext()) {
assertEquals(10, (int) recordStore.scanUniquenessViolations(index).getCount().join());
context.commit();
}
indexBuilder.buildIndexAsync().handle((ignore, e) -> {
assertNotNull(e);
RuntimeException runE = FDBExceptions.wrapException(e);
assertNotNull(runE);
assertThat(runE, instanceOf(RecordIndexUniquenessViolation.class));
return null;
}).join();
}
// Case 7: The second of these two transactions should fail on not_committed, and then
// there should be a uniqueness violation.
fdb.run(context -> {
FDBRecordStore.deleteStore(context, subspace);
return null;
});
openSimpleMetaData();
try (FDBRecordContext context = openContext()) {
for (int i = 0; i < 5; i++) {
recordStore.saveRecord(records.get(i));
}
context.commit();
}
openSimpleMetaData(hook);
try (FDBRecordContext context = openContext()) {
recordStore.markIndexWriteOnly(index).join();
context.commit();
}
try (OnlineIndexer indexBuilder = OnlineIndexer.newBuilder().setDatabase(fdb).setMetaData(metaData).setIndex(index).setSubspace(subspace).build()) {
try (FDBRecordContext context = openContext()) {
context.getReadVersion();
try (FDBRecordContext context2 = fdb.openContext()) {
context2.getReadVersion();
FDBRecordStore recordStore2 = recordStore.asBuilder().setContext(context2).build();
indexBuilder.buildUnbuiltRange(recordStore, null, Key.Evaluated.scalar(5L)).join();
recordStore2.saveRecord(records.get(8));
context.commit();
context2.commitAsync().handle((ignore, e) -> {
assertNotNull(e);
RuntimeException runE = FDBExceptions.wrapException(e);
assertThat(runE, instanceOf(RecordCoreRetriableTransactionException.class));
assertNotNull(runE.getCause());
assertThat(runE.getCause(), instanceOf(FDBException.class));
FDBException fdbE = (FDBException) runE.getCause();
assertEquals(FDBError.NOT_COMMITTED.code(), fdbE.getCode());
return null;
}).join();
}
}
try (FDBRecordContext context = openContext()) {
for (int i = 5; i < records.size(); i++) {
recordStore.saveRecord(records.get(i));
}
context.commit();
}
try (FDBRecordContext context = openContext()) {
assertEquals(10, (int) recordStore.scanUniquenessViolations(index).getCount().join());
context.commit();
}
indexBuilder.buildIndexAsync().handle((ignore, e) -> {
assertNotNull(e);
RuntimeException runE = FDBExceptions.wrapException(e);
assertNotNull(runE);
assertThat(runE, instanceOf(RecordIndexUniquenessViolation.class));
return null;
}).join();
}
}
use of com.apple.foundationdb.record.RecordIndexUniquenessViolation in project fdb-record-layer by FoundationDB.
the class FDBRecordStoreUniqueIndexTest method buildUniqueInCheckVersion.
/**
* Validate the behavior when a unique index is added on existing data that already has duplicates on the same data.
* The new index should not be built, as that is impossible to do without breaking the constraints of the index, but
* it also shouldn't fail the store opening or commit, as otherwise, the store would never be able to be opened.
*
* @throws Exception from store opening code
*/
@Test
public void buildUniqueInCheckVersion() throws Exception {
try (FDBRecordContext context = openContext()) {
openSimpleRecordStore(context);
// create a uniqueness violation on a field without a unique index
recordStore.saveRecord(TestRecords1Proto.MySimpleRecord.newBuilder().setRecNo(1066L).setNumValue2(42).build());
recordStore.saveRecord(TestRecords1Proto.MySimpleRecord.newBuilder().setRecNo(1412L).setNumValue2(42).build());
commit(context);
}
final Index uniqueIndex = new Index("unique_num_value_2_index", Key.Expressions.field("num_value_2"), IndexTypes.VALUE, IndexOptions.UNIQUE_OPTIONS);
assertTrue(uniqueIndex.isUnique());
try (FDBRecordContext context = openContext()) {
openSimpleRecordStore(context, metaDataBuilder -> metaDataBuilder.addIndex("MySimpleRecord", uniqueIndex));
assertFalse(recordStore.isIndexReadable(uniqueIndex), "index with uniqueness violations should not be readable after being added to the meta-data");
final List<RecordIndexUniquenessViolation> uniquenessViolations = recordStore.scanUniquenessViolations(uniqueIndex).asList().get();
assertThat(uniquenessViolations, not(empty()));
for (RecordIndexUniquenessViolation uniquenessViolation : uniquenessViolations) {
assertThat(uniquenessViolation.getPrimaryKey(), either(equalTo(Tuple.from(1066L))).or(equalTo(Tuple.from(1412L))));
}
commit(context);
}
}
use of com.apple.foundationdb.record.RecordIndexUniquenessViolation in project fdb-record-layer by FoundationDB.
the class OnlineIndexerUniqueIndexTest method resolveUniquenessViolations.
@Test
public void resolveUniquenessViolations() {
List<TestRecords1Proto.MySimpleRecord> records = LongStream.range(0, 10).mapToObj(val -> TestRecords1Proto.MySimpleRecord.newBuilder().setRecNo(val).setNumValue2(((int) val) % 5).build()).collect(Collectors.toList());
Index index = new Index("simple$value_2", field("num_value_2"), EmptyKeyExpression.EMPTY, IndexTypes.VALUE, IndexOptions.UNIQUE_OPTIONS);
FDBRecordStoreTestBase.RecordMetaDataHook hook = metaDataBuilder -> metaDataBuilder.addIndex("MySimpleRecord", index);
openSimpleMetaData();
try (FDBRecordContext context = openContext()) {
records.forEach(recordStore::saveRecord);
context.commit();
}
openSimpleMetaData(hook);
try (FDBRecordContext context = openContext()) {
recordStore.markIndexWriteOnly(index).join();
context.commit();
}
try (OnlineIndexer indexBuilder = OnlineIndexer.newBuilder().setDatabase(fdb).setMetaData(metaData).setIndex(index).setSubspace(subspace).build()) {
indexBuilder.buildIndexAsync().handle((ignore, e) -> {
assertNotNull(e);
RuntimeException runE = FDBExceptions.wrapException(e);
assertNotNull(runE);
assertThat(runE, instanceOf(RecordIndexUniquenessViolation.class));
return null;
}).join();
}
try (FDBRecordContext context = openContext()) {
Set<Tuple> indexEntries = new HashSet<>(recordStore.scanUniquenessViolations(index).map(v -> v.getIndexEntry().getKey()).asList().join());
for (Tuple indexKey : indexEntries) {
List<Tuple> primaryKeys = recordStore.scanUniquenessViolations(index, indexKey).map(RecordIndexUniquenessViolation::getPrimaryKey).asList().join();
assertEquals(2, primaryKeys.size());
recordStore.resolveUniquenessViolation(index, indexKey, primaryKeys.get(0)).join();
assertEquals(0, (int) recordStore.scanUniquenessViolations(index, indexKey).getCount().join());
}
for (int i = 0; i < 5; i++) {
assertNotNull(recordStore.loadRecord(Tuple.from(i)));
}
for (int i = 5; i < records.size(); i++) {
assertNull(recordStore.loadRecord(Tuple.from(i)));
}
recordStore.markIndexReadable(index).join();
context.commit();
}
}
use of com.apple.foundationdb.record.RecordIndexUniquenessViolation in project fdb-record-layer by FoundationDB.
the class FDBRecordStore method scanUniquenessViolations.
@Override
@Nonnull
public RecordCursor<RecordIndexUniquenessViolation> scanUniquenessViolations(@Nonnull Index index, @Nonnull TupleRange range, @Nullable byte[] continuation, @Nonnull ScanProperties scanProperties) {
RecordCursor<IndexEntry> tupleCursor = getIndexMaintainer(index).scanUniquenessViolations(range, continuation, scanProperties);
return tupleCursor.map(entry -> {
int indexColumns = index.getColumnSize();
Tuple valueKey = TupleHelpers.subTuple(entry.getKey(), 0, indexColumns);
Tuple primaryKey = TupleHelpers.subTuple(entry.getKey(), indexColumns, entry.getKey().size());
Tuple existingKey = entry.getValue();
return new RecordIndexUniquenessViolation(index, new IndexEntry(index, valueKey, entry.getValue()), primaryKey, existingKey);
});
}
Aggregations