Skip to content

Commit 93885d5

Browse files
cj-vanaclaude
andcommitted
Fix packet deduplication and node status handling
- Add unique index on origin_key to prevent duplicate packets from multiple observers - Update insertPacket() to use ON CONFLICT DO NOTHING for deduplication - Change updateObserverLastSeen() to only update gateway nodes, not room_server - Room servers now follow normal 15-minute inactive timeout 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 6287135 commit 93885d5

File tree

4 files changed

+22
-2
lines changed

4 files changed

+22
-2
lines changed

services/mqtt-collector/src/db.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ function getTodayDateString(): string {
4949

5050
/**
5151
* Insert a new packet into the database with all fields
52+
* Uses ON CONFLICT to skip duplicate packets (same origin_key from multiple observers)
5253
*/
5354
export async function insertPacket(packet: ParsedPacket): Promise<void> {
5455
const client = getDb();
@@ -60,6 +61,7 @@ export async function insertPacket(packet: ParsedPacket): Promise<void> {
6061
score, duration, route, len, payload_len, direction
6162
)
6263
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
64+
ON CONFLICT(origin_key) DO NOTHING
6365
`,
6466
args: [
6567
packet.nodeId,

src/app/api/health/route.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,12 +258,12 @@ function calculateNetworkScore(
258258
}
259259

260260
/**
261-
* Update observer node's last_seen timestamp when bot is active
261+
* Update observer/gateway node's last_seen timestamp when bot is active
262262
*/
263263
async function updateObserverLastSeen(): Promise<void> {
264264
try {
265265
await db.execute({
266-
sql: `UPDATE nodes SET last_seen = datetime('now') WHERE name LIKE '%Observer%' OR node_type = 'room_server'`,
266+
sql: `UPDATE nodes SET last_seen = datetime('now') WHERE node_type = 'gateway'`,
267267
args: [],
268268
});
269269
} catch {
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
-- Migration: Add unique constraint on origin_key to prevent duplicate packets
2+
-- Created: 2026-01-02
3+
--
4+
-- Problem: When multiple observers see the same packet, it gets inserted multiple times,
5+
-- inflating packet counts. The origin_key field contains the packet hash which should be unique.
6+
--
7+
-- Solution: Add unique index on origin_key and use ON CONFLICT DO NOTHING in insertPacket()
8+
9+
-- First, remove any existing duplicates (keep the first occurrence by id)
10+
DELETE FROM packets WHERE id NOT IN (
11+
SELECT MIN(id) FROM packets WHERE origin_key IS NOT NULL GROUP BY origin_key
12+
) AND origin_key IS NOT NULL;
13+
14+
-- Add unique constraint on origin_key
15+
CREATE UNIQUE INDEX IF NOT EXISTS idx_packets_origin_key_unique ON packets(origin_key);

src/lib/db/schema.sql

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,6 @@ CREATE INDEX IF NOT EXISTS idx_packets_timestamp ON packets(timestamp);
4444
CREATE INDEX IF NOT EXISTS idx_packets_node_id ON packets(node_id);
4545
CREATE INDEX IF NOT EXISTS idx_nodes_last_seen ON nodes(last_seen);
4646
CREATE INDEX IF NOT EXISTS idx_stats_node_date ON node_stats_daily(node_id, date);
47+
48+
-- Unique constraint on origin_key to prevent duplicate packets from multiple observers
49+
CREATE UNIQUE INDEX IF NOT EXISTS idx_packets_origin_key_unique ON packets(origin_key);

0 commit comments

Comments
 (0)