Skip to content

rocks_params

Specifies additional tuning parameters for RocksDB when kind = "RocksDB".

The following parameters are possible:

increase_parallelism

Since: Version 2023.03.31-36aa20de

The functionality described in this section requires version 2023.03.31-36aa20de of KumoMTA, or a more recent version.

By default, RocksDB uses only one background thread for flush and compaction. You can use this option to increase the number of threads available for this purpose. A good number is the number of cores available to the system:

kumo.on('init', function()
  local params = {
    increase_parallelism = kumo.available_parallelism(),
  }
  kumo.define_spool {
    name = 'data',
    path = '/var/spool/kumo/data',
    kind = 'RocksDB',
    rocks_params = params,
  }
  kumo.define_spool {
    name = 'meta',
    path = '/var/spool/kumo/meta',
    kind = 'RocksDB',
    rocks_params = params,
  }
end)

optimize_level_style_compaction

Since: Version 2023.03.31-36aa20de

The functionality described in this section requires version 2023.03.31-36aa20de of KumoMTA, or a more recent version.

When set, its value is the number of bytes to use for the memtable_memory_budget and enable the use of level-style compaction.

Level style compaction is the default.

Larger values allocate more memory to use for write and compaction buffers and can increase throughput to RocksDB.

This option is incompatible with optimize_universal_style_compaction.

kumo.on('init', function()
  local params = {
    -- Use 64MB as the base write_buffer size
    optimize_level_style_compaction = 64 * 1024 * 1024,
  }
  kumo.define_spool {
    name = 'data',
    path = '/var/spool/kumo/data',
    kind = 'RocksDB',
    rocks_params = params,
  }
  kumo.define_spool {
    name = 'meta',
    path = '/var/spool/kumo/meta',
    kind = 'RocksDB',
    rocks_params = params,
  }
end)

optimize_universal_style_compaction

Since: Version 2023.03.31-36aa20de

The functionality described in this section requires version 2023.03.31-36aa20de of KumoMTA, or a more recent version.

When set, its value is the number of bytes to use for the memtable_memory_budget and enable the use of universal-style compaction.

Larger values allocate more memory to use for write and compaction buffers and can increase throughput to RocksDB.

This option is incompatible with optimize_level_style_compaction.

kumo.on('init', function()
  local params = {
    -- Use 64MB as the base write_buffer size
    optimize_universal_style_compaction = 64 * 1024 * 1024,
  }
  kumo.define_spool {
    name = 'data',
    path = '/var/spool/kumo/data',
    kind = 'RocksDB',
    rocks_params = params,
  }
  kumo.define_spool {
    name = 'meta',
    path = '/var/spool/kumo/meta',
    kind = 'RocksDB',
    rocks_params = params,
  }
end)

paranoid_checks

Since: Version 2023.03.31-36aa20de

The functionality described in this section requires version 2023.03.31-36aa20de of KumoMTA, or a more recent version.

When true, RocksDB performs additional consistency checks during open and on every read/write operation, including block-level checksum verification. This catches more on-disk corruption at the cost of additional CPU. Defaults to false.

compression_type

Since: Version 2023.03.31-36aa20de

The functionality described in this section requires version 2023.03.31-36aa20de of KumoMTA, or a more recent version.

Controls the compression algorithm applied to SST blocks.

The official kumod builds link a statically-built RocksDB that includes only the "None" and "Snappy" (the default) backends. Other values are accepted by the configuration parser but will fail at runtime unless you are running a custom build of kumod that links a RocksDB compiled with the corresponding compression backend enabled:

Value Available in official builds?
"None" yes
"Snappy" (default) yes
"Zlib" no -- requires a custom build
"Bz2" no -- requires a custom build
"Lz4" no -- requires a custom build
"Lz4hc" no -- requires a custom build
"Zstd" no -- requires a custom build

Snappy offers a good balance of CPU cost and compression ratio for typical message payloads. "None" is appropriate when the underlying storage is already compressed (e.g. a btrfs or ZFS volume with compression enabled) or when CPU is tightly constrained.

Changing compression_type on an existing spool

The compression algorithm is recorded per-SST at write time; RocksDB happily mixes SSTs written with different algorithms in the same database. Changing this option therefore does not require any migration step:

  • SSTs already on disk continue to be read using whatever algorithm they were originally written with.
  • New SSTs (memtable flushes and compaction output) are written using the newly-configured algorithm.
  • Over time, normal background compaction rewrites old SSTs and the database gradually migrates to the new algorithm. You can force the migration to complete immediately by running kcli spool-compact, at the cost of pausing background work for the duration of the compaction.

The one caveat is that the build of kumod you switch to must still link the algorithm used by your existing SSTs. Because the official builds only include "None" and "Snappy", the only safe switches without a custom build are between those two values.

If disk-space budgeting is a concern, note that switching from Snappy to "None" increases the on-disk footprint as old SSTs are rewritten by compaction. Plan capacity before flipping the switch in that direction.

