Search in sources :

Example 6 with FDBStoreTimer

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

the class FDBRecordStoreStateCacheTest method conflictWhenCachedChanged.

/**
 * Make sure that if one transaction changes the store header then an open store in another transaction that
 * loaded the store state from cache will fail at commit time with conflict.
 */
@ParameterizedTest(name = "conflictWhenCachedChanged (test context = {0})")
@MethodSource("testContextSource")
public void conflictWhenCachedChanged(@Nonnull StateCacheTestContext testContext) throws Exception {
    FDBRecordStoreStateCache origStoreStateCache = fdb.getStoreStateCache();
    try {
        fdb.setStoreStateCache(testContext.getCache(fdb));
        RecordMetaData metaData1 = RecordMetaData.build(TestRecords1Proto.getDescriptor());
        RecordMetaDataBuilder metaDataBuilder = RecordMetaData.newBuilder().setRecords(TestRecords1Proto.getDescriptor());
        metaDataBuilder.addIndex("MySimpleRecord", "num_value_2");
        RecordMetaData metaData2 = metaDataBuilder.getRecordMetaData();
        assertThat(metaData1.getVersion(), lessThan(metaData2.getVersion()));
        FDBRecordStore.Builder storeBuilder;
        // Initialize the record store with a meta-data store
        try (FDBRecordContext context = openContext()) {
            context.getTimer().reset();
            FDBRecordStore recordStore = FDBRecordStore.newBuilder().setContext(context).setMetaDataProvider(metaData1).setKeySpacePath(path).create();
            assertEquals(1, context.getTimer().getCount(FDBStoreTimer.Counts.STORE_STATE_CACHE_MISS));
            assertEquals(metaData1.getVersion(), recordStore.getRecordStoreState().getStoreHeader().getMetaDataversion());
            commit(context);
            storeBuilder = recordStore.asBuilder();
        }
        // Load the record store state into the cache.
        try (FDBRecordContext context1 = testContext.getCachedContext(fdb, storeBuilder);
            FDBRecordContext context2 = testContext.getCachedContext(fdb, storeBuilder)) {
            context1.setTimer(new FDBStoreTimer());
            context2.setTimer(new FDBStoreTimer());
            FDBRecordStore recordStore1 = storeBuilder.copyBuilder().setContext(context1).setMetaDataProvider(metaData1).open();
            assertEquals(1, context1.getTimer().getCount(FDBStoreTimer.Counts.STORE_STATE_CACHE_HIT));
            assertEquals(metaData1.getVersion(), recordStore1.getRecordMetaData().getVersion());
            assertEquals(metaData1.getVersion(), recordStore1.getRecordStoreState().getStoreHeader().getMetaDataversion());
            // Update the meta-data in the second transaction
            FDBRecordStore recordStore2 = storeBuilder.copyBuilder().setContext(context2).setMetaDataProvider(metaData2).open();
            assertEquals(1, context2.getTimer().getCount(FDBStoreTimer.Counts.STORE_STATE_CACHE_HIT));
            assertEquals(Collections.singletonList(recordStore2.getRecordMetaData().getRecordType("MySimpleRecord")), recordStore2.getRecordMetaData().recordTypesForIndex(recordStore2.getRecordMetaData().getIndex("MySimpleRecord$num_value_2")));
            assertEquals(metaData2.getVersion(), recordStore2.getRecordMetaData().getVersion());
            assertEquals(metaData2.getVersion(), recordStore2.getRecordStoreState().getStoreHeader().getMetaDataversion());
            context2.commit();
            // Add a write to context1 so that the conflict ranges actually get checked.
            recordStore1.saveRecord(TestRecords1Proto.MySimpleRecord.newBuilder().setRecNo(1066).setNumValue2(1415).build());
            // Should conflict on store header even though not actually read in this transaction
            assertThrows(FDBExceptions.FDBStoreTransactionConflictException.class, context1::commit);
        }
        // New transaction should now see the new meta-data version
        try (FDBRecordContext context = openContext()) {
            context.getTimer().reset();
            // Trying to load with the old meta-data should fail
            assertThrows(RecordStoreStaleMetaDataVersionException.class, () -> storeBuilder.copyBuilder().setContext(context).setMetaDataProvider(metaData1).open());
            assertEquals(1, context.getTimer().getCount(FDBStoreTimer.Counts.STORE_STATE_CACHE_MISS));
            // Trying to load with the new meta-data should succeed
            FDBRecordStore recordStore = storeBuilder.copyBuilder().setContext(context).setMetaDataProvider(metaData2).open();
            assertEquals(1, context.getTimer().getCount(FDBStoreTimer.Counts.STORE_STATE_CACHE_HIT));
            assertEquals(metaData2.getVersion(), recordStore.getRecordStoreState().getStoreHeader().getMetaDataversion());
        }
    } finally {
        fdb.setStoreStateCache(origStoreStateCache);
    }
}
Also used : RecordMetaData(com.apple.foundationdb.record.RecordMetaData) RecordMetaDataBuilder(com.apple.foundationdb.record.RecordMetaDataBuilder) FDBRecordContext(com.apple.foundationdb.record.provider.foundationdb.FDBRecordContext) FDBStoreTimer(com.apple.foundationdb.record.provider.foundationdb.FDBStoreTimer) FDBRecordStore(com.apple.foundationdb.record.provider.foundationdb.FDBRecordStore) FDBExceptions(com.apple.foundationdb.record.provider.foundationdb.FDBExceptions) ParameterizedTest(org.junit.jupiter.params.ParameterizedTest) MethodSource(org.junit.jupiter.params.provider.MethodSource)

