This page outlines configuration options relevant to using the disk storage feature of OPA. Configuration options are to be found in the configuration docs.
While it’s possible to load data larger than the allotted memory resources into OPA using disk storage, there are limitations to be aware of:
Partitions
Partitions determine how the JSON data is split up when stored in the underlying key-value store. For example, this table shows how an example document would be stored given different configured partitions:
{
"users": {
"alice": { "roles": ["admin"] },
"bob": { "roles": ["viewer"] }
}
}
Partitions | Keys | Values |
---|---|---|
(1) none | /users | {"alice": {"roles": ["admin"]}, "bob": {"roles": ["viewer"]}}} |
— | — | — |
(2) /users | /users/alice | {"roles": ["admin"]} |
/users/bob | {"roles": ["viewer"]} | |
— | — | — |
(3) /users/* | /users/alice/roles | ["admin"] |
/users/bob/roles | ["viewer"] |
Partitioning has consequences on performance: in the example above, the number of keys to retrieve from the database (and the amount of data of its values) varies.
Query | Partitions | Number of keys read |
---|---|---|
data.users | (1) | 1 |
(2) | 2 | |
(3) | 2 | |
— | — | — |
data.users.alice | (1) | 1 with bob data thrown away |
(2) | 2 | |
(3) | 2 |
For example, retrieving the full extent of data.users
from the disk store
will require a single key fetch with the partitions of (1).
With (2), the storage engine will fetch two keys and their values.
Retrieving a single user’s data, e.g. data.users.alice
, will require
reading a single key and all the users data with (1); but throw away most
of it: all the data not belonging to alice
.
There is no one-size-fits-all setting for partitions: good settings depend on the actual usage, and that comes down to the policies that are used with OPA. Commonly, you would optimize the partition settings for those queries that are performance critical.
To figure out suboptimal partitioning, please have a look at the exposed metrics.
OPA stores some internal values (such as bundle metadata) in the data store,
under /system
. Partitions for that part of the data store are managed by
OPA, and providing any overlapping partitions in the config will raise an
error.
Metrics
Using the REST API, you can include the ?metrics
query string
to gain insights into the disk storage access related to a certain OPA query.
$ curl 'http://localhost:8181/v1/data/tenants/acme1/bindings/user1?metrics' | opa eval -I 'input.metrics' -fpretty
{
"counter_disk_read_bytes": 339,
"counter_disk_read_keys": 3,
"counter_server_query_cache_hit": 1,
"timer_disk_read_ns": 40736,
"timer_rego_external_resolve_ns": 251,
"timer_rego_input_parse_ns": 656,
"timer_rego_query_eval_ns": 66616,
"timer_server_handler_ns": 117539
}
The timer_disk_*_ns
timers give an indication about how much time
was spent with the different disk operations.
Available timers are
timer_disk_read_ns
timer_disk_write_ns
timer_disk_commit_ns
Also note the counter_disk_*
counters in the metrics:
counter_disk_read_keys
: number of keys retrievedcounter_disk_written_keys
: number of keys writtencounter_disk_deleted_keys
: number of keys deletedcounter_disk_read_bytes
: bytes retrieved
Suboptimal partition settings can be spotted when the amount of keys and bytes retrieved for a query is unproportional to the actual data returned: the query likely had to retrieve a giant JSON object, and most of it was thrown away.
Debug Logging
Pass --log-level debug
to opa run
to see all the underlying storage
engine’s logs.
When debug logging is enabled, the service will output some statistics about the configured disk partitions and their key sizes.
[DEBUG] partition /tenants/acme3/bindings (pattern /tenants/*/bindings): key count: 10000 (estimated size 598890 bytes)
[DEBUG] partition /tenants/acme4/bindings (pattern /tenants/*/bindings): key count: 10000 (estimated size 598890 bytes)
[DEBUG] partition /tenants/acme8/bindings (pattern /tenants/*/bindings): key count: 10000 (estimated size 598890 bytes)
[DEBUG] partition /tenants/acme9/bindings (pattern /tenants/*/bindings): key count: 10000 (estimated size 598890 bytes)
[DEBUG] partition /tenants/acme0/bindings (pattern /tenants/*/bindings): key count: 10000 (estimated size 598890 bytes)
[DEBUG] partition /tenants/acme2/bindings (pattern /tenants/*/bindings): key count: 10000 (estimated size 598890 bytes)
[DEBUG] partition /tenants/acme6/bindings (pattern /tenants/*/bindings): key count: 10000 (estimated size 598890 bytes)
Note that this process will iterate over all database keys. It only happens on startup, when debug logging is enabled.
Fine-tuning Badger settings (superflags)
While partitioning should be the first thing to look into to tune the memory usage and performance of the on-disk storage engine, this configurable gives you the means to change many internal aspects of how Badger uses memory and disk storage.
The configurables correspond to Badger options that can be set on the library’s Options struct.
The following configurables can not be overridden:
dir
valuedir
detectconflicts
Aside from conflict detection, Badger in OPA uses the default options you can find here.
Conflict detection is disabled because the locking scheme used within OPA does not allow for having multiple concurrent writes.
Example
storage:
disk:
directory: /tmp/disk
badger: nummemtables=1; numgoroutines=2; maxlevels=3
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.