DEV Community

flisk
flisk

Posted on

How Flisk Routes Agent Traffic: A Sequence Diagram of One Turn

The earlier articles covered the agent, the MCP resource, and the control plane. This one follows a single turn: one request from a client to an agent, through the gateway, the checkpoint store, and back.

The sequence follows the Flisk agent docs. The model call is a placeholder, since your runTurn decides what it looks like.

The sequence

sequenceDiagram
    autonumber
    actor User as Client
    participant GW as Traefik gateway
    participant Agent as Agent service
    participant Store as Checkpoint volume
    participant Model as Model provider (your code)

    User->>GW: POST /api/bot/turn with x-session-id
    GW->>Agent: route by session header
    Agent->>Store: load checkpoint for the session
    alt no checkpoint yet
        Store-->>Agent: empty state
    else checkpoint exists
        Store-->>Agent: prior messages
    end
    Agent->>Agent: append the user's input
    Agent->>Model: runTurn(checkpoint, input)
    Model-->>Agent: reply and updated state
    Agent->>Store: commit the checkpoint atomically
    Store-->>Agent: committed
    Agent-->>GW: reply
    GW-->>User: reply

    Note over Agent,Store: On SIGTERM an in-flight turn finishes and checkpoints first
    Note over Store: The named volume survives flisk down

Where state lives

The session state sits in a checkpoint store, a file per session on a named Docker volume. The turn reads it first and writes it last. The write is atomic, so a crash partway through does not leave a half-saved conversation.

Because the volume outlives flisk down, a restarted agent loads the same state the next turn needs. The docs also say an explicit volume removal is the only way to lose it, which is a deliberate safeguard.

What routing does

The gateway uses the x-session-id header to choose the session. Two conversations never share a checkpoint, even when they hit the same agent service. The header is also the one thing a client must get right: a missing or changed session ID starts a new conversation.

What a restart does

If the agent restarts between turns, the next turn loads the checkpoint and carries on. If it restarts during a turn, the drain on SIGTERM lets the in-flight turn finish and checkpoint first. The docs say live-update reloads use the same path.

A crash rather than a clean stop skips the drain. The atomic commit then protects the last saved state, and the turn in progress is lost. Plan for that in your client: retry the request with the same session ID, and check whether the turn was saved.

What to check before you trust it

Send two turns with one session ID, restart the stack, and send a third turn. The third reply should reflect the first two. Then send a turn with a new session ID and confirm it starts fresh. Those two checks cover the parts that break quietly.

Summary

One turn goes through the gateway, which routes by session. The agent loads the checkpoint, calls your model code, commits the new state atomically, and replies. The state survives a restart because it lives on a volume, and a clean stop drains the turn in progress. Test the two-turn, restart, third-turn path before you depend on it.

Top comments (1)

Collapse
 
max_quimby profile image
Max Quimby •

The commit-last + atomic-write ordering is the quiet hero here. We run a similar per-session checkpoint and the thing that bit us wasn't the crash case you describe — it was the double-delivery case. Client retries the same x-session-id after a crash-mid-turn, but the turn actually did checkpoint before SIGKILL, so the retry appends the user input twice and the model sees a duplicated turn. Your "check whether the turn was saved" line is doing a lot of work; in practice we had to make the turn write a monotonic turn-id into the checkpoint so the agent can dedupe a replayed request instead of trusting the client to know.

Two questions: (1) is the checkpoint write a single atomic rename, or append-then-fsync? The rename approach survives a torn write but costs you the full-state rewrite each turn. (2) Does Traefik pin a session to one agent replica, or can two concurrent requests with the same session-id race the checkpoint volume? That's the failure that doesn't show up until you scale past one replica.