Example 7 with FDBStoreTimer

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

the class LocatableResolverTest method testDirectoryCacheWithUncommittedContext.

@Test
public void testDirectoryCacheWithUncommittedContext() {
    FDBDatabase fdb = FDBDatabaseFactory.instance().getDatabase();
    fdb.clearCaches();
    // In the scoped directory layer test, this can conflict with initializing the reverse directory layer
    fdb.getReverseDirectoryCache().waitUntilReadyForTesting();
    final String key = "hello " + UUID.randomUUID();
    FDBStoreTimer timer = new FDBStoreTimer();
    long resolved;
    try (FDBRecordContext context = fdb.openContext(null, timer)) {
        // Ensure initial get read version is instrumented
        context.getReadVersion();
        resolved = context.asyncToSync(FDBStoreTimer.Waits.WAIT_DIRECTORY_RESOLVE, globalScope.resolve(context, key));
        assertAll(() -> assertThat("directory resolution should not have been from cache", timer.getCount(FDBStoreTimer.Events.DIRECTORY_READ), equalTo(1)), () -> assertThat("should only have opened at most 2 child transaction", timer.getCount(FDBStoreTimer.Counts.OPEN_CONTEXT), lessThanOrEqualTo(3)), () -> assertThat("should only have gotten one read version", timer.getCount(FDBStoreTimer.Events.GET_READ_VERSION), equalTo(1)), () -> assertThat("should have only committed the inner transaction", timer.getCount(FDBStoreTimer.Events.COMMIT), lessThanOrEqualTo(1)));
    // do not commit transaction (though child transaction updating the resolved key should have been committed)
    }
    // Should read cached value
    timer.reset();
    long resolved2;
    try (FDBRecordContext context = fdb.openContext(null, timer)) {
        // Ensure initial get read version is instrumented
        context.getReadVersion();
        resolved2 = context.asyncToSync(FDBStoreTimer.Waits.WAIT_DIRECTORY_RESOLVE, globalScope.resolve(context, key));
        assertAll(() -> assertThat("resolved value from cache does not match initial resolution", resolved2, equalTo(resolved)), () -> assertEquals(0, timer.getCount(FDBStoreTimer.Events.DIRECTORY_READ), "should not have read from the directory layer"), () -> assertEquals(1, timer.getCount(FDBStoreTimer.Counts.OPEN_CONTEXT), "should not have opened any additional contexts"), () -> assertEquals(1, timer.getCount(FDBStoreTimer.Events.GET_READ_VERSION), "should not need any additional read versions"), () -> assertEquals(0, timer.getCount(FDBStoreTimer.Events.COMMIT)));
    }
    // Clear the caches and see that the value in the database matches
    database.clearCaches();
    timer.reset();
    try (FDBRecordContext context = fdb.openContext(null, timer)) {
        long resolved3 = context.asyncToSync(FDBStoreTimer.Waits.WAIT_DIRECTORY_RESOLVE, globalScope.resolve(context, key));
        assertAll(() -> assertThat("resolved value from database does not match initial resolution", resolved3, equalTo(resolved)), () -> assertThat("directory resolution should not have been from cache", timer.getCount(FDBStoreTimer.Events.DIRECTORY_READ), equalTo(1)), () -> assertThat("should only have opened at most 2 child transaction", timer.getCount(FDBStoreTimer.Counts.OPEN_CONTEXT), lessThanOrEqualTo(3)), () -> assertThat("should only have gotten one read version", timer.getCount(FDBStoreTimer.Events.GET_READ_VERSION), equalTo(1)), () -> assertThat("should only have committed the inner transaction", timer.getCount(FDBStoreTimer.Events.COMMIT), lessThanOrEqualTo(1)));
    }
}
Also used : FDBRecordContext(com.apple.foundationdb.record.provider.foundationdb.FDBRecordContext) FDBStoreTimer(com.apple.foundationdb.record.provider.foundationdb.FDBStoreTimer) FDBDatabase(com.apple.foundationdb.record.provider.foundationdb.FDBDatabase) Test(org.junit.jupiter.api.Test) ParameterizedTest(org.junit.jupiter.params.ParameterizedTest)

