Blog / How-to guides

Semurg as a key-value store: put, get, delete, and where it stands

12 October 2026 · Semurg · 3 min read · Markdown

Verified: every command below ran on a fresh node of r11.78.20261012.bc0d94f (how)

key-valuecachedatabasehow-to

A key-value store answers one question very often: what is stored under this key? Semurg answers it with the same door it uses for objects and documents: a value under a whole-number key, read back by key. This post is about using that door as a key-value store, and about where it stands while its comparison is re-measured.

Under one whole-number key: a put stores a new version, a put-if-absent leaves a held value alone, a get answers present or absent, and a delete makes later gets answer deleted.A whole-number keyPut: a new versionPut if absent: a heldvalue staysGet: present or absentDelete: later gets saydeletedUnder one whole-number key: a put stores a new version, a put-if-absent leaves a held value alone, a get answers present or absent, and a delete makes later gets answer deleted.A whole-number keyPut: a new versionPut if absent: a heldvalue staysGet: present or absentDelete: later gets saydeleted

Put, get and delete

Keys are whole numbers; values are base64 bytes. One call puts many, and "content": true makes it a put-if-absent: a key that already holds a value keeps it, and the reply counts it as skipped.

A string key such as a user name or a session token is hashed in your code to a whole-number key, which the object door stores and reads.Your string keyHash it in your codeA whole-number keyThe object door
bash
export SEMURG_API_TOKEN=<your token>
curl -sS http://localhost:4000/v1/query \
  -H "Authorization: Bearer $SEMURG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"op": "object_put", "content": true,
       "objects": [{"id": 900000000701, "value_b64": "c2Vzc2lvbjogZGFuYQ=="},
                   {"id": 900000000702, "value_b64": "c2Vzc2lvbjogbGVl"}]}'

Expected: both keys are written, because neither held a value

Run that put a second time, before anything else, and both keys are skipped. Without "content": true, a put to a key that holds a value replaces it with a new version, and reads return the newest.

Get many keys in one call; each answers in the order you asked:

bash
export SEMURG_API_TOKEN=<your token>
curl -sS http://localhost:4000/v1/query \
  -H "Authorization: Bearer $SEMURG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"op": "object_get", "ids": [900000000701, 900000000702, 900000000799]}'

Expected: the two keys you put are present with their values, and the third is absent

Delete a key:

bash
export SEMURG_API_TOKEN=<your token>
curl -sS http://localhost:4000/v1/query \
  -H "Authorization: Bearer $SEMURG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"op": "object_delete", "ids": [900000000702]}'

Expected: one key deleted

Read it again and the key answers deleted, with the version it was deleted at, rather than pretending it never existed:

bash
export SEMURG_API_TOKEN=<your token>
curl -sS http://localhost:4000/v1/query \
  -H "Authorization: Bearer $SEMURG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"op": "object_get", "ids": [900000000702]}'

Expected: the deleted key answers deleted

Where it stands

We have taken our published benchmark rows down while every domain is re-measured on new hardware, with a dataset in RAM and one bigger than RAM running at the same time, as a stress test of Semurg itself. The re-measured key-value results will be on the benchmark page, wins and losses alike, with the release and the machine they ran on. Until then this post states no comparison.

What it does not do yet

  • Expire keys after a time to live.
  • List, scan or range over keys.
  • Compare-and-set, increment or append: a put writes the whole value.
  • Use string keys directly.
  • Import from Redis or Valkey directly.

Next

How these commands were checked

Our test farm ran each of the 4 command blocks marked runnable, exactly as written, with the answer checked against the expectation printed under it, against a fresh node of the published release r11.78.20261012.bc0d94f. A post is published only when every one of them passed there.

Related