compaction_readahead_size

Since: Version 2023.03.31-36aa20de

The functionality described in this section requires version 2023.03.31-36aa20de of KumoMTA, or a more recent version.

If non-zero, RocksDB performs larger reads when doing compaction. If you're running RocksDB on spinning disks, you should set this to at least 2MB so that RocksDB's compaction is doing sequential instead of random reads.

level_compaction_dynamic_level_bytes

Since: Version 2023.03.31-36aa20de

The functionality described in this section requires version 2023.03.31-36aa20de of KumoMTA, or a more recent version.

When true, RocksDB dynamically picks target sizes for each level to keep the overall LSM tree balanced even under heavily skewed write patterns. Recommended for level-style compaction with variable load. Defaults to false.

max_open_files

Since: Version 2023.03.31-36aa20de

The functionality described in this section requires version 2023.03.31-36aa20de of KumoMTA, or a more recent version.

Upper bound on the number of SST files RocksDB may keep open simultaneously. When unset, RocksDB defaults to keeping every SST file open, which gives the best read latency but can exhaust file descriptors on very large spools. Set this to bound descriptor use; closed files are reopened on demand at a small per-read cost.

write_buffer_size

Since: Dev Builds Only

The functionality described in this section requires a dev build of KumoMTA. You can obtain a dev build by following the instructions in the Installation section.

Size in bytes of the rocksdb memtable that buffers writes before being flushed to disk as a new SST file.

Smaller values produce smaller, more frequent SST files and trigger compactions sooner -- useful in test setups that need to force the storage through its full write/compact lifecycle quickly. Larger values amortize compaction overhead but increase memory use and recovery time after restart. Leave unset to use the rocksdb default.

level0_stop_writes_trigger

Since: Dev Builds Only

The functionality described in this section requires a dev build of KumoMTA. You can obtain a dev build by following the instructions in the Installation section.

Number of level-0 SST files at which rocksdb will stop accepting writes. Lower values transition the database into the write-stopped state more quickly when background compaction cannot keep up, which is useful for tests that need to deterministically observe that condition. Leave unset to use the rocksdb default.

log_level

Since: Version 2023.03.31-36aa20de

The functionality described in this section requires version 2023.03.31-36aa20de of KumoMTA, or a more recent version.

Controls the verbosity of the RocksDB LOG file that lives alongside the SST files in the spool directory. Possible values: "Debug", "Info" (the default), "Warn", "Error", "Fatal", "Header". Info is verbose enough to surface background compaction errors and is generally what you want; raise to Warn or Error only if log volume is a concern.

memtable_huge_page_size

Since: Version 2023.03.31-36aa20de

The functionality described in this section requires version 2023.03.31-36aa20de of KumoMTA, or a more recent version.

