# Broadcasting: Faye → ActionCable Migration Guide ## Current state ``` Model (SimplyStored::Couch) → ApplicationController.new.broadcast_user # ⚠️ anti-pattern → Mozo.broadcast_user → Mozo::Broadcaster::Faye.new.broadcast # HTTP POST to Faye → Faye server (Thin, port 9296) → WebSocket → browser clients ``` ## Target state ``` Model (Broadcastable concern) → Mozo.broadcast_user → Mozo::Broadcaster::ActionCable.new.broadcast # in-process async → ActionCable (Rails built-in) → WebSocket → browser clients ``` ## What this branch adds | File | Purpose | |------|---------| | `lib/mozo/broadcaster/action_cable.rb` | Drop-in ActionCable broadcaster adapter | | `config/cable.yml` | ActionCable configuration (async for single-server) | | `app/channels/application_cable/connection.rb` | WebSocket auth via auth_token | | `app/channels/mozo_channel.rb` | Channel authorization for user/supplier/employee | | `app/models/concerns/broadcastable.rb` | Clean module for models (replaces old monkey-patch) | | `config/routes.rb` | Mounts `/cable` WebSocket endpoint | | `config/initializers/model_broadcast.rb` | Fixed: delegates to Mozo directly (no more `ApplicationController.new`) | ## How to switch ### 1. Server (one-line change) In `config/initializers/mozo_settings.rb`, change: ```ruby # Mozo.broadcaster = Mozo::Broadcaster::Faye.new # old Mozo.broadcaster = Mozo::Broadcaster::ActionCable.new # new ``` ### 2. Client (mozo-user / mozo-supplier) **Old Faye client (conceptual):** ```js var client = new Faye.Client('https://events.mozo.bar/faye'); client.subscribe('/user/123', function(msg) { ... }); ``` **New ActionCable client:** ```js // Using @rails/actioncable npm package import { createConsumer } from "@rails/actioncable"; const consumer = createConsumer( `wss://mozo.bar/cable?auth_token=${authToken}` ); consumer.subscriptions.create( { channel: "MozoChannel", id: "user_123" }, { received(data) { // data = { event: "list_closed", data: { id: 42 } } handleEvent(data.event, data.data); } } ); ``` ### 3. Remove Faye Once stable: - Remove `gem 'faye'` from Gemfile - Remove `faye/` directory - Remove nginx `events.mozo.bar` vhost - Stop the Faye Thin process ## Benefits - **No extra process** — ActionCable runs inside Puma - **Async** — `broadcast` is non-blocking - **Simpler deploys** — one less service to manage - **WebSocket native** — no long-polling fallback complexity - **Rails auth** — cookies/sessions work automatically --- # Counter: DrbCounter → Redis Migration ## Current state ``` Supplier::Counters (app/models/supplier/counters.rb) → Mozo::Counter.incr/decr/get/set → Mozo::Counter.connection (Mozo::DrbCounter.object) → DRb → druby://localhost:9022 → InMemoryQCounter (separate Ruby process) → on startup: reloads counts from CouchDB → in-memory only (lost on restart) ``` ## Problems 1. **In-memory only** — restart the DRb process = lose all counts until CouchDB reload 2. **Single-process** — DRb runs one Ruby process, single point of failure 3. **Separate process** — another thing to monitor, deploy, and restart 4. **Race conditions** — between Puma workers, increment/decrement is not atomic across the DRb boundary 5. **Custom code** — `InMemoryQCounter` is 100 lines of hand-rolled counter logic ## Target state ``` Supplier::Counters → Mozo::Counter.incr/decr/get/set → Mozo::Counter.connection (Mozo::Counter::Redis.new) → Redis (localhost:6379) → persistent, atomic, multi-process safe ``` ## How to switch In `config/initializers/mozo_settings.rb`, change: ```ruby # Mozo::Counter.connection = Mozo::DrbCounter.object # old Mozo::Counter.connection = Mozo::Counter::Redis.new # new ``` That's it. All existing `Mozo::Counter.get/set/incr/decr` calls work unchanged. ## What Redis provides - **Atomic INCR/DECR** — no race conditions - **Persistence** — RDB snapshots + AOF, survives restarts - **Multi-process** — all Puma workers share the same Redis - **Already needed** — ActionCable uses Redis for pub/sub in production - **Battle-tested** — millions of deployments ## Migration steps 1. `apt-get install redis-server` — already done on vmi3300327 2. `gem 'redis', '~> 5.0'` — added to Gemfile 3. Switch `Mozo::Counter.connection` — one-line change in mozo_settings.rb 4. Stop the DRb counter process (`drb_counter/drb_counter.rb`)