Oak Composite NodeStore
The documentation of the Composite NodeStore implementation is work-in-progress. Please ask on oak-dev for things that are missing or unclear.
Overview
The CompositeNodeStore
is a NodeStore
implementation that wraps multiple NodeStore
instances
and exposes them through a single API. It is possible, for instance, to store all data in a
DocumentNodeStore
instance and relocate /libs
and /apps
in a SegmentNodeStore
instance.
Each node store wrapped by the composite node store instance is called a mount. The
CompositeNodeStore
can be configured with one or more mounts, each owning a defined set
of paths, and a default mount, owning the rest of the repository.
Mounts
The mounts are identified via their mount name.
The mount name is used to map a MountInfoProviderService
implementation to a NodeStore
implementation. For that the MountInfoProviderService
's name
property is prefixed by composite-mount-
in order to find the NodeStore
with the respective role
property value.
The mount name for the default mount is always composite-global
and mapped to the NodeStore
's role
property with that value (it is not prefixed by composite-mount-
).
Each non-default mount defines a number of entry path's which are used from the underlying NodeStore
. Other parts outside the mountPaths
are hidden.
Seed
In order to pre-populate the empty default store one can use the seed mount. That is automatically copied over to the default NodeStore
if the latter is not yet initialized as Composite default store (i.e. is lacking the :composite
child node below its root). This happens at most once!
Design limitations
Read-only mounts
The implementation allows for a default mount, which is read-write, and for any number of additional mounts, which are read-only. This limitation is by design and is not expected to be removed in future Oak version.
There are two major reasons for this limitation
- Having a commit run accross two or more multiple node stores is complicated in terms of implementation. Atomic commits will be very hard to ensure in a performant manner across multiple stores. Additionally, it will impose implementation burders to each NodeStore in order to support this special-case scenario.
- There are multiple Oak subsystems that are not composite-aware and that would need to changed for that to happen, and this would again complicate the implementation for a special-case scenario.
Referenceable nodes
Referenceable nodes are not permitted in non-default mounts. The reason is cross-mount references can become invalid in scenarios where the set of mounts changes. Consider the following scenario:
Mounts:
- default mount
D
- non-default mount
N1
, currently mounted under /tmp - non-default mount
N2
, currently not mounted
In the repository, node /content/bar
references referenceable node /tmp/foo
(from N1). When
the repository is shut down and reconfigureed to use N2 instead of N1, the reference can be broken
unless we ensure that the reference stores used by N1 and N2 are the same. This does not happen
today.
This constraint also means that:
- versionable nodes are not permitted in non-default mounts, as they are referenceable
nt:resource
nodes (usually found as children ofnt:file
nodes) are not permitted. It is recommended to replace them withoak:Resource
( see also OAK-4567 ).
Checking for read-only access
The Composite NodeStore mounts various other node stores in read-only mode. Since the read-only mode
is not enfored via permissions, it may not be queried via Session.hasPermission
. Instead, the
read-only status is surfaced via Session.hasCapability
. See OAK-6563 for details.
Bootstrapping
In order to bootstrap/initialize the NodeStore which later is used as non-default mount, one needs to start Oak without the Composite NodeStore first. Only then it is possible to populate the NodeStore later acting as non-default mount (as only then it one can write to it).