KDB knowledge

Standby server: a live read-only copy (--standby-of, kdb promote)

A second KDB server that copies every database of a primary from its log, a moment behind, and serves reads.

A standby is a second kdb serve that keeps a copy of every database of another server, the primary. It reads the primary's log (the WAL) over HTTP and applies each transaction in order, so the copy is a moment behind the primary. Users of the standby can read; every change is made on the primary.

Use it to keep reports and dashboards off the primary, and to have a second copy on another computer.

Start a standby

# the primary: any kdb serve with a token
kdb serve --data-dir D:/kdb-data --token <secret>

# the standby: another folder (another computer), pointing at the primary
kdb serve --data-dir E:/kdb-copy --standby-of http://primary:9900 --standby-token <secret>
SettingMeaning
--standby-of URL (KDB_STANDBY_OF)the primary's address, http://host:port or https://host:port
--standby-token T (KDB_STANDBY_TOKEN)what the standby sends to the primary: the primary's --token, or an API token of an admin
KDB_STANDBY_POLL_MShow often an up-to-date standby asks again (default 100 ms)

The standby copies:

WhatHow
rows: INSERT, UPDATE, DELETE, loadsas logged, in commit order, one whole transaction at a time
schema changes: CREATE, ALTER, DROP TABLE, indexes, viewsas logged
partitions dropped by a TTL or DROP PARTITIONas logged by the primary
new and dropped databasesa new one is copied; a dropped one goes to _trash
users, rights and API tokens (_system)as logged; logins on the standby use them

Read-only

On a standby every change is refused with this server is a standby of http://...: it only reads; make changes on the primary. That covers INSERT, DDL, CREATE DATABASE and user statements.

WhereAnswer
POST /execHTTP 403, "sqlstate": "25006"
POST /write, POST /impHTTP 403, "sqlstate": "25006" (before the body is read)
PostgreSQL porterror 25006 (read_only_sql_transaction), as on a PostgreSQL hot standby

Timers, TTL checks, trigger jobs, materialized view refreshes and the audit log do not run there: the primary runs them, and their results come in its log.

PostgreSQL drivers see in_hot_standby = on and default_transaction_read_only = on, so target_session_attrs=read-only picks the standby.

How far is it?

The lag, over every database:

WhereFields
GET /api/stats (any login)standby.lag_lsn, standby.lag_bytes, standby.lag_seconds, standby.connected
GET /metricskdb_standby, kdb_standby_connected, kdb_standby_lag_lsn, kdb_standby_lag_bytes, kdb_standby_lag_seconds, kdb_standby_applied_transactions_total
the consolethe status bar: Standby (read-only) - lag 0.1 s

lag_lsn counts the log entries the primary has and the copy has not applied. lag_bytes is those entries in bytes, estimated from the bytes per entry copied so far. lag_seconds is how long the oldest of them has waited; when the primary cannot be reached, at least the time since it last answered. All three are 0 when the copy is up to date.

GET /api/standby (server admins) on the standby gives the detail:

FieldMeaning
lag_lsn, lag_bytes, lag_secondsas above
promotedkdb promote ran: this server is a primary now
connected, errorthe last request to the primary worked, or why not
databases[].applied_lsnthe last LSN of the primary this copy holds
databases[].primary_lsn, behind_lsnthe end of the primary's log when last asked, and the difference
databases[].last_commit_mswhen the last applied transaction committed on the primary
databases[].received_byteslog bytes copied since the standby started
databases[].errorwhy this database stopped copying

Promote: the standby becomes the primary

When the primary is lost, make the standby the primary:

kdb promote --url http://standby:9900 --token <secret>

That sends POST /api/promote (server admins). The standby stops copying (a transaction being applied ends first), and from that moment:

WhatAfter the promote
writes (HTTP, /write, /imp, PostgreSQL)taken; the log goes on from the last LSN copied
PostgreSQLnew connections see in_hot_standby = off
TTL checks, sweeps, refreshes, trigger jobs, timersrun here (the Timers page shows up after the next restart)
CREATE DATABASE, usersallowed
GET /api/standby"promoted": true

A second promote answers 409. A server that is no standby answers 409 too.

Before you promote:

After the promote, start the server again without --standby-of (and drop KDB_STANDBY_OF); with it, it would be a standby again. To get a new standby, start one against the new primary. The old primary's folder cannot follow the new one: copy it again from a backup of the new primary.

Starting from a backup

From an empty data folder the standby copies each database from the start of its log. That works only while the primary still keeps its whole log (ALTER DATABASE ... SET WAL_ARCHIVE). Otherwise start the standby on a copy of the primary's data: a restored backup, or the data folder copied while the primary was stopped. The standby asks for the log from where that copy ends.

When the primary no longer keeps the log a database needs, or the two no longer fit (the primary's database was dropped and made again, or restored), that database stops copying and databases[].error says so: copy it again from a backup of the primary. The other databases go on. Equal LSNs are not trusted alone: with each request the standby sends the commit time of the last transaction it holds, and the primary checks that its own log has that same transaction there. A database the primary cannot open for a moment is left as it is on the standby, never dropped.

GET /api/wal

What the standby reads. Server admins only: it holds every row, and the users' password hashes.

RequestAnswer
GET /api/walthe databases and where each one's log ends (format=tsv: one line each; - or null when the primary cannot open it right now)
GET /api/wal?db=x&from=<lsn>[&max_bytes=n][&wait=ms][&prev_ms=t]the committed transactions of x from LSN from on, binary; 410 when that part of the log is gone; 409 when the transaction at LSN from - 1 did not commit at prev_ms (another log)

Use HTTPS (--tls-cert) between computers: the log is sent as it is.