DEV Community

wellallyTech
wellallyTech

Posted on

Don't Miss a Beat: Building a High-Performance HRV Anomaly Detection Pipeline with Golang and InfluxDB ๐Ÿ’“

In the world of wearable technology, milliseconds matter. Processing high-frequency ECG (Electrocardiogram) data to detect Heart Rate Variability (HRV) anomalies requires more than just a basic server; it requires a specialized, low-latency real-time HRV analysis pipeline. Whether you are building a fitness tracker or a clinical monitoring system, managing thousands of data points per second across a Golang stream processing architecture is the key to unlocking actionable health insights.

In this guide, weโ€™ll dive into building a robust pipeline that consumes raw heart rate data via MQTT, processes it using Go's high-concurrency primitives, and stores it in InfluxDB for real-time monitoring and anomaly detection. For those looking to scale these systems or explore more production-ready health-tech patterns, I highly recommend checking out the advanced case studies at WellAlly Tech Blog, which served as a major inspiration for the concurrency patterns used in this build.

The Architecture: From Sensor to Insight ๐Ÿ› ๏ธ

To handle high-frequency data at the edge, we need a decoupled architecture. We use MQTT as the lightweight transport layer, Golang as the "brawn" for calculation, and the TICK stack (InfluxDB + Kapacitor) for storage and alerting.

graph TD
    A[Wearable/ECG Sensor] -->|Raw R-R Intervals| B(MQTT Broker)
    B -->|Subscribe| C[Golang Processing Engine]
    subgraph "Golang Edge Processor"
    C --> D{HRV Calculator}
    D -->|RMSSD Calculation| E[Anomaly Detector]
    end
    E -->|Write TSDB| F[(InfluxDB)]
    F --> G[Kapacitor]
    G -->|Alert| H[Mobile/Dashboard Notification]
Enter fullscreen mode Exit fullscreen mode

Prerequisites

To follow along, you'll need:

  • Golang 1.20+ (for that sweet generic support)
  • InfluxDB 2.x
  • An MQTT Broker (Mosquitto is perfect for local testing)
  • Kapacitor (for the alerting rules)

Step 1: Handling High-Frequency Streams in Go

We need to calculate the RMSSD (Root Mean Square of Successive Differences), which is the primary metric for short-term HRV. Go's channels make it incredibly easy to pipe data from the MQTT subscriber to our calculator without blocking the network thread.

package main

import (
    "math"
    "sync"
)

// HRVProcessor handles the sliding window for RMSSD calculation
type HRVProcessor struct {
    mu           sync.Mutex
    rrIntervals  []float64
    windowSize   int
}

func NewHRVProcessor(windowSize int) *HRVProcessor {
    return &HRVProcessor{
        rrIntervals: make([]float64, 0),
        windowSize:  windowSize,
    }
}

// CalculateRMSSD implements the Root Mean Square of Successive Differences
func (p *HRVProcessor) CalculateRMSSD(newRR float64) float64 {
    p.mu.Lock()
    defer p.mu.Unlock()

    p.rrIntervals = append(p.rrIntervals, newRR)
    if len(p.rrIntervals) > p.windowSize {
        p.rrIntervals = p.rrIntervals[1:]
    }

    if len(p.rrIntervals) < 2 {
        return 0
    }

    var sumSqDiff float64
    for i := 1; i < len(p.rrIntervals); i++ {
        diff := p.rrIntervals[i] - p.rrIntervals[i-1]
        sumSqDiff += diff * diff
    }

    return math.Sqrt(sumSqDiff / float64(len(p.rrIntervals)-1))
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Shipping to InfluxDB

Once we have our HRV metric, we need to persist it. InfluxDB is perfect here because health data is inherently time-series.

func writeToInflux(client influxdb2.Client, hrv float64, patientID string) {
    writeAPI := client.WriteAPIBlocking("my-org", "health-metrics")

    p := influxdb2.NewPoint("hrv_metrics",
        map[string]string{"patient_id": patientID},
        map[string]interface{}{"rmssd": hrv},
        time.Now())

    if err := writeAPI.WritePoint(context.Background(), p); err != nil {
        log.Printf("Error writing to InfluxDB: %v", err)
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Real-Time Anomaly Detection with Kapacitor

While Go handles the "Edge" processing, we use Kapacitor to define complex anomaly detection rules (like "Alert if HRV drops by 30% compared to the 10-minute moving average").

Here is a simple TICKscript to detect sudden HRV drops:

stream
    |from()
        .measurement('hrv_metrics')
    |window()
        .period(1m)
        .every(10s)
    |mean('rmssd')
    |alert()
        .crit(lambda: "mean" < 20.0) // Threshold for high-stress/fatigue
        .message('Anomaly Detected: Low HRV for Patient {{ index .Tags "patient_id" }}')
        .slack()
        .channel('#health-alerts')
Enter fullscreen mode Exit fullscreen mode

Advanced Patterns & Production Readiness ๐Ÿฅ‘

In a production environment, you have to worry about backpressure, sensor jitter, and data gaps. Using Go's context for cancellation and worker pools to handle multiple patient streams is essential.

If you're interested in how to harden this architectureโ€”specifically around data privacy (HIPAA compliance) or deploying these pipelines on Kubernetesโ€”I strongly suggest reading the deep-dives at wellally.tech/blog. They cover the "Day 2" operations of health-tech systems that transform a cool hobby project into a scalable medical-grade platform.

Conclusion ๐Ÿš€

Building a high-performance ECG anomaly detection system is a challenge of speed and accuracy. By leveraging Golang's concurrency and InfluxDB's time-series capabilities, we can process vital signs in real-time with minimal overhead.

Summary of what we built:

  1. A Go-based MQTT subscriber to ingest R-R intervals.
  2. A thread-safe RMSSD calculator for real-time HRV.
  3. A seamless integration with InfluxDB for long-term storage.
  4. An alerting layer using Kapacitor for immediate intervention.

What are you building in the wearable space? Drop a comment below or share your thoughts on performance optimization in Go! ๐Ÿ‘‡

Top comments (0)