When set, RocksDB attempts to allocate memtable memory backed by transparent huge pages of the requested size, reducing TLB pressure on systems with large memtables. See the rocksdb set_memtable_huge_page_size documentation for caveats. Defaults to unset (use rocksdb's default of regular pages).

log_file_time_to_roll

Since: Version 2023.11.28-b5252a41

The functionality described in this section requires version 2023.11.28-b5252a41 of KumoMTA, or a more recent version.

How long a RocksDB LOG file may be appended to before it is rolled to a new file. Specified as a duration string (e.g. "24 hours"). Defaults to 24 hours.

obsolete_files_period

Since: Version 2023.11.28-b5252a41

The functionality described in this section requires version 2023.11.28-b5252a41 of KumoMTA, or a more recent version.

How often RocksDB scans for and deletes obsolete SST and log files. Specified as a duration string (e.g. "6 hours"). Defaults to 6 hours. Smaller values reclaim disk space sooner at the cost of additional periodic scanning work.

limit_concurrent_stores

Since: Version 2025.03.19-1d3f1f67

The functionality described in this section requires version 2025.03.19-1d3f1f67 of KumoMTA, or a more recent version.

When saving to RocksDB, KumoMTA first attempts the write with RocksDB's no_slowdown flag set. If memtable backpressure causes RocksDB to reject the immediate attempt, KumoMTA polls with a short backoff until the write is accepted or the deadline expires (see store_deadline).

This option caps the number of concurrent store calls that are allowed to hold a backpressure-retry slot at the same time. In a healthy spool no slot is held -- the first attempt succeeds immediately. Under backpressure, the cap prevents an unbounded number of in-flight retrying tasks from amplifying contention on the memtable lock.

You should experiment to find which setting works best for your workload, but the recommended starting point is shown below:

kumo.on('init', function()
  local params = {
    -- Use double the number of cores
    limit_concurrent_stores = kumo.available_parallelism() * 2,
  }
  kumo.define_spool {
    name = 'data',
    path = '/var/spool/kumo/data',
    kind = 'RocksDB',
    rocks_params = params,
  }
  kumo.define_spool {
    name = 'meta',
    path = '/var/spool/kumo/meta',
    kind = 'RocksDB',
    rocks_params = params,
  }
end)

limit_concurrent_loads

Since: Version 2025.03.19-1d3f1f67

The functionality described in this section requires version 2025.03.19-1d3f1f67 of KumoMTA, or a more recent version.

RocksDB does not have a non-blocking read API: load() always runs the underlying get() call in a blocking task on the tokio blocking pool, since the I/O wait must happen on some thread.

By default there is no spool-specific upper bound on the number of outstanding loads, and the blocking pool can grow to up to 512 threads.

Setting an explicit cap reduces CPU contention and context switches when many loads are in flight, at the cost of bounding throughput when the cache is cold and most loads have to wait on disk I/O. When set, this option defines a semaphore that limits how many load() calls may be running -- counting both the waiting and the executing tasks -- at the same time.

You should experiment to find which setting works best for your workload, but the recommended starting point is shown below:

kumo.on('init', function()
  local params = {
    -- Use double the number of cores
    limit_concurrent_loads = kumo.available_parallelism() * 2,
  }
  kumo.define_spool {
    name = 'data',
    path = '/var/spool/kumo/data',
    kind = 'RocksDB',
    rocks_params = params,
  }
  kumo.define_spool {
    name = 'meta',
    path = '/var/spool/kumo/meta',
    kind = 'RocksDB',
    rocks_params = params,
  }
end)

limit_concurrent_removes

Since: Version 2025.03.19-1d3f1f67

The functionality described in this section requires version 2025.03.19-1d3f1f67 of KumoMTA, or a more recent version.

When deleting from RocksDB, KumoMTA first attempts the delete with RocksDB's no_slowdown flag set. If memtable backpressure causes RocksDB to reject the immediate attempt, KumoMTA polls with a short backoff until the delete is accepted or the deadline expires (see store_deadline).

This option caps the number of concurrent remove calls that are allowed to hold a backpressure-retry slot at the same time. In a healthy spool no slot is held -- the first attempt succeeds immediately. Under backpressure, the cap prevents an unbounded number of in-flight retrying tasks from amplifying contention on the memtable lock.

You should experiment to find which setting works best for your workload, but the recommended starting point is shown below.

kumo.on('init', function()
  local params = {
    -- Use double the number of cores
    limit_concurrent_removes = kumo.available_parallelism() * 2,
  }
  kumo.define_spool {
    name = 'data',
    path = '/var/spool/kumo/data',
    kind = 'RocksDB',
    rocks_params = params,
  }
  kumo.define_spool {
    name = 'meta',
    path = '/var/spool/kumo/meta',
    kind = 'RocksDB',
    rocks_params = params,
  }
end)

store_deadline

Since: Dev Builds Only

The functionality described in this section requires a dev build of KumoMTA. You can obtain a dev build by following the instructions in the Installation section.

Upper bound on the wait that store() and remove() will tolerate when rocksdb is applying backpressure. Specified as a duration string (e.g. "30s"). Defaults to 30 seconds.

Callers may provide a shorter deadline (typically derived from an SMTP client's idle timeout); the effective deadline is the minimum of the two. Going longer than the caller-provided value risks the client timing out and retrying, which would produce duplicate deliveries -- this option therefore only narrows the effective deadline, it never extends it.

When the caller-provided deadline is the one that fires, the SMTP server surfaces a 4.4.5 data_processing_timeout exceeded response to the peer. When this spool-side deadline is the one that fires, the SMTP server surfaces 4.4.5 spool write timed out instead, so operators can tell the two cases apart.

error_latch_duration

Since: Dev Builds Only

The functionality described in this section requires a dev build of KumoMTA. You can obtain a dev build by following the instructions in the Installation section.

How long the composite "this database is wedged" signal must hold continuously before the load-shedding gate latches. Specified as a duration string. Defaults to 15 seconds.

The signal goes high whenever the rocksdb background-errors counter has grown above the value observed at process start, or any foreground spool operation has returned a rocksdb error since process start; see rocks_spool_load_shed_active for the full description. Brief blips that recover within this window do not latch the gate, which filters out transient auto-resumed errors.

Note that fatal foreground errors (Corruption or IOError) latch the gate immediately and do not consult this debounce window.

error_unlatch_duration

Since: Dev Builds Only

The functionality described in this section requires a dev build of KumoMTA. You can obtain a dev build by following the instructions in the Installation section.

How long the healthy state must hold continuously before the load-shedding gate auto-unlatches. Specified as a duration string. Defaults to 5 minutes. Only consulted when allow_error_unlatch is true.

A relatively long value gives operators time to inspect the database after a brief failure window before the daemon starts accepting writes again on its own.

allow_error_unlatch

Since: Dev Builds Only

The functionality described in this section requires a dev build of KumoMTA. You can obtain a dev build by following the instructions in the Installation section.

When true (the default), the load-shedding gate clears itself after error_unlatch_duration of observed recovery.

Set to false to require an operator restart to clear the gate, which is appropriate when you want a human to confirm the underlying cause is resolved before accepting traffic again.