LocatableResolver { @Nonnull public CompletableFuture<Long> resolve(@Nonnull String name) { return resolve((FDBStoreTimer)null, name, ResolverCreateHooks.getDefault()); } protected  LocatableResolver(@Nonnull FDBDatabase database,
                                @Nullable KeySpacePath path,
                                @Nullable CompletableFuture<ResolvedKeySpacePath> resolvedPath); ScopedValue<T> wrap(T value); @Nonnull FDBDatabase getDatabase(); @Nonnull CompletableFuture<Long> resolve(@Nonnull String name); @Nonnull CompletableFuture<Long> resolve(@Nullable FDBStoreTimer timer, @Nonnull String name); @API(API.Status.UNSTABLE) @Nonnull CompletableFuture<Long> resolve(@Nonnull FDBRecordContext context, @Nonnull String name); @Nonnull CompletableFuture<Long> resolve(@Nonnull String name,
                                           @Nonnull ResolverCreateHooks hooks); @Nonnull CompletableFuture<Long> resolve(@Nullable FDBStoreTimer timer,
                                           @Nonnull String name,
                                           @Nonnull ResolverCreateHooks hooks); @API(API.Status.UNSTABLE) @Nonnull CompletableFuture<Long> resolve(@Nonnull FDBRecordContext context,
                                           @Nonnull String name,
                                           @Nonnull ResolverCreateHooks hooks); @Nonnull CompletableFuture<ResolverResult> resolveWithMetadata(@Nonnull String name,
                                                                 @Nonnull ResolverCreateHooks hooks); @Nonnull CompletableFuture<ResolverResult> resolveWithMetadata(@Nullable FDBStoreTimer timer,
                                                                 @Nonnull String name,
                                                                 @Nonnull ResolverCreateHooks hooks); @API(API.Status.UNSTABLE) @Nonnull CompletableFuture<ResolverResult> resolveWithMetadata(@Nonnull FDBRecordContext context, @Nonnull String name, @Nonnull ResolverCreateHooks hooks); @Nonnull CompletableFuture<ResolverResult> mustResolveWithMetadata(@Nonnull FDBRecordContext context, @Nonnull String name); CompletableFuture<Long> mustResolve(@Nonnull FDBRecordContext context, @Nonnull String name); @Nonnull CompletableFuture<String> reverseLookup(@Nullable FDBStoreTimer timer, @Nonnull Long value); @Nonnull CompletableFuture<Void> exclusiveLock(); @Nonnull CompletableFuture<Void> enableWriteLock(); @Nonnull CompletableFuture<Void> disableWriteLock(); @Nonnull CompletableFuture<Void> retireLayer(); @Nonnull CompletableFuture<Void> incrementVersion(); @Nonnull CompletableFuture<Integer> getVersion(@Nullable FDBStoreTimer timer); @Nonnull CompletableFuture<Boolean> retired(@Nullable FDBStoreTimer timer); @Nonnull CompletableFuture<Boolean> retiredSkipCache(@Nonnull FDBRecordContext context); @Nonnull CompletableFuture<Void> updateMetadataAndVersion(@Nonnull final String key,
                                                            @Nullable final byte[] metadata); @VisibleForTesting final CompletableFuture<Void> setMapping(FDBRecordContext context, String key, Long value); @VisibleForTesting abstract CompletableFuture<Void> setWindow(long count); @Nonnull @API(API.Status.DEPRECATED) @Deprecated Subspace getMappingSubspace(); @Nonnull abstract CompletableFuture<Subspace> getMappingSubspaceAsync(); @Nonnull @API(API.Status.DEPRECATED) @Deprecated Subspace getBaseSubspace(); @Nonnull abstract CompletableFuture<Subspace> getBaseSubspaceAsync(); @Nonnull abstract ResolverResult deserializeValue(byte[] value); @Override String toString(); @Override boolean equals(Object obj); @Override int hashCode();  }
@Test public void testLookupCaching() { KeySpace keySpace = new KeySpace(new KeySpaceDirectory("path", KeyType.STRING, "path")); ResolvedKeySpacePath path1; try (FDBRecordContext context = database.openContext()) { path1 = keySpace.resolveFromKey(context, Tuple.from("path")); } LocatableResolver resolver = scopedDirectoryGenerator.apply(database, path1); Long value = resolver.resolve("foo").join(); for (int i = 0; i < 5; i++) { Long fetched = resolver.resolve("foo").join(); assertThat("we should always get the original value", fetched, is(value)); } CacheStats stats = database.getDirectoryCacheStats(); assertThat("subsequent lookups should hit the cache", stats.hitCount(), is(5L)); }
@Test public void testDirectoryIsolation() { KeySpace keySpace = new KeySpace( new KeySpaceDirectory("path", KeyType.STRING, "path") .addSubdirectory(new KeySpaceDirectory("to", KeyType.STRING, "to") .addSubdirectory(new KeySpaceDirectory("dirLayer1", KeyType.STRING, "dirLayer1")) .addSubdirectory(new KeySpaceDirectory("dirLayer2", KeyType.STRING, "dirLayer2")) ) ); try (FDBRecordContext context = database.openContext()) { ResolvedKeySpacePath path1 = keySpace.resolveFromKey(context, Tuple.from("path", "to", "dirLayer1")); ResolvedKeySpacePath path2 = keySpace.resolveFromKey(context, Tuple.from("path", "to", "dirLayer2")); LocatableResolver resolver = scopedDirectoryGenerator.apply(database, path1); LocatableResolver sameResolver = scopedDirectoryGenerator.apply(database, path1); LocatableResolver differentResolver = scopedDirectoryGenerator.apply(database, path2); List<String> names = ImmutableList.of("a", "set", "of", "names", "to", "resolve"); List<Long> resolved = new ArrayList<>(); List<Long> same = new ArrayList<>(); List<Long> different = new ArrayList<>(); for (String name : names) { resolved.add(resolver.resolve(context.getTimer(), name).join()); same.add(sameResolver.resolve(context.getTimer(), name).join()); different.add(differentResolver.resolve(context.getTimer(), name).join()); } assertThat("same resolvers produce identical results", resolved, contains(same.toArray())); assertThat("different resolvers are independent", resolved, not(contains(different.toArray()))); } }
@Test public void testResolveWithWeakReadSemantics() { final boolean tracksReadVersions = database.isTrackLastSeenVersionOnRead(); final boolean tracksCommitVersions = database.isTrackLastSeenVersionOnCommit(); try { database.setTrackLastSeenVersionOnRead(true); database.setTrackLastSeenVersionOnCommit(false); final String key = "hello " + UUID.randomUUID(); long resolvedValue; try (FDBRecordContext context = database.openContext()) { resolvedValue = globalScope.resolve(context, key).join(); } database.clearCaches(); final FDBRecordContextConfig config = FDBRecordContextConfig.newBuilder() .setWeakReadSemantics(new FDBDatabase.WeakReadSemantics(0, Long.MAX_VALUE, true)) .build(); try (FDBRecordContext context = database.openContext(config)) { long resolvedAgainValue = globalScope.resolve(context, key).join(); assertEquals(resolvedValue, resolvedAgainValue, "resolved value changed between transactions"); } } finally { database.setTrackLastSeenVersionOnRead(tracksReadVersions); database.setTrackLastSeenVersionOnCommit(tracksCommitVersions); } }