Commit Graph
11 Commits
Author SHA1 Message Date
Markk116 ae9064ee76 fix up pg replication example 2026-03-10 22:47:17 +01:00
Markk116andGitHub a1e2effe96 Merge pull request #2 from Markk116/claude/pg-replication-module-ATq5z
feat: add pg_replication module for reactive WAL-based cache invalidation
2026-03-09 12:08:20 +01:00
Claude b57dbd0fa0 feat: add pg_replication module for reactive WAL-based cache invalidation
Implements a PostgreSQL logical replication module that streams WAL
changes, buffers them per XID for transaction atomicity, and publishes
CacheKey invalidation events to the existing framework broadcast channel.

Key design choices:
- Transaction atomicity: changes are buffered until COMMIT so the fanout
  always receives a consistent view. A 1000-row bulk-insert emits one
  table-level CacheKey, not 1000 row events.
- Native protocol: uses a self-contained raw TCP + postgres-protocol
  implementation for the replication connection (tokio-postgres 0.7 does
  not expose copy_both_simple publicly).
- Corrected pgoutput v1 parser: original maybe_sql_integration code
  incorrectly read XID from DML messages; DML messages carry no XID in
  proto v1 — only Begin does.
- Clean integration: publishes CacheKey::Channel events to store.events
  so the existing fanout task picks them up with zero changes to the
  fanout loop.
- New cx.subscribe(key) API on RenderContext for manually registering
  subscription keys (needed when queries go through PgPool, not cx.run).

New files:
  src/pg_replication/mod.rs       — PgReplicationListener, key helpers
  src/pg_replication/wal_parser.rs — pgoutput v1 binary protocol parser
  src/pg_replication/emitter.rs   — WalEmitter (tx-buffering + CacheKey emit)
  src/pg_replication/proto.rs     — raw TCP PG wire-protocol connection
  examples/pg_replication/main.rs — live message board example
  docker-compose.yml              — PG 16 container with wal_level=logical

Run the example:
  docker compose up -d
  cargo run --example pg_replication --features pg_replication

https://claude.ai/code/session_01SLGgXeqKV2o7KTmCaQZZPg
2026-03-08 22:12:01 +00:00
Markk116 2358ca29c7 dump some extra files 2026-03-08 22:34:03 +01:00
Markk116andGitHub ed85af4a4f Merge pull request #1 from Markk116/claude/fix-sse-pipe-events-y7Nxd
Fix SSE pipe: subscriptions never registered, malformed events, fanout skipping
2026-03-08 21:08:37 +01:00
Claude 0ab0f79749 Fix SSE pipe: subscriptions never registered, malformed events, fanout skipping
Three bugs preventing any SSE events from reaching clients:

1. SubscriptionRegistry::clone() deep-cloned the DashMaps instead of sharing
   them. The sse_handler spawned a task with a disconnected clone, so
   subscriptions.update() wrote into a throwaway copy — the fanout always saw
   an empty registry and found zero connections to push to. Fixed by wrapping
   both DashMaps in Arc so clone() is a cheap pointer copy sharing live state.

2. The SSE event data payload used .join("\ndata: ") to pre-embed the `data:`
   prefix into the string, then handed it to axum's Event::data() which does
   its own \n-splitting to add `data:` prefixes. This produced double-prefixed
   lines (`data: data: elements ...`) that DataStar couldn't parse. Fixed by
   using .join("\n") and letting axum format the data lines correctly.

3. The fanout skipped connections with None signals (new connections that had
   not yet received a client action). This broke clock-style server-push events
   on fresh connections. Fixed by falling back to "{}" instead of continue-ing.

https://claude.ai/code/session_0168cLRd1wr6LK9FjBsjA37W
2026-03-08 19:24:22 +00:00
Markk116 b10326b242 bodged refactor 2026-03-08 20:10:15 +01:00
Markk116 71e63e4410 incremental progess 2026-03-08 17:22:13 +01:00
Claude 6605bf452f Fix DataStar v1 RC8 signal attribute incompatibilities
Two bugs that caused signals to not work on the frontend:

1. data-on-signal-change → data-on-signal-patch
   The data-on-signal-change attribute was removed in RC8.
   The replacement is data-on-signal-patch.

2. data-bind-newTodo="" → data-bind="newTodo"
   HTML normalises attribute names to lowercase at parse time, so
   data-bind-newTodo becomes data-bind-newtodo in the DOM. DataStar
   would then bind to signal $newtodo instead of $newTodo, breaking
   the two-way binding. The value syntax data-bind="newTodo" preserves
   the camelCase name correctly.

https://claude.ai/code/session_01JThiQbp3Bn9J1VhBpRuz4u
2026-03-06 20:13:55 +00:00
Claude 8535d39012 Refactor: untangle framework library from todo example
The codebase is now split into two clear layers:

Framework library (src/):
- lib.rs — public API surface, re-exports all framework types
- signal.rs — SignalOpts, Signal, SignalStore, mangle/global/client_only helpers
- query.rs — Query trait, CacheKey, query_one!/query_all! macros
  (macro paths updated from $crate::framework::* to $crate::*)
- context.rs — RenderContext, ComponentTree, ComponentNode
- connection.rs — ConnectionManager, SubscriptionRegistry, ConnectionState
- sse.rs — format_patch_elements, format_patch_signals
- server.rs — AppState, SharedState, sse_handler (pure framework plumbing)

Todo example (examples/todo/):
- main.rs — SQLite setup, seeding, server bootstrap
- app.rs — router, document_shell, resolve_and_render, render_page,
  action_then_render, and all HTTP handlers
- queries.rs — Todo model, ListTodos/GetTodo queries, mutation helpers
- todo_list.rs — TodoList page component
- about.rs — About page component

Run the example independently with: cargo run --example todo

https://claude.ai/code/session_01JThiQbp3Bn9J1VhBpRuz4u
2026-03-06 16:51:53 +00:00
Markk116 2569596ca5 First Commit, it compiles! 2026-03-05 12:38:22 +01:00