Liking cljdoc? Tell your friends :D

dev.zeko.stube.store

Pluggable persistence for conversation values.

A conversation is just a map of plain Clojure data (see dev.zeko.stube.conversation). Slice 3 adds a small protocol for swapping out the storage backend without touching the kernel:

(s/start! {:port  8080
           :store (dev.zeko.stube.store/file-store "/var/lib/stube/convs")})

Three operations are enough:

opwhen
load-allonce at startup, to repopulate memory
save!after every successful swap-conv!
delete!when a conversation ends (:end, reaper)

The default is in-memory-store, which keeps the slice-0 behaviour unchanged: the in-process conversation atom on the active dev.zeko.stube.runtime kernel is the only copy of the truth and save! is a no-op.


defflow is in-memory only — by design

dev.zeko.stube.flow continuations are live cloroutine objects, not EDN values. A conversation that contains a defflow instance is therefore not durable: on a clean restart its on-disk copy is gone (the file-store logs a warning and skips the save).

This is a deliberate property of the framework, not a gap. defflow is the ergonomic for transient flows — wizards a user completes in one sitting, multi-step UIs whose value comes from the linear-code shape. If the flow needs to survive a deploy or a process crash, write it as a hand-rolled task component instead: a :start hook plus named resume keys threads the same state through an EDN-clean map, and persists transparently through this store. See the Durable flows: defflow vs. task components section of the tutorial for a side-by-side example.

The kernel does not refuse to register or run a defflow-containing conversation against a file-store; the live behaviour is normal. Only the durable copy is skipped, and the warning fires so you can feel the boundary.

Pluggable persistence for conversation values.

A conversation is just a map of plain Clojure data (see
[[dev.zeko.stube.conversation]]).  Slice 3 adds a small protocol for swapping
out the storage backend without touching the kernel:

    (s/start! {:port  8080
               :store (dev.zeko.stube.store/file-store "/var/lib/stube/convs")})

Three operations are enough:

| op           | when                                        |
|--------------|---------------------------------------------|
| `load-all`   | once at startup, to repopulate memory       |
| `save!`      | after every successful `swap-conv!`         |
| `delete!`    | when a conversation ends (`:end`, reaper)   |

The default is [[in-memory-store]], which keeps the slice-0 behaviour
unchanged: the in-process conversation atom on the active
`dev.zeko.stube.runtime` kernel is the only copy of the truth and
`save!` is a no-op.

----------------------------------------------------------------------
defflow is in-memory only — by design
----------------------------------------------------------------------

`dev.zeko.stube.flow` continuations are live cloroutine objects, not
EDN values.  A conversation that contains a `defflow` instance is
therefore not durable: on a clean restart its on-disk copy is gone
(the [[file-store]] logs a warning and skips the save).

This is a deliberate property of the framework, not a gap.  `defflow`
is the ergonomic for transient flows — wizards a user completes in
one sitting, multi-step UIs whose value comes from the linear-code
shape.  If the flow needs to survive a deploy or a process crash,
write it as a hand-rolled task component instead: a `:start` hook
plus named resume keys threads the same state through an EDN-clean
map, and persists transparently through this store.  See the
*Durable flows: defflow vs. task components* section of the tutorial
for a side-by-side example.

The kernel does not refuse to register or run a `defflow`-containing
conversation against a [[file-store]]; the live behaviour is normal.
Only the durable copy is skipped, and the warning fires so you can
feel the boundary.
raw docstring

ConversationStorecljprotocol

Backend for persisting conversation values. See namespace docstring.

Backend for persisting conversation values.  See namespace docstring.

delete!clj

(delete! this cid)

Remove the persisted conversation with id cid. Idempotent.

Remove the persisted conversation with id `cid`.  Idempotent.

load-allclj

(load-all this)

Return a map {cid → conv} of every persisted conversation. Called once at server startup before the http listener accepts requests.

Return a map `{cid → conv}` of every persisted conversation.
Called once at server startup before the http listener accepts
requests.

save!clj

(save! this conv)

Atomically replace the persisted value for (:conv/id conv). Returns conv on success. May log + return the conv unchanged on a non-fatal serialisation problem; should throw only on a backend-level failure (disk full, etc.).

Atomically replace the persisted value for `(:conv/id conv)`.
Returns `conv` on success.  May log + return the conv unchanged
on a non-fatal serialisation problem; should throw only on a
backend-level failure (disk full, etc.).
sourceraw docstring

file-storeclj

(file-store dir)

Persist every conversation as one EDN file in dir. The directory is created if it does not exist.

(file-store "./conv-store")

Files are named <cid>.edn. Writes go to a sibling temp file and are renamed atomically, so a crash mid-write never leaves a partial read on disk. Reads use clojure.edn/read-string with no eval and the default tagged-literal handler, so a corrupted file is the worst attacker reach.

If a conversation contains values that are not EDN-printable (almost always a dev.zeko.stube.flow cloroutine continuation), the store logs a warning to *err* and skips the save without raising. The conversation stays live in memory; only its on-disk copy is stale. This is the documented defflow boundary — see the namespace docstring for how to write a durable equivalent.

Persist every conversation as one EDN file in `dir`.  The directory
is created if it does not exist.

    (file-store "./conv-store")

Files are named `<cid>.edn`.  Writes go to a sibling temp file and
are renamed atomically, so a crash mid-write never leaves a partial
read on disk.  Reads use `clojure.edn/read-string` with no eval and
the default tagged-literal handler, so a corrupted file is the worst
attacker reach.

If a conversation contains values that are not EDN-printable (almost
always a `dev.zeko.stube.flow` cloroutine continuation), the store
logs a warning to `*err*` and *skips* the save without raising.  The
conversation stays live in memory; only its on-disk copy is stale.
This is the documented `defflow` boundary — see the namespace
docstring for how to write a durable equivalent.
sourceraw docstring

in-memory-storeclj

(in-memory-store)

The default store. Keeps no copy of its own — the runtime kernel's in-process conversation atom IS the source of truth — and treats every operation as a no-op. Use this for tests, REPL iteration, and any deployment where you genuinely don't need crash-resume.

The default store.  Keeps no copy of its own — the runtime kernel's
in-process conversation atom IS the source of truth — and treats every
operation as a no-op.  Use this for tests, REPL iteration, and any
deployment where you genuinely don't need crash-resume.
sourceraw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close