Example 8 with FDBStoreTimer

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

the class LocatableResolverTest method testCachesWinnerOfConflict.

@Test
public void testCachesWinnerOfConflict() {
    FDBDatabase fdb = FDBDatabaseFactory.instance().getDatabase();
    fdb.clearCaches();
    // In the scoped directory layer test, this can conflict with initializing the reverse directory layer
    fdb.getReverseDirectoryCache().waitUntilReadyForTesting();
    final String key = "hello " + UUID.randomUUID();
    long resolved;
    final FDBStoreTimer timer = new FDBStoreTimer();
    try (FDBRecordContext context1 = fdb.openContext(null, timer);
        FDBRecordContext context2 = fdb.openContext(null, timer)) {
        // Ensure both started
        context1.getReadVersion();
        context2.getReadVersion();
        // Both contexts try to create the key
        CompletableFuture<Long> resolvedFuture1 = globalScope.resolve(context1, key);
        CompletableFuture<Long> resolvedFuture2 = globalScope.resolve(context2, key);
        long resolved1 = context1.asyncToSync(FDBStoreTimer.Waits.WAIT_DIRECTORY_RESOLVE, resolvedFuture1);
        long resolved2 = context2.asyncToSync(FDBStoreTimer.Waits.WAIT_DIRECTORY_RESOLVE, resolvedFuture2);
        assertAll(() -> assertThat("two concurrent resolutions of the same key should match", resolved1, equalTo(resolved2)), () -> assertThat("at least one transaction should read from database", timer.getCount(FDBStoreTimer.Events.DIRECTORY_READ), greaterThanOrEqualTo(1)), () -> assertThat("should not open more transactions than the two parents and five children", timer.getCount(FDBStoreTimer.Counts.OPEN_CONTEXT), lessThanOrEqualTo(7)), () -> assertThat("should not have committed more than the five children", timer.getCount(FDBStoreTimer.Events.COMMIT), lessThanOrEqualTo(5)));
        resolved = resolved1;
    }
    timer.reset();
    try (FDBRecordContext context = fdb.openContext(null, timer)) {
        context.getReadVersion();
        long resolvedAgain = context.asyncToSync(FDBStoreTimer.Waits.WAIT_DIRECTORY_RESOLVE, globalScope.resolve(context, key));
        assertAll(() -> assertThat("resolved value in cache should match initial resolution", resolvedAgain, equalTo(resolved)), () -> assertThat("should have resolved from cache", timer.getCount(FDBStoreTimer.Events.DIRECTORY_READ), equalTo(0)));
    }
}
Also used : FDBRecordContext(com.apple.foundationdb.record.provider.foundationdb.FDBRecordContext) FDBStoreTimer(com.apple.foundationdb.record.provider.foundationdb.FDBStoreTimer) FDBDatabase(com.apple.foundationdb.record.provider.foundationdb.FDBDatabase) Test(org.junit.jupiter.api.Test) ParameterizedTest(org.junit.jupiter.params.ParameterizedTest)

Example 9 with FDBStoreTimer

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

the class LocatableResolverTest method testDoesNotCacheValueReadFromReadYourWritesCache.

/**
 * This is mainly to test a counter factual where the same transaction is used to actually resolve the value as
 * is used by the caller. In that case, one could accidentally pollute the cache with uncommitted data. To protect
 * against that, this test is designed to fail if someone changes the resolution logic so that uncommitted data
 * (even possibly uncommitted data re-read from the same transaction that wrote it) might be put in the cache.
 */
