KairosDB is a distributed time-series database built on Apache Cassandra, designed for high-throughput metric ingestion with a REST and Telnet API — but when the Cassandra cluster loses a node and KairosDB's connection pool drains, a write backlog exceeds the internal queue limit and incoming data points are silently dropped, or the query timeout for a wide time range triggers a circuit breaker, there is no built-in outbound alerting. KairosDB exposes a /api/v1/health/status endpoint and a /api/v1/version endpoint, but these require active polling and give no push notifications when the Cassandra backend degrades.
Vigilmon gives you external visibility into KairosDB through HTTP monitors on its health and version endpoints, a sidecar that exercises the write and query API, and heartbeat monitors that confirm your metrics ingestion pipeline is successfully storing and retrieving data. This tutorial covers both.
Why KairosDB Needs External Monitoring
KairosDB's built-in tooling (the web UI, JVM heap metrics, and Cassandra nodetool status) requires a human operator or internal alerting stack actively checking those outputs. External monitoring with Vigilmon adds:
- KairosDB process availability alerting when the JVM process becomes unreachable before your pipeline stalls
- Cassandra backend health checks detecting connection pool exhaustion or write rejection before data loss accumulates
- REST API liveness verification confirming the HTTP API serves requests — not just that the TCP port accepts connections
- Write latency monitoring catching Cassandra GC pauses or compaction pressure before timeouts propagate to clients
- End-to-end ingestion heartbeats confirming your metrics pipeline writes to KairosDB and reads back successfully
These layers matter because KairosDB failures are often gradual: the health endpoint may return healthy while the Cassandra connection pool is saturated and write latency climbs from 50ms to 5000ms, causing client timeouts long before the health endpoint reflects the problem.
Step 1: Probe KairosDB's Built-in Health Endpoints
KairosDB exposes REST endpoints for version and health status:
# Check KairosDB version
curl -s http://kairosdb.example.com:8080/api/v1/version
# Expected response:
# {"version":"KairosDB 1.3.0"}
# Check health status (checks Cassandra datastore connectivity)
curl -s http://kairosdb.example.com:8080/api/v1/health/status
# Expected response:
# ["JVM-Thread-Deadlock: OK","Datastore-Query: OK"]
# Check health check details
curl -s http://kairosdb.example.com:8080/api/v1/health/check
Point a Vigilmon monitor directly at http://kairosdb.example.com:8080/api/v1/health/status for immediate availability alerting. Configure it to check for Datastore-Query: OK in the response body.
Test the metric names list endpoint to confirm the data path is working:
# List known metric names (confirms Cassandra read path is operational)
curl -s http://kairosdb.example.com:8080/api/v1/metricnames
# Expected: {"results":["..."]}
Step 2: Build a KairosDB Health Sidecar
For comprehensive health checks — write latency, Cassandra query path, and API response time — build a sidecar that exercises KairosDB's REST API end-to-end.
Python Health Sidecar
# kairosdb_health.py
from flask import Flask, jsonify
import requests
import time
import os
app = Flask(__name__)
KAIROSDB_URL = os.environ.get('KAIROSDB_URL', 'http://localhost:8080')
WRITE_LATENCY_MS = int(os.environ.get('WRITE_LATENCY_MS', '2000'))
PROBE_METRIC = os.environ.get('PROBE_METRIC', 'vigilmon.probe.write')
@app.route('/health/kairosdb')
def kairosdb_health():
try:
r = requests.get(f'{KAIROSDB_URL}/api/v1/health/status', timeout=5)
if r.status_code != 204 and r.status_code != 200:
return jsonify({'status': 'down', 'error': f'http_{r.status_code}'}), 503
# KairosDB returns 204 No Content when all checks pass
if r.status_code == 200:
checks = r.json() if r.text else []
failed = [c for c in checks if 'OK' not in c]
if failed:
return jsonify({'status': 'down', 'failed_checks': failed}), 503
except Exception as e:
return jsonify({'status': 'down', 'error': str(e)}), 503
return jsonify({'status': 'ok'}), 200
@app.route('/health/kairosdb/write')
def kairosdb_write():
"""Test a datapoint write to KairosDB and measure latency."""
ts_ms = int(time.time() * 1000)
payload = {
'name': PROBE_METRIC,
'datapoints': [[ts_ms, 1]],
'tags': {'source': 'vigilmon_probe'}
}
try:
start = time.time()
r = requests.post(
f'{KAIROSDB_URL}/api/v1/datapoints',
json=[payload],
timeout=10
)
elapsed_ms = int((time.time() - start) * 1000)
if r.status_code not in (200, 204):
return jsonify({'status': 'down', 'error': f'write_http_{r.status_code}',
'body': r.text[:200]}), 503
except Exception as e:
return jsonify({'status': 'down', 'error': str(e)}), 503
if elapsed_ms > WRITE_LATENCY_MS:
return jsonify({'status': 'degraded', 'write_latency_ms': elapsed_ms,
'threshold_ms': WRITE_LATENCY_MS}), 200
return jsonify({'status': 'ok', 'write_latency_ms': elapsed_ms}), 200
@app.route('/health/kairosdb/query')
def kairosdb_query():
"""Test a metric query to confirm the Cassandra read path is available."""
ts_end = int(time.time() * 1000)
ts_start = ts_end - 300_000 # last 5 minutes
payload = {
'start_absolute': ts_start,
'end_absolute': ts_end,
'metrics': [{'name': PROBE_METRIC, 'tags': {'source': ['vigilmon_probe']}}]
}
try:
start = time.time()
r = requests.post(
f'{KAIROSDB_URL}/api/v1/datapoints/query',
json=payload,
timeout=15
)
elapsed_ms = int((time.time() - start) * 1000)
if r.status_code != 200:
return jsonify({'status': 'down', 'error': f'query_http_{r.status_code}'}), 503
except Exception as e:
return jsonify({'status': 'down', 'error': str(e)}), 503
return jsonify({'status': 'ok', 'query_latency_ms': elapsed_ms}), 200
@app.route('/health/kairosdb/metricnames')
def kairosdb_metricnames():
"""Check the metric names endpoint to confirm Cassandra catalog access."""
try:
r = requests.get(f'{KAIROSDB_URL}/api/v1/metricnames', timeout=10)
if r.status_code != 200:
return jsonify({'status': 'down', 'error': f'http_{r.status_code}'}), 503
results = r.json().get('results', [])
except Exception as e:
return jsonify({'status': 'down', 'error': str(e)}), 503
return jsonify({'status': 'ok', 'metric_count': len(results)}), 200
if __name__ == '__main__':
app.run(port=8098)
Node.js Health Sidecar
// kairosdb-health.js
const express = require('express');
const axios = require('axios');
const app = express();
const KAIROSDB_URL = process.env.KAIROSDB_URL || 'http://localhost:8080';
const WRITE_LATENCY_MS = parseInt(process.env.WRITE_LATENCY_MS || '2000', 10);
const PROBE_METRIC = process.env.PROBE_METRIC || 'vigilmon.probe.write';
app.get('/health/kairosdb', async (req, res) => {
try {
const r = await axios.get(`${KAIROSDB_URL}/api/v1/health/status`, {
timeout: 5000,
validateStatus: s => s === 204 || s === 200
});
if (r.status === 200 && Array.isArray(r.data)) {
const failed = r.data.filter(c => !c.includes('OK'));
if (failed.length) return res.status(503).json({ status: 'down', failed_checks: failed });
}
return res.status(200).json({ status: 'ok' });
} catch (e) {
return res.status(503).json({ status: 'down', error: e.message });
}
});
app.get('/health/kairosdb/write', async (req, res) => {
const ts = Date.now();
const payload = [{ name: PROBE_METRIC, datapoints: [[ts, 1]], tags: { source: 'vigilmon_probe' } }];
const start = Date.now();
try {
const r = await axios.post(`${KAIROSDB_URL}/api/v1/datapoints`, payload, {
timeout: 10000,
validateStatus: s => s === 200 || s === 204
});
const elapsed = Date.now() - start;
if (elapsed > WRITE_LATENCY_MS) {
return res.status(200).json({ status: 'degraded', write_latency_ms: elapsed });
}
return res.status(200).json({ status: 'ok', write_latency_ms: elapsed });
} catch (e) {
return res.status(503).json({ status: 'down', error: e.message });
}
});
app.listen(3015, () => console.log('kairosdb-health listening on 3015'));
Step 3: Configure Vigilmon HTTP Monitor for KairosDB
- Log in to vigilmon.online and go to Monitors → New Monitor
- Choose HTTP / HTTPS
- Set the URL to
http://kairosdb.example.com:8080/api/v1/health/status - Set the check interval to 1 minute
- Under Expected response, configure:
- Status code:
204(KairosDB returns 204 when all checks pass; 500 when any fail) - Response time threshold:
3000ms
- Status code:
- Under Alert channels, assign your metrics infrastructure on-call channel
- Save the monitor
Add a write latency sidecar monitor:
- URL:
https://your-app.example.com/health/kairosdb/write - Expected:
200, body contains"status":"ok" - Interval: 2 minutes
- Alert channel: metrics pipeline Slack channel
Add a query path monitor:
- URL:
https://your-app.example.com/health/kairosdb/query - Expected:
200 - Interval: 5 minutes
- Alert channel: monitoring infrastructure channel
Add a metric catalog monitor:
- URL:
https://your-app.example.com/health/kairosdb/metricnames - Expected:
200 - Interval: 5 minutes
- Alert channel: database operations channel
If you run multiple KairosDB instances behind a load balancer, add per-instance health monitors so a single-node JVM crash is detected independently of the load balancer health check.
Step 4: Heartbeat Monitoring for KairosDB Ingestion Pipelines
Write latency probes confirm the REST API can accept individual data points — but they don't verify that your full metrics collection pipeline (agent → KairosDB → dashboard query) is functioning end-to-end. A KairosDB node can accept individual probe writes while your bulk metrics agent has stalled due to a Cassandra token ring inconsistency or a misconfigured retention TTL silently expiring data.
Vigilmon heartbeat monitors detect these failures: your ingestion pipeline pings Vigilmon after successfully completing a full write-and-query cycle.
Set Up the Heartbeat Monitor
- In Vigilmon, go to Monitors → New Monitor → Heartbeat
- Set the name:
kairosdb-ingestion-pipeline - Set the expected interval: 5 minutes
- Set the grace period: 15 minutes
- Save — copy the heartbeat URL, e.g.
https://vigilmon.online/heartbeat/abc123xyz
Wire It Into a KairosDB Ingestion Probe
# kairosdb_ingestion_probe.py — write a test metric and ping heartbeat on success
import requests
import time
import os
KAIROSDB_URL = os.environ['KAIROSDB_URL']
VIGILMON_HB = os.environ['VIGILMON_HEARTBEAT_URL']
PROBE_METRIC = 'vigilmon.ingestion.probe'
ts_ms = int(time.time() * 1000)
payload = [{'name': PROBE_METRIC, 'datapoints': [[ts_ms, 1.0]],
'tags': {'source': 'heartbeat'}}]
# Write the test metric
write_r = requests.post(f'{KAIROSDB_URL}/api/v1/datapoints', json=payload, timeout=10)
if write_r.status_code not in (200, 204):
raise RuntimeError(f'KairosDB write failed: {write_r.status_code} {write_r.text}')
print(f'Test metric written at ts={ts_ms}ms.')
# Brief wait for Cassandra write to be readable
time.sleep(3)
# Query back the data point to verify the read path
query_payload = {
'start_absolute': ts_ms - 1000,
'end_absolute': ts_ms + 60_000,
'metrics': [{'name': PROBE_METRIC, 'tags': {'source': ['heartbeat']}}]
}
query_r = requests.post(
f'{KAIROSDB_URL}/api/v1/datapoints/query',
json=query_payload,
timeout=15
)
if query_r.status_code != 200:
raise RuntimeError(f'KairosDB query failed: {query_r.status_code} {query_r.text}')
queries = query_r.json().get('queries', [])
data_points = queries[0]['results'][0]['values'] if queries else []
if not data_points:
raise RuntimeError('Probe datapoint not found in KairosDB query result.')
print(f'Probe datapoint verified ({len(data_points)} point(s) returned).')
# Send heartbeat to Vigilmon
requests.get(VIGILMON_HB, timeout=10)
print('Vigilmon heartbeat sent.')
Step 5: Alert Routing for KairosDB Failures
Configure alert routing in Vigilmon:
| Monitor | Alert Channel | Priority | |---|---|---| | KairosDB /api/v1/health/status | Slack + PagerDuty | P1 | | Write latency sidecar | Slack + PagerDuty | P1 | | Query path sidecar | Slack | P2 | | Metric catalog sidecar | Slack | P3 | | Heartbeat: ingestion pipeline | Slack + PagerDuty | P1 |
Set response time thresholds for early warning:
- Alert at
2000msfor the health status endpoint (slow health checks precede Cassandra connectivity failures) - Alert at
3000msfor write latency probes (elevated write latency signals Cassandra compaction pressure) - Alert at
10000msfor query path probes (slow queries indicate Cassandra read repair overhead or wide partition scans)
For production monitoring systems where KairosDB backs alerting pipelines or capacity dashboards, configure a tight grace period on heartbeats: a pipeline that runs every 5 minutes should alert within 20 minutes of silence, not on the next morning's on-call handover.
Summary
KairosDB failures span JVM process availability, Cassandra backend connectivity, write path latency, and end-to-end ingestion. External monitoring catches all four:
| Monitor Type | What It Covers |
|---|---|
| HTTP monitor on /api/v1/health/status | KairosDB and Cassandra datastore health |
| HTTP monitor on write sidecar | Cassandra write path latency |
| HTTP monitor on query sidecar | Cassandra read path availability |
| HTTP monitor on metricnames sidecar | Cassandra catalog (keyspace) access |
| Heartbeat monitor on ingestion probe | End-to-end write-and-query cycle |
Get started free at vigilmon.online — your first KairosDB availability monitor is running in under two minutes.