AmberPost 09
Release notes
09

Two point nine: my kdb+ tickerplant, on Amber

Amber now talks to q: its wire format, its logs, the databases it writes. The test was a real kdb+ options tickerplant, run on Amber with its .q files unchanged. Then I rewrote it the Amber way.

v2.9.0 2026-10-13·6 min read ·Andrea Bonucci

A while ago I wrote a kdb+ tickerplant (it's on GitHub): a feed pushing options quotes every millisecond, a tickerplant that logs them and fans them out, an rdb, a cep that keeps live metrics per option, an hdb, a gateway that asks both databases at once, and a monitor. Plain q, nothing clever. For 2.9.0 the test was simple. Run those .q files on Amber, unchanged.

What the plant asked for

Mostly the parts of q a demo never touches:

  • q's wire format, both ways. hopen, h x, neg[h] x, h[] for a deferred reply, and .z.pg .z.ps .z.po .z.pc .z.w doing their jobs. A q client can query Amber and the other way round.
  • A real timer. \t and .z.ts. The feed is a 1 ms timer.
  • Logs and -11!. The rdb and the cep replay today's log when they start, so a crash loses nothing.
  • The hdb q wrote. Its column files, enums and sym file. My repo had lost its .d files (git and dot files), so Amber reads tables without them too. Funny thing: q can't load that repo anymore, Amber can.
  • Writing it back as q would. After the rdb's eod[], q still reads the database.
  • A console that behaves. A process with a port open reads its console a line at a time, like q, so rlwrap works and what the handlers print doesn't stair-step across the screen.

Plus the small stuff a real script is made of: a select over several indented lines, { [name] ...} with a blank before the args, .z.D as local time, @[h;msg;f] on a handle.

Bugs only real code finds

Two I'd never have hit with tests I wrote myself. Every tickerplant defines upd, and Amber's update ran a function called upd too, so inside an rdb any update called the rdb's handler instead. And .z.D in a query inside a lambda looked like a local named z, which in k makes a lambda take three arguments, so it came back as a projection. Both are fixed and tested now.

Then the Amber way

Running q unchanged is the compatibility test. Then I wrote the plant again the way you'd write it for Amber. The feed builds everything that doesn't move once and sends ten columns a tick, no table. The cep keeps one vector per metric with a slot per option and updates them in place: no select, no group by, a table only when you ask for one.

whattime
cep, one batch of 20 quotes, the q original~175 µs
cep, one batch of 20 quotes, Amber-native~6.7 µs
the tp's whole upd (log it, send it twice, stamp it)~12 µs
Amber to Amber, sync round trip51 µs
feed to tp to subscriber, p50, either version220–250 µs

One laptop, WSL2, both plants on Amber 2.9.0, the same probe subscribed to each tp.

The last row is the honest one. Between processes, what you wait for is waking them up, not what they compute: at one tick a millisecond every process sleeps between batches, and on this machine the wake-up costs more than all the work. Spinning instead of sleeping is the next thing to try.

Try it

Everything q-side is on the Talking to q page. The short version:

amber tp.q -p 5000          / any q script; a .q file loads the q libraries first
amber rdb.q -p 5002
h:hopen 5002
h"count quotes"
2.9.0 also brings q's other temporal types (minutes, seconds, months, timespans and their typed nulls) as real vectors; they're on the temporal page. Everything else is in the changelog.