There are four main operations in libelektra-kdb
: open
, get
, set
and close
.
For each of these there is a kdb*
function the user calls to trigger the operation and plugins export a function for each of the operations they support (at least get
).
Additionally, plugins may implement commit
and error
.
These are part of the set
operation and there is no corresponding kdbCommit
or kdbError
function available in libelektra-kdb
.
The operations get
and set
also have different phases:
- The
get
operation has:init
,resolver
,cachecheck
,prestorage
,storage
andpoststorage
. - The
set
operation has:resolver
,prestorage
,storage
andpoststorage
followed byprecommit
,commit
andpostcommit
if the previous phases where successful or byprerollback
,rollback
andpostrollback
if the previous phases failed.
These phases are implemented by a backend plugin. Read the Documentation on Backend Plugins for more information on what backend plugins do.
Note The steps of the operations described below, are referenced in the source code with
// Step X
comments.
The open
operation implemented in kdbOpen
is the first thing that happens to all KDB
instances.
The basic flow of this operation is:
- Create empty
KDB
instance - Configure
KDB
instance for bootstrap - Run bootstrap
get
operation: This loads the contents ofsystem:/elektra/mountpoints
so that the mountpoints can be configured. - Process contract and set up plugins for hooks (see Hooks)
- Parse mountpoints:
This transforms the contents of
system:/elektra/mountpoints
into the internal state stored in aKDB
instance. - Reconfigure
KDB
with real mountpoints: This switches theKDB
instance from bootstrap mode to use the real mountpoint state created above. - Add hard coded mountpoints to
KDB
instance: There are a few hard coded mountpoints (root mountpoints,system:/elektra/modules
,system:/elektra/version
, etc.) that are always present. They are added in this step.
Namespaces in mountpoint configs:
dir:/
,user:/
andsystem:/
mountpoints can be created without restrictions, except for the reserved sections listed below.spec:/
mountpoints can be created with the same restrictions, but they are also treated specially duringget
andset
.proc:/
mountpoints are always read-only and receive special treatment duringget
default:/
mountpoints are read-only and receive special treatment duringget
, specifically they only go through thepoststorage
phase- mountpoints in all other namespaces are entirely illegal
Note The special treatments of the various namespaces are explained below in the sections for the
get
andset
operation.
Reserved sections:
- Creating a mountpoint for
/elektra
or below in any namespace is forbidden. This section of the KDB is reserved for Elektra's own config. system:/elektra/mountpoints
,user:/elektra/mountpoints
anddir:/elektra/mountpoints
are all required for the bootstrap process and use a hard coded backend. The backends are implemented by a standard file-based backend plugin that is defined at compile-time oflibelektra-kdb
.system:/elektra/version
andsystem:/elektra/modules
will always use hard coded read-only backends containing information about this Elektra installation. The backends are implemented by special purpose backend plugins.
The purpose of the get
operation is to read data stored in backends into a KDB
instance.
Note: Some details of a
get
operation are defined in the contract with backend plugins.
Properties of kdbGet()
:
- After calling
kdbGet (kdb, ks, parentKey)
, the KeySetks
will contain all keys (including their values) that are stored in any backend with a mountpoint that is belowparentKey
. - After calling
kdbGet (kdb, ks, parentKey)
, belowparentKey
the KeySetks
will mostly contain keys that are stored in a backend. The exception here areproc:/
andspec:/
keys. For other namespaces, all keys belowparentKey
will be removed fromks
. Forproc:/
andspec:/
only keys that overlap with a backend that was loaded will be removed fromks
. - The KeySet
ks
may contain other keys not belowparentKey
:- Keys that are not below
parentKey
, but are stored in a backend that contains other keys which are belowparentKey
. These keys are returned, because backends are treated as one atomic unit. Either all keys within a backend are read, or none of them are. - Keys that were already present in
ks
whenkdbGet()
was called and do not conflict with the goal of representing the current state of the KDB belowparentKey
.
- Keys that are not below
- After calling
kdbGet (kdb, ks, parentKey)
, the KeyparentKey
will only have themeta:/error/*
ormeta:/warnings/#/*
metakeys, if the errors/warnings originate from thiskdbGet()
call. In other words,kdbGet ()
first clears any existing errors/warnings and only then starts doing the actual work. - It is an error to use a
parentKey
with a namespace other than:default:/
,proc:/
,spec:
,dir:/
,user:/
,system:/
or cascading
To the caller it looks as if kdbGet()
had removed all keys below parentKey
, as well as some others, from ks
and then loaded the data from the backends.
Which backends are actually read is an implementation detail.
Which keys are removed from ks
depends on the backends that are read.
kdbGet()
will always try to be efficient in achieving its goal of reading the keys below parentKey
.
It is only guaranteed that below parentKey
the KeySet ks
correctly represents the state of the KDB.
For the rest of ks
there are no such guarantees.
Note: In the list below "phase" always refers to a phase of the
get
operation as described in the backend plugin contract.
The flow of this operation is:
- Determine the backends needed to read all keys below
parentKey
- Run the
open
operation for all required backends that haven't been opened - Run the
init
phase on all the backends that haven't been initialized - Run the
resolver
phase on all backends - From now on ignore all backends, which indicated that there is no update.
- If all backends are now ignored, return.
- If a global cache plugin is enabled: Ask the global cache plugin for the cache handles (normally modification times) for all backends.
- If all backends have an existing cache entry:
Run the
cachecheck
phase on all backends - If all backends indicated the cache is still valid: Ask the global cache plugin for the cached data and return.
- Run the
prestorage
andstorage
phase on all backends. - Run the
poststorage
phase of allspec:/
backends. - Merge the data from all backends
- If enabled, run the
gopts/get
hook. - Run the
spec/copy
hook. - Split data back into individual backends.
- Run the
poststorage
phase for all non-spec:/
backends. - Remove all keys which are below the parent key of any backend that has been read from
ks
. - Merge the data from all backends into
ks
. - If a global cache plugin is enabled, update cache.
- Run the
notification/send
hook. Then return.
Note: In case of error, we abort immediately, restore
ks
to its original state and return.
Influence of namespaces:
spec:/
backends go throughinit
,resolver
,cache
,presetstorage
andstorage
phases as normal, but theirpoststorage
phase is called earlier. This is required, because any validation and post-processing ofspec:/
keys needs to happen, before they are used as the specification for other keys in the actualpoststorage
phase.dir:/
,user:/
andsystem:/
go through all phases as described above.proc:/
mountpoints go through all the phases as described above, but they are not stored in the cache.default:/
backends only go through thepoststorage
phase. This is becausedefault:/
keys are generated from the specification (stored asspec:/
keys). Therefore, nodefault:/
keys can exist before the specification is processed by thespec/copy
hook.- keys with other namespaces are always illegal in
ks
The purpose of the set
operation is to write data from a KDB
instance into backends.
Note: Some details of a
set
operation are defined in the contract with backend plugins.
Properties of kdbSet()
:
- When calling
kdbSet (kdb, ks, parentKey)
the contents (key names, values and metadata) ofks
will mostly not be modified. The only modifications that are made toks
are those that originate from thespec/copy
hook. - All keys in
ks
that are belowparentKey
will be persisted in the KDB, when akdbSet (kdb, ks, parentKey)
call returns successfully. Additionally, any key inks
that shares a backend with another key which is belowparentKey
will also be persisted. - Calling
kdbSet
may result in an error, ifkdbGet
wasn't called on thisKDB
instance with the sameparentKey
at least once. - After calling
kdbSet (kdb, ks, parentKey)
, the KeyparentKey
will only have themeta:/error/*
ormeta:/warnings/#/*
metakeys, if the errors/warnings originate from thiskdbSet()
call. In other words,kdbSet ()
first clears any existing errors/warnings and only then starts doing the actual work. - It is an error to use a
parentKey
with a namespace other than:default:/
,proc:/
,spec:
,dir:/
,user:/
,system:/
or cascading
The flow of this operation is:
- Determine the backends needed to write all keys below
parentKey
. - Check that all backends are opened and initialized (i.e.
kdbGet()
was called). - Run the
spec/copy
hook onks
(to add metakeys to newly created keys). - Deep-Copy
ks
(belowparentKey
) into a new KeySetset_ks
- Split
set_ks
into individual backends - Determine which backends contain changed data.
Any backend that contains a key that needs sync (via
KEY_FLAG_SYNC
) could contain changed data. From now on ignore all backends that have not changed. From now on also ignore all backends that were initialized as read-only. Issue a warning, if a change was detected (viaKEY_FLAG_SYNC
) in a read-only backend.Note: Steps 4-6 might be combined into a single procedure that deep-copies only keys from changed backends into separate KeySets per backend
- Run the
resolver
andprestorage
on all backends (abort immediately on error and go to e). - Merge the results into a new version of
set_ks
. - Run the
spec/remove
hook onset_ks
(to remove copied metakeys). - Split
set_ks
into individual backends again. - Run the
storage
andpoststorage
phases on all backends (abort immediately on error and go to e). - If everything was successful:
Run the
precommit
andcommit
phases on all backends (abort immediately on error and go to e), then run thepostcommit
phase on all backends (record all errors as warnings and ignore them) and return.
-
If there was an error:
Run the
prerollback
,rollback
andpostrollback
phases on all backends and return.
Influence of namespaces:
default:/
andproc:/
keys are completely ignored bykdbSet()
spec:/
,dir:/
,user:/
andsystem:/
go through all phases as described above.- keys with other namespaces are always illegal in
ks
The close
operation is very simple.
It simply frees up all resources used by a KDB
instance.