Skip to content

Add pub/sub channels for SSE (#1914) - #1940

Merged
bpamiri merged 4 commits into
developfrom
peter/sse-channels-pubsub
Mar 14, 2026
Merged

bpamiri merged 4 commits into
developfrom
peter/sse-channels-pubsub

Conversation

@bpamiri

@bpamiri bpamiri commented Mar 13, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Adds channel-based pub/sub layer on top of existing SSE primitives (renders, streams, events all unchanged)
  • In-memory adapter (Channel.cfc) — ConcurrentHashMap-backed, instant delivery, single-server
  • Database adapter (DatabaseAdapter.cfc) — _wheels_events table with auto-create, poll-based, multi-server
  • Global publish() function — call from controllers, models, jobs, anywhere
  • Controller mixin (subscribeToChannel()) — long-lived SSE stream with heartbeats, timeout, disconnect detection
  • JS client (wheels-sse.js) — zero-dependency EventSource wrapper with auto-reconnect + exponential backoff
  • Tests — Channel engine core, DatabaseAdapter round-trip, controller mixin availability
  • Docs — Full API reference at .ai/wheels/channels/channels.md

Files

File Type Purpose
vendor/wheels/Channel.cfc New Core in-memory pub/sub engine
vendor/wheels/channel/DatabaseAdapter.cfc New Database-backed adapter (auto-creates _wheels_events table)
vendor/wheels/controller/channels.cfc New Controller mixin (auto-loaded)
vendor/wheels/Global.cfc Modified publish() and $getChannelEngine()
vendor/wheels/public/assets/js/wheels-sse.js New JS client library (~120 lines)
vendor/wheels/tests/specs/controller/channelSpec.cfc New Channel engine + mixin tests
vendor/wheels/tests/specs/channel/DatabaseAdapterSpec.cfc New Database adapter tests
.ai/wheels/channels/channels.md New API docs + usage patterns

Closes #1914

Test plan

  • CI passes — channel engine tests (in-memory subscribe/publish/unsubscribe, error isolation)
  • CI passes — database adapter tests (publish/poll round-trip, channel filtering, lastEventId, cleanup)
  • Controller mixin methods available on dummy controller
  • Existing SSE tests still pass (no changes to sse.cfc)
  • Manual: publish() from model callback delivers to subscribeToChannel() client

🤖 Generated with Claude Code

Adds a higher-level channel abstraction over the existing SSE primitives
with in-memory and database adapters, a global publish() function,
controller mixin for subscribeToChannel(), JS client library, and tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions github-actions Bot added docs enhancement javascript Pull requests that update javascript code labels Mar 13, 2026
@github-actions

github-actions Bot commented Mar 13, 2026 •

Copy link
Copy Markdown
Contributor

Wheels Test Matrix

Engine MySQL PostgreSQL SQL Server H2 Oracle SQLite
lucee5 ✅ ✅ ✅ ✅ ✅ ✅
lucee6 ✅ ✅ ✅ ✅ ✅ ✅
lucee7 ✅ ✅ ✅ ✅ ✅ ✅
adobe2018 ✅ ✅ ✅ -- ✅ --
adobe2021 ✅ ✅ ✅ -- ✅ ✅
adobe2023 ✅ ✅ ✅ -- ✅ ✅
adobe2025 ✅ ✅ ✅ -- ✅ ✅
boxlang ✅ ✅ ✅ -- ✅ ✅

Results for commit e6a6ecc.

@github-actions

github-actions Bot commented Mar 13, 2026 •

Copy link
Copy Markdown
Contributor

Wheels Test Results

    42 files  ±    0   3 780 suites  +84   25m 26s ⏱️ -40s
78 162 tests +1 260  77 774 ✅ +1 253  388 💤 +7  0 ❌ ±0 
80 808 runs  +1 260  80 420 ✅ +1 253  388 💤 +7  0 ❌ ±0 

Results for commit c2cc9f9. ± Comparison against base commit e5e2d59.

This pull request skips 7 tests.
adobe2021/sqlite :: Stress Testing for Race Conditions ‑ should handle concurrent model access with cache manipulation
adobe2023/sqlite :: Stress Testing for Race Conditions ‑ should handle concurrent model access with cache manipulation
adobe2025/sqlite :: Stress Testing for Race Conditions ‑ should handle concurrent model access with cache manipulation
boxlang/sqlite :: Stress Testing for Race Conditions ‑ should handle concurrent model access with cache manipulation
lucee5/sqlite :: Stress Testing for Race Conditions ‑ should handle concurrent model access with cache manipulation
lucee6/sqlite :: Stress Testing for Race Conditions ‑ should handle concurrent model access with cache manipulation
lucee7/sqlite :: Stress Testing for Race Conditions ‑ should handle concurrent model access with cache manipulation

♻️ This comment has been updated with latest results.

bpamiri and others added 3 commits March 13, 2026 21:57
MySQL DATETIME and Oracle TIMESTAMP have second-level precision, so
events published within the same second get identical createdAt values.
The previous > comparison missed events in the same second as the
reference event. Switch to >= with an explicit id != exclusion to
handle all databases correctly.
Rename _wheels_events to wheels_events — Oracle rejects leading
underscores in unquoted identifiers. Matches the existing wheels_jobs
naming convention.

Cherry-pick engine pinning and CI hardening from peter/lucli-module:
- Pin Lucee 7 to 7.0.1+100 (7.0.2+106 breaks encodeForHTML)
- Pin all engines to last known good versions
- Add --no-cache to docker build, memory limits, SQLite skip

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2025.0.06+33156 → 2025.0.06+331564 (missing trailing digit)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs enhancement javascript Pull requests that update javascript code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pub/Sub for SSE channels

1 participant