Skip to content

The board is polled every two seconds and its frames carry no ordering key #116

Description

@EdmondDantes

Defect and feature in one change

The board is discovered by polling instead of being announced, and its frames carry no ordering key.

broadcastBoards() walks every project, reloads all of its issues from SQLite, diffs them, then sleeps two seconds — for the server's lifetime, whether or not anyone is connected (Server.php:1061-1076). Work happens when nothing changed; a change waits up to two seconds to cross a connection that is already open; the cost scales with projects rather than with activity. The SSE path carries its own copy of the same poll-and-diff (Server.php:894ff), which is why the bootstrap defect fixed in #97 existed on one path and not the other.

A run trace has none of this: TraceBus pushes and TraceWire::row() fixes the wire form, so a replayed row and a live one are identical on both transports and every row carries a seq the client de-dupes by. The board has no equivalent, and that is the residual race left by #97 — a late subscriber's bootstrap and a concurrent live change on the same socket have nothing to order them by, so a stale frame can overwrite a fresh one.

The design is written

dev/design/board-cursor.md in this repository: one wire form {producerId, seq, tsMs, kind, data} shared by every producer and both transports, a BoardFeed value object holding one producer's counter and last-sent map, tsMs ordering across producers and seq within one, the poll deleted in favour of the writing worker announcing its own change, and coalescing plus a debounce so a run touching its ticket ten times a second yields one frame.

Three decisions in it are still open: how the dirty signal is wired (a TraceBus subscription against explicit marks at the row-edit sites), the debounce value, and what to do with a tsMs tie.

Depends on

The multi-worker change, because the numbering scheme exists to survive it: with one worker a single in-process counter would do, and with several there is no shared memory to hold one.

Acceptance

  • broadcastBoards() and the server-global $sent cursor are gone.
  • A change reaches an open board in milliseconds, and a burst of changes to one ticket arrives as one frame.
  • A client that connects while a run is writing ends with the same board as one that was connected all along, whichever order the frames arrive in.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions