Skip to content
v0.5.1GitHub

Operating

Configuration reference

applies to v0.5.1

Every key of /etc/luradb/luradb.toml: its default, what it does and why it exists. The page is assembled from the shipped configuration file and the server code of each release, so no key is missing and none is stale. Type a key into the search to land on its entry.

swagger_enabled server.swagger_enabled

Since v0.1.0
Serves the Swagger UI and /api-docs/openapi.json; with auth on, they need a valid key.
Default
false
Effect

With true, the server serves two things next to the API: the Swagger UI at the path of server.swagger_url and the OpenAPI document of the running binary at /api-docs/openapi.json. The document path is fixed. With false, neither route is registered.

While auth.enabled is on, both routes need a valid API key sent as a Bearer token. Any account passes, an admin or a regular account without any domain permission; a missing or unknown key is answered with 401. A connection from a trusted user ID on the Unix socket (see auth.trusted_uids) passes without a key. With authentication off, the routes are open.

The document lists every endpoint, the admin-only ones included. luradb --dump-openapi prints the same document and exits.

Background

The UI lets you read the API description and send requests from the browser, which is useful while you build a client. It also hands the full description of the API, including its administrative endpoints, to everyone who may open it. On a host that clients on other machines can reach, keep it off unless authentication is on; with authentication off, anyone who reaches the port can open it.

Referenced on
First five minutes · Security · Compatibility · REST API

swagger_url server.swagger_url

Since v0.1.0
URL path of the Swagger UI.
Default
"/test-ui"
Effect

The URL path under which the Swagger UI is served. The server reads it only while server.swagger_enabled is true. The UI answers on the path with a trailing slash; the same path without the slash redirects to it with 303. The OpenAPI document stays at /api-docs/openapi.json whatever the path is.

The value must start with /. The start does not check it: a value such as docs makes the process abort with the message Paths must start with a / from the Swagger UI component. The value / collides with the root route of server.hello_enabled and aborts the start with Overlapping method route. Handler for GET / already exists while that route is on.

Background

The default path rarely needs a change. Change it when something else on the same host name already uses it, for example behind a reverse proxy that routes by path.

Referenced on
First five minutes

bind_address server.bind_address

Since v0.1.0
Address of the HTTP and HTTPS listeners; with auth off, only loopback is allowed.
Default
"127.0.0.1"
Effect

The IP address that the TCP listeners bind to: the plain HTTP listener (server.port), the HTTPS listener (server.tls_port) and the MCP listener (mcp.port). The value is an IPv4 or IPv6 address, not a host name. 0.0.0.0 and :: bind to all interfaces of that family; ::1 is the IPv6 loopback address. The Unix socket of server.unix_socket_path is not affected.

A value that is not an IP address refuses the start with invalid config: server.bind_address 'localhost' is not a valid IP address. With auth.enabled off, an address that is not a loopback address refuses the start as well, with invalid config: auth.enabled = false and server.bind_address = '0.0.0.0' is not loopback. With authentication on, any address is accepted.

Background

The server is reachable only from its own host until you change this key, and it cannot run without authentication on an address that other machines can reach. To accept remote clients, set the address of the network interface they connect to (or a wildcard address), turn authentication on, and consider TLS: either the server’s own (server.tls_enabled) or a reverse proxy in front of it. A reverse proxy on the same host can keep the loopback address.

Referenced on
Install · Security · systemd and Docker · Compatibility · Limits and defaults · MCP server · REST API

port server.port

Since v0.1.0
Port of the plain HTTP listener.
Default
3000
Effect

The port of the plain HTTP listener, which binds on server.bind_address. The value is a whole number from 0 to 65535; anything else is rejected when the file is read.

The listener exists only while server.http_enabled is true. The port must differ from server.tls_port, even while one of the two listeners is off, and the start is refused otherwise. While the MCP listener runs, mcp.port must differ from this port as well.

A port that another process holds makes the start fail with the bind error of the operating system, and so does a port below 1024 unless the host lets unprivileged users bind it. The packaged service runs as the unprivileged user luradb and its unit grants no capability for low ports.

Background

The REST API, the health check and, when switched on, the Swagger UI all answer on this port, so it is the one setting that decides where clients connect. A host often runs other services; change the port when 3000 is taken, or when a firewall rule or a proxy configuration expects a fixed port. Clients on the same host can use the Unix socket of server.unix_socket_path instead and need no port.

Referenced on
Install · Security · systemd and Docker · Troubleshooting · Limits and defaults · REST API

http_enabled server.http_enabled

Since v0.2.0
Starts the plain HTTP listener; at least one of HTTP and HTTPS must be on.
Default
true
Effect

With false, the server does not open the plain HTTP listener and logs HTTP listener disabled by config (server.http_enabled = false). at start. The Unix socket and the MCP listener are not affected.

At least one of this key and server.tls_enabled must be true. If both are false, the start is refused with invalid config: server.http_enabled and server.tls_enabled are both false — no listener would start. The port numbers are still compared while one listener is off, see server.port.

Background

Turn the plain listener off when clients must reach the server over HTTPS only, so that no cleartext port exists. Health checks and other tools then have to use the HTTPS port as well; /health answers there like on the plain port.

Referenced on
Security · Limits and defaults

hello_enabled server.hello_enabled

Since v0.1.0
Registers the root route / that answers with the hello message.
Default
truePackaged value false
Effect

With true, the server answers GET / with status 200 and a JSON body of the form {"message": "…"}, the text coming from server.hello_message. With false, the route does not exist; with authentication off, GET / then answers 404.

The route sits outside the authentication layer. It answers without an API key even while auth.enabled is on.

Background

The route is a plain “is something listening here” answer. /health is the endpoint for monitoring: it reports the version, the state of every data shard and the resource state. Leave the root route off unless you want a response that every client gets without a key.

Referenced on
Security · REST API

hello_message server.hello_message

Since v0.1.0
Response text of the root route.
Default
"Hello from LuraDB"
Effect

The text returned in the message field of the root route. Any string is accepted and no length is checked; the server escapes it for JSON, so quotes in the text are safe. The value is read only while server.hello_enabled is true.

Background

The text lets you tell instances apart, for example by environment name. The route answers without an API key, so the text is visible to every client that can reach the port; do not put anything in it that should not be public.

Referenced on
—

unix_socket_path server.unix_socket_path

Since v0.1.0
Unix domain socket for local clients. /run/luradb is created by systemd (RuntimeDirectory). Access: group luradb (fixed socket mode 0o660).
Default
not set (no socket)Packaged value "/run/luradb/luradb.sock"
Effect

When the key is set, the server also listens on a Unix domain socket at this path and serves the same API through it as on the TCP listeners. Without the key there is no socket. The socket comes in addition to the TCP listeners: server.http_enabled and server.tls_enabled must still not both be false.

The path must be absolute, and its directory must exist; the server does not create it. The start is refused with unix_socket_path must be absolute: … or parent directory of unix_socket_path does not exist: … otherwise. A file at the path that is not a socket also refuses the start (unix_socket_path exists and is not a socket: …) and is left untouched. A socket file left behind by an earlier run is removed and created again. The server sets the mode of the socket to 0660; the mode is not configurable. The owner and group are the account the server runs as. The socket file is removed again on an orderly shutdown.

Requests on the socket are authenticated like those on the TCP listeners: with an API key as Bearer token while auth.enabled is on. The exception are processes whose user ID is listed in auth.trusted_uids: the kernel reports the user ID of the connecting process to the server, and such a connection gets admin access without a key.

Background

A local client that uses the socket needs no TCP port, and who may connect is decided by file permissions. With trusted user IDs, local tools such as backup or monitoring scripts can work without an API key stored in a file.

The packaged service runs as user and group luradb and has systemd create /run/luradb with mode 0750 (RuntimeDirectory), which is why the packaged configuration places the socket there: members of the group luradb can reach the directory and, with mode 0660, the socket. A client in that group but with a user ID that is not trusted still has to send an API key.

Referenced on
Install · Security · systemd and Docker · Limits and defaults

tls_enabled server.tls_enabled

Since v0.2.0
Starts the native HTTPS listener, opt-in. Quickstart to enable: packaging/INSTALL.md "Enabling HTTPS".
Default
false
Effect

With true, the server terminates TLS itself: it opens an HTTPS listener on server.tls_port at server.bind_address, using the certificate and key from server.tls_cert_path and server.tls_key_path. The listener offers HTTP/2 and HTTP/1.1 and does not ask clients for a certificate. A TLS handshake that does not finish within 10 seconds is dropped.

The plain HTTP listener keeps running next to it unless server.http_enabled is false; the server does not redirect from one to the other. A plain HTTP request to the HTTPS port, or the other way round, fails at the connection.

The MCP listener shares the setting. With true and mcp.enabled, the listener on mcp.port serves HTTPS only, with the same certificate.

The certificate and the key are loaded once while the server starts. An unreadable or invalid file refuses the start with a message that names the key, such as cannot open server.tls_key_path '…': No such file or directory (os error 2) or server.tls_cert_path '…' contains no certificate. With false, the two paths are not read at all, and a missing file does no harm.

Background

TLS needs a certificate and a key that the operator provides. Native TLS lets a single host serve clients over HTTPS without a reverse proxy. Because the files are read only at start, a renewed certificate takes effect after a restart of the service, and a renewal tool needs a hook that restarts it. If you terminate TLS in a reverse proxy instead, leave this key off and see proxy.trusted_proxies for how the server treats the client address the proxy forwards.

Referenced on
Security · Limits and defaults · MCP server

tls_port server.tls_port

Since v0.2.0
Port of the HTTPS listener; must differ from the HTTP port.
Default
3443
Effect

The port of the HTTPS listener, which binds on server.bind_address. The value is a whole number from 0 to 65535; anything else is rejected when the file is read. The listener exists only while server.tls_enabled is true.

The port must differ from server.port, whether or not both listeners run. The start is refused otherwise with invalid config: server.tls_port (N) must differ from server.port (N). While mcp.enabled and TLS are both on, mcp.port must differ from this port too. A port held by another process makes the start fail with the bind error of the operating system.

Background

Change the port when it is taken, or when a firewall rule or a proxy expects a fixed port. Ports below 1024, such as 443, are not available to the unprivileged account of the packaged service unless the host allows unprivileged users to bind them.

Referenced on
Security · Troubleshooting · Limits and defaults

tls_cert_path server.tls_cert_path

Since v0.2.0
PEM certificate of the HTTPS listener, optionally with its chain.
Default
"/etc/luradb/tls/server.crt"
Effect

The PEM file with the server certificate. The file may hold the certificate chain: the server reads every certificate in it and presents them in the order they appear, so the server certificate must come first. The server reads the file once, while it starts, and only while server.tls_enabled is true.

A file that cannot be opened, cannot be parsed or holds no certificate refuses the start, with cannot open server.tls_cert_path '…', cannot parse server.tls_cert_path '…' or server.tls_cert_path '…' contains no certificate. A certificate that does not match the key of server.tls_key_path refuses it with invalid TLS certificate/key pair.

Background

The service account must be able to read the file. The package creates /etc/luradb/tls, owned by root with the group luradb and mode 0750, for the certificate and the key; the server never writes there. A certificate that is renewed in place is not picked up before the next start of the service.

Referenced on
Security · systemd and Docker · Limits and defaults

tls_key_path server.tls_key_path

Since v0.2.0
PEM private key (PKCS#8, RSA or EC) of the HTTPS listener.
Default
"/etc/luradb/tls/server.key"
Effect

The PEM file with the private key that belongs to the certificate of server.tls_cert_path. If the file holds several private keys, the server uses the first one. It reads the file once, while it starts, and only while server.tls_enabled is true.

A file that cannot be opened or parsed, or holds no private key, refuses the start, with cannot open server.tls_key_path '…', cannot parse server.tls_key_path '…' or server.tls_key_path '…' contains no private key. A key that does not belong to the certificate refuses it with invalid TLS certificate/key pair.

Background

The key is a secret. Give the file only to root and the service group, for example mode 0640 with owner root and group luradb, and keep it in the directory /etc/luradb/tls that the package creates for it with group access for luradb and no access for others. Like the certificate, the key is read only at start.

Referenced on
Security · systemd and Docker · Troubleshooting · Limits and defaults

enabled auth.enabled

Since v0.1.0
Requires a key on every request; off is only allowed on a loopback address.
Default
falsePackaged value true
Effect

With true, every request except GET /health needs an API key, sent as Authorization: Bearer <key>. A missing or unknown key is answered with 401 Unauthorized. An admin account may use every endpoint. A regular account may use only the domains it holds a permission for (Read, Write or Ddl, each level covering the lower ones) and is answered with 403 Forbidden for anything else, including every endpoint that has no domain in its path, such as the account, metrics and configuration endpoints. The exceptions are GET /version and GET /store-api/auth/whoami, which accept any valid key, and the domain listings, which show a regular account the domains it may see. The root route of server.hello_enabled is open as well.

With false, the server checks nothing and every client has full access. The server only starts in this mode when server.bind_address is a loopback address; otherwise it refuses the start with invalid config: auth.enabled = false and server.bind_address = '…' is not loopback. Every start in this mode logs a warning that the server is unauthenticated, and GET /store-api/auth/whoami answers with the role Disabled. The accounts of auth.admins.name are still created at start.

Other settings depend on this one. auth.trusted_uids has an effect only with authentication on. Registering a shared-memory client (shm.enabled) is open to everyone with authentication off and limited to the trusted user IDs with it on. mcp.oauth_enabled requires it, and the start is refused with invalid config: mcp.oauth_enabled = true requires auth.enabled = true otherwise. With mcp.enabled on and authentication off, the server warns that any client that reaches the MCP port has full access. With authentication on and no admin configured, it warns Auth is enabled but no admins are configured in luradb.toml, and no admin key exists.

Background

A database that listens on a network address without authentication gives every client that reaches it full access to all data. The loopback rule makes that state impossible to reach by mistake: to open the server to other machines, you also have to turn authentication on. Switch authentication off only for local development or for a server whose every client is on the same host and trusted, such as a private test instance on a developer machine.

Referenced on
First five minutes · Install · Monitoring · Security · MCP server · REST API

trusted_uids auth.trusted_uids

Since v0.1.0
Unix user IDs that authenticate by their peer credentials instead of a key. Warning: these UIDs get full access without an API key via the Unix socket and SHM.
Default
[]
Effect

A list of Unix user IDs, whole numbers from 0 up; a negative or non-numeric entry is rejected when the file is read. A process that connects over the Unix socket of server.unix_socket_path and whose user ID is in the list is treated as an admin without an API key. The kernel tells the server the user ID of the process at the moment it accepts the connection, so a client cannot claim another one. GET /store-api/auth/whoami answers such a connection with the role TrustedPeer and no name.

Only the Unix socket evaluates the list. A request on the HTTP or HTTPS listener still needs a key, whatever user ID the client runs as. The list has an effect only while auth.enabled is on. The same list also decides which local processes may register as a shared-memory client when shm.enabled is on: with authentication on, only listed user IDs may register, because that protocol carries no API key.

An empty list trusts nobody.

Background

The list lets local tools such as backup or monitoring scripts work with the database without an admin key stored in a file or an environment variable; the operating system account takes the place of the key. Listing a user ID gives every process of that account the same power as the admin key, so list only accounts whose processes you would trust with it, and prefer dedicated service accounts. Listing 0 gives every root process admin access.

A client that may reach the socket but is not listed can still use it with an API key. The socket file allows access to the service group, see server.unix_socket_path.

Referenced on
Write path · Security

name auth.admins.name

Since v0.1.0
Name of the admin account.
Default
"admin"
Effect

The name of an admin account. Each [[auth.admins]] entry describes one account, with a name and a key (auth.admins.api_key); both are required, and an entry without one refuses the start with missing field followed by the field name.

At every start the server compares the entries with the admin accounts in its system store (storage.system_dir). It creates or updates one account with the role Admin per entry, deletes every admin account that has no entry, together with its permissions, and leaves regular accounts alone. The start log shows Auth: admin '<name>' reconciled per entry and Auth: removed stale admin '<name>' per deleted account. Renaming an entry therefore replaces the account: the old name is removed, the new one is created. The accounts are created or removed even while auth.enabled is off.

The name is what GET /store-api/auth/whoami and the account list return for the key. The server checks it for nothing at start, so spaces and colons are accepted, unlike the names of accounts that are created through the API, which are 1 to 50 characters of letters, digits, _ and -. If two entries have the same name, the later one wins, and the key of the earlier one stops working. If the name belongs to a regular account, the account becomes an admin with the new key.

Background

Give each person or system that administers the server its own entry. A key can then be rotated or withdrawn by changing or deleting its entry and restarting, without touching the other admins. Regular accounts for applications are created through the API, with domain permissions, and survive restarts; the configuration file holds only the accounts that may do everything.

Referenced on
Install · Security

api_key auth.admins.api_key

Since v0.1.0
Secret of the admin account, sent as a Bearer token. Placeholder — postinst replaces it with a random key on first install.
Default
"@GENERATED_ON_INSTALL@"
Effect

The secret of the admin account auth.admins.name. A client sends it as Authorization: Bearer <api_key>. The server accepts any non-empty text and checks no format; an empty text creates the account, but no request can authenticate with it. The key is required for every [[auth.admins]] entry.

At start the server stores only the SHA-256 hash of the key in its system store and discards the text. The configuration file keeps the text, so protect the file: the package installs it with mode 0640, owner root, group luradb. The endpoint that returns the effective configuration (GET /store-api/config) leaves the key out.

A change of the text takes effect at the next start. From then on the old key is refused with 401, and so are the OAuth access tokens that MCP clients obtained with the old key, because a token is valid only while the account still has the key it was issued for.

The server does not know the placeholder @GENERATED_ON_INSTALL@ of the example file. Started from that file unchanged, it would accept this text as the admin key. The package replaces the placeholder on the first installation with lura_ and 32 random hexadecimal characters and leaves an existing key alone on updates and reinstalls. The Docker image does the same on its first start, using the environment variable LURADB_ADMIN_KEY if it is set and printing a generated key to the container log.

Background

Admin keys are the credentials that are not created through the API, so a new installation can be reached before any account exists. Choose a long random text; the keys the server generates for regular accounts are lura_ and 64 random hexadecimal characters. To rotate an admin key, replace the text and restart the service; to retire a key, delete the entry. Clients that send the old key are refused from the restart on.

Referenced on
Install · Security

db_path storage.db_path

Since v0.1.0
Page file of the buffer pool.
Default
luradb.dbPackaged value "/var/lib/luradb/luradb.db"
Effect

The path of the page file that backs the buffer pool of buffer_pool.pool_size. Each data shard has a file of its own: the server inserts a directory shard_<k> before the file name, so a path /var/lib/luradb/luradb.db becomes /var/lib/luradb/shard_0/luradb.db for shard 0, and so on. The server creates the directories and the files at start. A relative path resolves against the working directory of the server process.

In v0.5.1 nothing is stored in this file. No engine of the server reads or writes pages through the buffer pool, and the files stay at zero bytes while the server writes data. The path still takes part in the checks for data paths: it must not resolve to the same location as another data path (invalid config: storage.db_path and storage.wal_path resolve to the same location '…'), symbolic links included. The volume that holds it is watched for free space like the other data volumes, see resources.disk_free_block.

Background

Keep the file beside the other data paths when you relocate the data directory, so that all volumes the server needs are planned together. There is nothing to size or tune for it: the data lives in the write-ahead log, the SSTables and the value log (storage.wal_path, storage.sstable_dir, storage.vlog_path).

Referenced on
Limits and defaults

wal_path storage.wal_path

Since v0.1.0
Write-ahead log: a directory of segment files of about 64 MiB each, which the server creates, writes and deletes; never change anything in it by hand. The path may be a symlink, the segments then lie at its target. A log file of an earlier LuraDB version at the path becomes such a directory on the first start; an earlier version does not start on it.
Default
luradb.walPackaged value "/var/lib/luradb/luradb.wal"
Effect

The directory of the write-ahead log of the key-value engine. It holds segment files named 00000001.wal, 00000002.wal and so on; a segment is closed at 64 MiB and the next one is prepared ahead of time. All data shards of the key-value engine share this one log, so unlike the other data files it is not placed in a shard_<k> directory. The other engines keep their logs elsewhere: json.wal_path, rel.wal_path and a wal directory inside storage.system_dir. The server creates the directory and its parent at start and manages the files itself.

The storage thread of the server appends every write to the log and waits for the data to be on disk before the write is confirmed. How much of the log the server keeps is set by storage.wal_max_size.

No two data paths may resolve to the same location (symbolic links resolved); the start is refused with invalid config: … resolve to the same location '…' otherwise. A relative path resolves against the working directory of the server process.

Background

A confirmed write is safe because it is in this log; after a crash the server replays what the SSTables do not yet hold. Every write waits for the log to reach the disk, so the latency of the volume that holds the log sets the lower bound for the latency of writes. The server watches the free space of this volume as well: when it falls below the block threshold, writes are refused with 507 (resources.disk_free_block). A separate volume for the log is therefore a way to keep log space and data space apart; the service account needs write access to the directory.

Referenced on
Install · Upgrading · Write path · systemd and Docker · Limits and defaults

vlog_path storage.vlog_path

Since v0.1.0
Value log for large values.
Default
luradb.vlogPackaged value "/var/lib/luradb/luradb.vlog"
Effect

The value log of the key-value engine. Values of at least lsm.vlog_inline_threshold bytes are appended to it, and the SSTables keep only a pointer to them. Each data shard has its own file: the server inserts a directory shard_<k> before the file name, so /var/lib/luradb/luradb.vlog becomes /var/lib/luradb/shard_0/luradb.vlog for shard 0. The server creates it at start. A relative path resolves against the working directory of the server process.

When the value log is cleaned (see janitor.dead_bytes_threshold), the live values move into a new generation, whose file name is the configured name with .2 appended, then .3, and so on. Because of these names no other data path may equal the name followed by a dot and digits; the start is refused if one does. The size of the value logs of the key-value engine, all shards and generations together, is the vlog_size_bytes field of GET /health.

The JSON engine and the relational engine have their own value logs, json.vlog_path and rel.vlog_path, and the system store keeps one in storage.system_dir.

Background

Keeping large values out of the SSTables means that a compaction rewrites only keys and pointers, not the values themselves. The price is that overwritten and deleted values stay in the log as dead space until the cleanup rewrites the log, so this volume needs room for the data plus the dead space that builds up between cleanups. The server watches its free space with the other data volumes, and a cleanup waits while the volume has less room than the live values take.

Referenced on
Write path · systemd and Docker · Limits and defaults

sstable_dir storage.sstable_dir

Since v0.1.0
SSTable directory.
Default
luradb_sstablesPackaged value "/var/lib/luradb/sstables"
Effect

The directory of the SSTables of the key-value engine. Each data shard keeps its files in a subdirectory shard_<k> of it, with the files sstable_<number>.sst and a MANIFEST that lists them and their levels. The server creates the directory at start. A relative path resolves against the working directory of the server process.

A flush of a MemTable (lsm.memtable_size_threshold) adds a file at level 0, and compactions (compaction.l0_threshold) merge files into the levels below. The files hold the keys and the values below lsm.vlog_inline_threshold bytes; longer values are in the value log, and the files hold a pointer to them.

The server does not move data when the path changes. A start with another path opens an empty set of SSTables there, and the data in the old directory is not read. If storage.system_dir is not set in the file, the server derives it from this path: it is luradb_sys in the parent directory of the SSTable directory, so changing this key then moves the system store, with the accounts, as well.

Background

A compaction writes its output before it deletes its inputs, so the volume needs free room of at least the size of the inputs of the next compaction; the server holds the compaction back and logs Compaction of level … waits for disk space while it has less. Plan the volume with headroom beyond the data size, and see resources.disk_free_warn for the thresholds at which the server warns and refuses writes.

Referenced on
Install · Write path · systemd and Docker · Limits and defaults

system_dir storage.system_dir

Since v0.5.1
Directory of the system store, which keeps the users, API key hashes and permissions apart from domain data. Its write-ahead log wal is a directory of segment files the server creates, writes and deletes; never change anything in it by hand. wal may be a symlink, the segments then lie at its target.
Default
luradb_sys beside the SSTable directoryPackaged value "/var/lib/luradb/luradb_sys"
Effect

The directory of the system store, an LSM instance of its own that holds what belongs to no domain: the accounts with their key hashes and permissions, the OAuth clients and tokens of the MCP listener, and the server’s markers such as the shard count of the data directory. It is not divided into shards. The server creates the directory at start and puts four things in it: a wal directory with the write-ahead log, a vlog file, an sstables directory and a lock file.

The lock file keeps a second server process off the data. A process that finds it taken stops with <directory> is in use by another LuraDB process.

If the key is not set in the file, the server uses luradb_sys in the parent directory of storage.sstable_dir. The path takes part in the checks for data paths: the directory and the three items in it must not resolve to the same location as another data path.

Background

The system store is not subject to the refusal of writes. When writes are refused for lack of space or memory, requests under /store-api/auth/ still go through, so that sign-in and the management of keys keep working; their few records go into the system store, from the space that is left below the block threshold (resources.disk_free_block).

Because the directory holds the accounts and the shard count (multicore.shards), move or restore it together with the rest of the data directory. A server started with a new, empty directory creates the admin accounts of auth.admins.name again, but does not have the regular accounts, their permissions or the recorded shard count of the old directory.

Referenced on
Upgrading · Write path · systemd and Docker · Troubleshooting · Compatibility · Limits and defaults

wal_queue_capacity storage.wal_queue_capacity

Since v0.5.1
Writes waiting for the storage thread, which writes the write-ahead logs of all engines; a full queue holds further writes back. 1 to 65536.
Default
1024
Effect

The length of the queue in front of the storage thread, which writes the logs of all engines. A write takes a place in the queue before it is stamped and appended; when no place is free, the write waits until one is. The client sees a slower answer, not an error.

The value is a whole number from 1 to 65536. Anything else refuses the start with invalid config: storage.wal_queue_capacity (…) must be 1 to 65536; a negative number is rejected when the file is read. The value also adds to the size of the queue for wake-ups from other threads that each data shard and the control-plane thread allocate (together with multicore.shard_queue_capacity), which is why the upper limit exists.

Background

The storage thread writes the records that have gathered as one group and waits once per group and log for the data to reach the disk. The queue bounds how many writes can wait for the thread at the same time. Each queued write holds its record in memory until the thread has written it, so the length of the queue times the size of the writes bounds the memory it can hold. The default suits most installations; raise it only if many concurrent writers are slowed by waiting for a place, and measure the effect.

Referenced on
Write path · Limits and defaults

wal_max_size storage.wal_max_size

Since v0.5.1
Cap on the write-ahead log of each engine, the system store's included. Once its segments reach it, the shards whose records in the oldest segment are not in SSTables yet write them there, and the segment is deleted; a larger cap means fewer such writes and a longer recovery after a crash. At least 128 MiB. A number with a unit: B, kB, MB, GB, TB (powers of 1000) or KiB, MiB, GiB, TiB (powers of 1024), as in "512 MiB".
Default
"256 MiB"
Effect

Each engine (key-value, JSON, relational) keeps a write-ahead log, and so does the system store. A log is a directory of segment files of 64 MiB each. This key caps the segments of one log.

The value is a number with a unit, as in "256 MiB". The units are B, kB, MB, GB and TB (powers of 1000) and KiB, MiB, GiB and TiB (powers of 1024), in any case; the number may have decimals. The smallest allowed value is 128 MiB, which is two segments, so the segment to drop is never the one being written. A number without a unit, an unknown unit or a value below 128 MiB refuses the start with an invalid config: storage.wal_max_size … message.

Once a second the server looks at each log. When its closed segments plus one full segment reach the cap, the shards that still hold records of the oldest segment only in memory write them to SSTables, and the segment is deleted. A segment that has held records for five minutes is closed and dropped the same way, whatever the cap, so a quiet database does not keep records in its log for long. A log stays within about one segment of the cap.

Background

A write is confirmed once it is in the log, and the log is what recovery replays after a crash. A segment can only be deleted when every shard has its records in SSTables. A shard that rarely writes would keep old segments alive for a long time, so the cap makes such shards write their records out and lets the server delete the segment.

A larger cap means fewer of these early writes and a longer recovery after a crash, because more records sit in the log. A smaller cap, down to 128 MiB, keeps the log and the recovery short at the price of more early writes. Each log can use the cap plus about one segment of disk space, so the volumes of storage.wal_path, json.wal_path, rel.wal_path and storage.system_dir need room for that. A shard also writes its MemTable out on its own when it reaches lsm.memtable_size_threshold, which shortens the log without the cap.

The default of 256 MiB (four segments) suits most installations. Raise it on a host with sustained heavy writes and plenty of disk, lower it on a small volume.

Referenced on
Write path · Limits and defaults

disk_free_warn resources.disk_free_warn

Since v0.5.1
Free space below which the server warns.
Default
"15% max 10 GB"
Effect

Once a second the server reads the free space of every volume that one of its data paths lies on: the SSTable and system directories, the directories of the page file, the write-ahead log and the value log, those of the JSON and relational engines while they are on, the backup directory while backups are on, and the log directory when log.path is set. Volumes that several paths share are watched once. “Free” is the space an unprivileged process can use, without the share reserved for root.

When the free space of a volume falls below this threshold, the state of the server changes to warning. The server logs Resources running short: disk <path> free … of …, threshold …. Nothing is refused yet; writes are refused below the block threshold. and repeats the line every 60 seconds while the state lasts. GET /health answers status: "degraded" with resources.disk_state: "warning", still with HTTP status 200. The metrics endpoint shows the free space per volume. Nothing is refused. The state returns to normal, with a log line, once every volume has been above the threshold for two readings in a row.

The value is a size, a percentage of the volume, or both. A size is a number with a unit: B, kB, MB, GB, TB (powers of 1000) or KiB, MiB, GiB, TiB (powers of 1024), in any case, as in "2 GB". A percentage is above 0 and up to 100, decimals allowed, as in "5%" or "2.5%". A percentage followed by max and a size, as in "15% max 10 GB", takes the smaller of the two. The server resolves the value once at start, separately for each volume, against the total size of that volume. A value in another form refuses the start with invalid config: resources.disk_free_warn (…) needs a size such as "2 GB", a percentage above 0 up to 100 such as "5%", or a percentage with a cap such as "15% max 10 GB".

For every volume, the resolved thresholds must satisfy disk_free_block <= disk_free_resume <= disk_free_warn, and the block threshold must be at least 64 MiB. Otherwise the start is refused with a message that lists the three resolved values in bytes.

Background

The warning is the early notice: it fires while nothing is wrong yet, so that monitoring, which can read the degraded status of /health, and the operator have time to add space or remove data before the server has to refuse writes (resources.disk_free_block). Choose a threshold that gives that time at the rate your data grows. A percentage with a cap suits both small and large volumes: with "20% max 50 GB", a 100 GB volume warns below 20 GB and a 1 TB volume below 50 GB, instead of at 200 GB.

Referenced on
Resource guard · Monitoring · Troubleshooting

disk_free_block resources.disk_free_block

Since v0.5.1
Free space below which the server refuses writes.
Default
"5% max 2 GB"
Effect

When the free space of any watched volume falls below this threshold, the server refuses writes. The volumes and the form of the value are described at resources.disk_free_warn. A refused write is answered with 507 Insufficient Storage, the header Retry-After: 5 and the body 507 Insufficient Storage: write blocked: disk space below threshold, retry in 5 s. The write is not applied. The server logs Writes refused: … as an error with the volume, its free space and the threshold, and repeats the line every 60 seconds while the state lasts.

Everything that writes is refused: puts, deletes, bulk requests, new domains, tables and indexes, and new backups. The exception is a new backup whose directory (backup.dir) lies on a volume that is above its warn threshold, as long as memory is not the reason for the block. Three groups go on: reads, GET /health and the downloads of existing backups; sign-in and the management of accounts and keys, that is every request under /store-api/auth/; and the deletion of a backup, which frees space. Deleting keys does not free space, because a delete is a write and is refused too.

The TTL sweeper (ttl_sweeper.enabled) writes no tombstones while writes are refused. A compaction or a value-log cleanup waits whenever its volume has less room than its inputs or its live values take, blocked or not. The state ends as described at resources.disk_free_resume.

The threshold must be at least 64 MiB on every volume and may not exceed the resume threshold; the start is refused otherwise, see resources.disk_free_warn.

Background

A server that fills its disk cannot finish the work that is already under way: a flush needs room for its SSTable, and a failed write of a data file can force the server to stop. The block threshold leaves free space for that work and for the sign-in records, which still go through below the threshold, and it gives clients a defined answer, 507 with Retry-After, instead of errors from a full disk.

Set the threshold above the space the server may still need after it stops accepting writes: the SSTables for the MemTables in memory (lsm.memtable_size_threshold per engine and shard) and the output of a compaction that is running, which can be as large as its inputs. The readings come once a second, so a fast writer can use up a good part of the margin before the state changes.

Referenced on
Resource guard · Troubleshooting · Limits and defaults · Status codes

disk_free_resume resources.disk_free_resume

Since v0.5.1
Free space each volume needs before refused writes resume.
Default
"10% max 4 GB"
Effect

The free space that every watched volume needs before refused writes are accepted again. The form of the value is described at resources.disk_free_warn. While any volume is below this threshold after a block (resources.disk_free_block), the state stays blocked: the server keeps answering writes with 507 and logs Writes refused: …, naming the volumes that hold the block.

When every volume has been above the threshold for two consecutive readings, which come once a second, and memory is below its resume level, the server logs Writes are accepted again and moves to the warning state; if a volume is still below the warn threshold, the line adds Still low: …. Two more readings above the warn threshold bring it back to normal (resources.disk_free_warn).

The threshold must lie between the block and the warn threshold on every volume: disk_free_block <= disk_free_resume <= disk_free_warn. Otherwise the start is refused, see resources.disk_free_warn.

Background

The gap between the block and the resume threshold keeps the server from switching between accepting and refusing writes every second. Without a gap, the first writes after a block would fill the volume past the block threshold again. Choose a resume threshold that leaves room for a useful stretch of writing. You reach it by freeing space, for example by deleting old backups, which is allowed while writes are refused.

Referenced on
Resource guard · Troubleshooting

memory_budget resources.memory_budget

Since v0.5.1
Anonymous memory the server may use: a percentage of the host's memory, or of its cgroup's memory.max when that is lower, or a size; at least 256 MiB. Writes warn from 80 %, are refused from 95 % and resume below 90 % of it.
Default
"75%"
Effect

The amount of anonymous memory, in bytes, that the server may use: the RssAnon figure of the process, which is its heap and anonymous mappings and leaves out the page cache and the memory-mapped SSTables. The value is a size, a percentage, or a percentage with a cap, in the form described at resources.disk_free_warn. A percentage refers to the memory of the host (MemTotal) or, if it is lower, to the memory.max of the cgroup (version 2) that the process runs in, so a container limit is honoured. The start log names the result and its source: Memory budget … bytes of … (MemTotal) or (cgroup memory.max).

The budget must resolve to at least 256 MiB, or the start is refused: with invalid config: resources.memory_budget (…) must be at least 256 MiB for a size, and with … resolves to … bytes of …; it must be at least 256 MiB for a percentage.

The server compares the reading, taken once a second, with the budget. From 80 % it warns, logs Resources running short: memory … and reports degraded in GET /health with resources.memory_state: "warning". From 95 % it refuses writes exactly as for a full disk, with 507, Retry-After: 5 and the reason memory budget exhausted in the body, see resources.disk_free_block. It accepts them again once the reading is below 90 %. While writes are refused for memory, the server writes the MemTables out on the first second and every ten seconds after it, and returns free heap to the operating system once such a flush is done, to bring the reading down; the log shows Memory block: flushing MemTables and returning free heap to the kernel.

At start, the server adds up the memory it holds regardless of load: two MemTables per engine and shard (lsm.memtable_size_threshold for the key-value engine, the built-in size for the others), the block caches, the buffer pool (buffer_pool.pool_size), and, when they are on, the shared-memory segments (shm.data_buffer_size) and the registered I/O buffers (io_engine.enabled). If the sum is more than half of the budget, it logs The fixed memory of the server, … bytes (…), takes more than half the memory budget of … bytes.

Background

A database that grows until the operating system or the container runtime kills it loses its in-memory state and has to recover from the write-ahead log. The budget makes the server act first: it warns, then stops taking writes that would grow memory, and flushes. Reads continue.

The default leaves a quarter of the memory for the page cache and for other processes on the host. Lower the budget when other services share the host. Raise it only if you know that nothing else needs the memory. If the fixed-memory warning appears, the budget is too small for the configured MemTables, caches and pools, or those are too large for the host.

Referenced on
Multicore · Resource guard · Monitoring · systemd and Docker · Troubleshooting · Limits and defaults · Status codes

enabled io_engine.enabled

Since v0.1.0
Registers I/O buffers with the storage engine thread's io_uring ring; not used on the data path yet. The storage thread — a dedicated io_uring thread that writes the write-ahead logs of all engines and batches concurrent writes into one fsync per log — runs either way; the server does not start without it.
Default
false
Effect

With true and exactly one data shard (multicore.shards), the shard allocates 128 buffers of 64 KiB, registers them with its io_uring ring and keeps a handle to the value log and to every SSTable that exists at start. A successful start logs IoEngine ready: 128 registered buffers x 65536 bytes., and the per-shard memory line ends with registered I/O buffers on. No read or write of the engines uses the buffers; nothing in the server calls the code that would.

If the kernel rejects the registration, the server logs IoEngine disabled: … Continuing without registered buffers. and runs without them. With more than one data shard the key has no effect, and the start logs io_engine.enabled has no effect with more than one data shard.

Background

The registered buffers are scaffolding: the code to use them exists, but the engines do not call it. Switching the key on costs 8 MiB of pinned memory, which counts toward the fixed memory that resources.memory_budget is compared with, and one more open file per SSTable, and it gives nothing back in this release. Leave it off. The packaged service sets LimitMEMLOCK=infinity so that the registration can succeed.

Referenced on
—

cpu_offload_threads multicore.cpu_offload_threads

Since v0.5.1
Parallel CPU-heavy tasks moved off the request thread: 0 is automatic (cores minus 2, at least 1), otherwise at most the number of cores.
Default
0
Effect

The number of CPU-heavy tasks that may run at the same time on helper threads instead of on the threads that serve requests. A task that finds all places taken waits for one; it does not fail. In this release the server offloads the parsing of the body of a JSON bulk load and the parsing of an uploaded file that creates a relational table. The same number caps the pool of blocking threads of the frontend runtime.

0 is automatic: the number of cores minus two, at least 1. The start log shows the result as CPU offload permits: N. An explicit value must not exceed the number of cores the process can use; the start is refused with invalid config: multicore.cpu_offload_threads (…) must be 0 (auto) or 1 to N (available cores) otherwise.

Background

Parsing a large body takes a core for as long as the body is large. Done on the thread that runs the request, it would hold up that thread’s other work, so the server moves it to a bounded pool and keeps cores free for the rest. Lower the value to keep more cores free for the data shards and readers on a host that parses large uploads; raising it above the automatic value only helps when many large uploads arrive at once and the cores are otherwise idle.

Referenced on
Multicore · Troubleshooting · Limits and defaults

frontend_workers multicore.frontend_workers

Since v0.5.1
Threads that accept connections and parse HTTP/TLS in front of the data shards: 0 is automatic (cores / 6, 2 to 4 on hosts with at least 4 cores, otherwise 1), otherwise at most the number of cores.
Default
0
Effect

The number of threads of the frontend runtime. They accept the connections of the HTTP, HTTPS and Unix socket listeners, run the TLS handshakes and parse HTTP, resolve the client address behind a trusted proxy (proxy.trusted_proxies), check the API key, answer point reads of one key, document or row directly, and hand every other request on to a data shard, the control plane or the reader pool. The MCP listener has a runtime of its own and takes no frontend worker.

0 is automatic: a sixth of the cores, but at least 2 and at most 4, on hosts with at least 4 cores, and 1 on smaller hosts. The start log shows the result in the line N cores: M frontend workers, …. An explicit value must not exceed the number of cores the process can use; the start is refused with invalid config: multicore.frontend_workers (…) must be 0 (auto) or 1 to N (available cores) otherwise.

The result also takes part in the automatic count of multicore.reader_threads, which gets the cores that are left after the frontend workers and the data shards.

Background

The workers do the work that every request needs before and after the engines: connection handling, TLS and authentication. A worker serves many connections. Each connection runs one request at a time, so a client that wants parallelism opens several connections. Raise the value when many clients connect at once, or when TLS handshakes of short-lived connections use most of a core; keep it low on small hosts so that the cores stay available for the data shards.

Referenced on
Multicore · Limits and defaults

shards multicore.shards

Since v0.5.1
Data shards, each a thread with its own storage engines that writes the domains assigned to it. The data directory layout depends on it: 0 keeps the count an existing data directory was written with, and gives a new one cores / 4, 1 to 8. A set count that differs from the one the data was written with refuses the start; luradb --migrate-shards N changes the count of a data directory while the server is stopped.
Default
0
Effect

The number of data shards. A shard is one thread with its own storage engines, and it writes the domains assigned to it. The value is a whole number from 0 to 65535.

0 is automatic. A data directory that already exists keeps the count it was written with, also after an upgrade or a move to a host with another core count; a directory laid out without shard directories counts as one shard. A new data directory gets a quarter of the cores, at least 1 and at most 8. When the host would pick another count than the one stored, the start logs a warning that names the migration command.

A value above 0 is used as given, but only on a data directory written with that count. A different count refuses the start with shard count changed (stored N, configured M) and names luradb --migrate-shards M. The first start records the count. Only the migration tool changes it, on a stopped server.

Every domain lives on exactly one shard, chosen from the name of the domain and the same for all three engines. The writes of a domain run on the thread of its shard, so domains that fall on the same shard take turns. Reads do not wait for the thread of the shard: point reads run on the connection thread and longer reads on the reader pool, see multicore.reader_threads.

Each shard adds a thread. The block cache of each engine and the buffer pool are split evenly across the shards, but the MemTable threshold applies per engine and shard, so MemTable memory grows with the count. The automatic multicore.reader_threads takes the cores that are left after the shards and the multicore.frontend_workers.

Background

The shard count decides which thread owns which domain, so it is part of the layout of the data and not a knob to turn at run time. Data written for three shards cannot be served by a server that routes for two without moving it first. The server therefore refuses a mismatch instead of rerouting silently, and the count changes only through an offline tool that rewrites the data directory.

More shards let more domains write at the same time. They do not help a single busy domain, because its writes stay on one core, and they use cores and memory that readers could use. Leave the key at 0 unless the automatic count does not fit the host, for example when the writes are spread over many domains on a large host, or when a small host should keep its cores for readers.

Referenced on
Upgrading · Multicore · Troubleshooting · Compatibility

shard_queue_capacity multicore.shard_queue_capacity

Since v0.5.1
Requests one data shard takes at once, running or waiting; further requests are answered with 503 and Retry-After: 1. At least 1.
Default
1024
Effect

The number of requests that one data shard takes at the same time, running or waiting. Every shard has its own limit. A request counts from the moment the frontend hands it to the shard until it is answered. This covers the writes of the shard’s domains and the other requests that run on the shard’s thread; reads that run on the reader pool or on the frontend do not count (see multicore.reader_queue_capacity).

A request that finds the limit reached is not processed. It is answered at once with 503 Service Unavailable, the header Retry-After: 1 and the body 503 Service Unavailable: the server is overloaded, retry later, and the client may send it again. The value must be at least 1; the start is refused with invalid config: multicore.shard_queue_capacity (0) must be at least 1 otherwise. The start log shows it in the line N cores: …; shard queue capacity: N.

Writes without a domain, such as the management of accounts, run on the control-plane thread and are not limited by this key. The number also adds to the queue for wake-ups from other threads that each shard and the control-plane thread allocate, together with storage.wal_queue_capacity.

Background

Every domain lives on one shard and its writes run one after another on the thread of that shard. When writes arrive faster than the shard completes them, the limit stops the queue from growing without bound: the server sheds the excess quickly with a status that tells clients to retry, instead of letting requests wait ever longer in memory. A burst on one domain fills the lane of that shard only; domains on the other shards go on.

Raise the value if bursts of concurrent writes to a few domains produce 503 answers while the CPU has room. If the lane is full for long, the shard itself is the bottleneck, and a longer queue only moves the waiting from the client to the server. In that case, spread the writes over more domains or add shards (multicore.shards).

Referenced on
Multicore · Troubleshooting · Limits and defaults · Status codes

reader_threads multicore.reader_threads

Since v0.5.1
Threads that serve read requests next to the data shards: 0 is automatic (cores minus 2, minus the data shards and the frontend threads, at least 1), otherwise at most the number of cores.
Default
0
Effect

The number of threads of the reader pool. The pool runs every read that is not a lookup of a single key, document or row, which the frontend answers itself: key listings, counts, document searches, SQL requests, metrics, streams such as the watch endpoint, and downloads. It also takes over a single-item read that would have to wait for the disk, which the frontend then does not serve itself. It reads the same engines as the data shards and runs on its own threads, so reads do not wait for the thread of a shard.

0 is automatic: the cores minus two, minus the data shards and the frontend workers, at least 1. The start log shows the result in the line N cores: M frontend workers, K data shards, R reader threads; …. An explicit value must not exceed the number of cores the process can use; the start is refused with invalid config: multicore.reader_threads (…) must be 0 (auto) or 1 to N (available cores) otherwise. The server does not check that the frontend workers, the data shards and the reader threads together fit the cores.

Background

Reads scale with these threads, writes with the shards. A read-heavy installation gets more from additional reader threads than from additional shards. On a host that is busy with other work, set a value that fits the cores you want to give the database; the automatic value counts all cores of the host. If clients receive 503 answers on reads, look at multicore.reader_queue_capacity and at whether the pool has enough threads for the load.

Referenced on
Multicore · Troubleshooting · Limits and defaults

reader_queue_capacity multicore.reader_queue_capacity

Since v0.5.1
Read requests that may wait for a reader thread; a request beyond that is answered with 503 and Retry-After: 1. Running reads and open streams do not count. At least 1.
Default
4096
Effect

The number of read requests that may wait for the reader pool. A request waits from the moment the frontend hands it to the pool until the pool takes it up; reads that run on the pool and open streams no longer count. A request that finds the queue full is not processed. It is answered at once with 503 Service Unavailable, Retry-After: 1 and the body 503 Service Unavailable: the server is overloaded, retry later, and the client may send it again.

The value must be at least 1; the start is refused with invalid config: multicore.reader_queue_capacity (0) must be at least 1 otherwise. The start log shows it as Reader pool queue capacity: N.

Background

The queue absorbs bursts of reads that arrive faster than the pool takes them up. Without a limit, a flood of reads would wait in memory and grow the latency of every request behind it; with the limit, the excess is turned away quickly and clients can retry. A queue that is often full means the pool does not keep up, and more threads (multicore.reader_threads) help more than a longer queue. Raise the capacity if short bursts cause 503 while the CPU is idle.

Referenced on
Multicore · Troubleshooting · Limits and defaults · Status codes

pool_size buffer_pool.pool_size

Since v0.1.0
Pages (4 KiB each) the buffer pool keeps in memory.
Default
1024
Effect

The number of pages of 4 KiB that the buffer pool of the page file holds in memory, over all data shards. The server divides the number evenly among the shards, rounding down, and gives every shard at least one page; the start log shows the share of each shard as buffer pool N pages. A shard allocates its frames when it starts. The value is a whole number; a negative one is rejected when the file is read. 0 is accepted and gives each shard one page.

In v0.5.1 the pool serves no data. The page file (storage.db_path) stays empty, and no engine of the server fetches or allocates a page from the pool; the engines store their data in the write-ahead log, the SSTables and the value log. The key therefore sets only how much memory the server reserves. The pool counts toward the fixed memory that the server compares with resources.memory_budget, as the number of pages times 4 KiB.

Background

The pool is the page cache for the page file. Since the page file holds nothing in this release, a larger pool brings no benefit, and the default costs only a few MiB. Leave the key alone; reducing it saves little, and enlarging it only takes memory away from the MemTables and caches that are used.

Referenced on
Multicore

vlog_inline_threshold lsm.vlog_inline_threshold

Since v0.1.0
Values of at least this many bytes go to the value log instead of the SSTables.
Default
1024
Effect

A value of at least this many bytes is appended to the value log (storage.vlog_path) when it is written. The MemTable and the SSTables then keep only a pointer to it. A shorter value stays inline, in the MemTable and in the data blocks of the SSTables. For the REST API the size of a value is the size of the request body. The test is made once, when the value is written or replayed from the write-ahead log; changing the key does not move values that are already stored. With 0, every value goes to the value log.

The key applies to the key-value engine. The JSON and relational engines and the system store use a built-in value and ignore the key. The server does not check the value at start.

The size of the pointer, not of the value, counts toward the size of the MemTable (lsm.memtable_size_threshold), so values that go to the value log let a MemTable hold many more entries before it is flushed.

Background

The value log keeps large values out of the SSTables. A compaction rewrites keys and pointers but not the values, so large values are not rewritten at every level they pass through. The cost is a second read for a value in the log, and dead space after overwrites and deletes, which the cleanup of janitor.dead_bytes_threshold reclaims. A higher threshold keeps more values inline, which makes reads of them cheaper and SSTables and compactions larger; a lower one does the opposite. Change the default only with a measurement of your own workload.

Referenced on
Write path · Limits and defaults

memtable_size_threshold lsm.memtable_size_threshold

Since v0.1.0
MemTable size in bytes (keys plus stored values) at which it is written to disk.
Default
4194304
Effect

The size at which the active MemTable of a shard is frozen and queued to be written to an SSTable at level 0. The size is the number of bytes of the encoded keys, which are the stored key (the internal domain prefix and the user key) plus an 8-byte timestamp, and of the values kept inline. A value that goes to the value log counts only with its pointer (lsm.vlog_inline_threshold). The check runs at the start of every write, so the write that follows the moment the MemTable reaches the threshold freezes it, and a MemTable can exceed the threshold by the writes that are in flight. The flush loop then writes one frozen MemTable per turn (lsm.flush_check_interval_ms). The same check applies while the server replays the write-ahead log at start.

Each data shard has a MemTable of its own for the key-value engine, so the memory for them grows with the number of shards. The key applies to the key-value engine only; the JSON and relational engines and the system store use a built-in size and ignore it. The server does not check the value at start, and 0 is accepted. The start log prints the value for every shard in the line Shard N: … MemTable threshold … bytes per engine, even though only the key-value engine uses it.

For the memory the server holds regardless of load, it counts two MemTables per engine and shard; see resources.memory_budget.

Background

A MemTable absorbs writes in memory while the write-ahead log keeps them safe. A larger MemTable turns more writes into one SSTable, so there are fewer, bigger L0 files and less work for compaction, at the price of more memory and a longer replay of the log after a crash, because more records are in the log and not yet in an SSTable. A smaller one keeps the log and the recovery short and produces more L0 files, which reach the limit of compaction.l0_threshold sooner. Independently of this key, storage.wal_max_size makes shards write their MemTables out when the log has grown to the cap, so the log stays bounded.

Raise the key on a host with plenty of memory and heavy writes; lower it where memory is tight or where a short recovery matters more than throughput.

Referenced on
Multicore · Write path

max_key_length lsm.max_key_length

Since v0.1.0
Longest stored key in bytes, including the internal domain prefix.
Default
256
Effect

The limit, in bytes, for the keys of the key-value engine as the engine stores them. A stored key is the user key with a prefix of 19 bytes in front of it (d:, 16 hexadecimal digits that identify the domain, and :). The value must not exceed 100663296 bytes (96 MiB), the largest field that the recovery of the write-ahead log reads; the start is refused with invalid config: lsm.max_key_length (…) exceeds the WAL recovery field cap (100663296 bytes) — writes this large could never be recovered otherwise.

In v0.5.1 the engine applies the limit to the batches of operations it writes and to its own records, but not to the single-key writes of the REST API. A bulk delete by prefix (DELETE /store-api/kv/{domain}/keys?prefix=…) is such a batch. If one of the stored keys it would remove is longer than the limit, the request is answered with 500 and a body such as Key length 269 exceeds maximum of 256 bytes, and no key is deleted. A put or a single delete of a key is not checked against this key. A put is limited by domains.max_user_key_length, which answers a longer key with 400 Bad Request: Key length N exceeds maximum of M bytes.

The two limits therefore interact. If domains.max_user_key_length plus 19 is larger than this key, keys longer than this key minus 19 bytes can be written but not removed by a bulk delete by prefix. To keep the bulk delete working for every key you accept, set this key to at least domains.max_user_key_length plus 19.

The JSON and relational engines have their own limits (json.lsm.max_key_length and rel.lsm.max_key_length), and the system store uses a built-in one.

Background

A key is stored in every MemTable entry, every write-ahead log record and every SSTable entry, together with an 8-byte timestamp per version, so the length of the keys is paid for in memory and on disk. The limit guards the engine against keys of unreasonable size. Keep it as small as your keys allow, and raise it together with domains.max_user_key_length if you need longer keys.

Referenced on
Limits and defaults · Status codes · Key-value

max_value_size lsm.max_value_size

Since v0.1.0
Largest value in bytes; larger writes are rejected.
Default
524288
Effect

The largest value, in bytes, that the engine accepts on the paths where it checks the size. The value must not exceed 100663296 bytes (96 MiB), the largest field that the recovery of the write-ahead log reads; the start is refused with invalid config: lsm.max_value_size (…) exceeds the WAL recovery field cap (100663296 bytes) — writes this large could never be recovered otherwise.

In v0.5.1 the REST API does not apply the limit to the values of single keys. With the key set to 1000, a put of a 5000-byte value to a key-value domain is answered with 200. What limits such a put is the size of the request body: the server rejects a body above 2 MiB (2097152 bytes) with 413 and the body Failed to buffer the request body: length limit exceeded. The engine’s own check, which answers Value length N exceeds maximum of M bytes, applies to the engine’s batches and to its own records, and the value of a put to a key-value domain never reaches it.

The JSON engine and the relational engine have limits of their own (json.lsm.max_value_size and rel.max_row_size), and the system store uses a built-in one.

Background

The key is the size limit that the engine of the key-value store carries. Its upper bound exists so that no value can be written that the recovery of the write-ahead log would refuse to read after a crash. Do not rely on the key to cap the size of values in this release; the request limit of 2 MiB is the effective ceiling for a value. If you need a smaller ceiling, enforce it in the client or in a reverse proxy in front of the server.

Referenced on
Limits and defaults · Key-value

flush_check_interval_ms lsm.flush_check_interval_ms

Since v0.1.0
How often, in milliseconds, the flush loop checks for a full MemTable.
Default
100
Effect

The pause, in milliseconds, between two turns of the flush loop of a shard. In each turn the loop writes the oldest frozen MemTable of the shard to a new SSTable at level 0, if there is one, and then sleeps for this long. A MemTable is frozen by the write that follows the moment it reaches lsm.memtable_size_threshold. After a failed flush the loop waits for lsm.compaction_check_interval_ms instead. The loop writes at most one MemTable per turn; the shutdown of the server and the steps that need all data in SSTables (the cleanup of the value log, the write-ahead log cap of storage.wal_max_size) flush MemTables on their own.

The value is a whole number of milliseconds and is not checked at start. Do not use 0: with 0 the data shard threads ran at full load in a test of v0.5.1, and the start did not finish. The start log ended at the per-shard memory lines, with no data shard(s) ready and no listener.

Background

A frozen MemTable stays in memory, and stays in the write-ahead log, until the loop reaches it. A short pause gets data into SSTables quickly and keeps the memory of frozen MemTables and the log short, at the price of more wake-ups of an idle shard. With the default MemTable size, one flush per turn is an upper bound of a few tens of MiB per second and shard for the loop; if you raise the write rate beyond what one MemTable per turn can absorb, lower the pause or enlarge the MemTable. The pause is rarely the first setting to look at.

Referenced on
Write path

compaction_check_interval_ms lsm.compaction_check_interval_ms

Since v0.1.0
How often, in milliseconds, the compaction loop checks for work.
Default
1000
Effect

The pause, in milliseconds, between two turns of the compaction loop of a shard. In each turn the loop checks the levels of the shard’s SSTables against the limits of compaction.l0_threshold, compaction.l1_max_size and compaction.level_size_ratio. If a level is over its limit, it runs one compaction, for the lowest such level, and then sleeps for this long. A shard therefore starts at most one compaction per interval. The same value is the pause of the flush loop after a failed flush (lsm.flush_check_interval_ms).

The value is a whole number of milliseconds and is not checked at start. Do not use 0: with 0 the server used the CPU of several cores at full load in a test of v0.5.1, and /health did not answer within 5 seconds.

Background

Compactions rewrite data, so they compete with writes for the disk and the CPU, and the pause caps how often a shard starts one. The l0_sstable_count field of GET /health shows how many level-0 files are waiting. If it keeps growing under sustained writes, compaction does not keep up; a shorter pause lets a shard start its next compaction sooner, a longer one spares the disk. Leave the default unless you see that.

Referenced on
Write path

wal_event_channel_capacity lsm.wal_event_channel_capacity

Since v0.1.0
Change events buffered for watch streams; a watcher further behind gets reset.
Default
256
Effect

The number of change events that the key-value engine of a shard buffers for the watch streams (GET /store-api/kv/{domain}/watch). Every write and delete publishes an event after it is visible. A shard has one channel for the events of all its domains, and every open watch has a relay channel of the same size behind it. A watcher that falls further behind than this number of events loses the events it did not read. It receives event: reset with the reason lagged instead, and has to read the domain again before it applies events.

Because the channel carries the events of every domain on the shard, a watcher of a quiet domain can lag when other domains on its shard are busy.

The value must be at least 1. With 0, the start fails: the control-plane thread panics with broadcast channel capacity cannot be zero, and the server exits with Error: the control-plane thread stopped during startup. The key applies to the key-value engine; the JSON and relational engines and the system store use a built-in size.

Background

Writers never wait for a slow watcher. The channel drops the oldest events of a watcher that does not keep up, and the reset tells the client that its picture of the domain has a gap. A larger channel gives slow or bursty watchers more room before that happens, at the price of memory for the buffered events, which hold their keys. Raise the value if watchers regularly receive reset with the reason lagged during write bursts. The ring for resuming after a reconnect is a separate setting, lsm.watch_replay_buffer_size.

Referenced on
Limits and defaults · Key-value

watch_replay_buffer_size lsm.watch_replay_buffer_size

Since v0.4.0
KV watch replay-ring size. 0 = resume off (every reconnect with a Last-Event-ID gets reset); id: fields always exist.
Default
1024
Effect

The number of change events that each shard keeps for clients that reconnect to a watch stream. Every event of the stream carries an id:. A client that reconnects with that id in the Last-Event-ID header (or ?last_event_id=) gets the events it missed, without a gap, if they are still in the ring. Otherwise the stream starts with event: reset and a reason: window_exceeded if the id is older than the ring, restart if the server has been restarted since the id was issued, unknown_id for an id the server does not know. After a reset the client should read the domain again and then keep applying events.

The ring holds the events of all domains of a shard, and all watchers share it. Its size counts events, not time, so the window in time is the size divided by the write rate of the shard. The ring is filled up to its size and then overwritten from the oldest event; memory grows with the number of stored events, which hold their keys.

With 0, the ring stores nothing: every reconnect with a Last-Event-ID gets reset, but the events still carry ids. The key applies to the key-value engine; the JSON and relational engines have no watch. The channel that feeds the watchers that stay connected is set by lsm.wal_event_channel_capacity.

Background

A watch stream is a long-lived connection, and clients such as a browser’s EventSource reconnect on their own and send the last id. The ring lets them continue without reading the domain again after a short interruption. Size it for the longest interruption you want to bridge at the write rate of a shard: with 1000 writes per second on a shard, a ring of 60,000 events covers a minute. If the clients are always connected, or if they read the domain again after every reconnect anyway, the ring brings nothing, and 0 saves its memory.

Referenced on
Limits and defaults · Key-value

l0_threshold compaction.l0_threshold

Since v0.1.0
Number of level-0 SSTables that starts a compaction into level 1.
Default
4
Effect

The number of SSTables at level 0 of a shard at which the compaction loop (lsm.compaction_check_interval_ms) starts a compaction of level 0 into level 1. The comparison is “at least”: with the value 3, the compaction starts when the third file has arrived. Every flush of a MemTable (lsm.memtable_size_threshold) adds one file at level 0.

Files at level 0 can overlap each other in their key ranges, so the compaction merges all of them, together with the files of level 1 whose key ranges they touch, into new files at level 1 (the size of those is limited by compaction.max_sstable_size). Older versions of a key that no reader can still need are dropped, and, when no level below holds files, so are deleted and expired entries.

The server does not check the value. The number of level-0 files of all shards together is the l0_sstable_count field of GET /health, and compaction_runs in the system block of the metrics endpoint counts the compactions that have run.

Background

The value sets how many unmerged files a shard may carry. A low value merges early: fewer files overlap, so there are fewer places to look for a key, but data is rewritten more often. A high value postpones the work and leaves more files at level 0 for reads to go through. Lower it for read-heavy installations that suffer from a growing l0_sstable_count; raise it for write-heavy ones that can afford slower reads.

Referenced on
Write path · Monitoring

l1_max_size compaction.l1_max_size

Since v0.1.0
Level-1 size in bytes that starts a compaction into level 2.
Default
104857600
Effect

The total size, in bytes, of the SSTables at level 1 of a shard at which the compaction loop starts a compaction of level 1 into level 2. The comparison is “at least”. The compaction takes the largest single file of level 1 and merges it with the files of level 2 whose key ranges it touches. Deeper levels follow the limit of this key multiplied by the factor of compaction.level_size_ratio once per level below level 1: level 2 holds the limit times the factor, level 3 times the factor squared, and so on. The value is a whole number of bytes and is not checked at start.

A shard whose data stays below this size never moves beyond level 1. That has a consequence for deleted data: deleted and expired entries are dropped when a compaction runs and no level below its target holds files, and the compaction of level 0 into level 1 is such a run while level 2 is empty.

Background

The limit decides how much data sits in the first merged level before it is pushed down. A larger limit keeps small and medium data sets in a single level of files whose key ranges do not overlap, so that a read looks at one file there, and it postpones the first large compaction. A smaller limit pushes data down earlier and spreads it over more levels. It is a limit per shard: with several shards (multicore.shards) the data of the key-value engine is divided among them, and each shard reaches the limit with its own share.

Referenced on
Write path

level_size_ratio compaction.level_size_ratio

Since v0.1.0
Size factor between consecutive levels (level 2 holds 10 times level 1).
Default
10
Effect

The factor by which the size limit grows from one level to the next. Level 1 is limited by compaction.l1_max_size; level n from 2 on is limited to that value times this factor raised to the power n - 1. With a factor of 4 and a level-1 limit of 50 MiB, levels 2 and 3 are limited to 200 MiB and 800 MiB. A level is compacted into the next one when its total size reaches its limit. The calculation saturates instead of overflowing, so a very large value does not wrap around. The value is a whole number and is not checked at start.

Background

The factor shapes the tree: with a larger factor, the same amount of data spreads over fewer levels, and each level holds more before it is pushed down. With a smaller factor, there are more levels with smaller steps between them. Change it only together with compaction.l1_max_size and after you have measured the compaction work for your own data volume.

Referenced on
Write path

max_sstable_size compaction.max_sstable_size

Since v0.1.0
Largest SSTable in bytes a compaction writes; larger results are split.
Default
67108864
Effect

The size at which a compaction closes an output file and starts the next one. While it writes a file, the compaction adds up the length of every key and of every value, where a value that lives in the value log counts as its pointer and an inline value as its length plus 8 bytes. When the sum reaches this number, the file is closed at the next boundary between two user keys, so that all versions of a key stay in one file. A file is therefore at least as large as the limit unless the data runs out, and it is somewhat larger on disk than the sum, because of the structure around the data: in a test of v0.5.1 with a limit of 20000, the full files of a compaction were about 22 KB. The files that a flush writes at level 0 are not cut by this key; they are as large as the MemTable they come from.

The value is a whole number of bytes and is not checked at start. With 0, every user key gets a file of its own.

Background

Each compaction step merges one file of a level (the largest) with the files of the next level that it overlaps, or all files of level 0, so the size of the files bounds the work and the temporary disk space of one step: a compaction waits while its volume has less free space than its inputs take. Smaller files make the steps smaller and let a compaction touch less data that did not change, at the price of more files and a longer manifest. Larger files do the opposite. Change the default only if compactions of the current size are too slow or too disruptive.

Referenced on
Write path

check_interval_secs janitor.check_interval_secs

Since v0.1.0
How often, in seconds, the janitor checks whether a GC run is due.
Default
60
Effect

The pause, in seconds, between two checks of the janitor, the task that cleans the value log of a shard. The janitor sleeps for this long first and then runs one check, so the first check comes one interval after the start. Each check estimates how much of the value log is dead; it reads the pointers that the SSTables and MemTables of the shard hold to do so. If the estimate reaches janitor.dead_bytes_threshold and the log has at least janitor.min_vlog_size_bytes bytes, the check goes on to clean the log. The three keys of the [janitor] section apply to the key-value engine; the JSON and relational engines and the system store clean their logs with built-in values. The value is a whole number of seconds and is not checked at start. Do not use 0: with 0 the server used the CPU of several cores at full load in a test of v0.5.1, and /health did not answer within 5 seconds.

A cleanup writes two lines to the standard error output (the journal of the packaged service): [Janitor] Starting GC: generations=…, vlog_size=…, dead(est)=… (…%) and, when it is done, [Janitor] GC complete: …. The janitor_runs field in the system block of the metrics endpoint counts the cleanups that have run.

Background

The check itself costs a read of the pointers of the shard’s SSTables, which grows with the amount of data, while the dead space it looks for grows only as values are overwritten or deleted. A longer pause is cheaper on large stores and delays the reclaiming of space; a shorter one reclaims sooner. Shorten it for a test that has to see a cleanup quickly.

Referenced on
Write path

dead_bytes_threshold janitor.dead_bytes_threshold

Since v0.1.0
Share of dead bytes in the value log (0 to 1) that starts a GC run.
Default
0.30
Effect

The share of the value log that has to be dead before the janitor cleans it, as a number from 0 to 1 with decimals. The janitor estimates the share as the size of the log minus the bytes that the pointers in the MemTables and SSTables of the shard still reference, divided by the size of the log. A cleanup starts when the estimate is at least this number and the log has at least janitor.min_vlog_size_bytes bytes. The server does not check the value at start: a number above 1 is never reached, so the log is never cleaned, and 0 cleans at every check that passes the size minimum.

The estimate lags behind the writes. A value that was overwritten or deleted counts as live for as long as an older version of its key is still in a MemTable or an SSTable, so the dead share rises only once flushes and compactions have merged those versions away. In a test of v0.5.1, a log of 180,000 bytes with a threshold of 0.2 was cleaned after compactions had dropped the overwritten versions, with the log lines [Janitor] Starting GC: … dead(est)=84000 (46.7%) and [Janitor] GC complete: … reclaimed=84000 bytes.

A cleanup opens a new generation of the value log, copies the live values into it, rewrites the SSTables with the new pointers, and deletes the old generation (see storage.vlog_path). It needs free space on the volume of the value log for the live values; while the volume has less, the cleanup waits and logs Value log cleanup waits for disk space.

Background

A cleanup copies every live value, however little is dead, so its cost depends on the live part of the log, while the space it returns is the dead part. A low threshold cleans often and returns little each time; a high one lets dead space pile up and returns much at once, but needs more free disk space beforehand for the log to grow into. Choose it by how much extra disk space you can spare for dead values. A value of 0.5 lets the log be about twice as large as its live data before a cleanup.

Referenced on
Write path

min_vlog_size_bytes janitor.min_vlog_size_bytes

Since v0.1.0
Value-log size in bytes below which GC never runs.
Default
67108864
Effect

The size, in bytes, that the value log of a shard needs before the janitor may clean it, however much of it is dead. The size is that of all generation files of the shard’s log together (storage.vlog_path); the cleanup of janitor.dead_bytes_threshold is skipped while the sum is below it. The limit applies to each shard on its own, not to the sum over all shards. The value is a whole number of bytes and is not checked at start.

In a test of v0.5.1 with a value log of 180,000 bytes and a dead-bytes threshold of 0.2 that was met, a janitor with this key at 1 cleaned the log, and one with the default did not (janitor_runs stayed at 0).

Background

The cleanup copies the live values and rewrites SSTables, which is wasted effort for a small log. The minimum keeps small stores from being cleaned: until the log of a shard has grown to this size, dead values in it stay where they are. Lower the value where disk space is tight, or for a test; raise it where space is plentiful and you want fewer cleanups.

Referenced on
Write path

enabled ttl_sweeper.enabled

Since v0.4.0
Runs the sweeper; while off, expired keys are only hidden on read.
Default
true
Effect

With true, each shard runs a sweeper for the keys of its key-value engine that have a time to live (the ttl parameter of a put). The sweeper finds keys whose time has run out, writes a tombstone for each one and publishes a delete event for it to the watch streams. The tombstone is a logged write like any other delete. The sweeper does not run while writes are refused for lack of disk space or memory (resources.disk_free_block). Its pace is set by ttl_sweeper.interval_secs and ttl_sweeper.batch_size.

With false, expiry is lazy. A key past its time is hidden from reads: GET answers 404. Its data is removed only when a compaction that has no level below it passes over it, and no delete event is published for it. In a test of v0.5.1 with a key put with ttl=1, a watcher received a set and a delete event with the sweeper on and only the set event with it off; three seconds after the put, the GET answered 404 in both cases.

The system store has a sweeper of its own, because the OAuth clients and tokens of the MCP listener have a time to live. It runs when mcp.enabled, mcp.oauth_enabled and this key are all true. Setting this key to false therefore also stops the sweeping of expired OAuth records there.

Background

Reads never return an expired key either way, so the sweeper does not change what a client reads. It changes what watchers see, since expiries arrive as delete events, and it replaces the expired value with a tombstone instead of leaving the value until a compaction with no level below it passes over it. The price is a periodic pass over the keys of each shard. Turn the sweeper off if you use no time to live, or if neither watchers nor the early tombstone matter to you and the passes cost too much.

Referenced on
Write path · Key-value

interval_secs ttl_sweeper.interval_secs

Since v0.4.0
Seconds between two sweeper ticks.
Default
60
Effect

The pause, in seconds, between two ticks of the TTL sweeper of a shard. A tick runs at once when the shard starts, and the next one follows after this pause. In a tick the sweeper scans the keys of the shard’s MemTables and SSTables for expired versions, takes up to ttl_sweeper.batch_size of the keys it finds and writes a tombstone for each key that is still expired. The delay between the end of a key’s time and its delete event is therefore at most the pause plus the time of one tick, and more if more keys have expired than a tick takes; reads hide the key within a second of the end of its time whatever the pause is.

The value is a whole number of seconds and is not checked at start. Do not use 0: with 0 the server used the CPU of several cores at full load in a test of v0.5.1, /health did not answer within 5 seconds, and the process did not stop on SIGTERM.

The key has no effect while ttl_sweeper.enabled is false. It applies to the key-value engine and to the sweeper of the system store, which ttl_sweeper.enabled describes.

Background

Every tick reads the keys of all MemTables and SSTables of the shard, whatever the batch size is, so the cost of a tick grows with the data and the pause sets how often it is paid. A short pause makes the delete events follow the expiries closely and suits small data sets with watchers that react to them. On a large data set, a longer pause spares the disk and the CPU.

Referenced on
Write path

batch_size ttl_sweeper.batch_size

Since v0.4.0
Candidates checked per tick — discarded ones count too.
Default
500
Effect

The number of expired keys that one tick of the TTL sweeper looks at. The sweeper collects up to this many candidate keys, the smallest ones above the point where the last tick stopped, checks each of them again, and writes a tombstone only for those that are still expired. Candidates that turn out not to need a tombstone count toward the number too, so a tick may write fewer tombstones than this. A tick therefore writes at most this many tombstones.

The sweeper remembers where a tick stopped and goes on from there in the next one, so a backlog of expired keys is worked off tick by tick. When a tick finds fewer candidates than this number, the sweeper starts again from the beginning of the key space at the next tick. The position is not kept over a restart. The value is a whole number and is not checked at start; with 0, no tick finds a candidate, and the sweeper writes nothing. The memory of a tick is proportional to this number, not to the number of keys in the shard.

Background

A larger batch clears a backlog of expired keys in fewer ticks, but each tombstone is a logged write like a delete by a client, so a large batch makes a burst of writes next to the writes of clients. A smaller batch spreads the work more evenly over time and takes longer to catch up. With many keys that expire at the same moment, such as sessions with the same time to live, the batch size and ttl_sweeper.interval_secs together set how fast the delete events come: about one batch per pause.

Referenced on
Write path

max_name_length domains.max_name_length

Since v0.1.0
Longest domain name in characters ([a-zA-Z0-9_-]).
Default
50
Effect

The longest name, in characters, that a key-value domain may have. A name consists of the characters a-z, A-Z, 0-9, _ and -. A request that creates a longer name (POST /store-api/domains) is answered with 400 and 400 Bad Request: Domain name '…' exceeds maximum of N characters. The limit is checked when a domain is created; domains that already exist keep working when the value is lowered.

The key applies to key-value domains only. JSON and relational domains have a fixed limit of 50 characters that this key does not change. In a test of v0.5.1 with the key set to 10, a key-value domain with 11 characters was refused and a JSON domain with 11 characters was created. A key-value domain with a name of more than 50 characters can therefore exist, but a JSON or relational domain of the same name cannot.

The limit is also checked at every start against the name of domains.default_domain. A value below the length of that name refuses the start; with the value 5 and the default name the start fails with Error: 400 Bad Request: Domain name 'default' exceeds maximum of 5 characters.

Background

With the default, a name that is valid in one engine is valid in all three, because 50 characters is also the fixed limit of the JSON and relational engines. The same name in several engines is how backups (domain:<name>) and links between engines address a domain. Raise the value only if you need longer names for key-value domains and can accept that the other two engines refuse them.

Referenced on
Domains and engines · Limits and defaults

max_user_key_length domains.max_user_key_length

Since v0.1.0
Longest key a client may use, in bytes.
Default
256
Effect

The longest key, in bytes, that a client may use in a key-value domain. The limit counts bytes, not characters: a four-byte character such as an emoji counts four times. A put, a read, a delete, a set-to-null or a metadata request with a longer key is answered with 400 Bad Request: Key length 11 exceeds maximum of 10 bytes. Two rules are fixed: a key must not be empty and must be valid UTF-8. The prefix and substring parameters of listings, counts and bulk deletes are not checked against this key.

The engine stores a key with a prefix of 19 bytes in front of it, the same prefix that lsm.max_key_length counts. A key that is accepted here can therefore be longer than that limit allows for a stored key, and a bulk delete by prefix then fails for it. To keep both limits consistent, set this key to at most lsm.max_key_length minus 19.

The key applies to the key-value engine. JSON document keys and relational primary keys have their own limits (json.lsm.max_key_length and rel.lsm.max_key_length). The server does not check the value at start.

Background

Every version of a key is kept in the MemTable, in the write-ahead log and in the SSTables, so the length of the keys is paid for in memory and on disk. The default of 256 bytes is long enough for composite keys such as tenant:42:order:2026-11-03. Keep the limit as small as your keys allow. If you need longer keys, raise it together with lsm.max_key_length.

Referenced on
Domains and engines · Limits and defaults · Key-value

default_domain domains.default_domain

Since v0.1.0
Domain created at every start if it is missing.
Default
"default"
Effect

The name of the key-value domain that every start creates if it does not exist. A new data directory starts with one key-value domain of this name, and GET /store-api/domains lists it. The name follows the rules for domain names (the characters a-z, A-Z, 0-9, _, - and at most domains.max_name_length characters); a name that breaks them refuses the start with the message of the domain check, such as 400 Bad Request: Domain name '…' contains invalid characters (only [a-zA-Z0-9_-] allowed).

The domain is an ordinary domain. It can be deleted, and the next start creates it again: in a test of v0.5.1, DELETE /store-api/domains/default was answered with 202, the listing was empty a few seconds later, and after a restart the domain was back. If a start finds the domain still being deleted, it logs a warning and creates the domain at the first start after the purge has finished.

Changing the key moves nothing. In a test, a data directory that already had default listed both default and main after a start with default_domain = "main"; the old domain and its keys stay. The key applies to the key-value engine only. The JSON engine and the relational engine create a domain named default of their own, whatever the key says; with the key set to main, both of them still listed default.

Background

No request falls back to this domain: clients always name the domain in the path, and the server does not use the default domain anywhere else. Its only purpose is that a new installation has one domain to try the API on without creating one. Change the key if you want a different name for it. The server has no setting that starts it without such a domain; a domain you delete is created again at the next start.

Referenced on
First five minutes · Domains and engines · Key-value

purger_batch_size domains.purger_batch_size

Since v0.1.0
Keys removed per step while a deleted domain is purged in the background.
Default
100
Effect

The number of keys that one purge step removes from a deleted key-value domain. DELETE /store-api/domains/{name} answers 202 at once and makes the domain unreachable: requests for it get 410 Gone: Domain '…' is being deleted, and the name cannot be created again (409) until the purge has finished. The keys are removed in the background. Each step, every shard looks at each deleted domain it owns, reads the full list of its remaining keys, writes a tombstone for the first N of them, and leaves the rest for the next step. A step that finds no key left removes the domain itself. A domain with 200 keys needs four steps for the keys and a fifth to finish; in a test with the value 50 and a pace of one second (domains.purger_interval_secs), the name could be created again after 5.2 seconds.

The key applies to the key-value engine. The JSON and relational engines purge their deleted domains with a fixed batch of 100 keys every 5 seconds. No step runs while writes are refused for lack of disk space or memory (resources.disk_free_block).

With 0, a step removes no key. A domain that holds keys then stays in the deleting state, and its name stays blocked; in a test, creating the name again after a delete was answered with 409 Conflict: Domain '…' already exists for as long as the server ran. A deleted domain that holds no keys is still removed.

Background

Deleting a domain does not freeze the server, because the keys leave in small batches that share the write path with normal traffic; each tombstone is a logged write like any other delete. A larger batch finishes a big domain sooner and needs fewer steps, and every step reads the whole remaining key list, so a small batch on a large domain repeats that read many times. The defaults of 100 keys every 5 seconds remove 1,200 keys a minute from each deleted domain; raise the batch if deleting domains with millions of keys takes too long.

Referenced on
—

purger_interval_secs domains.purger_interval_secs

Since v0.1.0
Seconds between two purge steps.
Default
5
Effect

The number of seconds between two purge steps of a deleted key-value domain. Each shard runs one purge loop for the domains it owns: it takes a step (see domains.purger_batch_size), waits this many seconds, and takes the next. The first step runs at start. A domain that was deleted is therefore removed after at most one interval plus the time of the steps it needs, which depend on its number of keys.

The key applies to the key-value engine. The JSON and relational engines purge on a fixed pace of 5 seconds.

The value is not checked at start. With 0 the loop does not wait: in a test of v0.5.1 with one shard and no deleted domain, the process used about one core (478 ticks of CPU time in 5 seconds, against 1 tick with the default), and every shard runs such a loop. The server then does not stop on SIGTERM: in the test it kept running at that load for more than 20 minutes after the signal and had to be killed with SIGKILL. Use a value of at least 1.

Background

A deleted domain is removed in the background so that the delete request returns at once and the purge does not compete with normal traffic. A short interval clears deleted domains sooner, which frees the name for reuse sooner; a long interval spreads the load further. The loop also runs when no domain is deleted, where a step does little more than ask each shard for its deleted domains, so the default of 5 seconds costs almost nothing. Change it only if you delete domains often and need the names back sooner.

Referenced on
Backup and restore

max_bulk_delete_keys domains.max_bulk_delete_keys

Since v0.4.0
Cap on keys removed by one DELETE …/keys?prefix= bulk delete.
Default
10000
Effect

The most keys that one DELETE /store-api/kv/{domain}/keys?prefix=… request may remove. The request first collects the keys that match the prefix (and the optional contains filter), and only then deletes them, all in one atomic write. If more keys match than this key allows, nothing is deleted and the answer is 413 with 413 Payload Too Large: 4 keys match the selection, limit is 3. The request that is allowed answers 200 with the number of deleted keys, {"deleted":1}.

The value 0 rejects every selection that matches at least one key, and does not mean unlimited: in a test of v0.5.1, the request answered 413 Payload Too Large: 1 keys match the selection, limit is 0, and a prefix that matches nothing answered 200 with {"deleted":0}. The prefix must not be empty, so a request without one cannot empty the domain; removing a whole domain is DELETE /store-api/domains/{name}, which is for admins. The request takes one write token and one read token of the rate limit of the domain (rate_limit.default_write_iops).

The key applies to key-value domains only. The server does not check the value at start.

Background

A bulk delete is atomic: either every matching key is gone or none is. That needs the whole list of keys in memory and one large batch in the write-ahead log, so the cap bounds the memory and the size of that batch. To remove more keys than the cap allows, split them by prefix and send several requests, or delete the domain. Raise the cap only if you need larger atomic deletes and have measured the memory and the time of the delete.

Referenced on
Consistency and conditional writes · Limits and defaults · Status codes · Key-value

default_read_iops rate_limit.default_read_iops

Since v0.1.0
Reads per second per domain.
Default
1000
Effect

The read budget of each key-value domain and each relational domain, in operations per second. Every domain has a budget of its own, counted independently: a busy domain cannot use up the budget of another, and a key-value domain and a relational domain with the same name have separate budgets. The budget is a bucket that holds up to one second’s worth of operations and starts full. Each read takes one token out of it, and tokens flow back continuously at the rate of the key. A read that finds the bucket empty is answered with 429 Too Many Requests and the header Retry-After: 1; the body is 429 Too Many Requests: read rate limit exceeded for a key-value domain and rate limit exceeded for domain '…' for a relational one. After a quiet second a client can therefore send up to this many reads at once.

What counts as one read: in a key-value domain, a key lookup, a metadata lookup, a key listing and a count (one token for the whole listing, however many keys it returns); in a relational domain, a SELECT statement and every request that lists or reads tables, views or rows. A bulk delete by prefix also takes one read token for its scan. A point read that has to wait for the disk is taken over by the reader pool, and the token of the first attempt is given back, so such a read costs one token, not two. JSON domains are not limited: in a test of v0.5.1 with the write budget set to 3, twelve document writes in a row all succeeded. Domain listings, metrics and other requests that name no key-value or relational domain take no token.

The values are read at start. With 0, every read of every domain is refused with 429. The rejections of a domain are counted in the field rate_limit_rejections of its entry under domains in GET /store-api/metrics.

Background

The budget keeps one domain from taking the whole server: the clients of a domain that reaches its rate are slowed down, and other domains are not. It is a limit per domain, not a global one; ten domains that all reach their limits together use ten times the rate. The default of 1,000 reads a second suits a domain used by a few clients. A domain that serves many clients, or a client that reads in large bursts, needs a higher value. Raise it in steps and watch rate_limit_rejections and the response times.

Referenced on
Domains and engines · Troubleshooting · Limits and defaults · Status codes · Key-value · LuraSQL · REST API

default_write_iops rate_limit.default_write_iops

Since v0.1.0
Writes per second per domain.
Default
500
Effect

The write budget of each key-value domain and each relational domain, in operations per second. It works like the read budget of rate_limit.default_read_iops, with a bucket of its own: one second’s worth of tokens at the start, one token per write, continuous refill. A write that finds the bucket empty is refused with 429 Too Many Requests and Retry-After: 1, before anything is written; the body is 429 Too Many Requests: write rate limit exceeded for a key-value domain and rate limit exceeded for domain '…' for a relational one. In a test of v0.5.1 with the key set to 3, five puts in a row into one key-value domain were answered 200 200 200 429 429, while a put into a second domain was still accepted.

What counts as one write: in a key-value domain, a put, a delete, a set-to-null and a bulk delete by prefix (one token for the whole delete, however many keys it removes); in a relational domain, every INSERT, UPDATE, DELETE and DDL statement, every insert, update and delete of a row through the row endpoints, and a file import (tables/from-file, one token for the whole file). A relational statement is classified by the statement, not by the number of rows it affects: one INSERT with many rows takes one token. JSON domains are not limited.

The values are read at start. With 0, every write of every domain is refused with 429. The rejections of a domain are counted in the field rate_limit_rejections of its entry under domains in GET /store-api/metrics.

Background

A write waits for the write-ahead log to reach the disk before it is confirmed, so a client that writes without pause loads the log for everyone. The write budget caps what one domain may ask of it. It is a limit per domain, not a global one. Batch writes where your data allows it, since a bulk delete, a multi-row INSERT and a file import cost one token each, and raise the value for domains that need a higher steady rate than 500 writes a second.

Referenced on
Domains and engines · Troubleshooting · Limits and defaults · Status codes · Key-value · LuraSQL · REST API

default_max_storage_bytes rate_limit.default_max_storage_bytes

Since v0.1.0
Stored bytes per domain; 0 means no limit.
Default
0
Effect

Meant as the largest amount of stored data per key-value or relational domain, in bytes, with 0 for no limit. In v0.5.1 the value is read from the file and handed to every domain, but nothing enforces it: no code path compares the stored size of a domain with it. In a test with the key set to 1, puts of a 26-byte value into a new key-value domain were answered with 200.

The value must be a whole number of at least 0; text or a negative number refuses the start with a parse error of the configuration file.

Background

The key belongs to the same group as the read and write budgets (rate_limit.default_read_iops and rate_limit.default_write_iops), which are enforced. Do not rely on this key to cap the size of a domain in this release. If you need a ceiling, watch the volume with the resource thresholds (resources.disk_free_warn) and cap the size of data in the client or in a proxy in front of the server.

Referenced on
—

enabled json.enabled

Since v0.1.0
Starts the JSON document engine. While disabled, relational tables show JSON references as NULL.
Default
true
Effect

With true, the server runs the JSON document engine: it opens its write-ahead log, value log and SSTables (json.wal_path, json.vlog_path, json.sstable_dir), creates a JSON domain named default at start, and serves the /store-api/json/… endpoints. The engine runs on a storage instance of its own, so JSON work does not share MemTables, caches or compactions with the key-value engine.

With false, no JSON instance starts and the log says JSON engine disabled by config.. In a test of v0.5.1, no JSON files were created, every /store-api/json/… endpoint answered 503 Service Unavailable: JSON engine is disabled (json.enabled = false), and /health stayed ok. The other engines keep working, with these exceptions:

  • A relational table keeps its JSONREF columns, but their values read as null, because the documents cannot be looked up. Writing a value into such a column is refused with 409 and cross-engine json target unavailable for domain '…', and so is creating a table with such a column.
  • A backup of scope json or json:<domain> fails with json_engine_disabled; scope all leaves JSON out.
  • The tool list_domains of the MCP server names the engine under unavailable_engines.

Data already written stays in the files. While the engine is off, the paths of its files are not checked against the other data paths, but its limits (json.bulk_body_limit_bytes, json.lsm.max_key_length and json.lsm.max_value_size) are still validated at start.

Background

Switch the engine off if the installation stores no documents. It saves what the engine reserves: two MemTables of 4 MiB per shard, a block cache of 64 MiB (divided across the shards), and one write-ahead log that the storage thread has to write. The reservation is part of the fixed memory that the server compares with resources.memory_budget.

Referenced on
Domains and engines · JSON documents

wal_path json.wal_path

Since v0.1.0
Write-ahead log; relative paths resolve against the working directory. A directory of segment files the server creates, writes and deletes; never change anything in it by hand. The path may be a symlink, the segments then lie at its target. A log file of an earlier LuraDB version at the path becomes such a directory on the first start; an earlier version does not start on it.
Default
"luradb_json.wal"
Effect

The directory of the write-ahead log of the JSON engine. It holds segment files of 64 MiB each, named 00000001.wal, 00000002.wal and so on, which the server creates, writes and deletes; nothing in it should be changed by hand. All data shards of the JSON engine share this one log, so unlike the value log and the SSTables it is not placed in a shard_<k> directory. A log file of an earlier LuraDB version at the path becomes such a directory on the first start, and an earlier version does not start on it.

The path may be a symbolic link, and the segments then lie at its target. A relative path resolves against the working directory of the server process; the packaged service sets that to /var/lib/luradb, so the packaged luradb_json.wal is /var/lib/luradb/luradb_json.wal. The parent directory is created at start.

No two data paths may resolve to the same location (symbolic links resolved). In a test of v0.5.1, setting this key to the value of storage.wal_path refused the start with invalid config: storage.wal_path and json.wal_path resolve to the same location '…'. The check is skipped while json.enabled is false. How much of the log the server keeps is set by storage.wal_max_size, which caps each engine’s log.

Background

A write to a JSON domain is confirmed once it is in this log, and recovery after a crash replays what the SSTables do not yet hold. The write waits for the log to reach the disk, so the latency of the volume sets the lower bound for the latency of document writes. The storage thread of the server writes the logs of all engines and batches concurrent writes into one fsync per log. Place the log on a volume with room for storage.wal_max_size plus one segment.

Referenced on
Write path · systemd and Docker · Limits and defaults

vlog_path json.vlog_path

Since v0.1.0
Value log for large documents.
Default
"luradb_json.vlog"
Effect

The value log of the JSON engine. A stored document of 1,024 bytes or more is appended to it, and the SSTables keep only a pointer to it. The threshold is built in: the JSON engine does not use lsm.vlog_inline_threshold. A stored document is a little larger than the JSON that the client sent, because a version number and an incarnation id are stored with it. Each data shard has its own file: the server inserts a directory shard_<k> before the file name, so a path /var/lib/luradb/luradb_json.vlog becomes /var/lib/luradb/shard_0/luradb_json.vlog for shard 0. A relative path resolves against the working directory of the server process (under the packaged service, /var/lib/luradb).

When the value log is cleaned, the live values move into a new generation whose file name is the configured name with .2 appended, then .3, and so on. No other data path may equal this name followed by a dot and digits; the start is refused if one does. The cleanup runs with the built-in settings of the janitor, not with the janitor section of the file, which applies to the key-value engine.

No two data paths may resolve to the same location. The check is skipped while json.enabled is false. The key-value engine and the relational engine have their own value logs, storage.vlog_path and rel.vlog_path.

Background

Keeping large documents out of the SSTables means that a compaction rewrites keys and pointers, not the documents. The price is that overwritten and deleted documents stay in the log as dead space until the cleanup rewrites it, so the volume needs room for the data plus the dead space that builds up between cleanups. The cleanup waits while the volume has less room than the live values take.

Referenced on
Write path · systemd and Docker · Limits and defaults

sstable_dir json.sstable_dir

Since v0.1.0
SSTable directory.
Default
"luradb_json_sstables"
Effect

The directory of the SSTables of the JSON engine. Each data shard keeps its files in a subdirectory shard_<k> of it, with the files sstable_<number>.sst and a MANIFEST that lists them and their levels. The server creates the directory at start. A relative path resolves against the working directory of the server process (under the packaged service, /var/lib/luradb).

A flush of the MemTable of the JSON engine adds a file at level 0, and compactions merge files into the levels below. The JSON engine runs these with the built-in defaults (a flush at a MemTable of 4 MiB, a compaction at four level-0 files, level 1 at 100 MiB, a factor of 10 between levels, SSTables of at most 64 MiB), not with the lsm and compaction sections of the file, which apply to the key-value engine only. Documents of 1,024 bytes or more are not in the SSTables but in the value log (json.vlog_path).

The server does not move data when the path changes. A start with another path opens an empty set of SSTables there, and the data in the old directory is not read. No two data paths may resolve to the same location; the check is skipped while json.enabled is false.

Background

A compaction writes its output before it deletes its inputs, so the volume needs free room of at least the size of the inputs of the next compaction; the server holds a compaction back and logs Compaction of level … waits for disk space while it has less. Plan the volume with headroom beyond the data size, and see resources.disk_free_warn for the thresholds at which the server warns and refuses writes.

Referenced on
Write path · systemd and Docker · Limits and defaults

bulk_body_limit_bytes json.bulk_body_limit_bytes

Since v0.1.0
Largest request body of POST /store-api/json/{domain}/bulk in bytes. The whole request body is held in memory.
Default
67108864
Effect

The largest request body, in bytes, that POST /store-api/json/{domain}/bulk accepts. The body is NDJSON, one document per line, and it is read into memory as a whole before the first line is parsed. A larger body is refused with 413 and Failed to buffer the request body: length limit exceeded; in a test of v0.5.1 with the key set to 3000, a body of 4,190 bytes was refused and one of 830 bytes imported 20 documents. The value must be at least 1; 0 refuses the start with invalid config: json.bulk_body_limit_bytes (0) must be at least 1. The check runs even while json.enabled is false.

Every other JSON endpoint takes bodies of up to 2 MiB; the key does not change that. In a test with json.lsm.max_value_size raised to 8 MiB, a PUT of a document of 3 MB was still refused with the same 413. A document larger than 2 MiB, which also needs json.lsm.max_value_size above that size, can therefore enter only through the bulk endpoint, as one line of the body.

The bulk body is imported in batches of 100 documents; a document that fails (for example because it is too large or its key is invalid) is listed under errors in the 200 answer and does not stop the others.

Background

The limit exists because the body is held in memory while it is parsed and imported, so several concurrent imports add up. With the default of 64 MiB, an NDJSON export (GET /store-api/json/{domain}/export) of up to that size can be imported again in one request. Lower the value on a small host; for larger files, split them into several requests or raise it.

Referenced on
Consistency and conditional writes · Limits and defaults · Status codes · JSON documents

max_key_length json.lsm.max_key_length

Since v0.1.0
Longest stored key in bytes; document keys get this minus an internal prefix, at most 256 bytes.
Default
256
Effect

The limit, in bytes, for the keys of the JSON engine as the engine stores them. A document is stored under its key with a prefix of 21 bytes in front of it (DOC:, 16 hexadecimal digits that identify the domain, and :). The longest key a client may give a document is therefore this value minus 21, but never more than 256; with the default of 256 it is 235 characters. Document keys may contain only a-z, A-Z, 0-9, ., _ and -. In a test of v0.5.1 with the key set to 100, a document key of 80 characters was refused with 400 and invalid document key: key length 80 exceeds maximum of 79 characters, and one of 79 characters was accepted. A bulk import lists such a key under errors instead.

The index entries of a document are stored under longer keys: IDX:, the domain prefix, the field name, the indexed value and the document key. An entry whose key would exceed this limit is not written, and the server does not report it. The document is stored, but a search on that value does not find it. In the test with the limit 100, a document with an 80-character city value was stored and returned by GET, and a search for that value returned "total":0, while a document with the value Essen was found.

The value must be at least 69, the longest management key of the engine, and must not exceed 100663296 bytes (96 MiB), the largest field that the recovery of the write-ahead log reads. The start is refused with invalid config: json.lsm.max_key_length (10) must be at least 69, or with invalid config: json.lsm.max_key_length (…) exceeds the WAL recovery field cap (100663296 bytes) — writes this large could never be recovered. The check runs even while json.enabled is false.

Background

A key is stored in every MemTable entry, every write-ahead log record and every SSTable entry, so the length of the keys is paid for in memory and on disk. Keep the limit as small as your keys allow. Raise it if you index long string values and want every document to be found by them, or if you need document keys longer than 235 characters. For the key-value engine and the relational engine see lsm.max_key_length and rel.lsm.max_key_length.

Referenced on
Limits and defaults · JSON documents

max_value_size json.lsm.max_value_size

Since v0.1.0
Largest stored document in bytes.
Default
524288
Effect

The largest stored document of the JSON engine, in bytes. The size counts the document as it is stored, which is a little larger than the JSON that the client sent because a version number and an incarnation id are stored with it. A write of a larger document is refused with 413 and payload of 4166 bytes exceeds maximum of 4096 bytes; in a test of v0.5.1 with the key set to 4096, a document whose content was 4,108 bytes was stored as 4,166. A bulk import does not fail as a whole: it lists such a document under errors in its 200 answer, with the same message.

The limit applies on top of the size of the request. Single-document endpoints take bodies of up to 2 MiB (see json.bulk_body_limit_bytes), so with a larger value here a document above 2 MiB can still enter only through the bulk endpoint. In a test with the value 8 MiB, a PUT of a 3 MB document was refused with 413 and Failed to buffer the request body: length limit exceeded.

The value must be at least 4096 and must not exceed 100663296 bytes (96 MiB), the largest field that the recovery of the write-ahead log reads. The start is refused with invalid config: json.lsm.max_value_size (100) must be at least 4096 or invalid config: json.lsm.max_value_size (…) exceeds the WAL recovery field cap (100663296 bytes) — writes this large could never be recovered. The check runs even while json.enabled is false.

Background

A document is written, read and compacted as one value, so the limit bounds the size of every single write and read of the engine. Documents of 1,024 bytes or more are stored in the value log (json.vlog_path) and the SSTables keep a pointer. Raise the limit only for documents that are large by nature, and remember that a single write cannot exceed the request limit of 2 MiB unless it goes through the bulk endpoint.

Referenced on
Limits and defaults · JSON documents

enabled rel.enabled

Since v0.1.0
Starts the relational engine.
Default
true
Effect

With true, the server runs the relational engine (LuraSQL): it opens its write-ahead log, value log and SSTables (rel.wal_path, rel.vlog_path, rel.sstable_dir), creates a relational domain named default at start, and serves the /store-api/rel/… endpoints. The engine runs on a storage instance of its own, so SQL work does not share MemTables, caches or compactions with the other engines.

With false, no relational instance starts and the log says Relational engine disabled by config.. The whole relational REST surface is then absent: in a test of v0.5.1, GET /store-api/rel/domains and POST /store-api/rel/default/sql were answered with an empty 404, although the OpenAPI document lists 503 for the disabled engine on these endpoints. No relational files were created. The other engines keep working, with these exceptions:

  • A backup of scope rel or rel:<domain> fails with rel_engine_disabled; scope all leaves relational data out.
  • The tool list_domains of the MCP server names the engine under unavailable_engines.

Data already written stays in the files. While the engine is off, the paths of its files are not checked against the other data paths, but its limits (rel.max_tables_per_domain, rel.max_row_size, rel.max_limit, rel.max_sort_rows and rel.lsm.max_key_length) are still validated at start.

Background

Switch the engine off if the installation runs no SQL. It saves what the engine reserves: two MemTables of 4 MiB per shard, a block cache of 64 MiB (divided across the shards), and one write-ahead log that the storage thread has to write. The reservation is part of the fixed memory that the server compares with resources.memory_budget.

Referenced on
Domains and engines · Troubleshooting · LuraSQL

wal_path rel.wal_path

Since v0.1.0
Write-ahead log; relative paths resolve against the working directory. A directory of segment files the server creates, writes and deletes; never change anything in it by hand. The path may be a symlink, the segments then lie at its target. A log file of an earlier LuraDB version at the path becomes such a directory on the first start; an earlier version does not start on it.
Default
"luradb_rel.wal"
Effect

The directory of the write-ahead log of the relational engine. It holds segment files of 64 MiB each, named 00000001.wal, 00000002.wal and so on, which the server creates, writes and deletes; nothing in it should be changed by hand. All data shards of the relational engine share this one log, so unlike the value log and the SSTables it is not placed in a shard_<k> directory. A log file of an earlier LuraDB version at the path becomes such a directory on the first start, and an earlier version does not start on it.

The path may be a symbolic link, and the segments then lie at its target. A relative path resolves against the working directory of the server process; the packaged service sets that to /var/lib/luradb, so the packaged luradb_rel.wal is /var/lib/luradb/luradb_rel.wal. The parent directory is created at start.

No two data paths may resolve to the same location (symbolic links resolved); the start is refused with invalid config: … resolve to the same location '…' otherwise. The check is skipped while rel.enabled is false. How much of the log the server keeps is set by storage.wal_max_size, which caps each engine’s log.

Background

A DDL or DML statement is confirmed once its changes are in this log, and recovery after a crash replays what the SSTables do not yet hold. The statement waits for the log to reach the disk, so the latency of the volume sets the lower bound for the latency of writes. The logs of the other engines are storage.wal_path and json.wal_path. Place the log on a volume with room for storage.wal_max_size plus one segment.

Referenced on
Write path · systemd and Docker · Limits and defaults

vlog_path rel.vlog_path

Since v0.1.0
Value log for large rows.
Default
"luradb_rel.vlog"
Effect

The value log of the relational engine. An encoded row of 1,024 bytes or more is appended to it, and the SSTables keep only a pointer to it. The threshold is built in: the relational engine does not use lsm.vlog_inline_threshold. Each data shard has its own file: the server inserts a directory shard_<k> before the file name, so a path /var/lib/luradb/luradb_rel.vlog becomes /var/lib/luradb/shard_0/luradb_rel.vlog for shard 0. A relative path resolves against the working directory of the server process (under the packaged service, /var/lib/luradb).

When the value log is cleaned, the live rows move into a new generation whose file name is the configured name with .2 appended, then .3, and so on. No other data path may equal this name followed by a dot and digits; the start is refused if one does. The cleanup runs with the built-in settings of the janitor, not with the janitor section of the file, which applies to the key-value engine.

No two data paths may resolve to the same location. The check is skipped while rel.enabled is false. The key-value engine and the JSON engine have their own value logs, storage.vlog_path and json.vlog_path.

Background

Keeping large rows out of the SSTables means that a compaction rewrites keys and pointers, not the rows. The price is that overwritten and deleted rows stay in the log as dead space until the cleanup rewrites it, so the volume needs room for the data plus the dead space that builds up between cleanups. The cleanup waits while the volume has less room than the live values take.

Referenced on
Write path · systemd and Docker · Limits and defaults

sstable_dir rel.sstable_dir

Since v0.1.0
SSTable directory.
Default
"luradb_rel_sstables"
Effect

The directory of the SSTables of the relational engine. Each data shard keeps its files in a subdirectory shard_<k> of it, with the files sstable_<number>.sst and a MANIFEST that lists them and their levels. The server creates the directory at start. A relative path resolves against the working directory of the server process (under the packaged service, /var/lib/luradb).

The SSTables hold the rows, the index entries and the catalog of tables and views. A flush of the MemTable of the relational engine adds a file at level 0, and compactions merge files into the levels below. The relational engine runs these with the built-in defaults (a flush at a MemTable of 4 MiB, a compaction at four level-0 files, level 1 at 100 MiB, a factor of 10 between levels, SSTables of at most 64 MiB), not with the lsm and compaction sections of the file, which apply to the key-value engine only. Rows of 1,024 bytes or more are not in the SSTables but in the value log (rel.vlog_path).

The server does not move data when the path changes. A start with another path opens an empty set of SSTables there, and the data in the old directory is not read. No two data paths may resolve to the same location; the check is skipped while rel.enabled is false.

Background

A compaction writes its output before it deletes its inputs, so the volume needs free room of at least the size of the inputs of the next compaction; the server holds a compaction back and logs Compaction of level … waits for disk space while it has less. Plan the volume with headroom beyond the data size, and see resources.disk_free_warn for the thresholds at which the server warns and refuses writes.

Referenced on
Write path · systemd and Docker · Limits and defaults

max_tables_per_domain rel.max_tables_per_domain

Since v0.1.0
Most tables per relational domain. Catalog entries are held in memory.
Default
256
Effect

The most tables and views that one relational domain may hold. Both count: creating a table or a view in a domain that has reached the limit is refused with 400 and limit exceeded: max_tables_per_domain (max 2), which is the answer in a test of v0.5.1 with the key set to 2. Dropping a table or a view makes room again. The limit is checked when the object is created; lowering the key does not remove tables or views that already exist.

The value must be between 1 and 4294967295; anything else refuses the start with invalid config: rel.max_tables_per_domain (0) must be between 1 and 4294967295. The check runs even while rel.enabled is false.

Two other limits of a table are fixed: 128 columns and 16 indexes per table.

Background

The server keeps the catalog, the definitions of all tables and views of all domains, in memory. Changing a table (dropping it, dropping or renaming a column, renaming it) makes the server bind again every view of the domain to see which ones depend on it, so the number of objects also bounds that work. Raise the limit only for a domain that creates tables dynamically, for example one table per customer, and then watch the memory and the time of such statements.

Referenced on
Limits and defaults · LuraSQL

max_row_size rel.max_row_size

Since v0.1.0
Largest encoded row in bytes; the storage accepts values up to this or 512 KiB, whichever is larger.
Default
524288
Effect

The largest encoded row, in bytes, that an INSERT or an UPDATE may write. The size is that of the row in its stored form, not the length of the SQL text. A statement that would write a larger row is refused with 400 and row size 332 exceeds maximum of 200 bytes; in a test of v0.5.1 with the key set to 200, a row with one TEXT value of 300 characters was encoded to 332 bytes. The same limit applies to the row endpoints and to rows imported from a file, where it is reported per row.

The value must be at least 5 and must not exceed 100663296 bytes (96 MiB), the largest field that the recovery of the write-ahead log reads. The start is refused with invalid config: rel.max_row_size (1) must be at least 5 or invalid config: rel.max_row_size (…) exceeds the WAL recovery field cap (100663296 bytes) — writes this large could never be recovered. The check runs even while rel.enabled is false.

The engine’s own value limit is the larger of 512 KiB and this key, because the catalog needs its room. A smaller value here therefore limits the rows but not the catalog, and a larger one raises the limit of the engine to the same size. Fixed limits apply as well: a TEXT value, including KVREF and JSONREF, is at most 64 KiB (text length N exceeds maximum of 65536 bytes), and a SQL statement is at most 64 KiB. A row of the default size of 512 KiB therefore needs several large values.

Background

A row is stored, read and compacted as one value, so the limit bounds the size of every single write into a table. Rows of 1,024 bytes or more go to the value log (rel.vlog_path). Large objects can live in a JSON document or a key-value entry and be linked from the row (JSONREF, KVREF), which keeps the rows small. Raise the limit only for rows that are large by nature.

Referenced on
Limits and defaults · LuraSQL

max_limit rel.max_limit

Since v0.1.0
Most rows a query returns; a larger LIMIT is reduced to it. All result rows up to this limit are held in memory.
Default
10000
Effect

The most rows that one SELECT returns. A LIMIT above the value is reduced to it, and a statement without LIMIT returns at most 1,000 rows or this value, whichever is smaller. When the cap cut the result, the answer says so with "limit_applied": true. In a test of v0.5.1 with the key set to 5 and ten rows in the table, SELECT id FROM t LIMIT 100, SELECT id FROM t and the row endpoint GET …/rows?limit=100 each returned five rows with limit_applied true, and LIMIT 3 returned three. COUNT(*) is not affected. A negative LIMIT or OFFSET is refused with 400.

The same cap applies to the browse endpoint of a table (GET /store-api/rel/{domain}/tables/{table}/rows). A response whose serialized size is above 32 MiB is refused with 413 Payload Too Large: response exceeds max_response_bytes; reduce LIMIT, whatever this key says.

All rows of a result are collected in memory before the answer is sent, so the memory per statement grows with the number of rows and their size. The value must be at least 1; 0 refuses the start with invalid config: rel.max_limit (0) must be at least 1. The check runs even while rel.enabled is false.

Background

The cap keeps one careless SELECT from loading a whole large table into memory and into a single response. Clients that need more rows page through the result with LIMIT and OFFSET; an ORDER BY that needs a sort buffer is bound by rel.max_sort_rows before the limit applies. Raise the cap if your clients read larger result sets in one request and the host has the memory; lower it to protect a small host.

Referenced on
Limits and defaults · LuraSQL · REST API

max_sort_rows rel.max_sort_rows

Since v0.1.0
Most rows an ORDER BY sorts or an unindexed join scans; more fail with 400. Rows up to this limit are held in memory while sorting.
Default
100000
Effect

The most rows that the server collects in memory to sort a result, and the most rows that an unindexed join (rel.allow_unindexed_joins) may scan in one statement.

An ORDER BY needs the sort buffer unless the order is free. It is free when a single column is sorted and the chosen access path already delivers the rows in that order: the primary key, or a NOT NULL column that has an index. Otherwise the server reads all rows that pass the WHERE clause into memory, sorts them, and only then applies OFFSET and LIMIT. If more rows than the limit pass, the statement fails with 400 and sort buffer of 4 rows exceeds maximum of 3; narrow the result with WHERE/LIMIT or add a matching index, which is the answer in a test of v0.5.1 with the key set to 3 and ten rows. A LIMIT does not help, because the sort comes first; a WHERE clause that matches fewer rows, or an index on the sorted column, does.

For an unindexed join the key bounds the rows scanned in the right-hand table, counted over the whole statement: unindexed join fallback scan visited 5 rows, exceeding the limit of 4; add an index or narrow the result.

The value must be at least 1; 0 refuses the start with invalid config: rel.max_sort_rows (0) must be at least 1. The check runs even while rel.enabled is false.

Background

The server sorts in memory and does not spill to disk, so the sort buffer is the memory cost of an unindexed ORDER BY: the rows are held decoded, and a wide row costs more than a narrow one. The cap turns a sort that would not fit into an error at a known size instead of a slow or failed server. Prefer an index on the columns you sort by; raise the cap only if you sort larger results regularly and the host has the memory for them.

Referenced on
Limits and defaults · LuraSQL

allow_unindexed_joins rel.allow_unindexed_joins

Since v0.1.0
Lets a join on an unindexed column scan the table per row instead of failing with 400.
Default
false
Effect

Decides what a LEFT JOIN does when the column of the joined table in its ON clause is neither the primary key nor indexed.

With false, such a join is refused with 400 and a hint with the statement that fixes it, such as join column 'u.tid' is neither the primary key nor indexed; CREATE INDEX <name> ON u (tid). A join on a primary key or an indexed column looks up the matching rows directly. The check also applies when a view that contains such a join is created.

With true, the server instead scans the joined table once for each row of the left side. The scan is bounded: all rows visited by such scans in one statement count together against rel.max_sort_rows, and a statement that exceeds it fails with 400 and unindexed join fallback scan visited 5 rows, exceeding the limit of 4; add an index or narrow the result. In a test of v0.5.1 with a left table of three rows and a right table of three rows, the join succeeded with the default cap and failed with the key set to 4.

Background

An unindexed join costs about the number of left rows times the number of right rows, so it is fast on tiny tables and slow on large ones. The default refuses it so that a missing index shows up as an error you can fix instead of as a statement that gets slower as the data grows. Switch it on for development or for tables that stay small, and create an index for anything else.

Referenced on
LuraSQL

import_body_limit_bytes rel.import_body_limit_bytes

Since v0.4.0
Body-size cap for POST .../tables/from-file.
Default
67108864
Effect

The largest request body, in bytes, that POST /store-api/rel/{domain}/tables/from-file accepts. The endpoint takes a CSV or TSV file and creates a table from it; the file is read into memory as a whole before the table is created. A larger body is refused with 413 and Failed to buffer the request body: length limit exceeded; in a test of v0.5.1 with the key set to 100, a file of 169 bytes was refused and one of 20 bytes created a table with four rows. The other relational endpoints take bodies of up to 2 MiB; the key does not change that.

The import counts as one write of the rate limit of the domain (rate_limit.default_write_iops). Each row of the file then goes through the normal write path, so rel.max_row_size and rel.lsm.max_key_length apply to it; a row that fails is listed under errors in the 201 answer and does not stop the import. The server does not check the value at start.

Background

The limit exists because the body is held in memory while the file is parsed and imported, so several concurrent imports add up. The default of 64 MiB equals the limit for JSON bulk imports (json.bulk_body_limit_bytes). Lower it on a small host; for larger files, split them into several requests or raise the value.

Referenced on
Consistency and conditional writes · Limits and defaults · Status codes · LuraSQL

max_key_length rel.lsm.max_key_length

Since v0.1.0
Longest stored key in bytes; it bounds primary-key and index values.
Default
256
Effect

The limit, in bytes, for the keys of the relational engine as the engine stores them. The engine stores a row under a key made of the prefix ROW:, an identifier of the domain, an identifier of the table and the encoded primary key, and an index entry under a key made of the prefix IDX:, the same identifiers, the indexed value and the primary key. A statement that would write a longer key is refused with 400 and key length 108 exceeds maximum of 100 bytes. In a test of v0.5.1 with the key set to 100, the primary key of the row was an ASCII TEXT value of 80 characters and the stored key was 108 bytes: the key was 28 bytes longer than the value. With the default of 256, an ASCII TEXT primary key of 228 characters was accepted and one of 229 characters was refused.

An index lengthens the keys further, since each entry holds the indexed value and the primary key. CREATE INDEX on a table whose existing rows would make a key too long fails with the same message.

The value must be at least 73, the longest management key of the engine, and must not exceed 100663296 bytes (96 MiB), the largest field that the recovery of the write-ahead log reads. The start is refused with invalid config: rel.lsm.max_key_length (10) must be at least 73 or invalid config: rel.lsm.max_key_length (…) exceeds the WAL recovery field cap (100663296 bytes) — writes this large could never be recovered. The check runs even while rel.enabled is false.

Background

A key is stored in every MemTable entry, every write-ahead log record and every SSTable entry, so the length of the keys is paid for in memory and on disk. Keep the limit as small as your keys allow, and raise it if you use long TEXT columns as a primary key or an index. For the key-value engine and the JSON engine see lsm.max_key_length and json.lsm.max_key_length.

Referenced on
Limits and defaults · LuraSQL

channel_capacity events.channel_capacity

Since v0.4.0
Events buffered per consumer; one this far behind gets reset (reason: lagged).
Default
256
Effect

The number of events that the server buffers for each client of the event stream GET /store-api/events. The stream is for admins and carries lifecycle and DDL events of all three engines: domains created, deleted and purged, relational table, view and index changes, and JSON index changes. It carries no data changes; the watch of a key-value domain (lsm.wal_event_channel_capacity) does that. Each event is a JSON object with engine, type, domain, ts and, for tables, views and indexes, object.

A client that falls further behind than this number of events loses the events it did not read. It receives event: reset with the data {"reason":"lagged"} instead, and its id is the current head of the stream, so it can read the state again and continue from there. Writers are never held back by a slow client. With the default of 256, a client has to miss 256 DDL events before that happens.

The value must be at least 1. With 0, the start fails: the control-plane thread panics and the server exits with Error: the control-plane thread stopped during startup. The key does not change the size of the ring that serves reconnects, which is events.replay_buffer_size.

Background

Lifecycle and DDL events are rare compared with data writes, so a small buffer is normally enough. A burst can still produce many at once, for example when a backup is restored into many domains or a script creates hundreds of tables. Raise the value if clients regularly receive reset with the reason lagged during such bursts. The cost is memory for the buffered events, which hold names but no data.

Referenced on
Monitoring · Limits and defaults

replay_buffer_size events.replay_buffer_size

Since v0.4.0
Ring for Last-Event-ID resume; 0 = resume off.
Default
1024
Effect

The number of the latest events of the stream GET /store-api/events that the server keeps so that a client can reconnect without a gap. Every event carries an id: of the form g-<boot id>-<sequence>. A client that reconnects with the id of the last event it saw, in the Last-Event-ID header or in ?last_event_id= (the header wins), receives the events it missed and then the live events. In a test of v0.5.1 with three domains created and a ring of 2, a reconnect with the id of the first event received the second and third event, a reconnect with the id of the second received the third, and a reconnect with the id of the third received nothing until the next event.

When the ring cannot serve the id, the stream starts with event: reset and a reason: window_exceeded if the id is older than the oldest event in the ring, restart if the server has been restarted since the id was issued (the boot id differs), and unknown_id for an id that is not a valid id of this stream, such as xyz, or one from the future. After a reset the client should read the state again and then continue from the id in the reset event.

With 0 the ring keeps nothing: every reconnect with an id receives reset, but events still carry ids. The ring lives in memory and is lost at a restart. It grows with the number of stored events up to the value; an event holds names, not data.

Background

A stream is a long-lived connection, and clients such as a browser’s EventSource reconnect on their own and send the last id. The ring lets them continue without reading the state again after a short interruption. Lifecycle and DDL events are rare, so the default of 1,024 events covers long interruptions in most installations. Set 0 if every client reads the state again after a reconnect anyway. The watch of a key-value domain has a ring of its own (lsm.watch_replay_buffer_size).

Referenced on
Monitoring · Limits and defaults

level log.level

Since v0.1.0
Level: verbose, info or prod (warnings and errors only); RUST_LOG overrides it.
Default
"info"
Effect

The level of the messages that the server writes to its log: verbose, info or prod. prod writes warnings and errors only, info adds the normal operating messages (start, stop, shutdown, configuration, backup jobs), and verbose adds debugging messages. Internally the three values are the levels warn, info and debug; there is no trace level. Any other value refuses the start with a parse error of the configuration file that names the valid values.

The level applies to the messages of LuraDB itself. Messages of the libraries it uses are not logged at any level. The level is the default of every module; log.modules.auth, log.modules.api, log.modules.engine and log.modules.storage can set their own. The environment variable RUST_LOG, if set, replaces the whole setting including the modules; the server then uses its value as the filter directive.

Not every message goes through the level. The flush, compaction and value-log cleanup messages of the storage engines (lines that begin with [Engine] or [Janitor]) and a few error messages of the shutdown are written to standard error as they are, whatever the level, the format and log.path say. In a test of v0.5.1 with prod, a normal start wrote no line at all.

Background

At info a running server writes little: a start, a stop and the events of the background jobs. That is the level to keep in production. prod leaves the log empty for a healthy server, which makes every line a reason to look; use it where only problems matter. verbose adds, among others, one line for each request of the REST API with the client address (see proxy.trusted_proxies); turn it on for a module or for a short time to find a problem, not as a permanent setting.

Referenced on
Monitoring · Security · Troubleshooting

format log.format

Since v0.1.0
Line format: text or json.
Default
"text"
Effect

The line format of the log: text or json. Any other value refuses the start with a parse error of the configuration file.

In the text format a line is the time (UTC, with microseconds), the level, the module and the message, for example 2026-11-03T20:13:33.550752Z INFO luradb: Starting LuraDB.... The lines carry ANSI colour escape sequences, also in log files and also when the output is not a terminal: in a test of v0.5.1, 16 lines of a log file contained them, and GET /store-api/logs returned them inside the strings of lines.

In the json format each line is one JSON object, for example {"timestamp":"2026-11-03T20:13:35.718804Z","level":"INFO","fields":{"message":"Starting LuraDB..."},"target":"luradb"}, without escape sequences. The extra fields that a message carries, such as admin and backup_id in the audit lines of the backup API, are written into fields next to message.

The format also tells GET /store-api/logs how the lines of a file are built: the answer repeats it in format, and the client has to parse the lines itself (log.http_access). Messages that the server writes straight to standard error (see log.level) are always plain text.

Background

Use json when a log shipper or a log database reads the log: the fields are separate and there are no colour codes to strip. Keep text for reading the log by eye.

Referenced on
Monitoring

path log.path

Since v0.1.0
Directory for log files, created if missing. Not set: stdout only, which systemd hands to journald (journalctl -u luradb).
Default
not set
Effect

The directory for the log files. Without the key, or with an empty value, the log goes to standard output, which systemd hands to the journal (journalctl -u luradb). With a path, the server writes its log to files in that directory instead and writes nothing to standard output: in a test of v0.5.1, the process output stayed empty. The directory is created at start, with its parents. The file is named luradb.log with a suffix that depends on log.rotation. A relative path resolves against the working directory of the server process (under the packaged service, /var/lib/luradb).

If the directory cannot be created, the server does not start: in a test with a path under /proc, it exited with code 101 after a panic message that starts with initializing rolling file appender failed … failed to create log directory. The path must not resolve to the same location as another data path; the start is refused with invalid config: … resolve to the same location '…' otherwise.

Some lines never reach the file. The messages of the engines that begin with [Engine] or [Janitor], the line that names the reason of a refused start (Error: …) and the configuration errors that are found before the log is set up go to standard error (see log.level); under systemd they are in the journal.

The files are cleaned up by log.retention_days, and log.http_access serves them over the REST API.

Background

Under systemd the journal needs no setting of LuraDB: log.rotation and log.retention_days apply to files only. Use a path when you want plain files, for example for a log shipper that tails them or for GET /store-api/logs, or when the server does not run under systemd. The service account needs write access to the directory.

Referenced on
Resource guard · Monitoring · systemd and Docker · Troubleshooting · Limits and defaults

rotation log.rotation

Since v0.1.0
Rotation of the log files: none, daily or hourly; needs a log path.
Default
"daily"
Effect

When the server starts a new log file. daily starts one at midnight UTC, hourly at the full hour UTC. The files are named luradb.log.<date> and luradb.log.<date>-<hour>; in a test of v0.5.1 at 20:13 UTC, daily wrote luradb.log.2026-11-03 and hourly wrote luradb.log.2026-11-03-20. Old files are not touched by the rotation; log.retention_days deletes them.

Every other value, including none, writes one file luradb.log that grows without bound. The value is not checked: in a test, weekly started the server and wrote luradb.log, so a misspelled value silently turns the rotation off. With log.path not set, the key has no effect.

The file for the current period is chosen when the server writes its first line of that period, and the server appends to a file that already exists, so a restart within the period continues the same file.

Background

Daily rotation suits most installations: a file is one day of log, and removing old days is a matter of log.retention_days. Choose hourly when a day of log is too large to handle, for example on a server with log.level set to verbose. Choose none only if an external tool rotates the file.

Referenced on
Monitoring

retention_days log.retention_days

Since v0.1.0
Days to keep rotated log files; 0 keeps them forever.
Default
30
Effect

The age, in days, after which the server deletes a log file. A background task looks at the log directory (log.path) and deletes every file whose name begins with luradb.log and whose last modification is more than this many days ago. Other files in the directory are left alone. With 0 nothing is deleted.

The task runs only when a log path is set and the value is above 0. It does not run at start: its first pass comes 24 hours after the process started, and then it repeats every 24 hours of running time. A server that is restarted more often than once a day therefore never deletes anything. The age is the modification time, not the date in the file name, so the file of the current period is never old enough while the server writes to it; with log.rotation set to none the single file is never deleted.

The key has no effect when the log goes to standard output.

Background

Rotated files accumulate unless something deletes them. The task deletes by age only, with no regard to the number or the size of the files, so choose the age from the amount of log that your log.level and rotation produce and from the space of the volume. Lower the value on a small volume, raise it if you have to keep logs longer, and use 0 if an external tool manages the files. A server that is restarted daily needs such a tool, because the first pass comes only after a day of running time.

Referenced on
Monitoring

http_access log.http_access

Since v0.3.0
Serves GET /store-api/logs and /store-api/logs/files; needs a log path.
Default
false
Effect

With true, two admin-only endpoints expose the log files over the REST API. GET /store-api/logs/files lists the files luradb.log* of log.path, newest first, with their size and modification time. GET /store-api/logs returns the last lines of one file, oldest line first, as {"file", "format", "lines", "truncated"}: the parameter lines sets how many (default 100, at most 1,000; 0 is refused with 400), q keeps only lines that contain the text (case-sensitive; only matching lines count toward lines), and file names a file from the list (default: the newest). A file name must begin with luradb.log and contain no path separator, and only regular files are read, so a link is not followed. The server scans at most 4 MiB from the end of the file; if that was not enough for the lines asked for, truncated is true. A non-admin key gets 403.

With false both endpoints answer 503 Service Unavailable: log access is disabled (log.http_access = false). With true and no log.path, the start is refused with invalid config: log.http_access requires log.path, since the standard output cannot be read back. With auth.enabled off, the start logs a warning that any client that reaches the port can read the log files.

The lines are returned as they are in the file. In the text format (log.format) they contain colour escape sequences.

Background

The endpoints let a monitoring tool or an operator read the recent log without a login on the host, for example from a browser or from a script. They give access to everything the server logs, including the names of domains, admins and backups, which is why they are admin-only and off by default. They are a tail, not a log search: a request returns at most 1,000 lines from the last 4 MiB of one file. For more, read the files or use a log shipper.

Referenced on
Monitoring

auth log.modules.auth

Since v0.1.0
Level of the authentication module.
Default
not set
Effect

The log level of the authentication module, luradb::auth, in place of log.level. The values are the same as for the global level: verbose, info or prod. Without the key the module follows the global level. Any other value refuses the start with a parse error of the configuration file.

The module covers accounts, API keys, permissions and the OAuth records of the MCP server. In v0.5.1 it writes only three messages, all about the admin accounts of the configuration file at start: Auth: admin '<name>' reconciled and Auth: removed stale admin '<name>' at info, and Auth is enabled but no admins are configured in luradb.toml at warn. A rejected key (401) or a missing permission (403) is not logged by this module. The sign-in page of the MCP server belongs to the module luradb::mcp, which has no setting of its own and follows log.level.

The environment variable RUST_LOG, if set, replaces all of these settings.

Background

Set the key to quieten or to widen this module alone. In v0.5.1 there is little to widen, so the key matters only to keep the three start-up lines out of the log (prod) while the rest of the server logs at info.

Referenced on
Monitoring

api log.modules.api

Since v0.1.0
Level of the REST API.
Default
not set
Effect

The log level of the REST API, luradb::api, in place of log.level. The values are the same as for the global level: verbose, info or prod. Without the key the module follows the global level. Any other value refuses the start with a parse error of the configuration file.

The module covers the handlers of the REST endpoints and the layer that resolves the client address. In v0.5.1 it writes two kinds of messages. At verbose (debug) every request of the REST API produces one line, request client_ip=<address> peer_ip=<address>, which shows the address the server uses for the client and the address of the connection; with the key set to verbose and proxy.trusted_proxies set, it is how you check what a proxy passes on. At info, the backup endpoints write an audit line for each action with the admin’s name and the backup id (backup created admin=admin backup_id=bk_…), and at error an internal error of the backup API with its cause.

Requests that fail with a status code are not logged here. The environment variable RUST_LOG, if set, replaces all of these settings.

Background

Use verbose on this module to see the client addresses of requests, for example when you set up a reverse proxy, without turning on debugging for the whole server. Leave the module at info if you want the audit lines of the backup API in the log; prod suppresses them.

Referenced on
Monitoring

engine log.modules.engine

Since v0.1.0
Level of the storage engines, including the domain registry.
Default
not set
Effect

The log level of the storage engines, luradb::engines, in place of log.level. The values are the same as for the global level: verbose, info or prod. Without the key the module follows the global level. Any other value refuses the start with a parse error of the configuration file.

The module covers the key-value engine with its domain registry, the JSON engine and the relational engine. What it writes in v0.5.1 are the messages of the background jobs and of the recovery of metadata, for example [JsonDomainPurger] domain '…' fully purged and [RelDomainPurger] domain '…' fully purged at info; warnings such as Compaction of level 0 waits for disk space: …, Value log cleanup waits for disk space: …, [reindex] skipping corrupt document '…', [bulk_export] unparseable doc key … and [RelCatalog] cannot deserialize catalog entry …; and debugging lines of the sweeper of expired keys at debug.

Many messages of the engines do not belong to the module even though they come from the same code: the flush, compaction and value-log cleanup messages that begin with [Engine] or [Janitor] are written to standard error without a level (see log.level). The environment variable RUST_LOG, if set, replaces all of these settings.

Background

The engines log about their background jobs and about the recovery of their metadata. Set this key to make only the engines verbose while everything else stays at info, or to keep their warnings about disk space and corrupt data at info or prod while the global level is verbose for another reason. The packaged file shows engine = "prod" as a commented example.

Referenced on
Monitoring

storage log.modules.storage

Since v0.1.0
Level of the file layer (SSTables, value log, manifest).
Default
not set
Effect

The log level of the file layer, luradb::storage, in place of log.level. The values are the same as for the global level: verbose, info or prod. Without the key the module follows the global level. Any other value refuses the start with a parse error of the configuration file.

The module covers the file layer: the SSTable files, the manifest that lists them, the value log and the file manager. In v0.5.1 it writes a handful of messages: Removed N temporary files left in … at info when a start finds temporary files in an SSTable directory and removes them, cannot delete … at warn when a replaced file could not be removed, debugging lines about the manifest and about madvise at debug, and the error line data sync failed on <path>: …; stopping so the next start recovers from the write-ahead log (or data write failed …) that the server writes before it stops with exit code 3 because the disk failed to make data durable. That last line is an error, so the module’s level prod still lets it through.

The write-ahead log and the storage thread belong to luradb::core, which has no setting of its own and follows log.level. The environment variable RUST_LOG, if set, replaces all of these settings.

Background

The file layer is quiet unless something goes wrong with the files. Set the key to verbose to see the manifest updates when you look into the lifecycle of SSTables, or leave it at prod (the packaged file shows storage = "prod" as a commented example) to keep the module’s messages down to warnings and errors while another module is verbose.

Referenced on
Monitoring

trusted_proxies proxy.trusted_proxies

Since v0.1.0
Reverse proxies (IPs or CIDR ranges) whose X-Real-IP and X-Forwarded-For headers are trusted.
Default
[] (direct access, headers ignored)Packaged value ["127.0.0.1", "::1"]
Effect

A list of IP addresses or CIDR ranges ("203.0.113.7", "10.0.0.0/8", "2001:db8::/32") of reverse proxies whose headers the server believes. When a TCP connection comes from an address in the list, the server takes the address of the client from the request headers; for every other connection it uses the address of the connection itself and ignores the headers. The list is empty by default in the code, so the headers are ignored; the packaged file lists 127.0.0.1 and ::1. A value that does not parse refuses the start, late in the start sequence, with Invalid trusted_proxies config: invalid IP address syntax.

The client address is resolved like this: the header X-Real-IP wins if it holds a valid address. Otherwise the server reads X-Forwarded-For from the right and takes the first address that is not private. Private are the loopback addresses, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16, fc00::/7 and fe80::/10. If nothing yields an address, the address of the connection stays. In a test of v0.5.1 with 127.0.0.1 listed, X-Real-IP: 203.0.113.9 gave the client 203.0.113.9; X-Forwarded-For: 203.0.113.50, 198.51.100.7, 10.1.2.3 gave 198.51.100.7; a list of private addresses only gave 127.0.0.1.

In v0.5.1 the resolved address is used in two places. It is shown in the debug line of the REST API (see log.modules.api), and it is the key of the two limits of the sign-in of the MCP server (mcp.oauth_enabled): five failed sign-ins within 15 minutes and ten client registrations within an hour per sender, where an IPv6 sender counts by its /64 prefix. The list grants no access and does not change the rate limits of the domains (rate_limit.default_read_iops), which count per domain. In the same test with 127.0.0.1 not listed, a sender that sent five wrong keys blocked a second sender with a different X-Real-IP; with it listed, the second sender was not blocked.

Background

Behind a reverse proxy every connection arrives from the proxy, so without this list all clients share one address: five wrong keys from anyone lock the OAuth sign-in for everyone for 15 minutes. List the addresses of your proxies, and only those. A client address in the list would let that client choose the address the server believes. Leave the list empty for a server that clients reach directly.

Referenced on
Security · Troubleshooting · MCP server

enabled cors.enabled

Since v0.4.0
Cross-origin access for browser clients. Off by default.
Default
false
Effect

With true, the server answers cross-origin requests of browsers on the REST API according to cors.allowed_origins. It adds a CORS layer in front of everything the REST listeners serve: the HTTP, HTTPS and Unix-socket listeners, the Swagger UI and the root route. The layer sits outside the authentication, so a 401 carries the CORS headers too, and a browser can read it. The MCP listener does not use this layer; it has its own list, mcp.allowed_origins.

The layer answers the preflight request (OPTIONS with Origin and Access-Control-Request-Method) itself, with 200 and these fixed headers: Access-Control-Allow-Methods: GET,HEAD,POST,PUT,PATCH,DELETE, Access-Control-Allow-Headers: authorization,content-type,if-match,if-none-match,last-event-id and Access-Control-Max-Age: 3600. On the real responses it adds Access-Control-Expose-Headers: etag,x-expires-at, so a script can read the ETag for conditional writes and the expiry of a key. Methods, headers and max age are not configurable. For an origin in the list the response carries Access-Control-Allow-Origin with that origin; for any other origin it carries none, and the browser blocks the script from reading the response (the server still processes the request).

The server does not set Access-Control-Allow-Credentials; the API key travels in the Authorization header, which is allowed.

With false, no CORS layer exists and no response carries a CORS header. With true and an empty cors.allowed_origins, the start is refused with invalid config: cors.enabled = true but cors.allowed_origins is empty — no origin would ever be allowed. While the key is false, the list is not checked.

Background

Browsers forbid a script of one origin from reading the responses of another unless the server agrees. A single-page application on https://console.example.com that calls the API on another host needs this key. CORS is a rule that browsers enforce for their scripts; it is no access control for other clients, which ignore it, and the API key is still required for every request.

Referenced on
Security

allowed_origins cors.allowed_origins

Since v0.4.0
Exact origins, e.g. ["https://console.example.com", "http://localhost:5173"].
Default
[]
Effect

The origins that may read the responses of the REST API from a browser script. An origin is written as a browser sends it in the Origin header: scheme, host and, if it is not the default of the scheme, the port, for example https://console.example.com or http://localhost:5173. The server compares the whole value, byte by byte, with the request’s Origin header. http://localhost:5173 and http://localhost are two origins, and a different port, scheme or letter case is another origin.

Each entry is checked at start, but only while cors.enabled is true. An entry must have the form scheme://host[:port] in lower case, without user name, path, query or fragment; otherwise the start is refused with invalid config: cors.allowed_origins entry 'https://a.example.com/' is not a valid origin — expected exactly scheme://host[:port] (lowercase, no userinfo/path/query/fragment). A trailing slash, a capital letter and a missing scheme are all rejected this way.

The single entry "*" allows every origin. The server then sends Access-Control-Allow-Origin: * on every response, including those to requests without an Origin header, and logs a warning at start. Combined with other entries, "*" refuses the start with invalid config: cors.allowed_origins mixes the wildcard "*" with concrete origins — "*" must be the only entry.

Background

List the origins of the web applications that you operate and nothing else. The wildcard does not open the API to anyone by itself, since the API key is still required for every request, but it lets a script on any website read the responses once that script has a key. Use it for tests only.

Referenced on
Security

enabled mcp.enabled

Since v0.5.1
Starts the MCP listener. Off by default.
Default
false
Effect

With true, the server starts a second listener for the Model Context Protocol (MCP), the open protocol that assistants, agents and programs use to call tools and read resources. It listens on server.bind_address at mcp.port, serves the endpoint /mcp with the Streamable HTTP transport, and logs MCP listening on http://127.0.0.1:3100/mcp. The listener runs on one worker thread of its own, so its clients take no frontend worker. When server.tls_enabled is on, the MCP listener uses the same certificate and serves HTTPS only: in a test of v0.5.1, https://…/mcp answered and plain http://…/mcp did not connect.

The server has a catalog of 26 tools and offers a part of it, depending on mcp.read_only and mcp.allow_ddl. It also offers two resources: luradb://domains and the template luradb://{engine}/{name}/schema. A tool adds no behavior of its own. Each call becomes the REST requests that do the work, run through the same layers with the caller’s Authorization header, so the rights of the API key apply: MCP never gives more than the same key gives over REST. Two tools, metrics and create_domain, use admin-only routes and are listed for admin keys only.

With auth.enabled on, every request to /mcp needs Authorization: Bearer <key>: an API key, or an OAuth access token if mcp.oauth_enabled is on. A request without it gets 401 with WWW-Authenticate: Bearer, and one with an unknown key gets 401 with WWW-Authenticate: Bearer error="invalid_token". With authentication off, every client that reaches the port has full access, and the server warns at start: mcp.enabled is true and auth.enabled is false — any client that reaches the MCP port has full access.

A request body is limited to 4 MiB, and the result of a tool or a resource to 1 MiB; a larger result comes back as a tool error that tells the client to reduce the limit or narrow the request. Browser clients are admitted by mcp.allowed_origins; the Host header is not checked.

Background

MCP lets a client that speaks the protocol use LuraDB without a client library: it lists the tools, reads the schema of a domain and runs queries. The listener is separate from the REST ports, so you can expose, firewall and switch it on its own, and it is off by default. Keep authentication on, and put TLS in front of the listener, before you reach it from beyond the host.

Referenced on
Limits and defaults · MCP server · MCP with Claude

port mcp.port

Since v0.5.1
Port of the MCP listener on server.bind_address; must differ from the REST ports.
Default
3100
Effect

The TCP port of the MCP listener, on the address of server.bind_address. It has to differ from the ports of the REST listeners that are on: server.port while server.http_enabled is true, and server.tls_port while server.tls_enabled is true. A clash refuses the start with invalid config: mcp.port (3820) must differ from server.port. The check runs only while mcp.enabled is true; with the listener off, the value is not used. The port 0 is accepted and lets the operating system pick a free port, which the start log shows (MCP listening on …).

A port that another process already uses stops the start: in a test of v0.5.1 the server exited with Error: Address already in use (os error 98). The value is a port number from 0 to 65535; a larger number or text refuses the start with a parse error of the configuration file.

A reverse proxy or a client that reaches the listener from another host connects to this port, or to the port the proxy exposes for it (see mcp.public_url).

Background

The listener has a port of its own so that the MCP endpoint can be exposed, firewalled and proxied separately from the REST API. Change the value only for a clash with another service or for a convention of your network.

Referenced on
Troubleshooting · Limits and defaults · MCP server

read_only mcp.read_only

Since v0.5.1
Offers only the tools that read; set to false to also offer the tools that write keys, documents and rows.
Default
true
Effect

With true, the MCP server offers the 17 tools that only read: whoami, list_domains, describe_domain, metrics (admin keys only), list_tables, describe_table, list_views, list_json_indexes, health, kv_get, kv_scan, kv_count, json_get, json_list, json_search, json_count and sql_query. sql_query runs one SELECT and refuses every other statement. The tools are marked as read-only in their annotations.

With false, six tools are added that change data: kv_put, kv_delete, kv_set_null, json_put, json_delete and sql_execute, which runs one INSERT, UPDATE or DELETE. They are marked as destructive. In a test of v0.5.1, the listing had 17 tools with true and 23 with false. A tool that is not offered is not only hidden: calling it returns the error tool '…' is not available. A statement sent to the wrong SQL tool is refused with a hint, for example this statement does not belong to this tool; use sql_query.

The switch narrows what MCP offers; it does not widen the rights of a key. A write needs both the tool and a key with the permission to write the domain, and the rate limits and the other limits of the REST API apply to every call. The tools that create structure are switched by mcp.allow_ddl, and only when this key is false. No tool deletes a domain, a table, a view or an index.

Background

An assistant or an agent that explores data can do harm with a wrong or a manipulated instruction. With true, no MCP tool can change data, whatever the client sends and whatever key it holds. That is the setting for a server that is queried, not edited, through MCP. Switch to false only for clients you trust with writes, and give them a key that holds a permission on the domains they need and nothing more.

Referenced on
MCP server · MCP with Claude

allow_ddl mcp.allow_ddl

Since v0.5.1
Also offers the tools that create domains, tables, views and indexes; only with read_only = false. No tool deletes a domain, table, view or index.
Default
false
Effect

Adds the three tools that create structure to what the MCP server offers: create_domain (a domain of the engine kv, json or rel; admin keys only), create_json_index (an index on a field of a JSON domain) and sql_ddl, which runs one CREATE TABLE, CREATE INDEX, CREATE VIEW or ALTER TABLE … ADD COLUMN, RENAME COLUMN or RENAME TO. They are marked as not read-only; sql_ddl is marked as destructive, since a rename breaks clients as a drop would. In a test of v0.5.1 with both keys set, the listing had 26 tools, three more than with mcp.read_only alone set to false.

No tool drops anything. sql_ddl refuses DROP TABLE, DROP INDEX, DROP VIEW and ALTER TABLE … DROP COLUMN before they reach the SQL route, with dropping a table, an index or a view is not available over MCP or dropping a column is not available over MCP, whatever the permission of the key. Deleting a domain, an index or a table needs the REST API.

The key has an effect only while mcp.read_only is false. With read_only still true, the server starts and logs the warning mcp.allow_ddl has no effect while mcp.read_only is true. The rights of the key apply as for every call: sql_ddl needs the Ddl permission on the domain, and create_domain an admin key.

Background

Structure changes are a different risk from data changes: a new table or a renamed column changes what other clients see. The switch lets you allow an assistant to write rows without letting it change the schema, which is why it is off by default even for a server with read_only = false. Switch it on for development or for a controlled setup where the assistant builds tables, and keep the rights of its key narrow.

Referenced on
MCP server · MCP with Claude

allowed_origins mcp.allowed_origins

Since v0.5.1
Origins a browser-based MCP client may send, e.g. ["https://app.example.com"]. A request with any other Origin header is refused; requests without one pass.
Default
[]
Effect

The origins that a browser-based MCP client may send in the Origin header of a request to /mcp. Browsers add this header to requests from a web page; programs and desktop clients usually do not. A request without an Origin header passes the check. A request with an Origin that is not exactly in the list is refused with 403 and Forbidden: Origin is not allowed, whatever its credentials, including Origin: null. The comparison is by bytes: the scheme, the host and a non-default port must match as written. With the default empty list, every request that carries an Origin header is refused.

Each entry must have the form scheme://host[:port] in lower case, without user name, path, query or fragment. Unlike cors.allowed_origins, the wildcard is not allowed. An entry that breaks the form refuses the start with invalid config: mcp.allowed_origins entry 'https://App.example.com' is not a valid origin — expected exactly scheme://host[:port] (lowercase, no wildcard). The check runs only while mcp.enabled is true.

The list guards the endpoint /mcp. The OAuth endpoints (mcp.oauth_enabled) are not behind it; the sign-in form checks its own Origin against mcp.public_url. The MCP listener sends no CORS headers, since it does not use the CORS layer of cors.enabled; a proxy in front of it has to add them for a browser client of another origin. In a test of v0.5.1 with https://app.example.com in the list, a request from that origin passed the check and one from https://other.example.com got 403.

Background

A web page that a user opens can send requests to a service on localhost or on the local network, including by DNS rebinding, where the page’s own host name is made to point at that service. The check refuses pages that the operator did not name. Programs and desktop clients do not send an Origin and are not affected, so the default of an empty list serves them. Add an origin only for a web application that you operate and that talks to MCP from the browser.

Referenced on
MCP server

oauth_enabled mcp.oauth_enabled

Since v0.5.1
Lets MCP clients that only support OAuth sign in once on a LuraDB page with their API key. Needs auth.enabled = true and public_url.
Default
false
Effect

With true, the MCP listener can sign in clients with OAuth, with LuraDB itself as the authorization server, so that a client which supports only OAuth needs no API key in its configuration. The user signs in once on a LuraDB page with an API key; the client never sees that key. The start requires auth.enabled and mcp.public_url, and is refused with invalid config: mcp.oauth_enabled = true requires auth.enabled = true or invalid config: mcp.oauth_enabled = true requires mcp.public_url otherwise. With false, clients send an API key as a Bearer token, and the OAuth endpoints do not exist.

The endpoints, all on the MCP port: /.well-known/oauth-protected-resource and /.well-known/oauth-authorization-server (metadata, RFC 9728 and RFC 8414), /oauth/register (dynamic client registration, RFC 7591), /oauth/authorize (the sign-in page), /oauth/token and /oauth/revoke. Only public clients register (token_endpoint_auth_method none), and only the authorization code flow with PKCE S256 is offered, so a client needs no secret. A registration needs a client_name of at most 100 characters and one to ten redirect URIs of at most 2,048 characters each that are https://, or http:// on localhost, 127.0.0.1 or ::1. A client that never signs in is forgotten after 24 hours.

The flow, as seen in a test of v0.5.1: a request to /mcp without a token gets 401 and WWW-Authenticate: Bearer resource_metadata="<public_url>/.well-known/oauth-protected-resource". The client reads the metadata, registers, and sends the user to the sign-in page, which shows the client’s name and the host the user is sent back to, and asks for an API key. After Allow access, the browser is redirected to the client’s redirect URI with a one-time code (valid for 60 seconds), which the client exchanges at /oauth/token for an access token (lura_at_…, valid for 3,600 seconds) and a refresh token (lura_rt_…, valid for 30 days). Each use of a refresh token returns a new pair. Using a code or a refresh token a second time revokes the whole grant. An access token is valid on the MCP listener only: the REST API answered 401 to it.

A token carries no rights of its own. It stands for the account and the API key hash of the sign-in, and it stops working when that account is deleted, its key is rotated, or mcp.public_url changes. In a test, after the key of the account was rotated, the access token got 401 and the refresh token was refused with the user or its API key changed. The rights are those of the account, as for an API key. The sign-in form must be sent from the origin of public_url: a request with another or no Origin gets 403. Five failed sign-ins from one sender within 15 minutes block that sender with 429 Too many failed sign-ins; try again later, and ten client registrations per hour and sender are allowed; the sender is the client address as resolved with proxy.trusted_proxies, an IPv6 sender counted by its /64 prefix. The records of clients, codes and tokens live in the system store, and expired ones are removed by the sweeper of ttl_sweeper.enabled.

Background

Some MCP clients can authenticate only through OAuth and offer no field for a Bearer token. OAuth also keeps the API key out of their configuration files: the user types it into a LuraDB page, and the client holds a token that is limited to MCP, expires within an hour and can be revoked. Clients that can send a Bearer header need none of this and can use an API key. Switch OAuth on only for the clients that need it.

Referenced on
MCP server · MCP with Claude

public_url mcp.public_url

Since v0.5.1
Public base URL of the MCP listener as clients reach it, e.g. "https://db.example.com:3100" or the address of a reverse proxy; https:// is required unless the host is localhost, 127.0.0.1 or ::1. OAuth tokens are bound to it: changing it makes OAuth clients sign in again. Behind a proxy under a path, e.g. "https://db.example.com/luradb", the proxy must also pass /.well-known/oauth-authorization-server/luradb and /.well-known/oauth-protected-resource/luradb/mcp to the listener unchanged, without stripping /luradb: OAuth clients look for the metadata there.
Default
""
Effect

The base URL under which MCP clients reach the listener, as they see it: the scheme, the host, the port if it is not the default, and optionally a path, for example https://db.example.com:3100 or, behind a reverse proxy, https://db.example.com/luradb. It is used only with mcp.oauth_enabled, where it is the issuer of the OAuth tokens and the base of every address in the metadata the server publishes. With OAuth off, the value is ignored and not checked.

With OAuth on, the value is checked at start. It must be an absolute http:// or https:// URL with a host and no query, fragment or user name; anything else refuses the start with invalid config: mcp.public_url 'example.com' is not an absolute http(s) URL. Plain http:// is accepted only for the hosts localhost, 127.0.0.1 and ::1; otherwise the start is refused with invalid config: mcp.public_url 'http://example.com' must use https:// unless its host is localhost, 127.0.0.1 or ::1. A trailing slash is dropped. An empty value refuses the start with invalid config: mcp.oauth_enabled = true requires mcp.public_url.

The tokens are bound to the address <public_url>/mcp, the resource. Changing the value therefore invalidates the issued tokens, and clients sign in again. The metadata documents list the endpoints below the URL: for http://127.0.0.1:3821 the issuer is that URL, the sign-in page …/oauth/authorize, the token endpoint …/oauth/token, the registration endpoint …/oauth/register and the revocation endpoint …/oauth/revoke. A 401 from /mcp points to <public_url>/.well-known/oauth-protected-resource. The sign-in form accepts a request only from the origin of the URL (scheme, host and port in lower case, without the default port of the scheme).

If the URL has a path, a client looks for the metadata below /.well-known/ with the path appended, as RFC 8414 and RFC 9728 define. In a test with http://127.0.0.1:3821/luradb/, the server answered /.well-known/oauth-authorization-server/luradb and /.well-known/oauth-protected-resource/luradb/mcp, and answered 404 for /.well-known/oauth-protected-resource/luradb. The listener itself serves its endpoints at its root, /mcp and /oauth/…, and not below the path. A reverse proxy that publishes the listener under /luradb must therefore strip the prefix /luradb from the other paths, and pass the two well-known paths to the listener unchanged, with /luradb still in them.

Background

The listener cannot know the name under which clients reach it: it may sit behind a proxy, a load balancer or a port mapping, and the clients need absolute addresses in the metadata, in the redirects and in the check of the sign-in form. The operator states that name once. Use https outside tests, since the sign-in page takes an API key and the tokens are bearer tokens.

Referenced on
MCP server · MCP with Claude

enabled shm.enabled

Since v0.1.0
Publishes read snapshots in shared memory. Warning: members of the luradb group can read all data via SHM without an API key.
Default
false
Effect

With true, the server publishes a read copy of the key-value data in POSIX shared memory for local clients, and lets local clients register for a command channel. At start it creates three segments in /dev/shm, named after shm.instance_id: luradb_<id>_state (4 KiB) and the two data segments luradb_<id>_data_a and luradb_<id>_data_b (shm.data_buffer_size each), plus a lock file luradb_<id>.lock. A publisher builds a snapshot of all active key-value domains into the data segment that is not in use and then switches to it (shm.snapshot_interval_ms), so a client reads the current snapshot with plain memory reads. The snapshot holds the keys, the values that are stored inline, the expiry times and the NULL state. The value of a key that lives in the value log is only marked, and the client fetches it over its command channel. JSON and relational data are not part of it.

A client registers at shm.registration_socket_path and gets a channel of its own: a command ring and a response ring of 4 MiB each, through which it can get, put, delete, set to null and list keys. The segments of a client are removed when it disconnects; in a test, the four segments of a client existed while it was connected and were gone a second after it closed the socket.

The access control is that of the operating system. The code asks for the mode 0660 and the umask of the process applies: in a test with a umask of 022 the segments had the mode 0640, and the packaged service (UMask=0027) gives 0640 as well. The owner can write them and the group of the server can read them, so every process in that group can map the data segments and read all key-value data without an API key and without any permission of a domain. The command channel carries no API key either. A command names a domain, and no permission is checked; the only gate is the registration. With auth.enabled on, only the user IDs of auth.trusted_uids may register (ERR unauthorized for any other); with it off, anyone who can open the registration socket may.

A second server with the same instance id on the same host is refused at start with SHM instance '…' is already in use by another running process. Segments that a crash of the same id left behind are removed at start with a warning, and a clean stop removes them. If the setup fails, for example because the directory of the registration socket cannot be created, the start stops. The three data buffers count toward the fixed memory that the server compares with resources.memory_budget. The block shm of GET /store-api/metrics counts the publishes, the skipped publishes and the clients that were disconnected.

Background

Shared memory gives a client on the same host reads without HTTP: no connection and no request parsing. The price is that the operating system, not LuraDB, decides who may read. Enable it only on a host where every process of the group luradb may read all key-value data, and run clients under dedicated service accounts. Leave it off otherwise; the REST API, also over the Unix socket of server.unix_socket_path, is the interface that checks keys and permissions.

Referenced on
Write path · Monitoring · Security

instance_id shm.instance_id

Since v0.1.0
Name suffix of the shared-memory segments, so several servers can share a host ([A-Za-z0-9-]).
Default
"0"
Effect

The name that makes the shared-memory objects of one server distinct on a host. It becomes part of the names of the segments (/dev/shm/luradb_<id>_state, …_data_a, …_data_b, and …_cmd_<n> and …_resp_<n> per client), of the lock file /dev/shm/luradb_<id>.lock, and replaces {instance_id} in shm.registration_socket_path.

The value must not be empty and may contain only the characters A-Z, a-z, 0-9 and -; the underscore is not allowed. Anything else refuses the start with invalid config: shm.instance_id ('a_b') must be non-empty and contain only [A-Za-z0-9-]. The value is checked even while shm.enabled is false.

Two running servers on one host must have different ids. The lock file holds an exclusive lock for as long as the server runs, and a second server with the same id is refused with SHM instance '0' is already in use by another running process. The lock disappears with the process, so a crash does not block the next start.

Background

Segment names live in one namespace for the whole host. The id lets several servers, such as a production instance and a test instance, share a host without touching each other’s segments. The underscore is excluded because the server finds the leftovers of a crash by the name prefix luradb_<id>_: with an underscore allowed, the id 0 would match the segments of an instance 0_backup. Change the default only when you run more than one instance with shared memory on a host.

Referenced on
Write path · Limits and defaults

data_buffer_size shm.data_buffer_size

Since v0.1.0
Size in bytes of each shared-memory data segment. Two segments of this size plus one copy per rebuild are held in memory.
Default
268435456
Effect

The size, in bytes, of each of the two data segments in which the server publishes its snapshot of the key-value data. The server alternates between the two: it writes a new snapshot into the one that is not being read and then switches. The value must be at least 4096; smaller values refuse the start with invalid config: shm.data_buffer_size (100) must be at least 4096. The check runs even while shm.enabled is false.

The snapshot holds, for every active key-value domain in the order of the domain names, the keys, the inline values and an overhead of 64 bytes per entry. The server fills it only up to 7/8 of this size. When the data does not fit, the snapshot ends in the middle of a domain and leaves out the domains after it. The snapshot is published that way and the server logs a warning, SHM snapshot truncated in domain '…' (N later domain(s) omitted): accumulated size exceeded budget of … bytes (buffer …). A client then sees only part of the data. If the serialized snapshot is still too large for the segment, the publish fails and the server logs SHM snapshot publish failed: ….

The segments are created at their full size, but the server does not fill them; pages become resident as the snapshot is written. The server counts three buffers (the two segments and the copy made while a snapshot is rebuilt) toward its fixed memory (resources.memory_budget). The 4 MiB rings of each registered client come on top and do not depend on this key.

Background

The snapshot has to hold all inline key-value data that clients want to read, so size the buffer from your data: the sum of keys and inline values (lsm.vlog_inline_threshold) over all key-value domains, plus 64 bytes per key, with a margin of one eighth and room to grow. The default of 256 MiB is a ceiling, not an allocation. Raise it if the warning about a truncated snapshot appears; lower it to cap the memory that shared memory may use.

Referenced on
Write path · Limits and defaults

registration_socket_path shm.registration_socket_path

Since v0.1.0
Unix socket where SHM clients register; {instance_id} is replaced. It must differ from the Unix socket of the server section.
Default
"/run/luradb/{instance_id}.sock"
Effect

The Unix socket at which local clients register for shared memory. The text {instance_id} in the path is replaced by shm.instance_id; with the defaults the socket is /run/luradb/0.sock. A client connects, sends the line REGISTER, and receives OK <client id> <segment names> with the write end of a pipe attached (to wake the server after a command), or ERR unauthorized. The socket stays open as the sign of life of the client: when it closes, the server removes the segments of the client.

With auth.enabled on, only a process whose user ID is in auth.trusted_uids is accepted; the server reads the user ID from the connection, so a client cannot claim another one. In a test of v0.5.1, a process that was not listed got ERR unauthorized and a listed one got OK 1 /luradb_<id>_cmd_1 … with the four segment names of its channel. With authentication off, every process that can open the socket may register.

The server creates the socket with the mode 0660 when it starts, replaces a socket file left by an earlier run, and removes it when it stops. Unlike the socket of server.unix_socket_path, whose directory must exist, it creates the missing directory of this path; if that fails, the start stops with Error: create registration socket directory /run/luradb. The path must differ from the REST socket: the start is refused with invalid config: shm.registration_socket_path ('…') equals server.unix_socket_path — SHM clients could not register; use a different path. The check runs only while shm.enabled is true.

Background

Registration is separate from the REST socket because it speaks a different protocol: a line of text, then file descriptors, then rings of shared memory. A separate socket also makes the trust rule simple: the shared-memory channel has no API key, so the user ID of the connecting process is the only credential. The packaged service creates /run/luradb with the mode 0750 for the group luradb, which is where the default path puts the socket.

Referenced on
Write path · Limits and defaults

snapshot_interval_ms shm.snapshot_interval_ms

Since v0.1.0
How often, in milliseconds, the snapshot is checked for changes; it is rebuilt only after a change, from one consistent point across all data shards. Lower values mean fresher snapshots and more CPU load; every rebuild briefly holds back the writes of all data shards.
Default
100
Effect

How often, in milliseconds, the publisher checks whether the shared-memory snapshot is out of date. A check also happens after every MemTable flush. A check compares the position of the key-value write-ahead log and the generation of the domain list with those of the last snapshot. It builds a new snapshot only if they differ, or if a key with an expiry time in the last snapshot has expired since. Otherwise it only confirms the time in the header of the state segment. The build reads all key-value domains from one consistent point across the data shards and serializes them; while it takes that point, the shards hold their writes back for a moment.

The value must be at least 1; 0 refuses the start with invalid config: shm.snapshot_interval_ms (0) must be at least 1. The check runs even while shm.enabled is false.

A new snapshot cannot be published while a client still reads the segment that it would overwrite. The publish is then skipped and the server logs, on every tenth skip in a row, SHM snapshot stale: N consecutive publishes skipped, buffer … still pinned by (client_id, readers) …, and SHM snapshot publishing resumed after N skipped publishes when it works again. snapshot_consecutive_skips in the shm block of GET /store-api/metrics shows the run that is going on. To end a run, see shm.kick_blocking_client_after_secs.

Background

The interval is the staleness a client can see after a write, plus the time of the build: with a write every few milliseconds the snapshot is rebuilt at every interval, and a build reads and serializes all inline key-value data, so its cost grows with the size of that data (shm.data_buffer_size). A shorter interval gives fresher snapshots at the price of more CPU and more moments in which the shards hold their writes. Raise it if the snapshot is large and clients can live with older data.

Referenced on
Write path

kick_blocking_client_after_secs shm.kick_blocking_client_after_secs

Since v0.5.1
Seconds a client may keep the snapshot from being updated, by holding on to a read of the old one, before the server disconnects it; 0 = never. Warning: the disconnected client's reads still in progress may return torn data.
Default
0
Effect

How long, in seconds, a run of skipped snapshot publishes may last before the server disconnects the clients that cause it. A publish is skipped when a client still reads the data segment that the new snapshot would overwrite (see shm.snapshot_interval_ms). The server measures the run in time, from the first skipped publish after the last successful one. When the run has lasted longer than the value, the server closes the registration connection of every client that pins the blocked segment, which removes the segments of those clients, and logs SHM snapshot blocked longer than the kick threshold: disconnecting (client_id, readers) … pinning buffer …. The counter clients_kicked_total in the shm block of GET /store-api/metrics goes up for each client. If the segment is still pinned afterwards, the measurement starts again, so the server kicks at most once per interval of the value.

A client that is disconnected this way may be in the middle of a read of the old snapshot, and that read can return data that is partly overwritten. The client must treat the disconnect as an error and read again after registering anew.

With 0, the default, the server never disconnects a client. A client that keeps a read open without end then blocks every later publish: the snapshot stops changing, the server logs SHM snapshot stale: … on every tenth skipped publish, and snapshot_consecutive_skips in the metrics keeps rising. The value is a whole number of seconds from 0.

Background

A client that hangs or is paused in the middle of a read keeps the publisher from switching to the segment it reads. The key decides what is worth more: a snapshot that stays current for all clients, or the guarantee that no read is ever cut off. If a stuck client must not freeze the snapshot for the others, set a value longer than the longest read that your clients legitimately keep open.

Referenced on
—

enabled backup.enabled

Since v0.3.0
Enables backups; while off, the backup endpoints answer 503.
Default
false
Effect

With true, the server can write logical backups of its data: an archive file with the key-value entries, the JSON documents and the relational schemas and rows, which it can read back into the same or another server. At start it creates the backup directory (backup.dir), removes .part files that an earlier run left behind, logs Backup manager ready (N schedule(s)). and starts the scheduler of the [[backup.schedule]] entries. All backup endpoints are for admins: POST /store-api/backups starts a backup (202 with the id; the body names the scope and include_auth), GET /store-api/backups lists the archives, and there are endpoints to read, download, upload, restore and delete one.

With false, the endpoints answer 503 Service Unavailable: backup is disabled (backup.enabled = false) and no scheduler runs; the log says Backup disabled by config.. The directory is not created. The schedule entries and the directory are not checked while the key is false: in a test of v0.5.1, a schedule with an invalid cron expression did not stop the start.

One job runs at a time, a backup or a restore. A second request is answered with 409 Conflict: a backup or restore job is already running. A backup is written to <id>.ndjson.part, synced, and renamed to <id>.ndjson when it is complete. The id is bk_<UTC time>_<scope> for a backup on request and bk_<UTC time>_<schedule name> for a scheduled one. Each engine is exported from one consistent point across the data shards, and the file ends with a checksum line; an archive without it is listed as incomplete. The values of key-value entries are written in hex.

While the server refuses writes for lack of disk space or memory (resources.disk_free_block), a backup starts only if the backup directory is on a volume of its own that has room; a scheduled one is skipped with a warning otherwise. With auth.enabled off, the server warns at start that any client that reaches the port can create and download full backups. GET /store-api/metrics has a block backup with the time, duration and size of the last success, the time per schedule and the number of failures; it is null while backups are off.

Background

A logical backup stores keys, documents and rows, not the files of the data directory, and it can be restored into a single domain under a new name. It is taken while the server runs, from a consistent point; the shards hold their writes back only for the moment the point is taken. It is off by default because it writes files and makes data downloadable. Switch it on, give it a directory on a separate volume, and add a schedule so that backups happen without anyone remembering.

Referenced on
Upgrading · Backup and restore · Monitoring · Troubleshooting

dir backup.dir

Since v0.3.0
Directory of the backup files; relative paths resolve against the working directory. Warning: use a separate directory per server; retention deletes backups by schedule name.
Default
"luradb_backups"
Effect

The directory in which the server keeps its backup archives, one file bk_<UTC time>_<name>.ndjson per backup. The server creates the directory, with its parents, when it starts with backup.enabled on, and removes .ndjson.part and upload-*.part files in it that an earlier run left unfinished. A relative path resolves against the working directory of the server process (under the packaged service, /var/lib/luradb, so the default is /var/lib/luradb/luradb_backups). Only the files named bk_*.ndjson are archives; every other file in the directory is left alone.

The path must not be empty, and it must not resolve to the same location as another data path or the log directory; the start is refused with invalid config: backup.dir must not be empty or invalid config: storage.wal_path and backup.dir resolve to the same location '…'. The checks run only while backups are on.

Retention deletes in this directory. After each successful scheduled backup, the server deletes the older archives of that schedule beyond backup.schedule.keep_last, found by the schedule name in the manifest of each archive. Two servers that share a directory and both have a schedule of the same name therefore delete each other’s archives. Use a directory per server.

While the server refuses writes for lack of disk space or memory, a backup may start only if this directory is on a volume of its own that is above the warn threshold (resources.disk_free_warn).

Background

A backup on the volume of the data it protects is lost with that volume and uses the space the database needs to run. An archive is plain NDJSON, and the values of key-value entries are written in hex, so they take twice their size. Put the directory on another volume, or on a mount that is copied away. The default is a relative path, which under the packaged service lies on the data volume; set an absolute path on another volume.

Referenced on
Upgrading · Resource guard · Backup and restore · systemd and Docker · Troubleshooting · Limits and defaults

name backup.schedule.name

Since v0.3.0
Unique schedule name: 1 to 50 characters of [a-zA-Z0-9_-].
Default
not set
Effect

The name of a schedule, 1 to 50 characters from a-z, A-Z, 0-9, _ and -. The key is required: a schedule without it fails to parse, with an error that says missing field and names the key, and the server does not start. This holds also while backup.enabled is false, since the parse comes before every check. The name must be unique among the schedules; the start is refused with invalid config: backup.schedule.name ('a b') must be 1-50 characters of [a-zA-Z0-9_-] or invalid config: backup.schedule.name ('a') must be unique across schedules. The checks run only while backup.enabled is true.

The name ties the archives to their schedule. A scheduled backup is named bk_<UTC time>_<schedule name> (for example bk_20261103T202200Z_nightly), the manifest in the archive records the name, and the metrics list the time of the last success per schedule name (last_success_at_by_schedule in the backup block of GET /store-api/metrics). Retention uses the name to find the archives it may delete (backup.schedule.keep_last): those that are complete, were made by the server and carry this name in their manifest. If you rename a schedule, the archives of the old name are no longer matched and stay until you delete them.

Archives made on request (POST /store-api/backups) carry no schedule name and are never deleted by retention; neither are uploaded archives, whatever name their manifest carries.

Background

The name is the only link between a schedule and its archives, which is why it has to be unique and why retention cannot reach archives that carry another name. Use a name that says what the schedule does, such as nightly or hourly-orders, and keep it stable.

Referenced on
Backup and restore

cron backup.schedule.cron

Since v0.3.0
When the schedule runs: a 5-field cron expression in UTC that fires within 4 years.
Default
not set
Effect

When the schedule runs: a cron expression of five fields, minute hour day-of-month month weekday, separated by blanks and evaluated in UTC, not in the local time of the host. The key is required. Each field accepts *, a value, a list (1,15), a range (1-5) and a step (*/10 or 0-30/10), combined as a comma-separated list. The ranges are 0–59, 0–23, 1–31, 1–12 and 0–6, where 0 is Sunday and 7 is not accepted. Names (MON, JAN) and special forms (@daily, L, #) are not supported. Example: 0 3 * * * is every day at 03:00 UTC.

When both day-of-month and weekday are restricted, the schedule runs on a day that matches either of them: 0 0 1 * 1 runs on the first of every month and on every Monday. A field counts as restricted unless it is exactly *; a step such as */3 counts as restricted, which differs from Vixie cron.

The checks run at start while backup.enabled is true. An expression that does not parse refuses the start with a message that names the field, for example invalid config: backup.schedule.cron ('0 3 * * 7') is not a valid cron expression: cron field 'weekday': value 7 out of range [0, 6] in '7' (schedule 'a'), or, for four fields, … must have exactly 5 fields (minute hour day-of-month month weekday), got 4. An expression that never matches within the next four years (1,461 days), such as 30 February, is refused with invalid config: backup.schedule.cron ('0 0 30 2 *') has no date within 4 years — the schedule would never run (schedule 'a'); 29 February is accepted.

The scheduler wakes at the start of every UTC minute and starts the backup of each schedule whose expression matches that minute. There is no catch-up: if the server was stopped at the time, the run is missed and the next match counts. A start inside a matching minute does not run that slot. Only one backup or restore runs at a time (the job slot). A schedule that matches while the slot is busy is skipped with [Backup Scheduler] job slot busy, skipping this tick for schedule '…', so two schedules due in the same minute run one and skip the other. A schedule is also skipped, with a warning, while the server refuses writes for lack of disk space or memory and the backup directory has no volume of its own with room.

Background

Cron is the notation operators already know, and UTC avoids the shifts of daylight saving time: a schedule fires at the same instant all year, and the id and the manifest of each backup carry UTC times. Choose a time of low load, and keep the schedules of a server at different minutes so that none is skipped because another is running. A backup that is still running when the next match comes makes that match skip, so the interval between matches should be longer than a backup takes.

Referenced on
Backup and restore

scope backup.schedule.scope

Since v0.3.0
Data to back up: all, kv, json, rel, kv:<domain>, json:<domain>, rel:<domain> or domain:<name>.
Default
not set
Effect

What the schedule backs up. The key is required, and its value is one of:

all (every engine, all domains), kv, json or rel (all domains of one engine), kv:<domain>, json:<domain> or rel:<domain> (one domain of one engine), and domain:<name> (the domain of that name in every engine that has one). A domain name uses the characters a-z, A-Z, 0-9, _ and -. The same grammar is used for the scope of POST /store-api/backups.

The syntax is checked at start while backup.enabled is true; anything else refuses the start with invalid config: backup.schedule.scope ('everything') is not a valid scope: invalid backup scope 'everything': expected 'all', 'kv', 'json', 'rel', 'kv:<domain>', 'json:<domain>', 'rel:<domain>', or 'domain:<name>' (schedule 'a'). That a domain exists is not checked at start, but each time the schedule runs. A schedule for a domain that does not exist, or one whose scope needs an engine that is off (json, rel, json:…, rel:… with json.enabled or rel.enabled false), starts no backup and logs a warning such as [Backup Scheduler] schedule 'orders' failed to start: domain_not_found: orders. The other schedules go on. The scope all backs up only the engines that are on.

An archive of a relational domain holds its schema and its rows. For the scopes kv and all, the schedule can also include the accounts (backup.schedule.include_auth); other scopes ignore that setting.

Background

A narrow scope gives a small archive that is quick to take and to restore. Use all for the safety net of a whole installation, and add a second schedule with a domain scope for a domain that changes faster or matters more, for example domain:orders every hour. The archive file name carries the schedule name, not the scope, so the scope is recorded only in the manifest of the archive.

Referenced on
Backup and restore

include_auth backup.schedule.include_auth

Since v0.3.0
Also backs up users and permissions (scopes all and kv only). The backup file then contains API key hashes.
Default
not set
Effect

With true, the backups of this schedule also contain the accounts of the server: for each account its name, role, creation time and the hash of its API key (auth-user lines), and for each account the permissions it holds on domains (auth-perm lines). Without it (the default), an archive holds data only. In a test of v0.5.1 with an on-demand backup of scope all and include_auth set, the archive had one auth-user line for the admin account, with the fields name, api_key_hash, role and created_at.

The setting has an effect only for the scopes all and kv. For every other scope the server leaves the accounts out without a message, and the manifest of the archive says include_auth: false. The key is optional.

The archive then holds SHA-256 hashes of API keys, never the keys themselves. Treat the file as sensitive: it is only as safe as the directory it lies in (backup.dir), the volumes it is copied to and the admins who can download it. When the archive is restored, the accounts come back only if the restore request sets include_auth as well; otherwise the lines are ignored. The accounts that auth.admins.name lists are recreated at every start in any case.

Background

Without accounts, restoring an archive into a new installation leaves you with data and no users, and you have to create the accounts and their permissions again. With them, a restore rebuilds the access rules together with the data. The price is that the archive holds credentials in the form of hashes. Switch it on for the schedule of your full backup if you restore whole installations, and keep the archives as protected as the system store.

Referenced on
Backup and restore

keep_last backup.schedule.keep_last

Since v0.3.0
How many of this schedule's newest backups to keep (at least 1).
Default
not set
Effect

How many of the newest backups of this schedule the server keeps. The key is required and must be at least 1; 0 refuses the start with invalid config: backup.schedule.keep_last (0) must be at least 1 (schedule 'a'), and a schedule without it fails to parse with an error that says missing field and names the key. The check runs only while backup.enabled is true.

After every scheduled backup that finished complete, the server lists the archives in backup.dir and deletes the oldest ones of this schedule beyond the number, ordered by creation time. An archive belongs to the schedule if it is complete, was made by this server (not uploaded) and its manifest carries the schedule’s name (backup.schedule.name). A backup that failed triggers no deletion, so a failing schedule never deletes the good archives it should have replaced. Archives made on request, archives of other schedules and uploaded archives are never touched. In a test of v0.5.1, with a schedule mins of * * * * * and keep_last = 2, the directory held the two newest archives of mins after each run, and an archive made on request stayed.

Background

Without retention the directory grows with every run. The number is the length of the history you can go back to: with a nightly schedule and keep_last = 7 you hold the last seven nightly archives. Retention runs only after the new archive is complete, so the directory holds one archive more than the number for a short time; plan the volume for that. A delete by hand through the API works at any time and is not limited by this key.

Referenced on
Backup and restore · Troubleshooting

Keys that a past release accepted and the current one no longer uses. Their entries stay so that old links and search hits still land here.

scan_batch_size backup.scan_batch_size

Since v0.3.0Removed in v0.5.1
Number of entries that a backup or restore processed before it paused.
Default
—
Effect

The number of entries that a backup export or a restore processed before it paused for backup.scan_pause_msremoved. A restore also wrote JSON documents in groups of this size. A value of 0 was treated as 1. The default was 500. The key applied only with backup.enabled set to true.

Background

Since v0.5.1 the batch is fixed at 500 entries, the former default. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'backup.scan_batch_size' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
Backup and restore

scan_pause_ms backup.scan_pause_ms

Since v0.3.0Removed in v0.5.1
Pause in milliseconds after each batch of a backup or restore.
Default
—
Effect

The pause, in milliseconds, that a backup export or a restore made after each batch of backup.scan_batch_sizeremoved entries, so that other requests could run in between. 0 meant no pause. The default was 10. The key applied only with backup.enabled set to true.

Background

Since v0.5.1 the pause is fixed at 10 milliseconds, the former default. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'backup.scan_pause_ms' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
Backup and restore

capacity_bytes block_cache.capacity_bytes

Since v0.1.0Removed in v0.5.1
Maximum size in bytes of the block cache of the key-value engine.
Default
—
Effect

The maximum total size, in bytes, of the block cache of the key-value engine, the cache of SSTable data blocks that sat between the MemTable and the files. Blocks were evicted once the cached blocks together exceeded the value. The default was 67108864 (64 MiB). The server did not check the value.

Background

Since v0.5.1 each engine instance of each data shard has a block cache of its own, and the built-in capacity of 64 MiB is divided by the number of shards (multicore.shards); with 8 shards the start log shows block cache 8192 KiB per engine. The caches count toward the fixed memory that resources.memory_budget is compared with. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'block_cache.capacity_bytes' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

ghost_capacity block_cache.ghost_capacity

Since v0.1.0Removed in v0.5.1
Number of evicted block identifiers that the block cache of the key-value engine remembered.
Default
—
Effect

The number of identifiers of evicted blocks, without their data, that the block cache of the key-value engine remembered. A block that was requested again while its identifier was still remembered went straight to the main queue. The default was 10000. The server did not check the value.

Background

Since v0.5.1 the number is fixed at 10000, the former default. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'block_cache.ghost_capacity' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

small_ratio block_cache.small_ratio

Since v0.1.0Removed in v0.5.1
Share of the block cache of the key-value engine that the small queue could use.
Default
—
Effect

The share of block_cache.capacity_bytesremoved that the small queue of the block cache of the key-value engine could use, as a number from 0 to 1. The cache works as an S3-FIFO cache: a block that was read for the first time entered the small queue, and a block that was read again moved to the main queue, so one pass over a large range did not push the blocks out that were read often. The default was 0.10. The server did not check the value.

Background

Since v0.5.1 the ratio is fixed at 0.10, the former default. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'block_cache.small_ratio' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

registered_buffer_count io_engine.registered_buffer_count

Since v0.1.0Removed in v0.5.1
Number of buffers that the I/O engine registered with io_uring.
Default
—
Effect

The number of buffers that the I/O engine allocated and registered with the kernel for io_uring at start. It applied only with io_engine.enabled set to true; the default of that key was false. Its default was 128. A successful start logged IoEngine ready: 128 registered buffers x 65536 bytes. with the configured numbers. The engines did not read or write through the buffers, so the key only decided how much memory the registration pinned: the count times io_engine.registered_buffer_sizeremoved, 8 MiB with the defaults. The server did not check the value.

Background

The buffers were groundwork that no read or write path used. Since v0.5.1 the pool is fixed at 128 buffers of 64 KiB, and io_engine.enabled only switches it on. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'io_engine.registered_buffer_count' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

registered_buffer_size io_engine.registered_buffer_size

Since v0.1.0Removed in v0.5.1
Size in bytes of each buffer that the I/O engine registered with io_uring.
Default
—
Effect

The size, in bytes, of each of the buffers that the I/O engine registered with the kernel for io_uring at start. It applied only with io_engine.enabled set to true; the default of that key was false. Its default was 65536 (64 KiB). The engines did not read or write through the buffers, so the key only decided how much memory the registration pinned: io_engine.registered_buffer_countremoved times this size, 8 MiB with the defaults. The server did not check the value.

Background

The buffers were groundwork that no read or write path used. Since v0.5.1 the pool is fixed at 128 buffers of 64 KiB, and io_engine.enabled only switches it on. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'io_engine.registered_buffer_size' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

request_channel_capacity io_engine.request_channel_capacity

Since v0.1.0Removed in v0.5.1
Length of the request queue in front of the storage thread.
Default
—
Effect

The length of the queue in front of the storage thread: requests for the write-ahead log, the value log and the writing of SSTables went through a bounded channel of this size, and the sender waited while the channel was full. A value of 0 was treated as 1. It applied only with io_engine.enabled set to true; the default of that key was false. Its default was 1024.

Background

storage.wal_queue_capacity replaces it. The new key has the same default, 1024, and since v0.5.1 the storage thread always runs, so it always applies. A value set under the old name is not carried over. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'io_engine.request_channel_capacity' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

ring_depth io_engine.ring_depth

Since v0.1.0Removed in v0.5.1
Depth of the io_uring ring of the storage thread.
Default
—
Effect

The number of entries of the submission queue of the io_uring ring that the storage thread built. It applied only with io_engine.enabled set to true; the default of that key was false. Its default was 256. The server did not check the value.

Background

Since v0.5.1 the depth is fixed at 256. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'io_engine.ring_depth' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

sqpoll_enabled io_engine.sqpoll_enabled

Since v0.1.0Removed in v0.5.1
Whether the storage thread used an io_uring ring in SQPOLL mode.
Default
—
Effect

Whether the storage thread built its io_uring ring in SQPOLL mode, in which a kernel thread picks up submissions without a system call for each request. The storage thread is the dedicated thread that wrote the write-ahead log and the value log. It ran only with io_engine.enabled set to true, so with the default of false the key had no effect. The default of the key itself was true. When the kernel refused SQPOLL for lack of the capability CAP_SYS_NICE, the thread logged SQPOLL requires CAP_SYS_NICE, falling back to standard mode and used an ordinary ring. The server did not check the value.

Background

Since v0.5.1 the storage thread always runs and always asks for SQPOLL, with the same fallback; its line in the start log says whether SQPOLL is active. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'io_engine.sqpoll_enabled' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
Upgrading

sqpoll_idle_ms io_engine.sqpoll_idle_ms

Since v0.1.0Removed in v0.5.1
Idle time in milliseconds of the SQPOLL kernel thread of the storage thread.
Default
—
Effect

The time, in milliseconds, that the SQPOLL kernel thread of the storage thread’s ring kept polling for new submissions before it went to sleep. It applied only with io_engine.enabled set to true and io_engine.sqpoll_enabledremoved set to true. The default was 2000. The server did not check the value.

Background

Since v0.5.1 the idle time is fixed at 2000 milliseconds. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'io_engine.sqpoll_idle_ms' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

storage_thread_cpu io_engine.storage_thread_cpu

Since v0.1.0Removed in v0.5.1
CPU core to which the storage thread was pinned.
Default
—
Effect

The number of the CPU core to which the storage thread pinned itself. The default, -1, left the thread unpinned. When the pinning failed, the thread logged Storage thread CPU pinning to core N failed: … and went on unpinned. It applied only with io_engine.enabled set to true; the default of that key was false. The server did not check the value.

Background

Since v0.5.1 the storage thread is never pinned. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'io_engine.storage_thread_cpu' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

capacity_bytes json.block_cache.capacity_bytes

Since v0.1.0Removed in v0.5.1
Maximum size in bytes of the block cache of the JSON engine.
Default
—
Effect

The maximum total size, in bytes, of the block cache of the JSON engine, the cache of SSTable data blocks that sat between the MemTable and the files. Blocks were evicted once the cached blocks together exceeded the value. The default was 67108864 (64 MiB). The server did not check the value.

Background

Since v0.5.1 each engine instance of each data shard has a block cache of its own, and the built-in capacity of 64 MiB is divided by the number of shards (multicore.shards); with 8 shards the start log shows block cache 8192 KiB per engine. The caches count toward the fixed memory that resources.memory_budget is compared with. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'json.block_cache.capacity_bytes' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

ghost_capacity json.block_cache.ghost_capacity

Since v0.1.0Removed in v0.5.1
Number of evicted block identifiers that the block cache of the JSON engine remembered.
Default
—
Effect

The number of identifiers of evicted blocks, without their data, that the block cache of the JSON engine remembered. A block that was requested again while its identifier was still remembered went straight to the main queue. The default was 10000. The server did not check the value.

Background

Since v0.5.1 the number is fixed at 10000, the former default. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'json.block_cache.ghost_capacity' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

small_ratio json.block_cache.small_ratio

Since v0.1.0Removed in v0.5.1
Share of the block cache of the JSON engine that the small queue could use.
Default
—
Effect

The share of json.block_cache.capacity_bytesremoved that the small queue of the block cache of the JSON engine could use, as a number from 0 to 1. The cache works as an S3-FIFO cache: a block that was read for the first time entered the small queue, and a block that was read again moved to the main queue, so one pass over a large range did not push the blocks out that were read often. The default was 0.10. The server did not check the value.

Background

Since v0.5.1 the ratio is fixed at 0.10, the former default. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'json.block_cache.small_ratio' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

bulk_batch_size json.bulk_batch_size

Since v0.1.0Removed in v0.5.1
Number of documents that a JSON bulk import wrote in one batch.
Default
—
Effect

The number of documents that a bulk import into a JSON domain (POST /store-api/json/{domain}/bulk) wrote in one atomic batch. A key that appeared twice in the input started a new batch. A value of 0 was treated as 1. The default was 100.

Background

Since v0.5.1 the batch is fixed at 100 documents, the former default. The size of the request is still limited by json.bulk_body_limit_bytes. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'json.bulk_batch_size' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
Limits and defaults

l0_threshold json.compaction.l0_threshold

Since v0.1.0Removed in v0.5.1
Number of level-0 SSTables at which the JSON engine compacted level 0.
Default
—
Effect

The number of SSTables at level 0 at which the compaction loop of the JSON engine started a compaction of level 0 into level 1. The comparison was “at least”, and every flush of a MemTable added one file at level 0. The default was 4. The server did not check the value.

Background

Since v0.5.1 the JSON engine compacts with the built-in value 4, the former default, and it cannot be changed. The key-value engine keeps its own setting, compaction.l0_threshold. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'json.compaction.l0_threshold' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

l1_max_size json.compaction.l1_max_size

Since v0.1.0Removed in v0.5.1
Total size in bytes of level 1 at which the JSON engine compacted it.
Default
—
Effect

The total size, in bytes, of the SSTables at level 1 at which the compaction loop of the JSON engine started a compaction of level 1 into level 2. Deeper levels were limited to this size multiplied by json.compaction.level_size_ratioremoved once for each level below level 1. The default was 104857600 (100 MiB). The server did not check the value.

Background

Since v0.5.1 the JSON engine uses the built-in limit of 100 MiB, the former default, and it cannot be changed. The key-value engine keeps its own setting, compaction.l1_max_size. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'json.compaction.l1_max_size' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

level_size_ratio json.compaction.level_size_ratio

Since v0.1.0Removed in v0.5.1
Factor by which the size limit of the JSON engine grew from one level to the next.
Default
—
Effect

The factor by which the size limit of a level grew from one level to the next: level 1 was limited by json.compaction.l1_max_sizeremoved, and level n from 2 on to that value times this factor to the power n - 1. The default was 10. The server did not check the value.

Background

Since v0.5.1 the JSON engine uses the built-in factor 10, the former default, and it cannot be changed. The key-value engine keeps its own setting, compaction.level_size_ratio. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'json.compaction.level_size_ratio' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

max_sstable_size json.compaction.max_sstable_size

Since v0.1.0Removed in v0.5.1
Size at which a compaction of the JSON engine started a new output file.
Default
—
Effect

The size at which a compaction of the JSON engine closed an output file and started the next one. The size was summed from the lengths of the keys and values, a value in the value log counting as its pointer, and the file was closed at the next boundary between two user keys. The files of a flush were not cut by this key. The default was 67108864 (64 MiB). The server did not check the value.

Background

Since v0.5.1 the JSON engine uses the built-in size of 64 MiB, the former default, and it cannot be changed. The key-value engine keeps its own setting, compaction.max_sstable_size. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'json.compaction.max_sstable_size' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

check_interval_secs json.janitor.check_interval_secs

Since v0.1.0Removed in v0.5.1
Seconds between two checks of the value log janitor of the JSON engine.
Default
—
Effect

The pause, in seconds, between two checks of the janitor of the JSON engine, the task that cleans the value log. Each check estimated how much of the log was dead and cleaned it when the estimate reached json.janitor.dead_bytes_thresholdremoved and the log had at least json.janitor.min_vlog_size_bytesremoved bytes. The default was 60. The server did not check the value.

Background

Since v0.5.1 the JSON engine checks every 60 seconds, the former default, and it cannot be changed. The key-value engine keeps its own setting, janitor.check_interval_secs. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'json.janitor.check_interval_secs' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

dead_bytes_threshold json.janitor.dead_bytes_threshold

Since v0.1.0Removed in v0.5.1
Share of dead space in the value log at which the JSON engine cleaned it.
Default
—
Effect

The share of the value log that had to be dead, as a number from 0 to 1, before the janitor of the JSON engine cleaned the log. The janitor estimated the share from the size of the log and the bytes that the pointers in the MemTables and SSTables still referenced. The default was 0.30. The server did not check the value.

Background

Since v0.5.1 the JSON engine uses the built-in threshold 0.30, the former default, and it cannot be changed. The key-value engine keeps its own setting, janitor.dead_bytes_threshold. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'json.janitor.dead_bytes_threshold' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

min_vlog_size_bytes json.janitor.min_vlog_size_bytes

Since v0.1.0Removed in v0.5.1
Size in bytes that the value log of the JSON engine needed before it was cleaned.
Default
—
Effect

The size, in bytes, that the value log of the JSON engine needed before the janitor could clean it, however much of it was dead. The default was 67108864 (64 MiB). The server did not check the value.

Background

Since v0.5.1 the JSON engine uses the built-in minimum of 64 MiB, the former default, and it cannot be changed. The key-value engine keeps its own setting, janitor.min_vlog_size_bytes. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'json.janitor.min_vlog_size_bytes' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

compaction_check_interval_ms json.lsm.compaction_check_interval_ms

Since v0.1.0Removed in v0.5.1
Pause in milliseconds between two turns of the compaction loop of the JSON engine.
Default
—
Effect

The pause, in milliseconds, between two turns of the compaction loop of the JSON engine. In each turn the loop checked the levels of the SSTables against their limits, ran a compaction if a level was over its limit, and then slept for this long. The default was 1000. The server did not check the value.

Background

Since v0.5.1 the pause is fixed at 1000 milliseconds, the former default. The key-value engine keeps its own setting, lsm.compaction_check_interval_ms. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'json.lsm.compaction_check_interval_ms' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

flush_check_interval_ms json.lsm.flush_check_interval_ms

Since v0.1.0Removed in v0.5.1
Pause in milliseconds between two turns of the flush loop of the JSON engine.
Default
—
Effect

The pause, in milliseconds, between two turns of the flush loop of the JSON engine. In each turn the loop wrote a frozen MemTable to an SSTable, if there was one, and then slept for this long. The default was 100. The server did not check the value.

Background

Since v0.5.1 the pause is fixed at 100 milliseconds, the former default. The key-value engine keeps its own setting, lsm.flush_check_interval_ms. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'json.lsm.flush_check_interval_ms' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

memtable_size_threshold json.lsm.memtable_size_threshold

Since v0.1.0Removed in v0.5.1
Size in bytes at which a MemTable of the JSON engine was flushed.
Default
—
Effect

The size, in bytes, at which the active MemTable of the JSON engine was frozen and queued to be written to an SSTable at level 0. The size counted the encoded keys and the values kept inline, and a value in the value log counted only with its pointer. The default was 4194304 (4 MiB). The server did not check the value.

Background

Since v0.5.1 the JSON engine uses the built-in size of 4 MiB, the former default, and it cannot be changed. The key-value engine keeps its own setting, lsm.memtable_size_threshold. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'json.lsm.memtable_size_threshold' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

use_mmap json.lsm.use_mmap

Since v0.1.0Removed in v0.5.1
Whether the JSON engine memory-mapped its SSTables.
Default
—
Effect

Whether the JSON engine opened its SSTables as memory-mapped files (true, the default) or read each file completely into memory (false). The choice applied to every SSTable the engine opened: at start, after a flush, and after a compaction or a cleanup of the value log. The server did not check the value.

Background

Since v0.5.1 the SSTables of every engine are always memory-mapped. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'json.lsm.use_mmap' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

vlog_inline_threshold json.lsm.vlog_inline_threshold

Since v0.1.0Removed in v0.5.1
Value size in bytes from which the JSON engine stored values in the value log.
Default
—
Effect

A value of at least this many bytes was appended to the value log of the JSON engine when it was written; the MemTable and the SSTables then kept only a pointer to it. A shorter value stayed inline. The default was 1024. The server did not check the value.

Background

Since v0.5.1 the JSON engine uses the built-in value 1024, the former default, and it cannot be changed. The key-value engine keeps its own setting, lsm.vlog_inline_threshold. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'json.lsm.vlog_inline_threshold' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

wal_event_channel_capacity json.lsm.wal_event_channel_capacity

Since v0.1.0Removed in v0.5.1
Capacity of the write-event channel of the JSON engine.
Default
—
Effect

The capacity of the broadcast channel on which the engine of the JSON engine published its write events. Only the watch of the key-value engine read events from such a channel, so for the JSON engine the key sized a channel that nothing listened to. The default was 256. The server did not check the value.

Background

Since v0.5.1 the channel has the built-in capacity of 256, the former default. The key-value engine keeps its own setting, lsm.wal_event_channel_capacity. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'json.lsm.wal_event_channel_capacity' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

watch_replay_buffer_size json.lsm.watch_replay_buffer_size

Since v0.4.0Removed in v0.5.1
Size of the watch replay ring of the JSON engine, which the server never used.
Default
—
Effect

The size of the ring that keeps change events for clients that reconnect to a watch stream. The JSON engine has no watch, and the server set the ring size of its engine to 0 whatever this key said, so the value was read from the file and never used. The default was 1024.

Background

Watch streams of data changes exist for the key-value engine only; their replay ring is set by lsm.watch_replay_buffer_size. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'json.lsm.watch_replay_buffer_size' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

max_document_key_length json.max_document_key_length

Since v0.1.0Removed in v0.5.1
Longest document key of the JSON engine.
Default
—
Effect

The longest key, in characters, that a client could give a JSON document. A longer key was refused with 400 and invalid document key: key length N exceeds maximum of M characters, and a bulk import listed it under errors. The limit was the smaller of this value and json.lsm.max_key_length minus 21, the prefix with which the engine stores a document. The default was 256. The server did not check the value.

Background

Since v0.5.1 the cap is fixed at 256. The key length is still limited by json.lsm.max_key_length, which leaves 21 bytes less for the document key: with its default of 256 the longest document key is 235 characters. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'json.max_document_key_length' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
Limits and defaults

purger_batch_size json.purger_batch_size

Since v0.1.0Removed in v0.5.1
Number of keys that one purge step removed from a deleted JSON domain.
Default
—
Effect

The number of keys that one purge step of the JSON engine removed from a deleted domain. A deleted domain is unreachable at once, and a background task removes its keys in steps of this size: documents first, then index entries; once nothing is left the task removes the domain itself. A value of 0 was treated as 1. The default was 100.

Background

Since v0.5.1 the step is fixed at 100 keys, the former default; the purge of the key-value engine is set by domains.purger_batch_size. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'json.purger_batch_size' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

purger_interval_secs json.purger_interval_secs

Since v0.1.0Removed in v0.5.1
Seconds between two purge steps of the JSON engine.
Default
—
Effect

The number of seconds between two purge steps of the JSON engine, the background task that removes the keys of deleted domains in steps of json.purger_batch_sizeremoved. The default was 5. The server did not check the value.

Background

Since v0.5.1 the pause is fixed at 5 seconds, the former default; the purge of the key-value engine is set by domains.purger_interval_secs. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'json.purger_interval_secs' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

reindex_batch_size json.reindex_batch_size

Since v0.1.0Removed in v0.5.1
Number of documents that an index rebuild processed in one step.
Default
—
Effect

The number of documents that a rebuild of the indexes of a JSON domain (POST /store-api/json/{domain}/reindex) processed in one step. A value of 0 was treated as 1. The default was 500.

Background

Since v0.5.1 the batch is fixed at 500 documents, the former default. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'json.reindex_batch_size' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
Limits and defaults

reindex_pause_ms json.reindex_pause_ms

Since v0.1.0Removed in v0.5.1
Pause in milliseconds between two steps of an index rebuild.
Default
—
Effect

The pause, in milliseconds, that an index rebuild of a JSON domain made after each step of json.reindex_batch_sizeremoved documents, so that other requests could run in between. 0 meant no pause. The default was 10.

Background

Since v0.5.1 the pause is fixed at 10 milliseconds, the former default. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'json.reindex_pause_ms' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

domains log.modules.domains

Since v0.1.0Removed in v0.5.1
Log level of the domain registry module of the key-value engine.
Default
—
Effect

The log level of the module luradb::engines::lsm::domain, the domain registry of the key-value engine, in place of log.level. The values were verbose, info and prod, the same as for the global level; they set the filter to debug, info and warn. Without the key the module followed the global level. Any other value refused the start with a parse error of the configuration file.

Background

Since v0.5.1 the module has no setting of its own. Its name begins with luradb::engines, so log.modules.engine sets its level together with that of the other engine modules. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'log.modules.domains' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

use_mmap lsm.use_mmap

Since v0.1.0Removed in v0.5.1
Whether the key-value engine memory-mapped its SSTables.
Default
—
Effect

Whether the key-value engine opened its SSTables as memory-mapped files (true, the default) or read each file completely into memory (false). The choice applied to every SSTable the engine opened: at start, after a flush, and after a compaction or a cleanup of the value log. The server did not check the value.

Background

Since v0.5.1 the SSTables of every engine are always memory-mapped. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'lsm.use_mmap' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
Upgrading

ticker_interval_ms metrics.ticker_interval_ms

Since v0.1.0Removed in v0.5.1
Interval in milliseconds at which the metrics windows advance.
Default
—
Effect

How often, in milliseconds, the background ticker of the metrics store closed the current snapshot of every window and opened the next one. The default was 1000, one snapshot per second. A window held metrics.window_secsremoved snapshots, so another interval changed the time that the window covered while the answer still reported window_secs: with 500 the 60 snapshots covered 30 seconds. The server did not check the value.

Background

Since v0.5.1 the ticker runs once per second. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'metrics.ticker_interval_ms' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

window_secs metrics.window_secs

Since v0.1.0Removed in v0.5.1
Width in seconds of the rolling window of the metrics endpoint.
Default
—
Effect

The width of the rolling window, in seconds, over which GET /store-api/metrics counted operations and computed latency percentiles, for each domain and each engine. The window kept this many snapshots, one for each tick of the metrics ticker (metrics.ticker_interval_msremoved), and added them up; the answer repeats the number as window_secs. The default was 60. The server did not check the value.

Background

Since v0.5.1 the window is always 60 seconds, and the answers report window_secs as 60. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'metrics.window_secs' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

engine_queue_capacity multicore.engine_queue_capacity

Removed in v0.5.1
Length of the queue to the single engine thread, in builds before v0.5.0-rc.1.
Default
—
Effect

The length of the queue through which the frontend threads handed requests to the engine thread, when the storage engines still ran on a single thread. A full queue held further requests back. The default was 1024, and the value had to be at least 1: invalid config: multicore.engine_queue_capacity (0) must be at least 1 otherwise. The key existed only in development builds between v0.4.0 and v0.5.0-rc.1; no tagged release read it.

Background

multicore.shard_queue_capacity replaces it. With data shards (multicore.shards) every shard has a queue of its own: the key sets how many requests a shard takes at once, running or waiting, and a request that finds the queue full is answered with 503 and Retry-After: 1 instead of waiting. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'multicore.engine_queue_capacity' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

capacity_bytes rel.block_cache.capacity_bytes

Since v0.1.0Removed in v0.5.1
Maximum size in bytes of the block cache of the relational engine.
Default
—
Effect

The maximum total size, in bytes, of the block cache of the relational engine, the cache of SSTable data blocks that sat between the MemTable and the files. Blocks were evicted once the cached blocks together exceeded the value. The default was 67108864 (64 MiB). The server did not check the value.

Background

Since v0.5.1 each engine instance of each data shard has a block cache of its own, and the built-in capacity of 64 MiB is divided by the number of shards (multicore.shards); with 8 shards the start log shows block cache 8192 KiB per engine. The caches count toward the fixed memory that resources.memory_budget is compared with. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'rel.block_cache.capacity_bytes' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

ghost_capacity rel.block_cache.ghost_capacity

Since v0.1.0Removed in v0.5.1
Number of evicted block identifiers that the block cache of the relational engine remembered.
Default
—
Effect

The number of identifiers of evicted blocks, without their data, that the block cache of the relational engine remembered. A block that was requested again while its identifier was still remembered went straight to the main queue. The default was 10000. The server did not check the value.

Background

Since v0.5.1 the number is fixed at 10000, the former default. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'rel.block_cache.ghost_capacity' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

small_ratio rel.block_cache.small_ratio

Since v0.1.0Removed in v0.5.1
Share of the block cache of the relational engine that the small queue could use.
Default
—
Effect

The share of rel.block_cache.capacity_bytesremoved that the small queue of the block cache of the relational engine could use, as a number from 0 to 1. The cache works as an S3-FIFO cache: a block that was read for the first time entered the small queue, and a block that was read again moved to the main queue, so one pass over a large range did not push the blocks out that were read often. The default was 0.10. The server did not check the value.

Background

Since v0.5.1 the ratio is fixed at 0.10, the former default. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'rel.block_cache.small_ratio' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

l0_threshold rel.compaction.l0_threshold

Since v0.1.0Removed in v0.5.1
Number of level-0 SSTables at which the relational engine compacted level 0.
Default
—
Effect

The number of SSTables at level 0 at which the compaction loop of the relational engine started a compaction of level 0 into level 1. The comparison was “at least”, and every flush of a MemTable added one file at level 0. The default was 4. The server did not check the value.

Background

Since v0.5.1 the relational engine compacts with the built-in value 4, the former default, and it cannot be changed. The key-value engine keeps its own setting, compaction.l0_threshold. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'rel.compaction.l0_threshold' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

l1_max_size rel.compaction.l1_max_size

Since v0.1.0Removed in v0.5.1
Total size in bytes of level 1 at which the relational engine compacted it.
Default
—
Effect

The total size, in bytes, of the SSTables at level 1 at which the compaction loop of the relational engine started a compaction of level 1 into level 2. Deeper levels were limited to this size multiplied by rel.compaction.level_size_ratioremoved once for each level below level 1. The default was 104857600 (100 MiB). The server did not check the value.

Background

Since v0.5.1 the relational engine uses the built-in limit of 100 MiB, the former default, and it cannot be changed. The key-value engine keeps its own setting, compaction.l1_max_size. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'rel.compaction.l1_max_size' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

level_size_ratio rel.compaction.level_size_ratio

Since v0.1.0Removed in v0.5.1
Factor by which the size limit of the relational engine grew from one level to the next.
Default
—
Effect

The factor by which the size limit of a level grew from one level to the next: level 1 was limited by rel.compaction.l1_max_sizeremoved, and level n from 2 on to that value times this factor to the power n - 1. The default was 10. The server did not check the value.

Background

Since v0.5.1 the relational engine uses the built-in factor 10, the former default, and it cannot be changed. The key-value engine keeps its own setting, compaction.level_size_ratio. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'rel.compaction.level_size_ratio' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

max_sstable_size rel.compaction.max_sstable_size

Since v0.1.0Removed in v0.5.1
Size at which a compaction of the relational engine started a new output file.
Default
—
Effect

The size at which a compaction of the relational engine closed an output file and started the next one. The size was summed from the lengths of the keys and values, a value in the value log counting as its pointer, and the file was closed at the next boundary between two user keys. The files of a flush were not cut by this key. The default was 67108864 (64 MiB). The server did not check the value.

Background

Since v0.5.1 the relational engine uses the built-in size of 64 MiB, the former default, and it cannot be changed. The key-value engine keeps its own setting, compaction.max_sstable_size. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'rel.compaction.max_sstable_size' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

cross_engine_sweep_batch_size rel.cross_engine_sweep_batch_size

Since v0.1.0Removed in v0.5.1
Number of cells that the sweeper for links nulled per run.
Default
—
Effect

The largest number of cells that the sweeper of rel.cross_engine_sweep_interval_secsremoved set to NULL in one run for one link column of one domain; a larger backlog took several runs. A value of 0 was treated as 1. The default was 100.

Background

Since v0.5.1 the batch is fixed at 100 cells, the former default. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'rel.cross_engine_sweep_batch_size' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
Limits and defaults

cross_engine_sweep_interval_secs rel.cross_engine_sweep_interval_secs

Since v0.1.0Removed in v0.5.1
Seconds between two runs of the sweeper for links to deleted domains.
Default
—
Effect

The pause, in seconds, between two runs of the sweeper that cleaned up links from the relational engine to the other engines. When the key-value or JSON domain that a KVREF or JSONREF column points to is deleted, reads show the cell as NULL; the sweeper then set such cells to NULL in the stored rows. It ran only when at least one engine that rows could link to was running. A value of 0 was treated as 1. The default was 10.

Background

Since v0.5.1 the sweeper runs every 10 seconds, the former default. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'rel.cross_engine_sweep_interval_secs' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
Limits and defaults

default_limit rel.default_limit

Since v0.1.0Removed in v0.5.1
Number of rows returned when a query has no LIMIT.
Default
—
Effect

The number of rows that a SELECT without LIMIT, and the row endpoint of a table without a limit parameter, returned at most; rel.max_limit capped it. When more rows existed, the answer said so with "limit_applied": true. The default was 1000. The server did not check the value.

Background

Since v0.5.1 a query without a limit returns at most 1,000 rows, the former default, or rel.max_limit if that is smaller. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'rel.default_limit' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
Limits and defaults

check_interval_secs rel.janitor.check_interval_secs

Since v0.1.0Removed in v0.5.1
Seconds between two checks of the value log janitor of the relational engine.
Default
—
Effect

The pause, in seconds, between two checks of the janitor of the relational engine, the task that cleans the value log. Each check estimated how much of the log was dead and cleaned it when the estimate reached rel.janitor.dead_bytes_thresholdremoved and the log had at least rel.janitor.min_vlog_size_bytesremoved bytes. The default was 60. The server did not check the value.

Background

Since v0.5.1 the relational engine checks every 60 seconds, the former default, and it cannot be changed. The key-value engine keeps its own setting, janitor.check_interval_secs. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'rel.janitor.check_interval_secs' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

dead_bytes_threshold rel.janitor.dead_bytes_threshold

Since v0.1.0Removed in v0.5.1
Share of dead space in the value log at which the relational engine cleaned it.
Default
—
Effect

The share of the value log that had to be dead, as a number from 0 to 1, before the janitor of the relational engine cleaned the log. The janitor estimated the share from the size of the log and the bytes that the pointers in the MemTables and SSTables still referenced. The default was 0.30. The server did not check the value.

Background

Since v0.5.1 the relational engine uses the built-in threshold 0.30, the former default, and it cannot be changed. The key-value engine keeps its own setting, janitor.dead_bytes_threshold. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'rel.janitor.dead_bytes_threshold' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

min_vlog_size_bytes rel.janitor.min_vlog_size_bytes

Since v0.1.0Removed in v0.5.1
Size in bytes that the value log of the relational engine needed before it was cleaned.
Default
—
Effect

The size, in bytes, that the value log of the relational engine needed before the janitor could clean it, however much of it was dead. The default was 67108864 (64 MiB). The server did not check the value.

Background

Since v0.5.1 the relational engine uses the built-in minimum of 64 MiB, the former default, and it cannot be changed. The key-value engine keeps its own setting, janitor.min_vlog_size_bytes. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'rel.janitor.min_vlog_size_bytes' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

compaction_check_interval_ms rel.lsm.compaction_check_interval_ms

Since v0.1.0Removed in v0.5.1
Pause in milliseconds between two turns of the compaction loop of the relational engine.
Default
—
Effect

The pause, in milliseconds, between two turns of the compaction loop of the relational engine. In each turn the loop checked the levels of the SSTables against their limits, ran a compaction if a level was over its limit, and then slept for this long. The default was 1000. The server did not check the value.

Background

Since v0.5.1 the pause is fixed at 1000 milliseconds, the former default. The key-value engine keeps its own setting, lsm.compaction_check_interval_ms. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'rel.lsm.compaction_check_interval_ms' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

flush_check_interval_ms rel.lsm.flush_check_interval_ms

Since v0.1.0Removed in v0.5.1
Pause in milliseconds between two turns of the flush loop of the relational engine.
Default
—
Effect

The pause, in milliseconds, between two turns of the flush loop of the relational engine. In each turn the loop wrote a frozen MemTable to an SSTable, if there was one, and then slept for this long. The default was 100. The server did not check the value.

Background

Since v0.5.1 the pause is fixed at 100 milliseconds, the former default. The key-value engine keeps its own setting, lsm.flush_check_interval_ms. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'rel.lsm.flush_check_interval_ms' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

max_value_size rel.lsm.max_value_size

Since v0.1.0Removed in v0.5.1
Largest value in bytes that the storage of the relational engine accepted.
Default
—
Effect

The largest value, in bytes, that the storage of the relational engine accepted. A write of a value above it, such as a row, was refused by the engine with Value length N exceeds maximum of M bytes; rel.max_row_size limited the encoded row separately. The default was 524288 (512 KiB). The value must not exceed 100663296 bytes (96 MiB), the largest field that the recovery of the write-ahead log reads; the start was refused with invalid config: rel.lsm.max_value_size (…) exceeds the WAL recovery field cap (100663296 bytes) — writes this large could never be recovered otherwise.

Background

rel.max_row_size replaces it. Since v0.5.1 the storage limit of the relational engine follows that key: it is the larger of 512 KiB and rel.max_row_size, so one key sets the size of rows. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'rel.lsm.max_value_size' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

memtable_size_threshold rel.lsm.memtable_size_threshold

Since v0.1.0Removed in v0.5.1
Size in bytes at which a MemTable of the relational engine was flushed.
Default
—
Effect

The size, in bytes, at which the active MemTable of the relational engine was frozen and queued to be written to an SSTable at level 0. The size counted the encoded keys and the values kept inline, and a value in the value log counted only with its pointer. The default was 4194304 (4 MiB). The server did not check the value.

Background

Since v0.5.1 the relational engine uses the built-in size of 4 MiB, the former default, and it cannot be changed. The key-value engine keeps its own setting, lsm.memtable_size_threshold. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'rel.lsm.memtable_size_threshold' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

use_mmap rel.lsm.use_mmap

Since v0.1.0Removed in v0.5.1
Whether the relational engine memory-mapped its SSTables.
Default
—
Effect

Whether the relational engine opened its SSTables as memory-mapped files (true, the default) or read each file completely into memory (false). The choice applied to every SSTable the engine opened: at start, after a flush, and after a compaction or a cleanup of the value log. The server did not check the value.

Background

Since v0.5.1 the SSTables of every engine are always memory-mapped. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'rel.lsm.use_mmap' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

vlog_inline_threshold rel.lsm.vlog_inline_threshold

Since v0.1.0Removed in v0.5.1
Value size in bytes from which the relational engine stored values in the value log.
Default
—
Effect

A value of at least this many bytes was appended to the value log of the relational engine when it was written; the MemTable and the SSTables then kept only a pointer to it. A shorter value stayed inline. The default was 1024. The server did not check the value.

Background

Since v0.5.1 the relational engine uses the built-in value 1024, the former default, and it cannot be changed. The key-value engine keeps its own setting, lsm.vlog_inline_threshold. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'rel.lsm.vlog_inline_threshold' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

wal_event_channel_capacity rel.lsm.wal_event_channel_capacity

Since v0.1.0Removed in v0.5.1
Capacity of the write-event channel of the relational engine.
Default
—
Effect

The capacity of the broadcast channel on which the engine of the relational engine published its write events. Only the watch of the key-value engine read events from such a channel, so for the relational engine the key sized a channel that nothing listened to. The default was 256. The server did not check the value.

Background

Since v0.5.1 the channel has the built-in capacity of 256, the former default. The key-value engine keeps its own setting, lsm.wal_event_channel_capacity. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'rel.lsm.wal_event_channel_capacity' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

watch_replay_buffer_size rel.lsm.watch_replay_buffer_size

Since v0.4.0Removed in v0.5.1
Size of the watch replay ring of the relational engine, which the server never used.
Default
—
Effect

The size of the ring that keeps change events for clients that reconnect to a watch stream. The relational engine has no watch, and the server set the ring size of its engine to 0 whatever this key said, so the value was read from the file and never used. The default was 1024.

Background

Watch streams of data changes exist for the key-value engine only; their replay ring is set by lsm.watch_replay_buffer_size. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'rel.lsm.watch_replay_buffer_size' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

max_columns rel.max_columns

Since v0.1.0Removed in v0.5.1
Largest number of columns of a table.
Default
—
Effect

The largest number of columns a table could have. CREATE TABLE with more columns, ALTER TABLE … ADD COLUMN on a table that already had this many, and the creation of a table from a file with more columns were refused with limit exceeded: max_columns (max N); the REST API answered 400. The default was 128. The server did not check the value.

Background

Since v0.5.1 the limit is fixed at 128, the former default. rel.max_tables_per_domain still limits the tables of a domain. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'rel.max_columns' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
Limits and defaults

max_indexes_per_table rel.max_indexes_per_table

Since v0.1.0Removed in v0.5.1
Largest number of indexes of a table.
Default
—
Effect

The largest number of indexes a table could have. CREATE TABLE with more UNIQUE columns than this (the primary key does not count), and CREATE INDEX on a table that already had this many indexes, were refused with limit exceeded: max_indexes_per_table (max N); the REST API answered 400. The default was 16. The server did not check the value.

Background

Since v0.5.1 the limit is fixed at 16, the former default. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'rel.max_indexes_per_table' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
Limits and defaults

max_join_depth rel.max_join_depth

Since v0.1.0Removed in v0.5.1
Largest number of join stages of a statement.
Default
—
Effect

The largest number of join stages that one statement could use. Each LEFT JOIN counted as one, and so did each expanded link column; a statement over the limit was refused with 400 and join depth N exceeds the maximum of M. A view was checked when it was created. The default was 8. The server did not check the value.

Background

Since v0.5.1 the limit is fixed at 8, the former default. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'rel.max_join_depth' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
Limits and defaults

max_response_bytes rel.max_response_bytes

Since v0.1.0Removed in v0.5.1
Largest serialized response of a SQL statement or row listing in bytes.
Default
—
Effect

The largest serialized response, in bytes, of a POST /store-api/rel/{domain}/sql statement or of the row endpoint of a table, expanded links included. A larger response was refused with 413 and Payload Too Large: response exceeds max_response_bytes; reduce LIMIT. The default was 33554432 (32 MiB). The server did not check the value.

Background

Since v0.5.1 the limit is fixed at 32 MiB, the former default. Keep the result below it with LIMIT; rel.max_limit caps the number of rows. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'rel.max_response_bytes' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
Limits and defaults

max_statement_len rel.max_statement_len

Since v0.1.0Removed in v0.5.1
Longest SQL statement in bytes.
Default
—
Effect

The longest SQL statement, in bytes, that the relational engine accepted. A longer statement was refused before it was parsed, with 400 and statement length N exceeds maximum of M bytes. The default was 65536 (64 KiB). The server did not check the value.

Background

Since v0.5.1 the limit is fixed at 64 KiB, the former default. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'rel.max_statement_len' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
Limits and defaults

max_text_len rel.max_text_len

Since v0.1.0Removed in v0.5.1
Longest TEXT value in bytes.
Default
—
Effect

The longest value, in bytes, of a TEXT column, including the link columns KVREF and JSONREF, that an INSERT or UPDATE could write. A longer value was refused with 400 and text length N exceeds maximum of M bytes. The default was 65536 (64 KiB). The server did not check the value.

Background

Since v0.5.1 the limit is fixed at 64 KiB, the former default. rel.max_row_size still limits the whole row. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'rel.max_text_len' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
Limits and defaults

purger_batch_size rel.purger_batch_size

Since v0.1.0Removed in v0.5.1
Number of keys that one purge step removed from a deleted relational domain.
Default
—
Effect

The number of keys that one purge step of the relational engine removed from a deleted domain. A deleted domain is unreachable at once, and a background task removes its keys in steps of this size: rows, index entries and AUTOINCREMENT counters; once nothing is left the task removes the domain itself. The same task also reaped the data of dropped tables and indexes, with the same step size. A value of 0 was treated as 1. The default was 100.

Background

Since v0.5.1 the step is fixed at 100 keys, the former default; the purge of the key-value engine is set by domains.purger_batch_size. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'rel.purger_batch_size' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

purger_interval_secs rel.purger_interval_secs

Since v0.1.0Removed in v0.5.1
Seconds between two purge steps of the relational engine.
Default
—
Effect

The number of seconds between two purge steps of the relational engine, the background task that removes the keys of deleted domains in steps of rel.purger_batch_sizeremoved. The default was 5. The server did not check the value.

Background

Since v0.5.1 the pause is fixed at 5 seconds, the former default; the purge of the key-value engine is set by domains.purger_interval_secs. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'rel.purger_interval_secs' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

unix_socket_mode server.unix_socket_mode

Since v0.1.0Removed in v0.5.1
File mode of the Unix socket.
Default
—
Effect

The file mode that the server gave the socket named by server.unix_socket_path once it had bound it. The value was a whole number, written in decimal in TOML: 432 is 0o660. Without the key the mode was 0o660, which lets the owner and the group of the socket read and write it. The key had no effect while no socket path was set. The server did not check the value.

Background

Who may connect to the socket was decided by this mode together with the owner and the group of the socket and the permissions of its directory. Since v0.5.1 the mode is always 0o660; give clients access through the owner or the group of the socket. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'server.unix_socket_mode' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
Limits and defaults

command_buffer_size shm.command_buffer_size

Since v0.1.0Removed in v0.5.1
Size in bytes of the command and response rings of each shared-memory client.
Default
—
Effect

The size, in bytes, of the ring buffers that each client of the shared-memory interface received when it registered, for commands to the server and for responses. It applied only with shm.enabled set to true. The default was 4194304 (4 MiB). The value had to be a power of two and at least 4096; otherwise the start was refused with shm.command_buffer_size must be a power of two, got N or shm.command_buffer_size must be at least 4096 bytes, got N.

Background

Since v0.5.1 the rings have the fixed size of 4 MiB. The size of the snapshot buffers is still set by shm.data_buffer_size. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'shm.command_buffer_size' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

segment_mode shm.segment_mode

Since v0.1.0Removed in v0.5.1
File mode of the shared-memory segments.
Default
—
Effect

The file mode that the server gave the shared-memory segments it created, the state and data segments and the rings of each client. The value was a whole number, written in decimal in TOML: 432 is 0o660. It applied only with shm.enabled set to true. The default was 0o660 (432). The server did not check the value.

Background

Since v0.5.1 the mode is always 0o660, so the segments cannot be opened up to other users through the configuration. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'shm.segment_mode' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—

state_size shm.state_size

Since v0.1.0Removed in v0.5.1
Size in bytes of the state segment of the shared-memory interface.
Default
—
Effect

The size, in bytes, of the shared-memory segment state, which holds the state header of the shared-memory interface. It applied only with shm.enabled set to true. The default was 4096. The value had to be at least the size of the state header; otherwise the start was refused with shm.state_size must be at least N bytes, got M.

Background

Since v0.5.1 the segment has the fixed size of 4096 bytes. The v0.5.1 server accepts a luradb.toml that still sets the key: it starts normally, ignores the value and logs unknown config key 'shm.state_size' is ignored as a warning, once, at startup. Remove the line to silence the warning.

Referenced on
—