Class LocatableResolver
- java.lang.Object
-
- com.apple.foundationdb.record.provider.foundationdb.keyspace.LocatableResolver
-
- Direct Known Subclasses:
ExtendedDirectoryLayer
,ScopedDirectoryLayer
,ScopedInterningLayer
@API(MAINTAINED) public abstract class LocatableResolver extends Object
LocatableResolver
represents the concept of a locatable bi-directional mapping between Strings and Longs that is rooted at some location in the FDB key space. Resolvers that are located at different positions will allocate String to Long mappings independently. Subclasses ofLocatableResolver
are responsible for implementingcreate(FDBRecordContext, String)
,read(FDBRecordContext, String)
andreadReverse(FDBStoreTimer, Long)
operations. SeeScopedDirectoryLayer
for an implementation that leverages the FDB directory layer. When initializing, adefaultWriteSafetyCheck
can be specified that will be evaluated on writes to determine within the write transaction what the correctLocatableResolver
scope is for the write.
-
-
Nested Class Summary
Nested Classes Modifier and Type Class Description static class
LocatableResolver.LocatableResolverLockedException
Exception thrown when the locatable resolver is locked.
-
Field Summary
Fields Modifier and Type Field Description protected FDBDatabase
database
protected int
hashCode
protected com.apple.foundationdb.record.provider.foundationdb.keyspace.LocatableResolver.ResolverLocation
location
-
Constructor Summary
Constructors Modifier Constructor Description protected
LocatableResolver(FDBDatabase database, KeySpacePath path, CompletableFuture<ResolvedKeySpacePath> resolvedPath)
Created a locatable resolver.
-
Method Summary
All Methods Instance Methods Abstract Methods Concrete Methods Deprecated Methods Modifier and Type Method Description protected CompletableFuture<ResolverResult>
create(FDBRecordContext context, String key)
protected abstract CompletableFuture<ResolverResult>
create(FDBRecordContext context, String key, byte[] metadata)
abstract ResolverResult
deserializeValue(byte[] value)
Deserialize the raw bytes value stored in the mapping subspace.CompletableFuture<Void>
disableWriteLock()
Unlocks thisLocatableResolver
, so that calls toresolve(FDBStoreTimer, String)
will create entries if they do not exist.CompletableFuture<Void>
enableWriteLock()
Puts thisLocatableResolver
into a write locked state which will prevent any new entries from being created.boolean
equals(Object obj)
CompletableFuture<Void>
exclusiveLock()
Checks the lock state for the resolver and if it is unlocked puts thisLocatableResolver
into a write locked state which will prevent any new entries from being created.Subspace
getBaseSubspace()
Deprecated.blocks until the mapping subspace is fetched from the database, instead usegetBaseSubspaceAsync()
abstract CompletableFuture<Subspace>
getBaseSubspaceAsync()
Get aCompletableFuture
that will contain theSubspace
this resolver is rooted at (e.g.FDBDatabase
getDatabase()
Subspace
getMappingSubspace()
Deprecated.blocks until the mapping subspace is fetched from the database, instead usegetMappingSubspaceAsync()
abstract CompletableFuture<Subspace>
getMappingSubspaceAsync()
Get aCompletableFuture
that will contain theSubspace
where this resolver stores the mappings fromkey
String
s tovalue
Long
.protected abstract CompletableFuture<Subspace>
getStateSubspaceAsync()
CompletableFuture<Integer>
getVersion(FDBStoreTimer timer)
Gets the version as stored in the state key for thisLocatableResolver
.int
hashCode()
CompletableFuture<Void>
incrementVersion()
Increments the version of the data stored in this directory layer.CompletableFuture<Long>
mustResolve(FDBRecordContext context, String name)
Lookup the mapping forname
within the scope of the path that this object was constructed with.CompletableFuture<ResolverResult>
mustResolveWithMetadata(FDBRecordContext context, String name)
Lookup the mapping and metadata forname
within the scope of the path that this object was constructed with.protected abstract CompletableFuture<Optional<ResolverResult>>
read(FDBRecordContext context, String key)
protected abstract CompletableFuture<Optional<String>>
readReverse(FDBStoreTimer timer, Long value)
CompletableFuture<Long>
resolve(FDBRecordContext context, String name)
Map the Stringname
to a Long within the scope of the path that this object was constructed with.CompletableFuture<Long>
resolve(FDBRecordContext context, String name, ResolverCreateHooks hooks)
Map the Stringname
to a Long within the scope of the path that this object was constructed with.CompletableFuture<Long>
resolve(FDBStoreTimer timer, String name)
Map the Stringname
to a Long within the scope of the path that this object was constructed with.CompletableFuture<Long>
resolve(FDBStoreTimer timer, String name, ResolverCreateHooks hooks)
Map the Stringname
to a Long within the scope of the path that this object was constructed with.CompletableFuture<Long>
resolve(String name)
Map the Stringname
to a Long within the scope of the path that this object was constructed with.CompletableFuture<Long>
resolve(String name, ResolverCreateHooks hooks)
Map the Stringname
to a Long within the scope of the path that this object was constructed with.CompletableFuture<ResolverResult>
resolveWithMetadata(FDBRecordContext context, String name, ResolverCreateHooks hooks)
Map the Stringname
to aResolverResult
within the scope of the path that this object was constructed with.CompletableFuture<ResolverResult>
resolveWithMetadata(FDBStoreTimer timer, String name, ResolverCreateHooks hooks)
Map the Stringname
to aResolverResult
within the scope of the path that this object was constructed with.CompletableFuture<ResolverResult>
resolveWithMetadata(String name, ResolverCreateHooks hooks)
Map the Stringname
to aResolverResult
within the scope of the path that this object was constructed with.CompletableFuture<Boolean>
retired(FDBStoreTimer timer)
Check whether this resolver has been retired.CompletableFuture<Boolean>
retiredSkipCache(FDBRecordContext context)
Check whether this resolver has been retired.CompletableFuture<Void>
retireLayer()
Retire thisLocatableResolver
, indicating that it should not be used for any future resolve operations.CompletableFuture<String>
reverseLookup(FDBStoreTimer timer, Long value)
Lookup the String that maps to the provided value within the scope of the path that this object was constructed with.protected abstract CompletableFuture<Void>
setMapping(FDBRecordContext context, String key, ResolverResult value)
CompletableFuture<Void>
setMapping(FDBRecordContext context, String key, Long value)
abstract CompletableFuture<Void>
setWindow(long count)
String
toString()
protected abstract CompletableFuture<Void>
updateMetadata(FDBRecordContext context, String key, byte[] metadata)
CompletableFuture<Void>
updateMetadataAndVersion(String key, byte[] metadata)
Transactionally update the metadata for the providedkey
and (within the same transaction) increment the version of the resolver state (seeincrementVersion()
.<T> ScopedValue<T>
wrap(T value)
-
-
-
Field Detail
-
database
@Nonnull protected final FDBDatabase database
-
location
@Nonnull protected final com.apple.foundationdb.record.provider.foundationdb.keyspace.LocatableResolver.ResolverLocation location
-
hashCode
protected final int hashCode
-
-
Constructor Detail
-
LocatableResolver
protected LocatableResolver(@Nonnull FDBDatabase database, @Nullable KeySpacePath path, @Nullable CompletableFuture<ResolvedKeySpacePath> resolvedPath)
Created a locatable resolver.This constructor may seem a little strange in taking two paths that are, effectively, the same. The encouraged behavior for your resolvers is to create them from a
ResolvedKeySpacePath
, however for backward compatibility we still allow resolvers to be created from aFDBRecordContext
and aKeySpacePath
. All of the implementations ofLocatableResolver
are built such that if they are starting from aKeySpacePath
, they will then turn it into a resolved path, then use that to get the subspace, but if they are created from aResolvedKeySpacePath
, they will instead directly get the subspace. This constructor is handed both the path and the potentially completed resolved form of that path, and when logging the path, if the resolved path is completed, will log that since it provides more detail.- Parameters:
database
- the database to use for resolutionpath
- the path at which the resolver has its data locatedresolvedPath
- the resolved form of the path
-
-
Method Detail
-
wrap
public <T> ScopedValue<T> wrap(T value)
-
getDatabase
@Nonnull public FDBDatabase getDatabase()
-
resolve
@Nonnull public CompletableFuture<Long> resolve(@Nonnull String name)
Map the Stringname
to a Long within the scope of the path that this object was constructed with. Will return the value that's persisted in FDB or create it if it does not exist.For an instrumented version of this method, see
resolve(FDBStoreTimer, String)
.- Parameters:
name
- the value to resolve- Returns:
- a future for the resolved Long value
- See Also:
resolve(FDBStoreTimer, String)
-
resolve
@Nonnull public CompletableFuture<Long> resolve(@Nullable FDBStoreTimer timer, @Nonnull String name)
Map the Stringname
to a Long within the scope of the path that this object was constructed with. Will return the value that's persisted in FDB or create it if it does not exist.- Parameters:
timer
- theFDBStoreTimer
used for collecting metricsname
- the value to resolve- Returns:
- a future for the resolved Long value
-
resolve
@API(UNSTABLE) @Nonnull public CompletableFuture<Long> resolve(@Nonnull FDBRecordContext context, @Nonnull String name)
Map the Stringname
to a Long within the scope of the path that this object was constructed with. Will return the value that's persisted in FDB or create it if it does not exist. This method may create and commit a separate record context for the lookup, but it will reuse the same read version as the provided transaction, so it should not incur all of the overhead of starting a new transaction.This method is
API.Status.UNSTABLE
for the same reasons asresolveWithMetadata(FDBRecordContext, String, ResolverCreateHooks)
.- Parameters:
context
- the base context used toname
- the value to resolve- Returns:
- a future for the resolved Long value
-
resolve
@Nonnull public CompletableFuture<Long> resolve(@Nonnull String name, @Nonnull ResolverCreateHooks hooks)
Map the Stringname
to a Long within the scope of the path that this object was constructed with. Will return the value that's persisted in FDB or create it if it does not exist.For an instrumented version of this method, see
resolve(FDBStoreTimer, String, ResolverCreateHooks)
- Parameters:
name
- the value to resolvehooks
-ResolverCreateHooks
to run on create- Returns:
- a future for the resolved Long value
- See Also:
resolve(FDBStoreTimer, String, ResolverCreateHooks)
-
resolve
@Nonnull public CompletableFuture<Long> resolve(@Nullable FDBStoreTimer timer, @Nonnull String name, @Nonnull ResolverCreateHooks hooks)
Map the Stringname
to a Long within the scope of the path that this object was constructed with. Will return the value that's persisted in FDB or create it if it does not exist.- Parameters:
timer
- theFDBStoreTimer
used for collecting metricsname
- the value to resolvehooks
-ResolverCreateHooks
to run on create- Returns:
- a future for the resolved Long value
-
resolve
@API(UNSTABLE) @Nonnull public CompletableFuture<Long> resolve(@Nonnull FDBRecordContext context, @Nonnull String name, @Nonnull ResolverCreateHooks hooks)
Map the Stringname
to a Long within the scope of the path that this object was constructed with. Will return the value that's persisted in FDB or create it if it does not exist. This method may create and commit a separate record context for the lookup, but it will reuse the same read version as the provided transaction, so it should not incur all of the overhead of starting a new transaction.This method is
API.Status.UNSTABLE
for the same reasons asresolveWithMetadata(FDBRecordContext, String, ResolverCreateHooks)
.- Parameters:
context
- theFDBRecordContext
used to base possible child transactions onname
- the value to resolvehooks
-ResolverCreateHooks
to run on create- Returns:
- a future for the resolved Long value
-
resolveWithMetadata
@Nonnull public CompletableFuture<ResolverResult> resolveWithMetadata(@Nonnull String name, @Nonnull ResolverCreateHooks hooks)
Map the Stringname
to aResolverResult
within the scope of the path that this object was constructed with. If we are creating the entry forname
, TheResolverCreateHooks
provided ashooks
will be run. Any metadata that was already present or created can be seen by callingResolverResult.getMetadata()
on the returned result. Will return the value that's persisted in FDB or create it if it does not exist.For an instrumented version of this method, see
resolveWithMetadata(FDBStoreTimer, String, ResolverCreateHooks)
.- Parameters:
name
- the value to resolvehooks
-ResolverCreateHooks
to run on create- Returns:
- a future for the
ResolverResult
containing the resolved value and metadata - See Also:
resolveWithMetadata(FDBStoreTimer, String, ResolverCreateHooks)
-
resolveWithMetadata
@Nonnull public CompletableFuture<ResolverResult> resolveWithMetadata(@Nullable FDBStoreTimer timer, @Nonnull String name, @Nonnull ResolverCreateHooks hooks)
Map the Stringname
to aResolverResult
within the scope of the path that this object was constructed with. If we are creating the entry forname
, TheResolverCreateHooks
provided ashooks
will be run. Any metadata that was already present or created can be seen by callingResolverResult.getMetadata()
on the returned result. Will return the value that's persisted in FDB or create it if it does not exist.- Parameters:
timer
- theFDBStoreTimer
used for collecting metricsname
- the value to resolvehooks
-ResolverCreateHooks
to run on create- Returns:
- a future for the
ResolverResult
containing the resolved value and metadata
-
resolveWithMetadata
@API(UNSTABLE) @Nonnull public CompletableFuture<ResolverResult> resolveWithMetadata(@Nonnull FDBRecordContext context, @Nonnull String name, @Nonnull ResolverCreateHooks hooks)
Map the Stringname
to aResolverResult
within the scope of the path that this object was constructed with. If we are creating the entry forname
, TheResolverCreateHooks
provided ashooks
will be run. Any metadata that was already present or created can be seen by callingResolverResult.getMetadata()
on the returned result. Will return the value that's persisted in FDB or create it if it does not exist. This may create and commit separate transactions from the given context, but it will reuse the same read version as the given context, so it should not incur all of the overhead of starting a new transaction.This method is currently
API.Status.UNSTABLE
. The reason is that the thinking on the exact semantics of this method is currently in flux, and this method may be changed in the future to use the same transaction as the one provided. This is a more straightforward API, but it complicates the way that values from this function are cached, as it must be careful not to cache uncommitted results.- Parameters:
context
- theFDBRecordContext
used to base possible child transactions onname
- the value to resolvehooks
-ResolverCreateHooks
to run on create- Returns:
- a future for the
ResolverResult
containing the resolved value and metadata
-
mustResolveWithMetadata
@Nonnull public CompletableFuture<ResolverResult> mustResolveWithMetadata(@Nonnull FDBRecordContext context, @Nonnull String name)
Lookup the mapping and metadata forname
within the scope of the path that this object was constructed with. UnlikeresolveWithMetadata(FDBRecordContext, String, ResolverCreateHooks)
this method will not attempt to create the mapping if none exists.- Parameters:
context
- the transaction to use to access the databasename
- the value to resolve- Returns:
- a future for the
ResolverResult
- Throws:
NoSuchElementException
- if the value does not exist
-
mustResolve
public CompletableFuture<Long> mustResolve(@Nonnull FDBRecordContext context, @Nonnull String name)
Lookup the mapping forname
within the scope of the path that this object was constructed with. Unlikeresolve(FDBStoreTimer, String)
this method will not attempt to create the mapping if none exists.- Parameters:
context
- the transaction to use to access the databasename
- the value to resolve- Returns:
- a future for the resolved Long value
- Throws:
NoSuchElementException
- if the value does not exist
-
reverseLookup
@Nonnull public CompletableFuture<String> reverseLookup(@Nullable FDBStoreTimer timer, @Nonnull Long value)
Lookup the String that maps to the provided value within the scope of the path that this object was constructed with.- Parameters:
timer
- theFDBStoreTimer
used for collecting metricsvalue
- the value of the mapping to lookup- Returns:
- a future for the name that maps to this value
- Throws:
NoSuchElementException
- if the value is not found
-
exclusiveLock
@Nonnull public CompletableFuture<Void> exclusiveLock()
Checks the lock state for the resolver and if it is unlocked puts thisLocatableResolver
into a write locked state which will prevent any new entries from being created. If the resolver is not unlocked this will throw aLocatableResolver.LocatableResolverLockedException
. The lock state is cached for 30 seconds. When changing the lock state, transactions started at least 30 seconds after this method succeeds should see the updated state.- Returns:
- a future that completes when the write lock has been set
-
enableWriteLock
@Nonnull public CompletableFuture<Void> enableWriteLock()
Puts thisLocatableResolver
into a write locked state which will prevent any new entries from being created. Does not perform any checks on the current lock state. The lock state is cached for 30 seconds. When changing the lock state, transactions started at least 30 seconds after this method succeeds should see the updated state.- Returns:
- a future that completes when the write lock has been set
-
disableWriteLock
@Nonnull public CompletableFuture<Void> disableWriteLock()
Unlocks thisLocatableResolver
, so that calls toresolve(FDBStoreTimer, String)
will create entries if they do not exist. The lock state is cached for 30 seconds. When changing the lock state, transactions started at least 30 seconds after this method succeeds should see the updated state.- Returns:
- a future that completes when the write lock has been cleared
-
retireLayer
@Nonnull public CompletableFuture<Void> retireLayer()
Retire thisLocatableResolver
, indicating that it should not be used for any future resolve operations. This can be used to indicate that the current resolver has migrated to a new location and that clients should be using that resolver instead.- Returns:
- a future that completes when the write lock has been cleared
-
incrementVersion
@Nonnull public CompletableFuture<Void> incrementVersion()
Increments the version of the data stored in this directory layer. The last read state is cached for 30, meaning it will take up to that long for a successfulincrementVersion
to be seen bygetVersion(FDBStoreTimer)
.- Returns:
- A future that completes when the version has been incremented
-
getVersion
@Nonnull public CompletableFuture<Integer> getVersion(@Nullable FDBStoreTimer timer)
Gets the version as stored in the state key for thisLocatableResolver
. This is used in conjunction with the version of the forward directory cache (seeFDBDatabase.getDirectoryCacheVersion()
) to coordinate major changes to data in theLocatableResolver
that require any future reads to directly consult FDB rather than relying on the directory cache. On calls toresolve(FDBStoreTimer, String)
the cache version from theFDBDatabase
object is compared with the version stored in the resolver state. If the version in the resolver state is ahead of the version in the database object, the cache associated with that database is invalidated. Note: if the version known by the resolver is behind the version for the cache (e.g. our cached version of the state hasn't been refreshed recently enough to see some change) we don't need to do anything special, we can trust that values from a future version of the cache are backwards compatible to our current version.- Parameters:
timer
- The store timer to instrument the transaction with.- Returns:
- A future that will complete with the value of the current version
-
retired
@Nonnull public CompletableFuture<Boolean> retired(@Nullable FDBStoreTimer timer)
Check whether this resolver has been retired. A retired state indicates that this resolver was once active but has since been migrated to a new keyspace.- Parameters:
timer
- The store timer to instrument the transaction with.- Returns:
- A future that will complete with boolean indicating whether this resolver has been retired.
-
retiredSkipCache
@Nonnull public CompletableFuture<Boolean> retiredSkipCache(@Nonnull FDBRecordContext context)
Check whether this resolver has been retired. Skips the resolver state cache and reads the state directly. A retired state indicates that this resolver was once active but has since been migrated to a new keyspace.- Parameters:
context
- the transaction to use to access the database- Returns:
- A future that will complete with boolean indicating whether this resolver has been retired.
-
updateMetadataAndVersion
@Nonnull public CompletableFuture<Void> updateMetadataAndVersion(@Nonnull String key, @Nullable byte[] metadata)
Transactionally update the metadata for the providedkey
and (within the same transaction) increment the version of the resolver state (seeincrementVersion()
. An entrykey
must already exist in the directory, if it does not aNoSuchElementException
will be thrown. If you want to add metadata to a key you are creating see thecreateHook
parameter inresolveWithMetadata(FDBStoreTimer, String, ResolverCreateHooks)
.- Parameters:
key
- the key in the directory to modifymetadata
- the new metadata- Returns:
- a future that will finish when the update and increment operations are complete
-
read
protected abstract CompletableFuture<Optional<ResolverResult>> read(@Nonnull FDBRecordContext context, String key)
-
create
protected abstract CompletableFuture<ResolverResult> create(@Nonnull FDBRecordContext context, @Nonnull String key, @Nullable byte[] metadata)
-
create
protected final CompletableFuture<ResolverResult> create(@Nonnull FDBRecordContext context, @Nonnull String key)
-
readReverse
protected abstract CompletableFuture<Optional<String>> readReverse(FDBStoreTimer timer, Long value)
-
updateMetadata
protected abstract CompletableFuture<Void> updateMetadata(FDBRecordContext context, String key, byte[] metadata)
-
setMapping
protected abstract CompletableFuture<Void> setMapping(FDBRecordContext context, String key, ResolverResult value)
-
setMapping
public final CompletableFuture<Void> setMapping(FDBRecordContext context, String key, Long value)
-
setWindow
public abstract CompletableFuture<Void> setWindow(long count)
-
getStateSubspaceAsync
protected abstract CompletableFuture<Subspace> getStateSubspaceAsync()
-
getMappingSubspace
@Nonnull @API(DEPRECATED) @Deprecated public Subspace getMappingSubspace()
Deprecated.blocks until the mapping subspace is fetched from the database, instead usegetMappingSubspaceAsync()
TheSubspace
where this resolver stores the mappings fromkey
String
s tovalue
Long
. Direct access to this subspace is not needed by general users and extreme care should be taken when interacting with it.- Returns:
- the mapping subspace
-
getMappingSubspaceAsync
@Nonnull public abstract CompletableFuture<Subspace> getMappingSubspaceAsync()
Get aCompletableFuture
that will contain theSubspace
where this resolver stores the mappings fromkey
String
s tovalue
Long
. Direct access to this subspace is not needed by general users and extreme care should be taken when interacting with it.- Returns:
- a future that, when ready, will hold the mapping subspace
-
getBaseSubspace
@Nonnull @API(DEPRECATED) @Deprecated public Subspace getBaseSubspace()
Deprecated.blocks until the mapping subspace is fetched from the database, instead usegetBaseSubspaceAsync()
Get theSubspace
that this resolver is rooted at (e.g. the global resolverExtendedDirectoryLayer.global(FDBDatabase)
has a base subspace at the root of the FDB keyspace. Note that this is not the subspace where the resolver maintains its allocation keys (seegetMappingSubspaceAsync()
).- Returns:
- the base subspace
-
getBaseSubspaceAsync
@Nonnull public abstract CompletableFuture<Subspace> getBaseSubspaceAsync()
Get aCompletableFuture
that will contain theSubspace
this resolver is rooted at (e.g. the global resolverExtendedDirectoryLayer.global(FDBDatabase)
has a base subspace at the root of the FDB keyspace. Note that this is not the subspace where the resolver maintains its allocation keys (seegetMappingSubspaceAsync()
).- Returns:
- a future that, when ready, will hold the base subspace
-
deserializeValue
@Nonnull public abstract ResolverResult deserializeValue(byte[] value)
Deserialize the raw bytes value stored in the mapping subspace.- Parameters:
value
- raw value bytes.- Returns:
- the deserialized
ResolverResult
.
-
-