Overview
Discord events flow from the gateway through Serenity, into the runtime’s dispatch system, and finally to JavaScript event handlers in guild isolates.High-Level Flow
Step-by-Step Flow
1. Gateway Event Reception
Discord sends events over WebSocket to Serenity’s gateway client. Source:apps/runtime/src/discord_handler.rs:26
2. Event Handler Dispatch
Serenity callsDiscordHandler::dispatch() with a FullEvent.
apps/runtime/src/discord_handler.rs:26
3. Payload Conversion
Discord types are converted to serializable payloads using theexpose_payload macro.
apps/runtime/src/discord_handler.rs:426
The
expose_payload macro auto-generates serde::Serialize and TypeScript type definitions.4. Guild Filtering
flora is guild-only. Events without a guild_id are dropped.apps/runtime/src/discord_handler.rs:74
5. Event Dispatch to Runtime
The handler callsBotRuntime::dispatch_js_event() with event name, guild ID, and payload.
apps/runtime/src/discord_handler.rs:78
6. Worker Selection
The runtime routes the event to the appropriate worker using consistent hashing.apps/runtime/src/runtime/mod.rs:179
Guild routing:
- Hash guild_id using
DefaultHasher - Modulo by worker count to get index
- Guilds always route to same worker (sticky routing)
apps/runtime/src/runtime/mod.rs:252
7. Migration Queue Check
If the guild is migrating, events are queued instead of dispatched.apps/runtime/src/runtime/mod.rs:189
8. Backlog Check
Droppable events (typing, presence) are dropped if worker backlog is high.apps/runtime/src/runtime/mod.rs:202
Droppable events:
- Typing indicators
- Presence updates
- Other non-critical events
apps/runtime/src/runtime/constants.rs
9. Worker Command Send
The runtime sends aWorkerCommand::DispatchEvent to the worker’s channel.
apps/runtime/src/runtime/types.rs
10. Worker Thread Processing
The worker thread receives the command and processes it.apps/runtime/src/runtime/worker.rs:175
11. Runtime Lookup
The worker finds the guild’s JavaScript runtime.apps/runtime/src/runtime/worker.rs:738
12. Dispatch into V8
The event is dispatched into the V8 isolate with timeout.apps/runtime/src/runtime/worker.rs:777
13. Secret Scope Entry
Secrets are made available via thread-local storage during dispatch.flora_secrets ops to access guild secrets safely.
Source: apps/runtime/src/runtime/secrets.rs
14. JavaScript Handler Execution
The SDK’s dispatch function routes to registered handlers.15. Promise Resolution
The V8 event loop runs until the dispatch promise resolves or times out.apps/runtime/src/runtime/worker.rs:809
16. Timeout Handling
If dispatch times out, the runtime is terminated to prevent stale state.apps/runtime/src/runtime/worker.rs:826
17. Metrics Recording
Dispatch success/failure is recorded for observability.apps/runtime/src/runtime/worker.rs:835
Timeout Cascade
Each phase has configurable timeouts:| Phase | Config | Default | Effect |
|---|---|---|---|
| Bootstrap | boot_timeout_secs | 5s | Runtime init timeout |
| Load | load_timeout_secs | 30s | Script load timeout |
| Dispatch | dispatch_timeout_secs | 3s | Per-event timeout |
| Cron | cron_timeout_secs | 5s | Cron handler timeout |
| Migration | migration_timeout_ms | 500ms | Migration quiesce timeout |
Error Propagation
Errors flow backward through the chain:- JavaScript throws error
- V8 promise rejects
- Deno Core returns
AnyError - Worker sends error via oneshot channel
- BotRuntime receives error
- Error logged via tracing
Special Event Types
Broadcast Events
Events without a guild_id are broadcast to all workers and runtimes.apps/runtime/src/runtime/mod.rs:220
Cron Events
Cron jobs dispatch synthetic events with the pattern__cron:<name>.
apps/runtime/src/runtime/worker.rs:306
Backpressure Mechanisms
Worker Backlog
Each worker tracks pending events. Droppable events are dropped when backlog exceeds threshold.apps/runtime/src/runtime/constants.rs
Migration Queuing
During migration, events are queued and replayed after migration completes.apps/runtime/src/runtime/mod.rs:275
Performance Considerations
- Unbounded channels for worker commands (backpressure via backlog counter)
- Oneshot channels for response synchronization
- Parking lot mutexes for low-contention locks
- Lazy V8 compilation (scripts compiled on first load)
- Event loop reuse (isolates persist across events)