Search in sources :

Example 1 with RecordIndexUniquenessViolation

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

the class StandardIndexMaintainer method checkUniqueness.

protected <M extends Message> void checkUniqueness(@Nonnull FDBIndexableRecord<M> savedRecord, @Nonnull IndexEntry indexEntry) {
    Tuple valueKey = indexEntry.getKey();
    AsyncIterable<KeyValue> kvs = state.transaction.getRange(state.indexSubspace.range(valueKey));
    Tuple primaryKey = savedRecord.getPrimaryKey();
    final CompletableFuture<Void> checker = state.store.getContext().instrument(FDBStoreTimer.Events.CHECK_INDEX_UNIQUENESS, AsyncUtil.forEach(kvs, kv -> {
        Tuple existingEntry = unpackKey(getIndexSubspace(), kv);
        Tuple existingKey = state.index.getEntryPrimaryKey(existingEntry);
        if (!TupleHelpers.equals(primaryKey, existingKey)) {
            if (state.store.isIndexWriteOnly(state.index)) {
                addUniquenessViolation(valueKey, primaryKey, existingKey);
                addUniquenessViolation(valueKey, existingKey, primaryKey);
            } else {
                throw new RecordIndexUniquenessViolation(state.index, indexEntry, primaryKey, existingKey);
            }
        }
    }, getExecutor()));
    // Add a pre-commit check to prevent accidentally committing and getting into an invalid state.
    state.store.addIndexUniquenessCommitCheck(state.index, checker);
}
Also used : IndexEntry(com.apple.foundationdb.record.IndexEntry) LogMessageKeys(com.apple.foundationdb.record.logging.LogMessageKeys) IndexMaintainerState(com.apple.foundationdb.record.provider.foundationdb.IndexMaintainerState) FDBRecord(com.apple.foundationdb.record.provider.foundationdb.FDBRecord) LoggerFactory(org.slf4j.LoggerFactory) Subspace(com.apple.foundationdb.subspace.Subspace) Transaction(com.apple.foundationdb.Transaction) IndexScanType(com.apple.foundationdb.record.IndexScanType) Tuple(com.apple.foundationdb.tuple.Tuple) Range(com.apple.foundationdb.Range) KeyValueLogMessage(com.apple.foundationdb.record.logging.KeyValueLogMessage) Pair(org.apache.commons.lang3.tuple.Pair) RecordCoreException(com.apple.foundationdb.record.RecordCoreException) PipelineOperation(com.apple.foundationdb.record.PipelineOperation) RecordIndexUniquenessViolation(com.apple.foundationdb.record.RecordIndexUniquenessViolation) GroupingKeyExpression(com.apple.foundationdb.record.metadata.expressions.GroupingKeyExpression) FDBExceptions(com.apple.foundationdb.record.provider.foundationdb.FDBExceptions) FDBRecordStoreBase(com.apple.foundationdb.record.provider.foundationdb.FDBRecordStoreBase) QueryToKeyMatcher(com.apple.foundationdb.record.query.QueryToKeyMatcher) ByteArrayUtil(com.apple.foundationdb.tuple.ByteArrayUtil) FDBIndexableRecord(com.apple.foundationdb.record.provider.foundationdb.FDBIndexableRecord) KeyExpression(com.apple.foundationdb.record.metadata.expressions.KeyExpression) KeyValue(com.apple.foundationdb.KeyValue) FDBStoreTimer(com.apple.foundationdb.record.provider.foundationdb.FDBStoreTimer) Collection(java.util.Collection) Collectors(java.util.stream.Collectors) TupleRange(com.apple.foundationdb.record.TupleRange) Objects(java.util.Objects) KeyValueCursor(com.apple.foundationdb.record.provider.foundationdb.KeyValueCursor) List(java.util.List) EvaluationContext(com.apple.foundationdb.record.EvaluationContext) TupleHelpers(com.apple.foundationdb.tuple.TupleHelpers) API(com.apple.foundationdb.annotation.API) SplitHelper.unpackKey(com.apple.foundationdb.record.provider.foundationdb.SplitHelper.unpackKey) IndexMaintainer(com.apple.foundationdb.record.provider.foundationdb.IndexMaintainer) IndexMaintenanceFilter(com.apple.foundationdb.record.provider.foundationdb.IndexMaintenanceFilter) IndexAggregateFunction(com.apple.foundationdb.record.metadata.IndexAggregateFunction) KeyWithValueExpression(com.apple.foundationdb.record.metadata.expressions.KeyWithValueExpression) IndexOperation(com.apple.foundationdb.record.provider.foundationdb.IndexOperation) CompletableFuture(java.util.concurrent.CompletableFuture) AsyncUtil(com.apple.foundationdb.async.AsyncUtil) RangeSet(com.apple.foundationdb.async.RangeSet) Function(java.util.function.Function) CursorStreamingMode(com.apple.foundationdb.record.CursorStreamingMode) ArrayList(java.util.ArrayList) Key(com.apple.foundationdb.record.metadata.Key) ExecuteProperties(com.apple.foundationdb.record.ExecuteProperties) ScanProperties(com.apple.foundationdb.record.ScanProperties) IndexRecordFunction(com.apple.foundationdb.record.metadata.IndexRecordFunction) Nonnull(javax.annotation.Nonnull) Nullable(javax.annotation.Nullable) IndexOperationResult(com.apple.foundationdb.record.provider.foundationdb.IndexOperationResult) MoreAsyncUtil(com.apple.foundationdb.async.MoreAsyncUtil) IsolationLevel(com.apple.foundationdb.record.IsolationLevel) Logger(org.slf4j.Logger) Executor(java.util.concurrent.Executor) RecordType(com.apple.foundationdb.record.metadata.RecordType) AsyncIterable(com.apple.foundationdb.async.AsyncIterable) Message(com.google.protobuf.Message) RecordCursor(com.apple.foundationdb.record.RecordCursor) Collections(java.util.Collections) KeyValue(com.apple.foundationdb.KeyValue) RecordIndexUniquenessViolation(com.apple.foundationdb.record.RecordIndexUniquenessViolation) Tuple(com.apple.foundationdb.tuple.Tuple)

