Skip to content

Add a start/stop controller for the S2 storage sink - #401

Open
archandatta wants to merge 7 commits into
mainfrom
archand/kernel-2158/s2-storage-controller
Open

archandatta wants to merge 7 commits into
mainfrom
archand/kernel-2158/s2-storage-controller

Conversation

@archandatta

@archandatta archandatta commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add S2StorageController in server/lib/events/s2storage.go, a start/stop wrapper around S2StorageWriter that resolves the stream name through streamFn at Start
  • keep empty S2 config as a no-op without resolving the stream, and open at most one writer per process after a writer has successfully started
  • expose Running() and EverStarted() so later callers can distinguish active forwarding from any prior S2 persistence
  • cover never-start, repeated and concurrent Start, failed-start rollback, empty config, Stop without Start, and post-Stop state with unit tests

Why

An upcoming telemetry mode needs a browser instance to forward selected events without ever opening its S2 append session. cmd/api/main.go still opens S2StorageWriter directly at the existing boot and fork-identity start points; this branch only adds the lifecycle owner that a later change can wire in.

Existing behavior is unchanged: main.go, api.go, S2StorageWriter, StorageWriter, and S2 read-from-seq-0 behavior are untouched.

Testing

  • cd server && GOCACHE=/tmp/go-build-cache GOMODCACHE=/tmp/go-mod-cache go test ./lib/events/ -count=1 -race — ok, includes new controller lifecycle and concurrency tests
  • cd server && GOCACHE=/tmp/go-build-cache GOMODCACHE=/tmp/go-mod-cache go build ./... — passed
  • cd server && GOCACHE=/tmp/go-build-cache GOMODCACHE=/tmp/go-mod-cache go vet ./... — no findings
  • DOCKER_BUILDKIT=1 docker build -f images/chromium-headless/image/Dockerfile -t kernel-headless-test . — succeeded, image sha256:270be0da270964970e71c99daf259f6c907225012352eb4f309e97332796f560
  • headless container with S2_BASIN, S2_ACCESS_TOKEN, and S2_STREAM unset — GET /spec.yaml 200, zero S2 storage lines in /var/log/supervisord/kernel-images-api
  • headless container with fake S2_BASIN, S2_ACCESS_TOKEN, and S2_STREAMGET /spec.yaml 200, one S2 storage enabled line in /var/log/supervisord/kernel-images-api
  • telemetry API by curl in both S2 modes — PUT /telemetry 201, GET /telemetry 200, POST /telemetry/events 200, and GET /telemetry/stream?replay=all delivered the posted event frame
  • docker stop -t 30 in both S2 modes — clean shutdown signal received, zero drain incomplete / drain deadline exceeded warnings

Not run: real S2 credentials; none were available, so the S2-enabled live check used fake values and produced the expected submit ack error after posting an event.


Note

Low Risk
Additive library code and tests only; no production call sites wired yet, so runtime behavior is unchanged.

Overview
Introduces S2StorageController, a mutex-guarded start/stop owner around S2StorageWriter so S2 forwarding can stay off until explicitly started—needed for telemetry modes that must not open an append session at boot.

Start resolves the stream via a streamFn (so fork identity can be known later), skips work when basin/token or stream are empty (credentials missing does not call streamFn), and opens at most one writer: repeated or concurrent Start is idempotent, and Start after Stop does not reopen. Stop waits on in-flight start/stop, honors context cancellation, and leaves the writer in place if shutdown times out. Running() and EverStarted() expose active vs. ever-persisted state.

Adds s2storage_test.go with lifecycle, concurrency, rollback, logging, and context behavior coverage. S2StorageWriter and server wiring are unchanged in this PR.

Reviewed by Cursor Bugbot for commit 4e921bb. Bugbot is set up for automated code reviews on this repo. Configure here.

archandatta and others added 5 commits September 18, 2026 11:18
The writer is opened at boot today, which leaves no way to decide per
session whether events are persisted at all. Wrap it in a controller that
resolves the stream lazily and opens at most one writer, so a later change
can start it from the telemetry handler instead.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@archandatta
archandatta marked this pull request as ready for review September 18, 2026 14:25

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread server/lib/events/s2storage.go

@Sayan- Sayan- left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • p1: S2StorageController.Stop clears its writer even when shutdown exits before draining or closing storage. everStarted then prevents reopening, and later stops cannot reach the live writer. Reproduced 20/20 under race with a blocked append.
  • p2: the controller holds mu across streamFn, writer startup, and the full stop. A blocked callback or stop makes Stop(ctx) exceed its deadline while waiting for the lock and blocks both state accessors. Reproduced 20/20 under race.
  • p2: Start logs “S2 storage enabled” before startup succeeds. A canceled parent returns an error with both state flags false while retaining the enabled log. Reproduced 20/20 with captured logging.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 4e921bb. Configure here.

c.log.Info("S2 storage enabled", "basin", c.basin, "stream", stream)
c.mu.Lock()
c.writer, c.cancel, c.everStarted = w, cancel, true
c.mu.Unlock()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Writer can start after Stop returns

Medium Severity

If Stop times out while Start is still opening the append session, Start still publishes the writer afterward. Running is false when Stop returns, so a caller can treat the sink as down and later see events forwarded anyway.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 4e921bb. Configure here.

@archandatta
archandatta requested a review from Sayan- September 18, 2026 20:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants