DEV Community

Cover image for Architecting Zero-Latency RFID Attendee Tracking: Edge SQL and Go for High-Density Events
stampiq
stampiq

Posted on

Architecting Zero-Latency RFID Attendee Tracking: Edge SQL and Go for High-Density Events

When building an event analytics platform for Saudi Vision 2030 mega-events (like LEAP Riyadh or the Global AI Summit), standard web architecture paradigms completely break down[cite: 1, 14].

If you build a typical gate access control system—where an optical scanner reads a QR code, sends an HTTP request to a remote PostgreSQL database, waits for validation, and opens a turnstile—you will fail in production[cite: 1].

Why? Because when 8,000 delegates enter a convention center in Riyadh between 8:15 AM and 9:00 AM, the venue’s commercial fiber connection and local 5G macrocells instantly saturate. Your 150ms HTTP handshake balloons to 8 seconds, and your physical gates freeze.

To achieve zero-latency entry and continuous crowd flow, we redesigned our RFID attendee tracking architecture around two principles: Offline-First Edge Persistence and Passive UHF Telemetry[cite: 1, 14].


1. Ditching Barcodes for Passive UHF Telemetry

Optical scanners require line-of-sight, which limits gate throughput to roughly 10 delegates per minute per lane. To process enterprise crowds, we moved to passive Ultra-High Frequency (UHF) radio telemetry.

Instead of a phone screen, attendees are issued a smart badge encoded with an EPC Gen 2 (ISO 18000-6C) microchip. We place overhead RFID portals across the venue archways.

The Physics of Anti-Collision:
When a group of 15 attendees walks through a portal simultaneously, their backscattered radio signals collide. To resolve this, our edge readers use dynamic Q-algorithm arbitration (Frame-Slotted ALOHA):

  1. The reader broadcasts a slot parameter $Q$.
  2. Tags randomly select a slot $C = 2^Q$.
  3. If tags collide in the same slot, the reader increments $Q$ to expand the slot pool for the next millisecond round.

This allows a single overhead portal to resolve up to 400 distinct tag reads per second—capturing precise zone access and sponsor dwell times without anyone ever stopping to scan.


2. Offline-First Edge Resilience (SQLite WAL)

When the reader successfully decodes an EPC payload, it passes the binary frame to a local industrial edge controller (e.g., an ARM-based SBC running Linux). This controller must not wait for a cloud HTTP response to grant access.

Instead, we run a localized admission engine using SQLite in Write-Ahead Logging (WAL) mode[cite: 14].


javascript
// Local Node.js Edge Runtime (Simplified)
const Database = require('better-sqlite3');

const db = new Database('/var/data/gate_telemetry.db');

// WAL decouples reads from writes, preventing thread locks
db.pragma('journal_mode = WAL');
db.pragma('synchronous = NORMAL');
db.pragma('temp_store = MEMORY');

// Pre-load the expected attendee manifest into memory-mapped pages
const validateScan = db.prepare(`
  SELECT delegate_uuid, access_level 
  FROM local_manifest 
  WHERE epc_hash = ?
`);

const logEntry = db.prepare(`
  INSERT INTO telemetry_buffer (epc_hash, portal_id, direction, epoch_ms, synced)
  VALUES (?, ?, ?, ?, 0)
`);

function processRFIDFrame(frame) {
  // 1. Local sub-millisecond lookup
  const delegate = validateScan.get(frame.epc_hash);

  if (delegate && delegate.access_level >= REQUIRED_LEVEL) {
    // 2. Fire physical relay (turnstile/light) instantly
    hardwareRelay.open();

    // 3. Commit to local buffer (avg ~2ms write time)
    logEntry.run(frame.epc_hash, frame.portal_id, frame.direction, Date.now());
  }
}
Because SQLite WAL allows concurrent readers and writers, the continuous influx of RFID reads never blocks the background daemon, which is responsible for syncing data back to the cloud.

3. High-Throughput Cloud Ingestion (Go & Redis)
When the venue's wide-area network is stable, edge nodes continuously flush their local telemetry_buffer to our central cloud API.

To handle bursts of telemetry from dozens of portals simultaneously without overwhelming our relational database, our cloud ingestion layer is written in Go and buffers directly into Redis Streams[cite: 14].

Go
package main

import (
    "context"
    "encoding/binary"
    "net/http"
    "[github.com/redis/go-redis/v9](https://github.com/redis/go-redis/v9)"
)

var ctx = context.Background()
var rdb *redis.Client

func ingestTelemetryBatch(w http.ResponseWriter, r *http.Request) {
    // Parse compacted binary batch from edge node
    batch := parseBinaryPayload(r.Body)

    pipe := rdb.Pipeline()
    for _, scan := range batch {
        // Append to immutable stream buffer O(1)
        pipe.XAdd(ctx, &redis.XAddArgs{
            Stream: "stream:rfid_ingress",
            Values: map[string]interface{}{
                "epc":    scan.EPC,
                "portal": scan.PortalID,
                "ts":     scan.EpochMS,
            },
        })
    }

    if _, err := pipe.Exec(ctx); err != nil {
        http.Error(w, "Ingestion Failed", 500)
        return
    }

    w.WriteHeader(http.StatusAccepted)
}
4. Powering the Live Dashboard
A cluster of Go worker nodes consumes the stream:rfid_ingress via Redis Consumer Groups[cite: 14]. These workers apply deduplication logic, aggregate spatial headcounts, and push the live metrics via WebSockets directly to the operations team's KSA custom reporting dashboard[cite: 14].

This architecture provides event directors with real-time visibility into gate influx velocity and civil defense hall capacities, while guaranteeing that physical ingress never halts[cite: 14].

Engineering for Physical Scale
If you are building IoT telemetry pipelines or developing access control software, remember: physical hardware cannot wait on the cloud[cite: 14].

By pushing persistence to the edge with SQLite WAL and decoupling cloud ingestion with Redis Streams, you can build event infrastructure that scales effortlessly to tens of thousands of users[cite: 14]. To see these systems in production, check out StampIQ's event technology deployments across the GCC.
Enter fullscreen mode Exit fullscreen mode

Top comments (0)