Skip to content

refactor: stream store records as events - #9

Closed
pyropy wants to merge 1 commit into
mainfrom
srdjan/feat/iam-store-events
Closed

pyropy wants to merge 1 commit into
mainfrom
srdjan/feat/iam-store-events

Conversation

@pyropy

@pyropy pyropy commented Sep 10, 2026 •

Copy link
Copy Markdown

Description

RevocationStore.Stream yields a store.Event, a sum of a revocation record and a principal revocation record, so both kinds share one poll, cursor, settle window and dedup map. Nothing produces the second kind yet. Part of the Forge S3 tenant IAM work (RFC).

Change log

  • store.Event, EventKind, PrincipalRevocationRecord and constructors; Stream returns events
  • Memory backend streams from an append-only log (a re-revocation now streams both records, as Postgres already did)
  • Firehose handler dispatches on event kind
  • Tests moved to Event; stream-events tests on both backends

🤖 Generated with Claude Code

@pyropy
pyropy added this pull request to stack #12 September 10, 2026 16:19
@pyropy
pyropy force-pushed the srdjan/feat/iam-store-events branch from 55565a9 to 73ca334 Compare September 11, 2026 12:40
@pyropy
pyropy requested a lite review from Copilot September 11, 2026 12:41

Copilot AI 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.

🟡 Changes recommended

Unresolved moderate issues affect retry idempotency and stream deduplication.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Refactors revocation streaming to use typed events across storage backends and firehose handling.

Changes:

  • Adds event types, constructors, and accessors.
  • Updates PostgreSQL and memory streaming.
  • Adapts firehose dispatch and tests.
File summaries
File Summary
pkg/store/store.go Defines the event-based store API.
pkg/store/store_test.go Tests event helpers.
pkg/store/postgres/store.go Streams PostgreSQL records as events; moderate deduplication concern remains (1 vote).
pkg/store/postgres/store_test.go Updates PostgreSQL stream tests.
pkg/store/memory/store.go Adds append-only event logging; moderate retry-idempotency concern (2 votes) and a nit for additional coverage (1 vote).
pkg/store/memory/store_test.go Updates memory stream tests.
pkg/fx/app.go Dispatches firehose events by kind.
pkg/fx/app_test.go Adapts the firehose test store.
Review details

Suppressed comments (2)

pkg/store/memory/store.go:80

  • The append-only log is what makes the new re-revocation behavior observable, but the memory-store tests only add distinct delegations. If this is accidentally changed back to keeping only the latest entry per delegation, the current suite still passes. Please add a test that adds two different revocation invocations for the same delegation before starting the stream and asserts that both events are delivered (while Get still returns the latest record).
	s.log = append(s.log, logEntry{event: event, seq: s.nextSeq})

pkg/store/postgres/store.go:144

  • Event exposes a pointer to the record, so the callback can mutate rec.Revocation.RecordedAt before this line runs. That lets a consumer change the timestamp stored in seen (for example to zero), causing the row to be pruned from deduplication and delivered again on a later poll. Capture rec.RecordedAt() before invoking yield, or yield a protected copy, and use the captured value here.
				seen[link] = rec.RecordedAt()
  • Files reviewed: 8/8 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread pkg/store/memory/store.go
s.records = make(map[cid.Cid]store.RevocationRecord)
}
s.records[record.Revoke] = record
s.appendLocked(store.RevocationEvent(record))

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 20876b8: the memory store keeps the set of stored cause links and drops a repeated one from the log, mirroring Postgres's conflict rule on the invocation CID, while a different invocation for the same delegation is still appended. Tests cover both: a retried invocation streams once, and two revocations of one delegation both stream with Get returning the latest. The suppressed note about reading RecordedAt after yielding is also addressed there: the value is captured before the yield.

Stream now yields a store.Event sum type instead of a RevocationRecord,
so revocation and principal invalidation records can share one poll,
cursor, settle window and dedup map. Event carries a Kind and one of
Revocation or PrincipalRevocation, with Cause and RecordedAt accessors
for the fields the stream loop needs. Nothing produces principal events
yet.

The memory backend keeps its map for Get and streams from an
append-only log, so every stored record is streamed in the order it
was stored. The postgres backend wraps the rows it already reads. The
firehose handler switches on the event kind and refuses kinds it has no
wire format for. The memory backend logs one entry per cause link, so a
retried revocation invocation streams once, as the postgres backend's ON
CONFLICT (id) DO NOTHING already gives.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@pyropy
pyropy force-pushed the srdjan/feat/iam-store-events branch from 73ca334 to 20876b8 Compare September 14, 2026 10:11
@pyropy

pyropy commented Sep 15, 2026

Copy link
Copy Markdown
Author

Superseded by the per-key marker design in fil-one/RFC#30 (f61f474): no principal invalidation record; markers are revoked through the existing /ucan/revoke, and the client's nonce option is #15. Branch kept.

@pyropy pyropy closed this Sep 15, 2026
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