@Test
public void testDoesNotCacheValueReadFromReadYourWritesCache() {
    FDBDatabase fdb = FDBDatabaseFactory.instance().getDatabase();
    fdb.clearCaches();
    final String key = "hello " + UUID.randomUUID();
    final FDBStoreTimer timer = new FDBStoreTimer();
    long resolved;
    try (FDBRecordContext context = fdb.openContext(null, timer)) {
        // First time: nothing in cache or DB. Entry is created.
        resolved = context.asyncToSync(FDBStoreTimer.Waits.WAIT_DIRECTORY_RESOLVE, globalScope.resolve(context, key));
        assertEquals(1, timer.getCount(FDBStoreTimer.Events.DIRECTORY_READ), "should have read from the database");
        // Second time: if same context used to create and read, then this would read from transaction's read your writes cache, not the database
        long resolvedAgain = context.asyncToSync(FDBStoreTimer.Waits.WAIT_DIRECTORY_RESOLVE, globalScope.resolve(context, key));
        assertEquals(resolved, resolvedAgain, "resolving the same key should not change the value even in the same transaction");
    // do not commit main transaction
    }
    // Read from cache. If present, this should not have changed its value
    timer.reset();
    boolean cached;
    try (FDBRecordContext context = fdb.openContext(null, timer)) {
        long resolvedFromCache = context.asyncToSync(FDBStoreTimer.Waits.WAIT_DIRECTORY_RESOLVE, globalScope.resolve(context, key));
        cached = timer.getCount(FDBStoreTimer.Events.DIRECTORY_READ) == 0;
        if (cached) {
            assertEquals(resolved, resolvedFromCache, "resolved value should have changed when reading from cache");
        }
    }
    // Clear caches, and re-read from the database.
    if (cached) {
        fdb.clearCaches();
        timer.reset();
        try (FDBRecordContext context = fdb.openContext(null, timer)) {
            long resolvedFromDb = context.asyncToSync(FDBStoreTimer.Waits.WAIT_DIRECTORY_RESOLVE, globalScope.resolve(context, key));
            assertEquals(resolved, resolvedFromDb, "resolved value from database should have matched initial resolution");
        }
    }
}
Also used : FDBRecordContext(com.apple.foundationdb.record.provider.foundationdb.FDBRecordContext) FDBStoreTimer(com.apple.foundationdb.record.provider.foundationdb.FDBStoreTimer) FDBDatabase(com.apple.foundationdb.record.provider.foundationdb.FDBDatabase) Test(org.junit.jupiter.api.Test) ParameterizedTest(org.junit.jupiter.params.ParameterizedTest)

Example 10 with FDBStoreTimer

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

the class LeaderboardIndexTest method getFDB.

@BeforeEach
public void getFDB() {
    if (TRACE) {
        FDBDatabaseFactory.instance().setTrace("/tmp", "LeaderboardIndexTest");
    }
    fdb = FDBDatabaseFactory.instance().getDatabase();
    metrics = new FDBStoreTimer();
}
Also used : FDBStoreTimer(com.apple.foundationdb.record.provider.foundationdb.FDBStoreTimer) BeforeEach(org.junit.jupiter.api.BeforeEach)

Aggregations

FDBStoreTimer (com.apple.foundationdb.record.provider.foundationdb.FDBStoreTimer)29 Test (org.junit.jupiter.api.Test)21 ParameterizedTest (org.junit.jupiter.params.ParameterizedTest)15 FDBRecordContext (com.apple.foundationdb.record.provider.foundationdb.FDBRecordContext)14 FDBDatabase (com.apple.foundationdb.record.provider.foundationdb.FDBDatabase)9 Nonnull (javax.annotation.Nonnull)9 Arrays (java.util.Arrays)8 List (java.util.List)8 Function (java.util.function.Function)8 Collections (java.util.Collections)7 CompletableFuture (java.util.concurrent.CompletableFuture)7 Collectors (java.util.stream.Collectors)7 Assertions.assertEquals (org.junit.jupiter.api.Assertions.assertEquals)7 Assertions.assertFalse (org.junit.jupiter.api.Assertions.assertFalse)7 RecordCursor (com.apple.foundationdb.record.RecordCursor)6 FirableCursor (com.apple.foundationdb.record.cursors.FirableCursor)6 StoreTimer (com.apple.foundationdb.record.provider.common.StoreTimer)6 RecordQueryPlan (com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan)6 Iterator (java.util.Iterator)6 ExecutionException (java.util.concurrent.ExecutionException)6