$darkmode
Elektra 0.11.0
Cascading Lookups

This tutorial assumes that you are already familiar with namespaces. This tutorial will only explain cascading lookup.

When Elektra looks up a cascading key (i.e. key names without a namespace and a leading slash /, the namespaces are searched in the following order:

  • spec (contains metadata, e.g. to modify Elektra's lookup behavior)
  • proc (process-related information)
  • dir (directory-related information, e.g. .git or .htaccess)
  • user (user configuration)
  • system (system configuration)

If a key, for example, exists in both user and system namespace, the key in the user namespace takes precedence over the one in the system namespace. If there is no such key in the user namespace the key in the system namespace acts as a fallback.

But let's demonstrate this with an example:

Add a Key to the system Namespace

Configuration in the system namespace is the same for all users. Therefore, this namespace provides a default or fallback configuration.

With the default Elektra installation only an administrator can update configuration settings within the system namespace.

# Backup-and-Restore:/tests/tutorial
# Backup old override specification
kdb set user:/tests/overrides $(mktemp)
kdb export system:/tests/overrides dump > $(kdb get user:/tests/overrides)
kdb get /tests/tutorial/cascading/#0/current/test
# RET: 11
# STDERR: Did not find key '/tests/tutorial/cascading/#0/current/test'
# Now add the key ...
sudo kdb set system:/tests/tutorial/cascading/#0/current/test "hello world"
#> Create a new key system:/tests/tutorial/cascading/#0/current/test with string "hello world"
# ... and verify that it exists
kdb get /tests/tutorial/cascading/#0/current/test
#> hello world

Add a Key to the user Namespace

A user may now want to override the configuration in system, so he/she sets a key in the user namespace:

kdb set user:/tests/tutorial/cascading/#0/current/test "hello galaxy"
#> Create a new key user:/tests/tutorial/cascading/#0/current/test with string "hello galaxy"
# This key masks the key in the system namespace
kdb get /tests/tutorial/cascading/#0/current/test
#> hello galaxy

Note that configuration in the user namespace only affects this user. Other users would still get the key from the system namespace.

Add a Key to the dir Namespace

The dir namespace is associated with a directory. The configuration in the dir namespace applies to the current working directory and all its subdirectories. This is useful if you have project specific settings (e.g. your Git configuration or a .htaccess file).

As dir precedes the user namespace, configuration in dir can overwrite user configuration:

# create and change to a new directory ...
mkdir kdbtutorial
cd kdbtutorial
# ... and create a key in this directories dir-namespace
# By default this data will be saved in the directory `.dir`.
kdb set dir:/tests/tutorial/cascading/#0/current/test "hello universe"
#> Create a new key dir:/tests/tutorial/cascading/#0/current/test with string "hello universe"
# This key masks the key in the system namespace
kdb get /tests/tutorial/cascading/#0/current/test
#> hello universe
# But is only present in the associated directory
cd ..
kdb get /tests/tutorial/cascading/#0/current/test
# hello galaxy

Add a Key to the proc Namespace

The proc namespace is not accessible by the command line tool kdb, as it is unique for each running process using Elektra. So we have to omit an example for this namespace at this point. Elektrified applications can use this namespace to override configuration from other namespaces internally.

Add a Key to the spec Namespace

The spec namespace is used to store metadata about keys and therefore Elektra handles the spec namespace differently to other namespaces. The following part of the tutorial is dedicated to the impact of the spec namespace on cascading lookups.

Write Operations and the cascading Namespace

If a value is to be written to a cascading key, i.e., a key starting with '/', only cascading keys that resolve to an existing key will be used.

For example,

kdb set /tests/tutorial/cascading/#0/current/cascading_write_test value
# STDERR: Aborting: A cascading write to a non-existent key is ambiguous.
# RET: 12
kdb meta-set /tests/tutorial/cascading/#0/current/cascading_write_test metakey metavalue
# STDERR: Aborting: A cascading write to a non-existent key is ambiguous.
# RET: 12

will both fail, as no matching key exists. Since there are multiple hypothetical key names that would match the cascading name (keys of specific namespaces like user:, system:, ...) if they existed, it is not clear what the user intended and thus an error is produced.

To make the previous two operations meaningful, a matching key in the user: namespace is created:

kdb set user:/tests/tutorial/cascading/#0/current/cascading_write_test value
#> Create a new key user:/tests/tutorial/cascading/#0/current/cascading_write_test with string "value"

Now, the operations operate on a well-defined key and succeed this time:

kdb set /tests/tutorial/cascading/#0/current/cascading_write_test value
#> Set string to "value"
#> Using name user:/tests/tutorial/cascading/#0/current/cascading_write_test
kdb meta-set /tests/tutorial/cascading/#0/current/cascading_write_test metakey metavalue
#> Using name user:/tests/tutorial/cascading/#0/current/cascading_write_test

Override Links

The spec namespace is special as it can completely change how the cascading lookup works.

During a cascading lookup for a specific key, the default Elektra behavior can be changed by a corresponding spec-key, i.e. a key in the spec namespace with the same name.

For example, the metadata override/#0 of the respective spec-key can be specified to use a different key in favor of the key itself. This way, we can implement a redirect or symlink like behavior and therefore even config from current folder (dir namespace) can be overwritten.

The cascading lookup will consider the following metadata keys of spec-key:

  1. override/#n redirect to one of the specified keys
  2. namespaces/#n specifies which namespaces will be considered and in which order
  3. fallback/#n if no key was found these keys will act as a fallback
  4. default defines a default value for the key if none of the keys was found

Note: override/#n, namespaces/#n and fallback/#n are Elektra array keys. This means, such keys can exist several times, each with a different number for n, e.g. override/#0, override/#1... This way, we can define multiple values for a specific key with a defined order.

As you can see, override links are considered before everything else, which makes them really powerful.

Consider the following example:

First, we create a target key to demonstrate the override link mechanism:

sudo kdb set system:/tests/overrides/test "hello override"
#> Create a new key system:/tests/overrides/test with string "hello override"

Override links can be defined by adding them to the override/# metadata array key of the corresponding spec-key:

sudo kdb meta-set spec:/tests/tutorial/cascading/#0/current/test override/#0 /tests/overrides/test

Now when doing a cascading lookup, we get the value of our target key instead of the specified one:

kdb get /tests/tutorial/cascading/#0/current/test
#> hello override

As we used a cascading key for our override link (/tests/overrides/test) we can use this to allow users to provide their own tests/overrides/test keys. If a user sets the /tests/overrides/test key, the user namespace will be used (for a non-root user) and therefore the new target for our /tests/tutorial/cascading/#0/current/test key will be user:/tests/overrides/test instead of system:/tests/overrides/test.

kdb set user:/tests/overrides/test "hello user"
#> Create a new key user:/tests/overrides/test with string "hello user"
kdb get /tests/tutorial/cascading/#0/current/test
#> hello user

Furthermore, you can specify a custom order for the namespaces, set fallback keys and more. For more information, read the `elektra-spec` help page.

Cleanup

As last part in this tutorial we remove the modifications to the database we made previously.

kdb rm -r user:/tests/tutorial/
sudo kdb rm -r system:/tests/tutorial
sudo kdb rm -r system:/tests/overrides
kdb import system:/tests/overrides dump < $(kdb get user:/tests/overrides)
rm $(kdb get user:/tests/overrides)
kdb rm user:/tests/overrides
sudo kdb rm -r spec:/tests/tutorial/
rm -r kdbtutorial