Event System
OctoFHIR uses a unified event system to coordinate actions across modules when resources are created, updated, or deleted. This enables real-time cache invalidation, policy reloads, GraphQL subscriptions, and multi-instance synchronization.
Overview
Section titled “Overview”When a FHIR resource is modified through the REST API, GraphQL, or any other endpoint, the system automatically:
- Emits a
ResourceEventto the central event bus - Dispatches the event to all registered hooks
- Each hook performs its action asynchronously (cache invalidation, reload, etc.)
- If Redis is enabled, broadcasts the event to other server instances
┌──────────────────────────────────────────────────────────────┐│ FHIR CRUD Operation ││ POST /fhir/Patient → Storage → Event Broadcast │└──────────────────────────────────────────────────────────────┘ │ ▼┌──────────────────────────────────────────────────────────────┐│ Event Broadcaster ││ (tokio::broadcast channel) │└──────────────────────────────────────────────────────────────┘ │ │ │ │ ▼ ▼ ▼ ▼ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │ Policy │ │Gateway │ │ Search │ │ Audit │ │ Reload │ │ Reload │ │ Param │ │ Log │ └────────┘ └────────┘ └────────┘ └────────┘Built-in Hooks
Section titled “Built-in Hooks”OctoFHIR includes several hooks that react to resource changes:
Policy Reload Hook
Section titled “Policy Reload Hook”Automatically reloads the policy cache when AccessPolicy resources are modified.
Triggered by: AccessPolicy create/update/delete
Action: Notifies the policy reload service to refresh the in-memory policy cache, ensuring authorization decisions use the latest policies.
Gateway Reload Hook
Section titled “Gateway Reload Hook”Reloads the gateway routing table when App or CustomOperation resources change.
Triggered by: App, CustomOperation create/update/delete
Action: Rebuilds the dynamic API routes from the database, activating new endpoints or removing deleted ones.
Search Parameter Hook
Section titled “Search Parameter Hook”Updates the search parameter registry when SearchParameter resources are modified.
Triggered by: SearchParameter create/update/delete
Action: Upserts the parameter into the registry and clears the query cache to ensure new searches use the updated parameter.
GraphQL Subscription Hook
Section titled “GraphQL Subscription Hook”Forwards resource events to connected GraphQL subscription clients.
Triggered by: All resource types
Action: Broadcasts the change to clients subscribed to that resource type, enabling real-time UI updates.
Audit Hook
Section titled “Audit Hook”Logs resource changes as FHIR AuditEvent resources asynchronously.
Triggered by: All resource types (filtered by audit configuration)
Action: Creates an AuditEvent recording the action, actor, and outcome without blocking the API response.
Multi-Instance Synchronization
Section titled “Multi-Instance Synchronization”For horizontal scaling with multiple OctoFHIR instances, enable Redis to synchronize events across all instances.
Configuration
Section titled “Configuration”[redis]enabled = trueurl = "redis://localhost:6379"pool_size = 10timeout_ms = 5000How It Works
Section titled “How It Works”┌─────────────────────────┐ ┌─────────────────────────┐│ Instance 1 │ │ Instance 2 ││ │ │ ││ POST /fhir/Patient │ │ ││ │ │ │ ││ ▼ │ │ ││ EventBroadcaster ──────┼────────►│ EventBroadcaster ││ │ │ Redis │ │ ││ ▼ │ Pub/Sub │ ▼ ││ Local Hooks │ │ Local Hooks ││ (Policy, Gateway...) │ │ (Policy, Gateway...) │└─────────────────────────┘ └─────────────────────────┘When Redis is enabled:
- RedisPublishHook publishes events to the
octofhir:resource_eventschannel - RedisEventSync subscribes to the channel on each instance
- Received events are forwarded to the local broadcaster
- Local hooks process the event (cache invalidation, etc.)
This ensures all instances have consistent caches without manual coordination.
Event Types
Section titled “Event Types”ResourceEvent
Section titled “ResourceEvent”Emitted when a FHIR resource is created, updated, or deleted:
| Field | Type | Description |
|---|---|---|
event_type | Created | Updated | Deleted | Type of change |
resource_type | String | FHIR resource type (e.g., “Patient”) |
resource_id | String | Resource ID |
version_id | Option<i64> | Version number (if available) |
resource | Option<Value> | Full resource JSON (None for deletes) |
timestamp | OffsetDateTime | When the event occurred |
AuthEvent
Section titled “AuthEvent”Emitted for authentication-related actions:
| Field | Type | Description |
|---|---|---|
event_type | SessionCreated | LoginSucceeded | TokenRevoked | … | Type of auth event |
user_id | Option<String> | User ID (if applicable) |
client_id | String | OAuth client ID |
session_id | Option<String> | Session ID |
ip_address | Option<IpAddr> | Client IP address |
timestamp | OffsetDateTime | When the event occurred |
Hook Isolation
Section titled “Hook Isolation”Each hook runs in isolation to prevent failures from affecting other hooks or the API:
- Timeout: 30 seconds maximum execution time
- Panic Recovery: Panics are caught and logged
- Error Isolation: Errors in one hook don’t affect others
- Async Execution: Hooks run concurrently
Hook A: ✓ SuccessHook B: ✗ Error (logged, other hooks continue)Hook C: ✓ SuccessHook D: ⏱ Timeout (logged, other hooks continue)
API Response: Success (hooks don't block response)Performance
Section titled “Performance”The event system is designed for minimal impact on API latency:
| Operation | Latency | Notes |
|---|---|---|
| Event broadcast | ~1-5 μs | Non-blocking, returns immediately |
| Hook dispatch | ~10-100 μs | Plus async execution |
| Redis publish | ~1-5 ms | Network round-trip |
| Total API impact | < 10 μs | Hooks run after response |
Events are emitted after the storage operation completes but before sending the API response. Hook execution happens asynchronously, so it doesn’t add latency to the response.
Monitoring
Section titled “Monitoring”Enable debug logging to see event activity:
RUST_LOG=octofhir_core::events=debug,octofhir_server::hooks=debug cargo runExample logs:
DEBUG octofhir_storage::evented: Emitting Created event for Patient/123DEBUG octofhir_server::hooks::policy: Handling AccessPolicy changeINFO octofhir_server::hooks::gateway: Gateway routes reloadedDEBUG octofhir_server::events::redis: Published event to RedisBest Practices
Section titled “Best Practices”For Single Instance Deployments
Section titled “For Single Instance Deployments”- Redis is optional for single-instance deployments
- All hooks work locally without Redis
- Consider enabling audit logging for compliance
For Multi-Instance Deployments
Section titled “For Multi-Instance Deployments”- Always enable Redis for cache consistency
- Use the same Redis instance for all OctoFHIR servers
- Monitor Redis connectivity (graceful degradation on failure)
- Consider Redis Sentinel or Cluster for high availability
For Custom Applications
Section titled “For Custom Applications”When building applications that modify resources directly via the database:
- Use the OctoFHIR REST API instead of direct database writes
- This ensures events are emitted and all hooks are triggered
- Direct database changes bypass the event system
Troubleshooting
Section titled “Troubleshooting”Events Not Triggering
Section titled “Events Not Triggering”- Check that resources are modified via the API (not direct DB writes)
- Verify hooks are registered (check startup logs)
- Enable debug logging for event tracing
Multi-Instance Sync Issues
Section titled “Multi-Instance Sync Issues”- Verify Redis connectivity on all instances
- Check
redis.enabled = truein configuration - Monitor Redis pub/sub with
redis-cli SUBSCRIBE octofhir:resource_events - Check for Redis connection errors in logs
Hook Failures
Section titled “Hook Failures”- Check logs for hook error messages
- Verify required services are running (e.g., PostgreSQL for gateway reload)
- Hook failures are isolated and don’t affect other hooks
Future Enhancements
Section titled “Future Enhancements”- Webhook notifications - HTTP callbacks for external systems
- Event replay - Replay events from a point in time
- Event filtering - Subscribe to specific resource types only
- Metrics - Prometheus metrics for event throughput and latency