Example 2 with RecordIndexUniquenessViolation

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

the class BitmapValueIndexMaintainer method updateIndexKeys.

@Override
@Nonnull
protected <M extends Message> CompletableFuture<Void> updateIndexKeys(@Nonnull final FDBIndexableRecord<M> savedRecord, final boolean remove, @Nonnull final List<IndexEntry> indexEntries) {
    final int groupPrefixSize = getGroupingCount();
    final List<CompletableFuture<Void>> futures = unique && !remove ? new ArrayList<>(indexEntries.size()) : null;
    for (IndexEntry indexEntry : indexEntries) {
        final long startTime = System.nanoTime();
        final Tuple groupKey = TupleHelpers.subTuple(indexEntry.getKey(), 0, groupPrefixSize);
        Object positionObject = indexEntry.getKey().get(groupPrefixSize);
        if (positionObject == null) {
            continue;
        }
        if (!(positionObject instanceof Number)) {
            // This should be prevented by the checks in the meta-data builder, but just in case
            throw new RecordCoreException("position field in index entry is not a number").addLogInfo(LogMessageKeys.KEY, indexEntry.getKey(), LogMessageKeys.INDEX_NAME, state.index.getName(), LogMessageKeys.INDEX_TYPE, state.index.getType());
        }
        long position = ((Number) positionObject).longValue();
        final int offset = (int) Math.floorMod(position, (long) entrySize);
        position -= offset;
        final byte[] key = state.indexSubspace.pack(groupKey.add(position));
        // This has to be the same size every time, with all the unset bits, or else it gets truncated.
        // We really could use a new mutation that took a linear bit position to set / clear and only did length extension or something like that.
        final byte[] bitmap = new byte[(entrySize + 7) / 8];
        if (remove) {
            if (state.store.isIndexWriteOnly(state.index)) {
                // If the index isn't built, it's possible this key wasn't reached.
                // So initialize it to zeros (or leave it alone).
                state.transaction.mutate(MutationType.BIT_OR, key, bitmap);
            }
            // Otherwise the entry must already exist for us to be removing it,
            // so there is no danger that this will store all (but one) ones in a new key.
            Arrays.fill(bitmap, (byte) 0xFF);
            bitmap[offset / 8] &= ~(byte) (1 << (offset % 8));
            state.transaction.mutate(MutationType.BIT_AND, key, bitmap);
            Arrays.fill(bitmap, (byte) 0x00);
            state.transaction.mutate(MutationType.COMPARE_AND_CLEAR, key, bitmap);
        } else {
            if (unique) {
                // Snapshot read to see if the bit is already set.
                CompletableFuture<Void> future = state.transaction.snapshot().get(key).thenAccept(existing -> {
                    if (existing != null && (existing[offset / 8] & (byte) (1 << (offset % 8))) != 0) {
                        throw new RecordIndexUniquenessViolation(state.index, indexEntry, savedRecord.getPrimaryKey(), // Unfortunately, we don't know the other key.
                        null);
                    }
                });
                futures.add(future);
                // Then arrange to conflict for the position with any concurrent update.
                final byte[] conflictKey = new Subspace(key).pack(offset);
                state.transaction.addReadConflictKey(conflictKey);
                state.transaction.addWriteConflictKey(conflictKey);
            }
            bitmap[offset / 8] |= (byte) (1 << (offset % 8));
            state.transaction.mutate(MutationType.BIT_OR, key, bitmap);
        }
        if (state.store.getTimer() != null) {
            state.store.getTimer().recordSinceNanoTime(FDBStoreTimer.Events.MUTATE_INDEX_ENTRY, startTime);
        }
    }
    return futures != null ? AsyncUtil.whenAll(futures) : AsyncUtil.DONE;
}
Also used : IndexEntry(com.apple.foundationdb.record.IndexEntry) RecordCoreException(com.apple.foundationdb.record.RecordCoreException) CompletableFuture(java.util.concurrent.CompletableFuture) RecordIndexUniquenessViolation(com.apple.foundationdb.record.RecordIndexUniquenessViolation) Subspace(com.apple.foundationdb.subspace.Subspace) Tuple(com.apple.foundationdb.tuple.Tuple) Nonnull(javax.annotation.Nonnull)

Example 3 with RecordIndexUniquenessViolation

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

the class FDBRecordStore method markIndexReadable.

