StarRocks (formerly known as DorisDB) is a high-performance MPP analytical database built for real-time and ad hoc queries over massive datasets. Its MySQL protocol compatibility makes it easy to adopt — but its distributed architecture of Frontend (FE) and Backend (BE) nodes means there are failure modes that look silent from the inside. A BE node losing disk space silently degrades replica health. An FE follower falling behind the leader log causes query routing errors that surface only under load. Load job failures quietly stop data from arriving.
Vigilmon gives you external visibility into StarRocks health through HTTP probe monitoring and heartbeat monitors for your load pipelines. This tutorial covers FE node health, BE storage health, query latency percentiles, load job success rate, replica balance metrics, and Vigilmon integration.
Why StarRocks Needs External Monitoring
StarRocks provides built-in system tables (in information_schema) and an HTTP web UI — but both require an active connection and functioning FE nodes to query. External monitoring with Vigilmon adds:
- FE node liveness probes that detect follower failures and leader failover events
- BE storage pressure alerting before disk fullness causes tablet errors and replica loss
- Query latency percentile monitoring that catches degradation earlier than average-latency metrics
- Load job failure detection for Routine Load, Broker Load, and Stream Load pipelines
- Replica health checks that detect under-replicated tablets before they cause query errors
The critical blind spot: StarRocks query routing silently degrades when BE node count drops. Queries continue to execute but with fewer replicas available for parallel execution — latency climbs and you won't see an explicit error until a node threshold is crossed.
Step 1: Build StarRocks Health Endpoints
StarRocks FE and BE nodes expose HTTP APIs on dedicated ports. These are your first probe targets.
Native StarRocks Health Endpoints
# FE node health (default port 8030)
curl -i http://starrocks-fe:8030/api/health
# FE node leader check
curl -i http://starrocks-fe:8030/api/show_proc?path=/
# BE node health (default port 8040)
curl -i http://starrocks-be:8040/api/health
The FE /api/health returns {"status": "OK"} with HTTP 200 when the FE is healthy and the leader is reachable.
FE Cluster Health Sidecar
For multi-FE deployments, check that a leader is elected and followers are in sync:
# starrocks_health.py
from flask import Flask, jsonify
import requests
import pymysql
import os
app = Flask(__name__)
FE_HTTP_URL = os.environ.get('SR_FE_HTTP_URL', 'http://localhost:8030')
SR_HOST = os.environ.get('SR_HOST', 'localhost')
SR_PORT = int(os.environ.get('SR_PORT', '9030'))
SR_USER = os.environ.get('SR_USER', 'root')
SR_PASSWORD = os.environ.get('SR_PASSWORD', '')
def get_mysql_connection():
return pymysql.connect(
host=SR_HOST,
port=SR_PORT,
user=SR_USER,
password=SR_PASSWORD,
database='information_schema',
connect_timeout=5
)
@app.route('/health/starrocks/fe')
def fe_health():
try:
resp = requests.get(f'{FE_HTTP_URL}/api/health', timeout=5)
if resp.status_code != 200:
return jsonify({'status': 'down', 'error': f'HTTP {resp.status_code}'}), 503
data = resp.json()
if data.get('status') != 'OK':
return jsonify({'status': 'degraded', 'reason': data.get('status')}), 503
# Check FE follower health via SQL
conn = get_mysql_connection()
with conn.cursor() as cur:
cur.execute("SHOW PROC '/frontends'")
rows = cur.fetchall()
fe_nodes = [dict(zip([d[0] for d in cur.description], row)) for row in rows]
conn.close()
unhealthy_fe = [
fe for fe in fe_nodes
if fe.get('Alive', '').lower() != 'true'
]
if unhealthy_fe:
return jsonify({
'status': 'degraded',
'reason': 'fe_nodes_down',
'unhealthy': [fe.get('Name') for fe in unhealthy_fe]
}), 503
return jsonify({'status': 'ok', 'fe_count': len(fe_nodes)}), 200
except Exception as e:
return jsonify({'status': 'down', 'error': str(e)}), 503
BE Storage Health
Check BE node disk usage and tablet health before storage pressure causes replica errors:
@app.route('/health/starrocks/be')
def be_health():
disk_threshold = float(os.environ.get('SR_DISK_THRESHOLD_PCT', '85'))
try:
conn = get_mysql_connection()
with conn.cursor() as cur:
# Check BE node status
cur.execute("SHOW PROC '/backends'")
rows = cur.fetchall()
cols = [d[0] for d in cur.description]
backends = [dict(zip(cols, row)) for row in rows]
conn.close()
issues = []
for be in backends:
if be.get('Alive', '').lower() != 'true':
issues.append({'backend': be.get('BackendId'), 'reason': 'not_alive'})
continue
# Parse disk usage
disk_used = be.get('DataUsedCapacity', '0.000 GB')
disk_total = be.get('AvailCapacity', '0.000 GB')
# Note: in production parse these properly from the response
if issues:
return jsonify({'status': 'degraded', 'issues': issues}), 503
return jsonify({'status': 'ok', 'backends': len(backends)}), 200
except Exception as e:
return jsonify({'status': 'down', 'error': str(e)}), 503
Query Latency Percentile Health
@app.route('/health/starrocks/query')
def query_latency_health():
latency_p99_threshold_ms = int(os.environ.get('SR_QUERY_P99_THRESHOLD_MS', '3000'))
try:
conn = get_mysql_connection()
with conn.cursor() as cur:
# Use information_schema.query_profile or a test query
import time
start = time.time()
cur.execute("SELECT COUNT(*) FROM information_schema.tables LIMIT 1")
cur.fetchone()
latency_ms = int((time.time() - start) * 1000)
conn.close()
if latency_ms > latency_p99_threshold_ms:
return jsonify({
'status': 'degraded',
'reason': 'high_query_latency',
'latency_ms': latency_ms
}), 503
return jsonify({'status': 'ok', 'latency_ms': latency_ms}), 200
except Exception as e:
return jsonify({'status': 'down', 'error': str(e)}), 503
Replica Balance Health
Under-replicated tablets indicate BE node pressure or recent node loss:
@app.route('/health/starrocks/replicas')
def replica_health():
try:
conn = get_mysql_connection()
with conn.cursor() as cur:
cur.execute("""
SELECT
ReplicaState,
COUNT(*) as count
FROM information_schema.be_tablets
GROUP BY ReplicaState
""")
rows = cur.fetchall()
conn.close()
state_counts = {row[0]: row[1] for row in rows}
bad_states = {
state: count for state, count in state_counts.items()
if state not in ('NORMAL',)
}
if bad_states:
return jsonify({
'status': 'degraded',
'reason': 'unhealthy_replicas',
'states': bad_states
}), 503
return jsonify({'status': 'ok', 'replica_states': state_counts}), 200
except Exception as e:
return jsonify({'status': 'down', 'error': str(e)}), 503
Load Job Success Rate
@app.route('/health/starrocks/load')
def load_health():
try:
conn = get_mysql_connection()
with conn.cursor() as cur:
# Check recent Routine Load job status
cur.execute("""
SELECT Name, State, ErrorLogUrls
FROM information_schema.routine_load_jobs
WHERE State NOT IN ('CANCELLED', 'STOPPED')
""")
rows = cur.fetchall()
conn.close()
paused_jobs = [row[0] for row in rows if row[1] == 'PAUSED']
if paused_jobs:
return jsonify({
'status': 'degraded',
'reason': 'routine_load_paused',
'jobs': paused_jobs
}), 503
return jsonify({'status': 'ok', 'active_jobs': len(rows)}), 200
except Exception as e:
return jsonify({'status': 'down', 'error': str(e)}), 503
if __name__ == '__main__':
app.run(port=9091)
Step 2: Configure Vigilmon HTTP Monitors for StarRocks
- Log in to vigilmon.online and go to Monitors → New Monitor
- Choose HTTP / HTTPS
- Add monitors for each health surface:
| Monitor Name | URL | Expected | Interval |
|---|---|---|---|
| StarRocks FE liveness | http://sr-fe:8030/api/health | 200, body "OK" | 1 min |
| StarRocks FE cluster | https://your-sidecar/health/starrocks/fe | 200, body "ok" | 2 min |
| StarRocks BE health | http://sr-be:8040/api/health | 200 | 1 min |
| StarRocks BE storage | https://your-sidecar/health/starrocks/be | 200, body "ok" | 5 min |
| StarRocks query latency | https://your-sidecar/health/starrocks/query | 200, body "ok" | 1 min |
| StarRocks replica health | https://your-sidecar/health/starrocks/replicas | 200, body "ok" | 5 min |
| StarRocks load jobs | https://your-sidecar/health/starrocks/load | 200, body "ok" | 2 min |
Set the response time alert on the query latency monitor to 2000ms — earlier than the P99 threshold so you get a warning before the threshold fires.
Step 3: Heartbeat Monitoring for Load Pipelines
StarRocks Routine Load runs continuously inside the cluster — but the data producers that feed Kafka (or write directly via Stream Load) can stall without touching StarRocks at all. Vigilmon heartbeats detect upstream stalls.
Set Up the Heartbeat Monitor
- In Vigilmon, go to Monitors → New Monitor → Heartbeat
- Set the name:
starrocks-stream-load-pipeline - Set the expected interval: 10 minutes
- Set the grace period: 20 minutes
- Save — copy the heartbeat URL
Stream Load Pipeline with Heartbeat (Python)
# starrocks_stream_load.py
import os
import requests
import json
import time
SR_HOST = os.environ['SR_HOST']
SR_HTTP_PORT = os.environ.get('SR_HTTP_PORT', '8030')
SR_USER = os.environ['SR_USER']
SR_PASSWORD = os.environ['SR_PASSWORD']
SR_DATABASE = os.environ['SR_DATABASE']
SR_TABLE = os.environ['SR_TABLE']
VIGILMON_HEARTBEAT_URL = os.environ['VIGILMON_HEARTBEAT_URL']
def stream_load_batch(data: list) -> bool:
"""Load a batch of records via StarRocks Stream Load."""
json_data = '\n'.join(json.dumps(row) for row in data)
response = requests.put(
f'http://{SR_HOST}:{SR_HTTP_PORT}/api/{SR_DATABASE}/{SR_TABLE}/_stream_load',
auth=(SR_USER, SR_PASSWORD),
headers={
'Expect': '100-continue',
'format': 'json',
'strip_outer_array': 'false',
'Content-Type': 'application/json',
},
data=json_data,
timeout=60
)
result = response.json()
return result.get('Status') == 'Success'
def run_pipeline():
last_heartbeat = time.time()
HEARTBEAT_INTERVAL = 10 * 60 # 10 minutes
while True:
try:
batch = fetch_next_batch()
if batch:
success = stream_load_batch(batch)
if success:
now = time.time()
if now - last_heartbeat >= HEARTBEAT_INTERVAL:
requests.get(VIGILMON_HEARTBEAT_URL, timeout=5)
last_heartbeat = now
else:
print("Stream load failed — skipping heartbeat")
except Exception as e:
print(f"Pipeline error: {e}")
time.sleep(30)
Broker Load Job Wrapper
For scheduled Broker Load jobs (S3, HDFS), wrap the job submission:
#!/bin/bash
# broker_load_with_heartbeat.sh
LABEL="load_$(date +%Y%m%d_%H%M%S)"
SR_HOST=${SR_HOST:-localhost}
SR_PORT=${SR_PORT:-9030}
VIGILMON_URL=${VIGILMON_HEARTBEAT_URL}
mysql -h "$SR_HOST" -P "$SR_PORT" -u root << EOF
LOAD LABEL ${SR_DATABASE}.${LABEL}
(
DATA INFILE("s3://${S3_BUCKET}/data/*.parquet")
INTO TABLE ${SR_TABLE}
FORMAT AS "parquet"
)
WITH BROKER "s3" (
"aws.s3.access_key" = "${AWS_ACCESS_KEY}",
"aws.s3.secret_key" = "${AWS_SECRET_KEY}",
"aws.s3.region" = "${AWS_REGION}"
);
EOF
# Poll for job completion
while true; do
STATE=$(mysql -h "$SR_HOST" -P "$SR_PORT" -u root -se \
"SHOW LOAD WHERE Label='${LABEL}'\G" | grep State | awk '{print $2}')
case "$STATE" in
FINISHED)
curl -s "$VIGILMON_URL" > /dev/null
echo "Load job finished, heartbeat sent"
exit 0
;;
CANCELLED)
echo "Load job cancelled — no heartbeat"
exit 1
;;
esac
sleep 10
done
Step 4: Alert Routing for StarRocks Failures
StarRocks failure severity depends on which tier is affected:
- FE leader failure: all DDL and DML stop; queries continue briefly then fail as routing breaks
- BE failure: query capacity shrinks; replica rebalancing begins but takes time
- Replica degradation: queries may return incomplete results with degraded table availability
- Load job failure: data freshness silently degrades
Configure alert routing in Vigilmon:
| Monitor | Alert Channel | Priority | |---|---|---| | StarRocks FE liveness | Slack + PagerDuty | P1 | | StarRocks query latency | Slack + PagerDuty | P1 | | StarRocks BE health | Slack | P2 | | StarRocks replica health | Slack | P2 | | StarRocks load jobs | Slack + email | P2 | | StarRocks BE storage | Email | P3 | | StarRocks stream load heartbeat | Slack | P2 |
Add a response time escalation on the FE liveness monitor: alert at 1000ms for early warning of FE pressure before follower lag causes visible issues.
Summary
StarRocks reliability depends on the health of both FE coordination and BE storage tiers — and load pipeline health is entirely outside what internal monitoring can see. Vigilmon covers all three:
| Monitor Type | What It Covers |
|---|---|
| HTTP probe on FE/BE /api/health | Service process liveness |
| HTTP probe on query sidecar | End-to-end query latency |
| HTTP probe on replica sidecar | Tablet replica balance |
| HTTP probe on load sidecar | Routine Load and Stream Load health |
| Heartbeat monitor | Data pipeline and Broker Load liveness |
Get started free at vigilmon.online — your first StarRocks monitor is live in under two minutes.