KDB knowledge

Change revisions: GET /api/rev and kdb_rev()

A cheap number per table and per symbol that changes when committed rows change, so a chart polls it and reads its candles again only when they changed.

A chart that shows NABIL's candles does not need to read them every few seconds. It can ask for NABIL's revision instead, and read the candles again only when the revision changed.

Which column has revisions

Each table keeps revisions for the values of one column, when it has one of these:

TableColumn with revisions
PARTITION BY ..., SYMBOL(symbol)symbol (the column named in SYMBOL(...))
SORT BY (symbol, time), DEDUP KEY (symbol, time), or a PRIMARY KEY whose first column is a SYMBOLthat first column
anything else (SORT BY (time, symbol), no SYMBOL key)none: every value gets the table's revision

A table without a rev column still works: its values then change together, whenever any row of the table changes.

Revisions per value are for columns with up to 10,000 values, such as the symbols of a market. They live in memory, outside --memory. When a table's rev column gets more values than that (a SYMBOL primary key of customer ids, say), the table drops its list and goes on as if it had no rev column: every write then moves every value's revision. rev_values in GET /api/stats counts the values kept.

The same values decide what the result cache keeps: a cached WHERE symbol = 'NABIL' result stays valid while only other symbols are written.

GET /api/rev

Syntax:

GET /api/rev?table=<name>&symbol=<value>[&symbol=<value> ...][&column=<name>][&db=<name>]
ParameterMeaning
tablethe table (required)
symbola value of the rev column; repeat it for more (up to 1000). value= is the same
columnoptional: the rev column's name. A different name is a 400 that names the right one
dbthe database; the default database without it

The answer:

FieldMeaning
table, columnthe table and its rev column (null when it has none)
bootchanges when the database opens again
table_revchanges after every committed change of the table
revsone revision per value asked for

It needs read rights on the table: role read on the database, or GRANT SELECT on the table. An unknown table is a 404.

HTTP: The revisions of two symbols

Setup:

CREATE TABLE candles (time TIMESTAMP, symbol SYMBOL, close DOUBLE) TIMESERIES(time)
  PARTITION BY YEAR, SYMBOL(symbol) SORT BY (symbol, time) DEDUP KEY (symbol, time);
INSERT INTO candles VALUES ('2026-09-24T10:00:00Z', 'NABIL', 1510), ('2026-09-24T10:00:00Z', 'NEPSE', 2750);
GET /api/rev?table=candles&symbol=NABIL&symbol=NEPSE

Response:

HTTP 200 (application/json)

{
  "database": "demo",
  "table": "candles",
  "column": "symbol",
  "boot": "52589fb436f3",
  "table_rev": "52589fb436f3.0",
  "revs": {"NABIL": "52589fb436f3.0", "NEPSE": "52589fb436f3.0"}
}

HTTP: Asking by the wrong column says which one has revisions

Setup:

CREATE TABLE candles (time TIMESTAMP, symbol SYMBOL, close DOUBLE) TIMESERIES(time)
  PARTITION BY YEAR, SYMBOL(symbol) SORT BY (symbol, time) DEDUP KEY (symbol, time);
GET /api/rev?table=candles&column=close&symbol=NABIL

Response:

HTTP 400 (application/json)

{"error": "table 'candles' keeps revisions per value of column 'symbol', not 'close'"}

A page that polls:

let seen = null;
setInterval(async () => {
  const r = await (await fetch('/api/rev?table=candles&symbol=NABIL')).json();
  if (r.revs.NABIL !== seen) {
    seen = r.revs.NABIL;
    drawChart(await readCandles('NABIL'));  // only when NABIL changed
  }
}, 2000);

kdb_rev

Syntax:

kdb_rev('<table>')
kdb_rev('<table>', '<value>')

The same revisions in SQL, for clients that only speak the PostgreSQL protocol. With one argument: the table's revision (table_rev). With two: the revision of that value of the rev column. It gives the same text as GET /api/rev, and needs the same right: reading the table (a user whose SELECT on it was revoked gets an error). Unlike the route it runs as a statement, so it waits for the database lock like any SELECT. A NULL argument gives NULL; an unknown table is an error.

Example: A symbol's revision and the table's (sample data: sample)

SELECT length(kdb_rev('ochl_minutes', 'CITY')) > 0 AS has_rev,
       kdb_rev('ochl_minutes', 'CITY') = kdb_rev('ochl_minutes', 'CITY') AS stable;

Result:

has_rev (BOOL)stable (BOOL)
truetrue

(1 row)