/**
 * Marks an index as readable. See the version of
 * {@link #markIndexReadable(String) markIndexReadable()}
 * that takes a {@link String} as a parameter for more details.
 *
 * @param index the index to mark readable
 * @return a future that will either complete exceptionally if the index can not
 * be made readable or will contain <code>true</code> if the store was modified
 * and <code>false</code> otherwise
 */
@Nonnull
public CompletableFuture<Boolean> markIndexReadable(@Nonnull Index index) {
    if (recordStoreStateRef.get() == null) {
        return preloadRecordStoreStateAsync().thenCompose(vignore -> markIndexReadable(index));
    }
    addIndexStateReadConflict(index.getName());
    beginRecordStoreStateWrite();
    boolean haveFuture = false;
    try {
        Transaction tr = ensureContextActive();
        byte[] indexKey = indexStateSubspace().pack(index.getName());
        CompletableFuture<Boolean> future = tr.get(indexKey).thenCompose(previous -> {
            if (previous != null) {
                CompletableFuture<Optional<Range>> builtFuture = firstUnbuiltRange(index);
                CompletableFuture<Optional<RecordIndexUniquenessViolation>> uniquenessFuture = whenAllIndexUniquenessCommitChecks(index).thenCompose(vignore -> scanUniquenessViolations(index, 1).first());
                return CompletableFuture.allOf(builtFuture, uniquenessFuture).thenApply(vignore -> {
                    Optional<Range> firstUnbuilt = context.join(builtFuture);
                    Optional<RecordIndexUniquenessViolation> uniquenessViolation = context.join(uniquenessFuture);
                    if (firstUnbuilt.isPresent()) {
                        throw new IndexNotBuiltException("Attempted to make unbuilt index readable", firstUnbuilt.get(), LogMessageKeys.INDEX_NAME, index.getName(), "unbuiltRangeBegin", ByteArrayUtil2.loggable(firstUnbuilt.get().begin), "unbuiltRangeEnd", ByteArrayUtil2.loggable(firstUnbuilt.get().end), subspaceProvider.logKey(), subspaceProvider.toString(context), LogMessageKeys.SUBSPACE_KEY, index.getSubspaceKey());
                    } else if (uniquenessViolation.isPresent()) {
                        RecordIndexUniquenessViolation wrapped = new RecordIndexUniquenessViolation("Uniqueness violation when making index readable", uniquenessViolation.get());
                        wrapped.addLogInfo(LogMessageKeys.INDEX_NAME, index.getName(), subspaceProvider.logKey(), subspaceProvider.toString(context));
                        throw wrapped;
                    } else {
                        updateIndexState(index.getName(), indexKey, IndexState.READABLE);
                        clearReadableIndexBuildData(tr, index);
                        return true;
                    }
                });
            } else {
                return AsyncUtil.READY_FALSE;
            }
        }).whenComplete((b, t) -> endRecordStoreStateWrite()).thenApply(this::addRemoveReplacedIndexesCommitCheckIfChanged);
        haveFuture = true;
        return future;
    } finally {
        if (!haveFuture) {
            endRecordStoreStateWrite();
        }
    }
}
Also used : LogMessageKeys(com.apple.foundationdb.record.logging.LogMessageKeys) UnaryOperator(java.util.function.UnaryOperator) MetaDataException(com.apple.foundationdb.record.metadata.MetaDataException) RecordSerializer(com.apple.foundationdb.record.provider.common.RecordSerializer) Subspace(com.apple.foundationdb.subspace.Subspace) MutationType(com.apple.foundationdb.MutationType) RecordCoreException(com.apple.foundationdb.record.RecordCoreException) Map(java.util.Map) RecordIndexUniquenessViolation(com.apple.foundationdb.record.RecordIndexUniquenessViolation) QueryToKeyMatcher(com.apple.foundationdb.record.query.QueryToKeyMatcher) InvalidProtocolBufferException(com.google.protobuf.InvalidProtocolBufferException) Query(com.apple.foundationdb.record.query.expressions.Query) KeyExpression(com.apple.foundationdb.record.metadata.expressions.KeyExpression) Set(java.util.Set) TupleRange(com.apple.foundationdb.record.TupleRange) KeySpacePath(com.apple.foundationdb.record.provider.foundationdb.keyspace.KeySpacePath) ByteOrder(java.nio.ByteOrder) SyntheticRecordType(com.apple.foundationdb.record.metadata.SyntheticRecordType) RecordMetaDataProvider(com.apple.foundationdb.record.RecordMetaDataProvider) RecordStoreState(com.apple.foundationdb.record.RecordStoreState) TupleHelpers(com.apple.foundationdb.tuple.TupleHelpers) API(com.apple.foundationdb.annotation.API) FunctionNames(com.apple.foundationdb.record.FunctionNames) RecordMetaData(com.apple.foundationdb.record.RecordMetaData) IndexAggregateFunction(com.apple.foundationdb.record.metadata.IndexAggregateFunction) AsyncUtil(com.apple.foundationdb.async.AsyncUtil) RecordQuery(com.apple.foundationdb.record.query.RecordQuery) RangeSet(com.apple.foundationdb.async.RangeSet) RecordQueryPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan) Supplier(java.util.function.Supplier) FormerIndex(com.apple.foundationdb.record.metadata.FormerIndex) ArrayList(java.util.ArrayList) ByteScanLimiter(com.apple.foundationdb.record.ByteScanLimiter) ParameterRelationshipGraph(com.apple.foundationdb.record.query.ParameterRelationshipGraph) LoggableException(com.apple.foundationdb.util.LoggableException) CloseableAsyncIterator(com.apple.foundationdb.async.CloseableAsyncIterator) IndexRecordFunction(com.apple.foundationdb.record.metadata.IndexRecordFunction) Nullable(javax.annotation.Nullable) ByteArrayUtil2(com.apple.foundationdb.tuple.ByteArrayUtil2) IsolationLevel(com.apple.foundationdb.record.IsolationLevel) CursorLimitManager(com.apple.foundationdb.record.cursors.CursorLimitManager) ExecuteState(com.apple.foundationdb.record.ExecuteState) AtomicLong(java.util.concurrent.atomic.AtomicLong) RecordType(com.apple.foundationdb.record.metadata.RecordType) Index(com.apple.foundationdb.record.metadata.Index) DynamicMessageRecordSerializer(com.apple.foundationdb.record.provider.common.DynamicMessageRecordSerializer) SyntheticRecordPlanner(com.apple.foundationdb.record.query.plan.synthetic.SyntheticRecordPlanner) IndexEntry(com.apple.foundationdb.record.IndexEntry) LoggerFactory(org.slf4j.LoggerFactory) RecordCoreStorageException(com.apple.foundationdb.record.RecordCoreStorageException) ByteBuffer(java.nio.ByteBuffer) RecordQueryPlanner(com.apple.foundationdb.record.query.plan.RecordQueryPlanner) Transaction(com.apple.foundationdb.Transaction) Tuple(com.apple.foundationdb.tuple.Tuple) Range(com.apple.foundationdb.Range) KeyValueLogMessage(com.apple.foundationdb.record.logging.KeyValueLogMessage) PipelineOperation(com.apple.foundationdb.record.PipelineOperation) RecordMetaDataProto(com.apple.foundationdb.record.RecordMetaDataProto) ByteArrayUtil(com.apple.foundationdb.tuple.ByteArrayUtil) KeyValue(com.apple.foundationdb.KeyValue) ImmutableMap(com.google.common.collect.ImmutableMap) Predicate(java.util.function.Predicate) Collection(java.util.Collection) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) IndexQueryabilityFilter(com.apple.foundationdb.record.query.IndexQueryabilityFilter) AndComponent(com.apple.foundationdb.record.query.expressions.AndComponent) RecordCoreArgumentException(com.apple.foundationdb.record.RecordCoreArgumentException) Collectors(java.util.stream.Collectors) ByteString(com.google.protobuf.ByteString) List(java.util.List) EvaluationContext(com.apple.foundationdb.record.EvaluationContext) AggregateFunctionNotSupportedException(com.apple.foundationdb.record.AggregateFunctionNotSupportedException) RecordTypeKeyComparison(com.apple.foundationdb.record.query.expressions.RecordTypeKeyComparison) IndexTypes(com.apple.foundationdb.record.metadata.IndexTypes) Optional(java.util.Optional) MutableRecordStoreState(com.apple.foundationdb.record.MutableRecordStoreState) RecordTypeOrBuilder(com.apple.foundationdb.record.metadata.RecordTypeOrBuilder) SyntheticRecordFromStoredRecordPlan(com.apple.foundationdb.record.query.plan.synthetic.SyntheticRecordFromStoredRecordPlan) SpotBugsSuppressWarnings(com.apple.foundationdb.annotation.SpotBugsSuppressWarnings) Descriptors(com.google.protobuf.Descriptors) AsyncIterator(com.apple.foundationdb.async.AsyncIterator) HashMap(java.util.HashMap) CompletableFuture(java.util.concurrent.CompletableFuture) AtomicReference(java.util.concurrent.atomic.AtomicReference) Function(java.util.function.Function) CursorStreamingMode(com.apple.foundationdb.record.CursorStreamingMode) Key(com.apple.foundationdb.record.metadata.Key) ExecuteProperties(com.apple.foundationdb.record.ExecuteProperties) EndpointType(com.apple.foundationdb.record.EndpointType) ScanProperties(com.apple.foundationdb.record.ScanProperties) Suppliers(com.google.common.base.Suppliers) LinkedList(java.util.LinkedList) Nonnull(javax.annotation.Nonnull) EmptyKeyExpression(com.apple.foundationdb.record.metadata.expressions.EmptyKeyExpression) MoreAsyncUtil(com.apple.foundationdb.async.MoreAsyncUtil) Logger(org.slf4j.Logger) Iterator(java.util.Iterator) IndexState(com.apple.foundationdb.record.IndexState) StoreRecordFunction(com.apple.foundationdb.record.metadata.StoreRecordFunction) ReadTransaction(com.apple.foundationdb.ReadTransaction) AsyncIterable(com.apple.foundationdb.async.AsyncIterable) FDBRecordStoreStateCache(com.apple.foundationdb.record.provider.foundationdb.storestate.FDBRecordStoreStateCache) Message(com.google.protobuf.Message) RecordCursor(com.apple.foundationdb.record.RecordCursor) QueryComponent(com.apple.foundationdb.record.query.expressions.QueryComponent) VisibleForTesting(com.google.common.annotations.VisibleForTesting) Collections(java.util.Collections) Optional(java.util.Optional) TupleRange(com.apple.foundationdb.record.TupleRange) Range(com.apple.foundationdb.Range) Transaction(com.apple.foundationdb.Transaction) ReadTransaction(com.apple.foundationdb.ReadTransaction) RecordIndexUniquenessViolation(com.apple.foundationdb.record.RecordIndexUniquenessViolation) Nonnull(javax.annotation.Nonnull)

