Commit Graph
4 Commits
Author SHA1 Message Date
Claude 9dcb0d6dc8 Refactor Query contract: uniform ergonomics for SQLite + PostgreSQL
- Add `publish_key()` default method to `Query` trait (returns `None`)
- Add `mutation_exec!` macro for SQLite INSERT/UPDATE/DELETE ops; overrides
  `publish_key()` so the mutation carries its own invalidation key
- Add `Store::exec()` which runs a mutation and auto-publishes its key —
  action handlers no longer need a separate `store.publish()` call
- Add `PgQuery` trait + `pg_query_one!` / `pg_query_all!` macros
  (feature-gated on `pg_replication`) mirroring the SQLite Query API
- Add `cx.run_pg(pool, query)` to `RenderContext` which executes a PgQuery
  and auto-subscribes its cache key — no more manual `cx.subscribe()` calls
- Update todo example: replace plain mutation fns with `mutation_exec!` structs
  and `store.exec()` calls; remove `action_then_publish` helper
- Update pg_replication example: add `ListMessages` via `pg_query_all!` and
  replace manual subscribe + raw query with `cx.run_pg()`

https://claude.ai/code/session_01HXe8rAZPweU9j2piBo9MdG
2026-03-11 22:37:33 +00: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 b10326b242 bodged refactor 2026-03-08 20:10:15 +01: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