mirror of
https://github.com/bckelley/cgm-remote-monitor.git
synced 2026-08-24 03:14:12 -05:00
Adjusted internal documentation links and updated references within various Markdown files to reflect a new directory structure for audit and meta documents. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 4dee684a-a4b9-4c32-a818-3a27dae1b027 Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Event-Id: 971b8c75-c322-4ede-98ea-eb5ae31d4e5c Replit-Helium-Checkpoint-Created: true
594 lines
16 KiB
Markdown
594 lines
16 KiB
Markdown
# Real-Time Systems Audit
|
|
|
|
**Document Version:** 1.0
|
|
**Last Updated:** January 2026
|
|
**Scope:** Socket.IO namespaces, event bus patterns, client subscriptions, latency considerations
|
|
|
|
---
|
|
|
|
## 1. Executive Summary
|
|
|
|
Nightscout's real-time capabilities are critical for timely glucose monitoring alerts. This audit examines the event-driven architecture, WebSocket implementation, and opportunities for improvement.
|
|
|
|
### Real-Time Components
|
|
|
|
| Component | Technology | Purpose |
|
|
|-----------|------------|---------|
|
|
| Internal Event Bus | Node.js Stream | Inter-process communication |
|
|
| Legacy WebSocket | Socket.IO 4.5 | Client data updates |
|
|
| Storage Socket | Socket.IO 4.5 | API v3 CRUD events |
|
|
| Alarm Socket | Socket.IO 4.5 | Alert broadcasting |
|
|
|
|
---
|
|
|
|
## 2. Internal Event Bus
|
|
|
|
### 2.1 Architecture
|
|
|
|
**Location:** `lib/bus.js`
|
|
|
|
The event bus is a Node.js Stream that provides pub/sub functionality within the server process.
|
|
|
|
**Implementation:**
|
|
```javascript
|
|
var Stream = require('stream');
|
|
|
|
function init (settings) {
|
|
var stream = new Stream;
|
|
stream.readable = true;
|
|
|
|
// Heartbeat ticker
|
|
busInterval = setInterval(function() {
|
|
stream.emit('tick', ictus());
|
|
}, settings.heartbeat * 1000);
|
|
|
|
stream.teardown = function () {
|
|
clearInterval(busInterval);
|
|
stream.emit('teardown');
|
|
};
|
|
|
|
return stream;
|
|
}
|
|
```
|
|
|
|
### 2.2 Event Catalog
|
|
|
|
| Event | Source | Subscribers | Data |
|
|
|-------|--------|-------------|------|
|
|
| `tick` | Bus (timer) | Data loader | `{ now, beat, interval }` |
|
|
| `data-received` | API endpoints | Data loader | (none) |
|
|
| `data-loaded` | Data loader | Plugin system | (none) |
|
|
| `data-processed` | Plugin system | Runtime state | `sbx` |
|
|
| `notification` | Plugins, ack | Push notify, WebSocket | Notification object |
|
|
| `admin-notify` | Auth failures | Admin notifier | `{ title, message }` |
|
|
| `teardown` | Server shutdown | All cleanup handlers | (none) |
|
|
| `storage-socket-create` | API v3 | Storage socket | `{ col, doc }` |
|
|
| `storage-socket-update` | API v3 | Storage socket | `{ col, doc }` |
|
|
| `storage-socket-delete` | API v3 | Storage socket | `{ col, identifier }` |
|
|
|
|
### 2.3 Event Flow
|
|
|
|
```
|
|
┌─────────────┐
|
|
│ Timer │
|
|
│ (heartbeat) │
|
|
└──────┬──────┘
|
|
│ tick
|
|
▼
|
|
┌──────────────┐ ┌─────────────┐ ┌─────────────┐
|
|
│ API v1/v3 │────▶│ Event │────▶│ Data │
|
|
│ Endpoints │data-│ Bus │data-│ Loader │
|
|
└──────────────┘recv │ │loaded└─────────────┘
|
|
└──────┬──────┘ │
|
|
│ │
|
|
┌───────────────────┼───────────────────┼────────────────┐
|
|
│ │ │ │
|
|
▼ ▼ ▼ ▼
|
|
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
|
│ Plugin │ │ WebSocket │ │ Push │ │ Storage │
|
|
│ System │ │ Broadcast │ │ Notify │ │ Socket │
|
|
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
|
|
```
|
|
|
|
### 2.4 Timing Characteristics
|
|
|
|
| Event Trigger | Typical Interval | Latency |
|
|
|--------------|------------------|---------|
|
|
| Heartbeat tick | 60 seconds (configurable) | <1ms |
|
|
| Data received | On API write | <1ms |
|
|
| Data processed | After tick + load | 100-500ms |
|
|
| Notification | On plugin alarm | <10ms |
|
|
|
|
### 2.5 Issues and Recommendations
|
|
|
|
| Issue | Impact | Recommendation |
|
|
|-------|--------|----------------|
|
|
| No event typing | Runtime errors | Add TypeScript definitions |
|
|
| No event validation | Data corruption | Add schema validation |
|
|
| Single-threaded | Scalability | Consider Redis pub/sub |
|
|
| No persistence | Lost events on crash | Add event sourcing |
|
|
| No replay | Debugging difficulty | Add event logging |
|
|
|
|
---
|
|
|
|
## 3. Socket.IO Implementation
|
|
|
|
### 3.1 Server Setup
|
|
|
|
**Location:** `lib/server/websocket.js`
|
|
|
|
**Initialization:**
|
|
```javascript
|
|
var io = require('socket.io')(server, {
|
|
// Default configuration
|
|
pingTimeout: 60000,
|
|
pingInterval: 25000
|
|
});
|
|
|
|
io.on('connection', function (socket) {
|
|
// Handle connection
|
|
});
|
|
```
|
|
|
|
### 3.2 Socket.IO Namespaces
|
|
|
|
| Namespace | Path | Purpose | Auth Required |
|
|
|-----------|------|---------|---------------|
|
|
| Default | `/` | Legacy data updates | Optional |
|
|
| Storage | `/storage` | Collection CRUD events | Yes |
|
|
| Alarm | `/alarm` | Alert broadcasting | Yes |
|
|
|
|
### 3.3 Default Namespace (`/`)
|
|
|
|
**Location:** `lib/server/websocket.js`
|
|
|
|
**Client Connection:**
|
|
```javascript
|
|
const socket = io('https://nightscout.example.com/', {
|
|
query: { token: 'access-token' }
|
|
});
|
|
```
|
|
|
|
**Server Events (outbound):**
|
|
|
|
| Event | Payload | Trigger |
|
|
|-------|---------|---------|
|
|
| `dataUpdate` | `{ delta, ... }` | Data change |
|
|
| `alarm` | Notification object | Warning alarm |
|
|
| `urgent_alarm` | Notification object | Urgent alarm |
|
|
| `announcement` | Notification object | User announcement |
|
|
| `clear_alarm` | `{}` | Alarm cleared |
|
|
| `connect` | (none) | Connection established |
|
|
|
|
**Client Events (inbound):**
|
|
|
|
| Event | Payload | Action |
|
|
|-------|---------|--------|
|
|
| `authorize` | `{ client, secret, token, history }` | Authenticate |
|
|
| `ack` | `{ level, group, silenceTime }` | Acknowledge alarm |
|
|
|
|
### 3.4 Storage Namespace (`/storage`)
|
|
|
|
**Location:** `lib/api3/storageSocket.js`
|
|
|
|
**Subscription:**
|
|
```javascript
|
|
socket.emit('subscribe', {
|
|
accessToken: 'mytoken-abc123',
|
|
collections: ['entries', 'treatments'] // Optional filter
|
|
}, function(response) {
|
|
if (response.success) {
|
|
console.log('Subscribed to:', response.collections);
|
|
}
|
|
});
|
|
```
|
|
|
|
**Server Events:**
|
|
|
|
| Event | Payload | Description |
|
|
|-------|---------|-------------|
|
|
| `create` | `{ colName, doc }` | Document created |
|
|
| `update` | `{ colName, doc }` | Document updated |
|
|
| `delete` | `{ colName, identifier }` | Document deleted |
|
|
| `subscribed` | `{ collections }` | Subscription confirmed |
|
|
|
|
**Permission Mapping:**
|
|
```javascript
|
|
const permission = (col === 'settings')
|
|
? `api:${col}:admin`
|
|
: `api:${col}:read`;
|
|
```
|
|
|
|
### 3.5 Alarm Namespace (`/alarm`)
|
|
|
|
**Location:** `lib/api3/alarmSocket.js`
|
|
|
|
**Subscription:**
|
|
```javascript
|
|
socket.emit('subscribe', {
|
|
accessToken: 'mytoken-abc123'
|
|
}, function(response) {
|
|
if (response.success) {
|
|
console.log('Subscribed to alarms');
|
|
}
|
|
});
|
|
```
|
|
|
|
**Server Events:**
|
|
|
|
| Event | Payload | Level |
|
|
|-------|---------|-------|
|
|
| `announcement` | Notification object | INFO |
|
|
| `alarm` | Notification object | WARN |
|
|
| `urgent_alarm` | Notification object | URGENT |
|
|
| `clear_alarm` | `{}` | Clear |
|
|
|
|
**Acknowledgment:**
|
|
```javascript
|
|
socket.on('ack', function(level, group, silenceTime) {
|
|
ctx.notifications.ack(level, group, silenceTime);
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Client-Side Integration
|
|
|
|
### 4.1 Web Dashboard
|
|
|
|
**Location:** `lib/client/index.js`, `lib/client/socket.js`
|
|
|
|
**Connection Flow:**
|
|
1. Page loads → Get server status
|
|
2. Connect to default namespace
|
|
3. Send `authorize` with token
|
|
4. Subscribe to data updates
|
|
5. Handle real-time events
|
|
|
|
**Event Handlers:**
|
|
```javascript
|
|
socket.on('dataUpdate', function(data) {
|
|
// Merge delta into local cache
|
|
receiveDData.mergeDataUpdate(data.delta, ...);
|
|
// Trigger chart update
|
|
chart.update();
|
|
});
|
|
|
|
socket.on('alarm', function(alarm) {
|
|
// Show alarm notification
|
|
client.showNotification(alarm);
|
|
// Play alarm sound
|
|
audio.play();
|
|
});
|
|
```
|
|
|
|
### 4.2 Mobile/Third-Party Clients
|
|
|
|
**Common Patterns:**
|
|
1. Connect to appropriate namespace
|
|
2. Subscribe with access token
|
|
3. Handle `dataUpdate` or granular CRUD events
|
|
4. Reconnect on disconnect
|
|
|
|
**Reconnection Strategy:**
|
|
```javascript
|
|
const socket = io(serverUrl, {
|
|
reconnection: true,
|
|
reconnectionDelay: 1000,
|
|
reconnectionDelayMax: 5000,
|
|
reconnectionAttempts: Infinity
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## 5. Data Update Mechanism
|
|
|
|
### 5.1 Heartbeat-Driven Updates
|
|
|
|
**Configuration:** `HEARTBEAT` environment variable (default: 60 seconds)
|
|
|
|
**Flow:**
|
|
```
|
|
Timer (every 60s)
|
|
↓ emit('tick')
|
|
Event Bus
|
|
↓
|
|
Data Loader (debounced)
|
|
↓ query MongoDB
|
|
↓ merge new data
|
|
↓ emit('data-loaded')
|
|
Plugin System
|
|
↓ process data
|
|
↓ check notifications
|
|
↓ emit('data-processed')
|
|
WebSocket
|
|
↓ broadcast to clients
|
|
```
|
|
|
|
### 5.2 API-Triggered Updates
|
|
|
|
**Flow:**
|
|
```
|
|
API POST /entries
|
|
↓ save to MongoDB
|
|
↓ emit('data-received')
|
|
Event Bus
|
|
↓ (immediate, bypasses debounce delay)
|
|
Data Loader
|
|
↓ ... same as above
|
|
```
|
|
|
|
### 5.3 Delta Calculation
|
|
|
|
**Location:** `lib/data/calcdelta.js`
|
|
|
|
**Purpose:** Calculate minimal update for WebSocket clients
|
|
|
|
**Algorithm:**
|
|
1. Compare current data with last sent data
|
|
2. Identify new, modified, deleted items
|
|
3. Create delta object with changes only
|
|
4. Track last sent timestamp per client
|
|
|
|
**Delta Object:**
|
|
```javascript
|
|
{
|
|
delta: true,
|
|
lastUpdated: 1595001000000,
|
|
sgvs: [/* new/changed entries */],
|
|
treatments: [/* new/changed treatments */],
|
|
mbgs: [],
|
|
cals: [],
|
|
profiles: [],
|
|
devicestatus: []
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 6. Latency Analysis
|
|
|
|
### 6.1 End-to-End Latency
|
|
|
|
**Typical Path (CGM → Dashboard):**
|
|
|
|
| Stage | Typical Latency | Notes |
|
|
|-------|----------------|-------|
|
|
| CGM → Uploader | 5 minutes | CGM reading interval |
|
|
| Uploader → API | 100-500ms | Network + API processing |
|
|
| API → MongoDB | 10-50ms | Database write |
|
|
| MongoDB → Event Bus | <1ms | Same process |
|
|
| Event Bus → Plugins | 100-300ms | Data loading + processing |
|
|
| Plugins → WebSocket | <10ms | Broadcast |
|
|
| WebSocket → Client | 50-200ms | Network |
|
|
| **Total** | **5-6 minutes** | CGM interval is dominant |
|
|
|
|
### 6.2 Real-Time Delay Factors
|
|
|
|
| Factor | Impact | Mitigation |
|
|
|--------|--------|------------|
|
|
| Heartbeat interval | 0-60s delay | Reduce interval (trade-off: resources) |
|
|
| Debounce threshold | 5s delay | Reduce threshold |
|
|
| Plugin processing | 100-300ms | Optimize plugins |
|
|
| Network latency | Variable | CDN for static assets |
|
|
| Client processing | 50-100ms | Optimize JavaScript |
|
|
|
|
### 6.3 Latency Optimization Recommendations
|
|
|
|
1. **Reduce heartbeat interval** for critical updates (30s)
|
|
2. **Bypass debounce** for urgent data
|
|
3. **Priority queue** for alarm events
|
|
4. **Client prediction** to compensate for delay
|
|
5. **Optimistic updates** in UI
|
|
|
|
---
|
|
|
|
## 7. Scalability Considerations
|
|
|
|
### 7.1 Current Limitations
|
|
|
|
| Limitation | Impact | Threshold |
|
|
|------------|--------|-----------|
|
|
| Single process | No horizontal scaling | ~1000 concurrent connections |
|
|
| In-memory state | Lost on restart | N/A |
|
|
| No load balancing | Single point of failure | N/A |
|
|
| No connection limits | DoS vulnerability | N/A |
|
|
|
|
### 7.2 Scaling Strategies
|
|
|
|
**Vertical Scaling:**
|
|
- Increase Node.js memory
|
|
- Use worker threads for CPU tasks
|
|
- Optimize event handlers
|
|
|
|
**Horizontal Scaling:**
|
|
```
|
|
┌─────────────┐
|
|
│ Load │
|
|
│ Balancer │
|
|
└──────┬──────┘
|
|
┌───────────────┼───────────────┐
|
|
▼ ▼ ▼
|
|
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
|
│ Nightscout │ │ Nightscout │ │ Nightscout │
|
|
│ Instance 1 │ │ Instance 2 │ │ Instance 3 │
|
|
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
|
|
│ │ │
|
|
└───────────────┼───────────────┘
|
|
▼
|
|
┌─────────────┐
|
|
│ Redis │
|
|
│ Pub/Sub │
|
|
└─────────────┘
|
|
```
|
|
|
|
**Requirements for Horizontal Scaling:**
|
|
1. Redis adapter for Socket.IO
|
|
2. Shared session store
|
|
3. Database connection pooling
|
|
4. Sticky sessions (or Redis pub/sub)
|
|
|
|
### 7.3 Socket.IO Redis Adapter
|
|
|
|
**Implementation:**
|
|
```javascript
|
|
const { createAdapter } = require('@socket.io/redis-adapter');
|
|
const { createClient } = require('redis');
|
|
|
|
const pubClient = createClient({ url: process.env.REDIS_URL });
|
|
const subClient = pubClient.duplicate();
|
|
|
|
io.adapter(createAdapter(pubClient, subClient));
|
|
```
|
|
|
|
---
|
|
|
|
## 8. Reliability
|
|
|
|
### 8.1 Connection Handling
|
|
|
|
**Current Behavior:**
|
|
- Automatic reconnection (Socket.IO default)
|
|
- No connection health checks
|
|
- No graceful degradation
|
|
|
|
**Recommendations:**
|
|
1. Implement connection heartbeat
|
|
2. Add connection timeout handling
|
|
3. Queue messages during disconnect
|
|
4. Implement exponential backoff
|
|
|
|
### 8.2 Error Handling
|
|
|
|
**Current Issues:**
|
|
- Some errors silently swallowed
|
|
- No error event for clients
|
|
- No error aggregation
|
|
|
|
**Recommendations:**
|
|
```javascript
|
|
socket.on('error', function(error) {
|
|
console.error('Socket error:', error);
|
|
// Notify monitoring
|
|
// Attempt recovery
|
|
});
|
|
|
|
io.engine.on('connection_error', function(err) {
|
|
console.error('Connection error:', err);
|
|
});
|
|
```
|
|
|
|
### 8.3 Graceful Shutdown
|
|
|
|
**Location:** `lib/bus.js`
|
|
|
|
**Current Implementation:**
|
|
```javascript
|
|
stream.teardown = function () {
|
|
console.log('Initiating server teardown');
|
|
clearInterval(busInterval);
|
|
stream.emit('teardown');
|
|
};
|
|
```
|
|
|
|
**Recommendations:**
|
|
1. Notify connected clients of shutdown
|
|
2. Wait for pending operations
|
|
3. Close connections gracefully
|
|
4. Implement shutdown timeout
|
|
|
|
---
|
|
|
|
## 9. Monitoring
|
|
|
|
### 9.1 Current Metrics
|
|
|
|
- Connection count (via Socket.IO)
|
|
- Basic console logging
|
|
|
|
### 9.2 Recommended Metrics
|
|
|
|
| Metric | Type | Purpose |
|
|
|--------|------|---------|
|
|
| `socket_connections_total` | Gauge | Active connections |
|
|
| `socket_messages_sent_total` | Counter | Message volume |
|
|
| `socket_message_latency_ms` | Histogram | Performance |
|
|
| `event_bus_events_total` | Counter | Internal events |
|
|
| `data_update_latency_ms` | Histogram | Update pipeline |
|
|
|
|
### 9.3 Alerting Recommendations
|
|
|
|
| Condition | Threshold | Action |
|
|
|-----------|-----------|--------|
|
|
| Connection drop | >50% in 5min | Alert |
|
|
| Message latency | >5s p99 | Alert |
|
|
| Event bus backlog | >100 events | Warn |
|
|
| Memory usage | >80% | Warn |
|
|
|
|
---
|
|
|
|
## 10. Security Considerations
|
|
|
|
### 10.1 Authentication
|
|
|
|
- Default namespace: Optional auth
|
|
- Storage/Alarm namespaces: Required auth
|
|
- Token validated per subscription
|
|
|
|
### 10.2 Authorization
|
|
|
|
- Storage: Per-collection permission check
|
|
- Alarm: Any valid token accepted
|
|
|
|
### 10.3 Rate Limiting
|
|
|
|
**Current State:** No rate limiting on WebSocket
|
|
|
|
**Recommendations:**
|
|
```javascript
|
|
// Limit events per client
|
|
const rateLimit = require('socket-rate-limiter');
|
|
io.use(rateLimit({
|
|
points: 100, // 100 events
|
|
duration: 60 // per minute
|
|
}));
|
|
```
|
|
|
|
---
|
|
|
|
## 11. Recommendations Summary
|
|
|
|
### Critical
|
|
|
|
1. **Add connection rate limiting** - Prevent DoS
|
|
2. **Implement proper error handling** - Reliability
|
|
3. **Add health check endpoint** - Monitoring
|
|
|
|
### High Priority
|
|
|
|
4. **Add Redis adapter** for horizontal scaling
|
|
5. **Implement connection metrics** - Observability
|
|
6. **Add message queue** for reliability
|
|
|
|
### Medium Priority
|
|
|
|
7. **Reduce heartbeat interval** - Lower latency
|
|
8. **Implement graceful shutdown** - Zero downtime
|
|
9. **Add TypeScript definitions** - Developer experience
|
|
|
|
### Low Priority
|
|
|
|
10. **WebSocket compression** - Bandwidth reduction
|
|
11. **Binary protocol option** - Performance
|
|
12. **Event sourcing** - Audit trail
|
|
|
|
---
|
|
|
|
## 12. Related Documents
|
|
|
|
- [Architecture Overview](../meta/architecture-overview.md)
|
|
- [Security Audit](./security-audit.md)
|
|
- [API Layer Audit](./api-layer-audit.md)
|
|
- [Modernization Roadmap](../meta/modernization-roadmap.md)
|