Example 4 with RecordIndexUniquenessViolation

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

the class FDBRecordStoreIndexTest method uniquenessViolations.

@Test
public void uniquenessViolations() throws Exception {
    final String indexName = "MySimpleRecord$num_value_unique";
    try (FDBRecordContext context = openContext()) {
        openSimpleRecordStore(context);
        assertThat(recordStore.isIndexWriteOnly(indexName), is(false));
        recordStore.markIndexWriteOnly(indexName).get();
        assertThat(recordStore.isIndexWriteOnly(indexName), is(true));
        context.commit();
    }
    TestRecords1Proto.MySimpleRecord record1 = TestRecords1Proto.MySimpleRecord.newBuilder().setRecNo(1066L).setNumValueUnique(42).build();
    TestRecords1Proto.MySimpleRecord record2 = TestRecords1Proto.MySimpleRecord.newBuilder().setRecNo(1793L).setNumValueUnique(42).build();
    try (FDBRecordContext context = openContext()) {
        openSimpleRecordStore(context);
        recordStore.saveRecord(record1);
        recordStore.saveRecord(record2);
        context.commit();
    }
    Index index = recordStore.getRecordMetaData().getIndex("MySimpleRecord$num_value_unique");
    // Test scan uniqueness violations.
    try (FDBRecordContext context = openContext()) {
        openSimpleRecordStore(context);
        RecordCursorIterator<RecordIndexUniquenessViolation> cursor = recordStore.scanUniquenessViolations(index).asIterator();
        assertTrue(cursor.hasNext());
        RecordIndexUniquenessViolation first = cursor.next();
        assertEquals(Tuple.from(42L), first.getIndexEntry().getKey());
        assertEquals(Tuple.from(1066L), first.getPrimaryKey());
        assertEquals(Tuple.from(1793L), first.getExistingKey());
        assertTrue(cursor.hasNext());
        RecordIndexUniquenessViolation second = cursor.next();
        assertEquals(Tuple.from(42L), second.getIndexEntry().getKey());
        assertEquals(Tuple.from(1793L), second.getPrimaryKey());
        assertEquals(Tuple.from(1066L), second.getExistingKey());
        assertFalse(cursor.hasNext());
    }
    // Test scan uniqueness violations given value key.
    try (FDBRecordContext context = openContext()) {
        openSimpleRecordStore(context);
        RecordCursorIterator<RecordIndexUniquenessViolation> cursor = recordStore.scanUniquenessViolations(index, Key.Evaluated.scalar(42)).asIterator();
        assertTrue(cursor.hasNext());
        RecordIndexUniquenessViolation first = cursor.next();
        assertEquals(Tuple.from(42L), first.getIndexEntry().getKey());
        assertEquals(Tuple.from(1066L), first.getPrimaryKey());
        assertEquals(Tuple.from(1793L), first.getExistingKey());
        assertTrue(cursor.hasNext());
        RecordIndexUniquenessViolation second = cursor.next();
        assertEquals(Tuple.from(42L), second.getIndexEntry().getKey());
        assertEquals(Tuple.from(1793L), second.getPrimaryKey());
        assertEquals(Tuple.from(1066L), second.getExistingKey());
        assertFalse(cursor.hasNext());
    }
    // Test requesting to resolve uniqueness violations with a remaining record.
    try (FDBRecordContext context = openContext()) {
        openSimpleRecordStore(context);
        recordStore.resolveUniquenessViolation(index, Tuple.from(42), Tuple.from(1066L)).get();
        assertEquals(0, (int) recordStore.scanUniquenessViolations(index).getCount().get());
        assertNotNull(recordStore.loadRecord(Tuple.from(1066L)));
        assertNull(recordStore.loadRecord(Tuple.from(1793L)));
    }
    // Test requesting to resolve uniqueness violations with no remaining record.
    try (FDBRecordContext context = openContext()) {
        openSimpleRecordStore(context);
        recordStore.resolveUniquenessViolation(index, Tuple.from(42), null).get();
        assertEquals(0, (int) recordStore.scanUniquenessViolations(index).getCount().get());
        assertNull(recordStore.loadRecord(Tuple.from(1066L)));
        assertNull(recordStore.loadRecord(Tuple.from(1793L)));
    }
    // Test manually resolving uniqueness violations by deleting one record.
    try (FDBRecordContext context = openContext()) {
        openSimpleRecordStore(context);
        recordStore.deleteRecordAsync(Tuple.from(1793L)).get();
        assertEquals(0, (int) recordStore.scanUniquenessViolations(index).getCount().get());
        assertNotNull(recordStore.loadRecord(Tuple.from(1066L)));
        assertNull(recordStore.loadRecord(Tuple.from(1793L)));
    }
}
Also used : TestRecords1Proto(com.apple.foundationdb.record.TestRecords1Proto) RecordIndexUniquenessViolation(com.apple.foundationdb.record.RecordIndexUniquenessViolation) Index(com.apple.foundationdb.record.metadata.Index) ParameterizedTest(org.junit.jupiter.params.ParameterizedTest) Test(org.junit.jupiter.api.Test)

