Liking cljdoc? Tell your friends :D

co.multiply.tropical.session

Tab sessions: one island tree per browser tab, outliving any single SSE connection.

A session is created when the page is served, and the page is rendered from its first frame. The tab's stream (ring/stream-init) then attaches to it, and the session belongs to that page load from then on. Datastar drops that stream routinely: a hidden tab closes it, network blips retry it. A detached session keeps its islands, and so their resources, for grace-ms. A reconnect within that window resyncs from the cached frame without reopening anything. After the window, every island is unmounted and everything they held is released.

Each session is run by one pump: a virtual thread that owns all of the session's mutable state, its island runtime included, and consumes the session's inbox (co.multiply.tropical.signal.Inbox). The inbox holds lifecycle events (attach, detach, close) and the subscriptions marked since the pump last looked. A change to something the islands read only marks its subscription, once until the pump takes it. Rendering happens on the pump, never on the thread that made the change, so a shared resource's loop never renders on anyone's behalf. Changes that arrive while a frame is being sent coalesce into the next one, so a slow client gets fewer, later frames, never a backlog, and the inbox never holds more than one mark per subscription.

A session runs an app, a map:

  • :root (fn [{:keys [tab uid request]}] call): the session's root island, given the session's context, which every island reads with use-session. :request is the request that created the session, where co.multiply.tropical.ring created it.
  • :patch-signals optional, (fn [info] signals): a map of signals to send with each patch, or nil. See patch-signals.
  • :error-view optional, (fn [id e] hiccup): what renders in place of an island whose render throws (see runtime/runtime).
  • :first-frame-ms optional: how long, in ms, the session's first frame may wait for reads still pending (below). Defaults to 0.

The first frame a session delivers, to the page or over the stream that rebuilt the session, is the first a client sees from it. Its reads are often cold: a new tab, a first visit, every open tab after a restart. Sent at once, it shows their pending states, which the data replaces a moment later. So it waits until no read of its islands is pending, or :first-frame-ms passes, and goes out as it is then. It renders as reads land meanwhile, so islands that appear only once an earlier read lands get to read too. Later frames don't wait. A page waits at most 5 s for its frame (render-page!), so keep the wait well under that: 100 to 200 ms covers the reads worth waiting for.

Scripts the islands declare (co.multiply.tropical.script) go over the stream once each, appended to the page's head, ahead of the first frame that needs them: those of the first frame too, unless the page put them in its head (scripts-in-page!), in case it left them out. A session belongs to one page load, which keeps the modules it loaded, so a reconnect doesn't send them again.

A session whose pump fails, because a step outside the islands' renders threw, is closed, and so is its stream: the client reconnects into a new session instead of waiting on one that sends nothing.

Tab sessions: one island tree per browser tab, outliving any single SSE
connection.

A session is created when the page is served, and the page is rendered from
its first frame. The tab's stream (`ring/stream-init`) then attaches to it,
and the session belongs to that page load from then on. Datastar drops that
stream routinely: a hidden tab closes it, network blips retry it.
A detached session keeps its islands, and so their resources, for `grace-ms`.
A reconnect within that window resyncs from the cached frame without
reopening anything. After the window, every island is unmounted and
everything they held is released.

Each session is run by one pump: a virtual thread that owns all of the
session's mutable state, its island runtime included, and consumes the
session's inbox (`co.multiply.tropical.signal.Inbox`). The inbox holds
lifecycle events (attach, detach, close) and the subscriptions marked since
the pump last looked. A change to something the islands read only marks its subscription,
once until the pump takes it. Rendering happens on the pump, never on the
thread that made the change, so a shared resource's loop never renders on
anyone's behalf. Changes that arrive while a frame is being sent coalesce into
the next one, so a slow client gets fewer, later frames, never a backlog, and
the inbox never holds more than one mark per subscription.

A session runs an app, a map:

- `:root`           `(fn [{:keys [tab uid request]}] call)`: the session's root
                    island, given the session's context, which every island
                    reads with `use-session`. `:request` is the request that
                    created the session, where `co.multiply.tropical.ring`
                    created it.
- `:patch-signals`  optional, `(fn [info] signals)`: a map of signals to send
                    with each patch, or nil. See `patch-signals`.
- `:error-view`     optional, `(fn [id e] hiccup)`: what renders in place of
                    an island whose render throws (see `runtime/runtime`).
- `:first-frame-ms` optional: how long, in ms, the session's first frame may
                    wait for reads still pending (below). Defaults to 0.

The first frame a session delivers, to the page or over the stream that
rebuilt the session, is the first a client sees from it. Its reads are often
cold: a new tab, a first visit, every open tab after a restart. Sent at once,
it shows their pending states, which the data replaces a moment later. So it
waits until no read of its islands is pending, or `:first-frame-ms` passes,
and goes out as it is then. It renders as reads land meanwhile, so islands
that appear only once an earlier read lands get to read too. Later frames
don't wait. A page waits at most 5 s for its frame (`render-page!`), so keep
the wait well under that: 100 to 200 ms covers the reads worth waiting for.

Scripts the islands declare (`co.multiply.tropical.script`) go over the
stream once each, appended to the page's head, ahead of the first frame that
needs them: those of the first frame too, unless the page put them in its
head (`scripts-in-page!`), in case it left them out.
A session belongs to one page load, which keeps the modules it loaded, so a
reconnect doesn't send them again.

A session whose pump fails, because a step outside the islands' renders
threw, is closed, and so is its stream: the client reconnects into a new
session instead of waiting on one that sends nothing.
raw docstring

!sessionsclj

The open sessions, for display: an atom of a map keyed by tab, which changes only on a session's lifecycle events. Watch it through summary.

The open sessions, for display: an atom of a map keyed by tab,
which changes only on a session's lifecycle events. Watch it through
`summary`.
sourceraw docstring

close-all!clj

(close-all!)

Closes every session, as after reloading code in development. Each tab's stream ends, and Datastar reconnects about a second later into a fresh session, rendered by the code loaded then, whose first frame updates the page in place.

Call it once the reloaded code serves requests: a tab that reconnects before that gets a session of the old code. What only a page load renders, such as the page's head or an edited script, shows once the page is reloaded.

Closes every session, as after reloading code in development. Each tab's
stream ends, and Datastar reconnects about a second later into a fresh
session, rendered by the code loaded then, whose first frame updates the page
in place.

Call it once the reloaded code serves requests: a tab that reconnects before
that gets a session of the old code. What only a page load renders, such as
the page's head or an edited script, shows once the page is reloaded.
sourceraw docstring

frame-msclj

The least time, in ms, between a session's frames. Changes within it coalesce into the next frame, and the latest wins.

The least time, in ms, between a session's frames. Changes within it
coalesce into the next frame, and the latest wins.
sourceraw docstring

grace-msclj

How long, in ms, a session whose stream went away keeps its islands, and what they hold, for the tab to reconnect, before it closes.

How long, in ms, a session whose stream went away keeps its islands, and
what they hold, for the tab to reconnect, before it closes.
sourceraw docstring

heartbeat-msclj

How long, in ms, a quiet stream goes before it writes a heartbeat, an SSE comment: a dead connection is noticed only on write, and a proxy's idle timeout would close a live one.

How long, in ms, a quiet stream goes before it writes a heartbeat, an SSE
comment: a dead connection is noticed only on write, and a proxy's idle
timeout would close a live one.
sourceraw docstring

summaryclj

(summary)
(summary sessions)

How many sessions have their stream, and how many are waiting for it, as {:attached :detached}. Of every session, or of sessions, a value !sessions held.

How many sessions have their stream, and how many are waiting for it, as
`{:attached :detached}`. Of every session, or of `sessions`, a value
`!sessions` held.
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