Closed KrzysFR closed 4 years ago
I know that there are still probably some bugs and changes needed to the DL API, but this PR is so big already and require other changes that are not directly related to the DL. We will have to deal with it with other PRs.
Placeholder before the full commentary on this huge pull request!
Change required to the Directory Layer, and the subspace APIs in general, to allow safe caching of directory subspaces, even if the underlying metadata is mutated.
The goals that this PR wants to achieve are the following:
ISubspaceLocation<TSubspace>
which can be "resolved" into the concrete subspace implementation inside a transaction.The recently introduced
\xFF/metadataVersion
key helps in building a metadata cache that is "safe".We could use this key to keep a cache for the prefix of the different directories, but:
The changes to the DL API have some repercussions on the global API:
This is PR is very large, but unfortunately all these changes have to be done together in order to have a working build.
Most of the changes needed to update existing code:
db.Root["foo"]["bar"]
to easily create aDirectorySubspaceLocation
that points to some sub-folder under the root partitionFdbDirectorySubspace
instance in some init codepath, and instead replace that singleton with the correspondingDirectorySubspaceLocation
.this.Location.Resolve(tr)
instead. Avoid resolving the same location multiple times in the same transaction, even though the instance is cached per transaction.db.Directory
property (which is gone), prefer using the methods on FdbDirectorySubspaceLocation, i.e:await location.CreateOrOpenAsync(tr)
. Using thedb.DirectoryLayer
is still possible but should only be done with absolute paths, and only in layers that have to handle very specific scenarios. Most app code will be fine using subspace locations.TryOpenCachedAsync(...)
on the DirectoryLayer, and prefer usinglocation.Resolve(...)
which does the same thing, and is not restricted to directory subspaces only.Task
and make sure to either change them toWriteAsync(...)
or make them result aTask<TResult>
. This is because the compiler messes up the overload resolution, and will invoke a version that returns aTask<Task>
instead, and so the loop handler body will not be properly awaited!!!ReadAsync(tr => tr.GetAsync(this.Subspace.Encode(...)))
because they have to converted toasync tr => { var subspace = await this.Location.Resolve(tr); return await tr.GetAsync(subspace.Encode(...)); }
. It is very easy to forger to addawait
in thereturn await tr...
and end up with aTask<Task<Slice>>
instead of aTask<Slice>
!The most common issue is that there is a lot of code that would run in a non-async handler to Set or Clear some keys, and would used a globally cached Directory subspace instance. This code cannot work anymore, and has to be converted to async in order to call Resolve(..) on the subspace location.