Example 5 with RecordIndexUniquenessViolation

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

the class FDBRecordStoreIndexTest method uniquenessViolationsWithFanOut.

@Test
public void uniquenessViolationsWithFanOut() throws Exception {
    Index index = new Index("MySimpleRecord$repeater", field("repeater", FanType.FanOut), Index.EMPTY_VALUE, IndexTypes.VALUE, IndexOptions.UNIQUE_OPTIONS);
    RecordMetaDataHook hook = metaData -> metaData.addIndex("MySimpleRecord", index);
    try (FDBRecordContext context = openContext()) {
        openSimpleRecordStore(context, hook);
        assertThat(recordStore.isIndexWriteOnly(index), is(false));
        recordStore.markIndexWriteOnly(index).get();
        assertThat(recordStore.isIndexWriteOnly(index), is(true));
        context.commit();
    }
    TestRecords1Proto.MySimpleRecord record1 = TestRecords1Proto.MySimpleRecord.newBuilder().setRecNo(1066L).addRepeater(1).addRepeater(2).addRepeater(3).build();
    TestRecords1Proto.MySimpleRecord record2 = TestRecords1Proto.MySimpleRecord.newBuilder().setRecNo(1793L).addRepeater(2).addRepeater(3).build();
    TestRecords1Proto.MySimpleRecord record3 = TestRecords1Proto.MySimpleRecord.newBuilder().setRecNo(1849L).addRepeater(3).build();
    try (FDBRecordContext context = openContext()) {
        openSimpleRecordStore(context, hook);
        recordStore.saveRecord(record1);
        recordStore.saveRecord(record2);
        recordStore.saveRecord(record3);
        context.commit();
    }
    try (FDBRecordContext context = openContext()) {
        openSimpleRecordStore(context, hook);
        assertEquals(5, (int) recordStore.scanUniquenessViolations(index).getCount().get());
        assertEquals(0, (int) recordStore.scanUniquenessViolations(index, Key.Evaluated.scalar(1)).getCount().get());
        assertEquals(2, (int) recordStore.scanUniquenessViolations(index, Key.Evaluated.scalar(2)).getCount().get());
        assertEquals(3, (int) recordStore.scanUniquenessViolations(index, Key.Evaluated.scalar(3)).getCount().get());
        RecordCursorIterator<RecordIndexUniquenessViolation> cursor = recordStore.scanUniquenessViolations(index, Key.Evaluated.scalar(3)).asIterator();
        assertTrue(cursor.hasNext());
        RecordIndexUniquenessViolation next = cursor.next();
        assertEquals(Tuple.from(3L), next.getIndexEntry().getKey());
        assertEquals(Tuple.from(1066L), next.getPrimaryKey());
        assertThat(next.getExistingKey(), is(oneOf(Tuple.from(1793L), Tuple.from(1849L))));
        assertTrue(cursor.hasNext());
        next = cursor.next();
        assertEquals(Tuple.from(3L), next.getIndexEntry().getKey());
        assertEquals(Tuple.from(1793L), next.getPrimaryKey());
        assertThat(next.getExistingKey(), is(oneOf(Tuple.from(1066L), Tuple.from(1849L))));
        assertTrue(cursor.hasNext());
        next = cursor.next();
        assertEquals(Tuple.from(3L), next.getIndexEntry().getKey());
        assertEquals(Tuple.from(1849L), next.getPrimaryKey());
        assertThat(next.getExistingKey(), is(oneOf(Tuple.from(1066L), Tuple.from(1793L))));
        assertFalse(cursor.hasNext());
        cursor = recordStore.scanUniquenessViolations(index, Key.Evaluated.scalar(2)).asIterator();
        assertTrue(cursor.hasNext());
        next = cursor.next();
        assertEquals(Tuple.from(2L), next.getIndexEntry().getKey());
        assertEquals(Tuple.from(1066L), next.getPrimaryKey());
        assertEquals(Tuple.from(1793L), next.getExistingKey());
        assertTrue(cursor.hasNext());
        next = cursor.next();
        assertEquals(Tuple.from(2L), next.getIndexEntry().getKey());
        assertEquals(Tuple.from(1793L), next.getPrimaryKey());
        assertEquals(Tuple.from(1066L), next.getExistingKey());
        assertFalse(cursor.hasNext());
    }
    try (FDBRecordContext context = openContext()) {
        openSimpleRecordStore(context, hook);
        recordStore.resolveUniquenessViolation(index, Key.Evaluated.scalar(3), null).get();
        assertEquals(0, (int) recordStore.scanUniquenessViolations(index).getCount().get());
        assertNull(recordStore.loadRecord(Tuple.from(1066L)));
        assertNull(recordStore.loadRecord(Tuple.from(1793L)));
        assertNull(recordStore.loadRecord(Tuple.from(1849L)));
    }
    try (FDBRecordContext context = openContext()) {
        openSimpleRecordStore(context, hook);
        recordStore.resolveUniquenessViolation(index, Tuple.from(3), Tuple.from(1066L)).get();
        assertEquals(0, (int) recordStore.scanUniquenessViolations(index).getCount().get());
        assertNotNull(recordStore.loadRecord(Tuple.from(1066L)));
        assertNull(recordStore.loadRecord(Tuple.from(1793L)));
        assertNull(recordStore.loadRecord(Tuple.from(1849L)));
    }
    try (FDBRecordContext context = openContext()) {
        openSimpleRecordStore(context, hook);
        recordStore.resolveUniquenessViolation(index, Tuple.from(3), Tuple.from(1793L)).get();
        assertEquals(0, (int) recordStore.scanUniquenessViolations(index).getCount().get());
        assertNull(recordStore.loadRecord(Tuple.from(1066L)));
        assertNotNull(recordStore.loadRecord(Tuple.from(1793L)));
        assertNull(recordStore.loadRecord(Tuple.from(1849L)));
    }
    try (FDBRecordContext context = openContext()) {
        openSimpleRecordStore(context, hook);
        recordStore.resolveUniquenessViolation(index, Tuple.from(3), Tuple.from(1849L)).get();
        assertEquals(0, (int) recordStore.scanUniquenessViolations(index).getCount().get());
        assertNull(recordStore.loadRecord(Tuple.from(1066L)));
        assertNull(recordStore.loadRecord(Tuple.from(1793L)));
        assertNotNull(recordStore.loadRecord(Tuple.from(1849L)));
    }
}
Also used : BeforeEach(org.junit.jupiter.api.BeforeEach) Arrays(java.util.Arrays) LogMessageKeys(com.apple.foundationdb.record.logging.LogMessageKeys) MetaDataException(com.apple.foundationdb.record.metadata.MetaDataException) IndexScanType(com.apple.foundationdb.record.IndexScanType) Pair(org.apache.commons.lang3.tuple.Pair) FDBError(com.apple.foundationdb.FDBError) RecordCoreException(com.apple.foundationdb.record.RecordCoreException) RecordIndexUniquenessViolation(com.apple.foundationdb.record.RecordIndexUniquenessViolation) Expressions.concat(com.apple.foundationdb.record.metadata.Key.Expressions.concat) GroupingKeyExpression(com.apple.foundationdb.record.metadata.expressions.GroupingKeyExpression) Tag(org.junit.jupiter.api.Tag) Query(com.apple.foundationdb.record.query.expressions.Query) KeyExpression(com.apple.foundationdb.record.metadata.expressions.KeyExpression) IndexOptions(com.apple.foundationdb.record.metadata.IndexOptions) Set(java.util.Set) FanType(com.apple.foundationdb.record.metadata.expressions.KeyExpression.FanType) TupleRange(com.apple.foundationdb.record.TupleRange) Matchers.instanceOf(org.hamcrest.Matchers.instanceOf) RecordMetaDataProvider(com.apple.foundationdb.record.RecordMetaDataProvider) RecordStoreState(com.apple.foundationdb.record.RecordStoreState) TupleHelpers(com.apple.foundationdb.tuple.TupleHelpers) Assertions.assertTrue(org.junit.jupiter.api.Assertions.assertTrue) InvalidIndexEntry(com.apple.foundationdb.record.provider.foundationdb.indexes.InvalidIndexEntry) AutoContinuingCursor(com.apple.foundationdb.record.cursors.AutoContinuingCursor) Matchers.is(org.hamcrest.Matchers.is) Assertions.assertThrows(org.junit.jupiter.api.Assertions.assertThrows) Assertions.fail(org.junit.jupiter.api.Assertions.fail) FunctionNames(com.apple.foundationdb.record.FunctionNames) RecordMetaData(com.apple.foundationdb.record.RecordMetaData) Assertions.assertNotNull(org.junit.jupiter.api.Assertions.assertNotNull) IndexAggregateFunction(com.apple.foundationdb.record.metadata.IndexAggregateFunction) Assertions.assertNull(org.junit.jupiter.api.Assertions.assertNull) AsyncUtil(com.apple.foundationdb.async.AsyncUtil) ArrayList(java.util.ArrayList) Strings(com.google.common.base.Strings) CloseableAsyncIterator(com.apple.foundationdb.async.CloseableAsyncIterator) MatcherAssert.assertThat(org.hamcrest.MatcherAssert.assertThat) Assertions.assertEquals(org.junit.jupiter.api.Assertions.assertEquals) FDBRecordStoreBase.indexEntryKey(com.apple.foundationdb.record.provider.foundationdb.FDBRecordStoreBase.indexEntryKey) Nullable(javax.annotation.Nullable) Matchers.greaterThanOrEqualTo(org.hamcrest.Matchers.greaterThanOrEqualTo) IsolationLevel(com.apple.foundationdb.record.IsolationLevel) Tags(com.apple.test.Tags) TestRecords1EvolvedProto(com.apple.foundationdb.record.TestRecords1EvolvedProto) ExecutionException(java.util.concurrent.ExecutionException) Assertions.assertArrayEquals(org.junit.jupiter.api.Assertions.assertArrayEquals) ParameterizedTest(org.junit.jupiter.params.ParameterizedTest) Index(com.apple.foundationdb.record.metadata.Index) FDBException(com.apple.foundationdb.FDBException) ThenKeyExpression(com.apple.foundationdb.record.metadata.expressions.ThenKeyExpression) IndexEntry(com.apple.foundationdb.record.IndexEntry) StoreTimer(com.apple.foundationdb.record.provider.common.StoreTimer) LoggerFactory(org.slf4j.LoggerFactory) Assertions.assertNotEquals(org.junit.jupiter.api.Assertions.assertNotEquals) Random(java.util.Random) Tuple(com.apple.foundationdb.tuple.Tuple) Range(com.apple.foundationdb.Range) Assertions.assertFalse(org.junit.jupiter.api.Assertions.assertFalse) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) Expressions.concatenateFields(com.apple.foundationdb.record.metadata.Key.Expressions.concatenateFields) ImmutableSet(com.google.common.collect.ImmutableSet) TestRecords1Proto(com.apple.foundationdb.record.TestRecords1Proto) ImmutableMap(com.google.common.collect.ImmutableMap) Matchers.lessThanOrEqualTo(org.hamcrest.Matchers.lessThanOrEqualTo) Collection(java.util.Collection) CompletionException(java.util.concurrent.CompletionException) IndexQueryabilityFilter(com.apple.foundationdb.record.query.IndexQueryabilityFilter) TypeSafeMatcher(org.hamcrest.TypeSafeMatcher) Collectors(java.util.stream.Collectors) Test(org.junit.jupiter.api.Test) List(java.util.List) EvaluationContext(com.apple.foundationdb.record.EvaluationContext) Matchers.equalTo(org.hamcrest.Matchers.equalTo) IndexTypes(com.apple.foundationdb.record.metadata.IndexTypes) Optional(java.util.Optional) TestNoIndexesProto(com.apple.foundationdb.record.TestNoIndexesProto) LazyCursor(com.apple.foundationdb.record.cursors.LazyCursor) EnumSource(org.junit.jupiter.params.provider.EnumSource) CompletableFuture(java.util.concurrent.CompletableFuture) Iterators(com.google.common.collect.Iterators) Key(com.apple.foundationdb.record.metadata.Key) FieldKeyExpression(com.apple.foundationdb.record.metadata.expressions.FieldKeyExpression) HashSet(java.util.HashSet) ExecuteProperties(com.apple.foundationdb.record.ExecuteProperties) ScanProperties(com.apple.foundationdb.record.ScanProperties) RecordCursorIterator(com.apple.foundationdb.record.RecordCursorIterator) BooleanSource(com.apple.test.BooleanSource) Nonnull(javax.annotation.Nonnull) Expressions.field(com.apple.foundationdb.record.metadata.Key.Expressions.field) EmptyKeyExpression(com.apple.foundationdb.record.metadata.expressions.EmptyKeyExpression) Matchers.hasEntry(org.hamcrest.Matchers.hasEntry) Description(org.hamcrest.Description) Matchers.oneOf(org.hamcrest.Matchers.oneOf) Logger(org.slf4j.Logger) RecordMetaDataBuilder(com.apple.foundationdb.record.RecordMetaDataBuilder) RecordTypeBuilder(com.apple.foundationdb.record.metadata.RecordTypeBuilder) IndexState(com.apple.foundationdb.record.IndexState) TestRecordsIndexFilteringProto(com.apple.foundationdb.record.TestRecordsIndexFilteringProto) Message(com.google.protobuf.Message) Collections(java.util.Collections) TestRecords1Proto(com.apple.foundationdb.record.TestRecords1Proto) RecordIndexUniquenessViolation(com.apple.foundationdb.record.RecordIndexUniquenessViolation) Index(com.apple.foundationdb.record.metadata.Index) ParameterizedTest(org.junit.jupiter.params.ParameterizedTest) Test(org.junit.jupiter.api.Test)

Aggregations

RecordIndexUniquenessViolation (com.apple.foundationdb.record.RecordIndexUniquenessViolation)9 Tuple (com.apple.foundationdb.tuple.Tuple)7 Index (com.apple.foundationdb.record.metadata.Index)6 IndexEntry (com.apple.foundationdb.record.IndexEntry)5 Key (com.apple.foundationdb.record.metadata.Key)5 List (java.util.List)5 Collectors (java.util.stream.Collectors)5 Nonnull (javax.annotation.Nonnull)5 Test (org.junit.jupiter.api.Test)5 RecordCoreException (com.apple.foundationdb.record.RecordCoreException)4 TestRecords1Proto (com.apple.foundationdb.record.TestRecords1Proto)4 IndexTypes (com.apple.foundationdb.record.metadata.IndexTypes)4 EmptyKeyExpression (com.apple.foundationdb.record.metadata.expressions.EmptyKeyExpression)4 Set (java.util.Set)4 CompletableFuture (java.util.concurrent.CompletableFuture)4 FDBError (com.apple.foundationdb.FDBError)3 FDBException (com.apple.foundationdb.FDBException)3 Range (com.apple.foundationdb.Range)3 AsyncUtil (com.apple.foundationdb.async.AsyncUtil)3 EvaluationContext (com.apple.foundationdb.record.EvaluationContext)3