Skip to main content
Version: 1.0

Trigger

Trigger allows you to define evaluation rules with SQL. GreptimeDB evaluates these rules periodically; once the condition is met, a notification is sent out.

Key Features

  • SQL-native: Define trigger rules in SQL, reusing GreptimeDB's built-in functions without a learning curve
  • Multi-stage state management: Built-in pending / firing / inactive state machine prevents flapping and duplicate notifications
  • Rich context: Custom labels and annotations with automatic injection of query result fields to pinpoint root causes
  • Ecosystem-friendly: Alert payload fully compatible with Prometheus Alertmanager—use its grouping, inhibition, silencing, and routing without adapters

Quick Start Example

This section walks through an end-to-end alerting scenario: monitor system load (load1) and fire alerts when load exceeds a threshold.

In this quick start, you will:

  • Create a load1 table to store host load metrics
  • Define a Trigger with conditions, labels, annotations, and notifications
  • Simulate data ingestion with normal and abnormal values
  • Watch alerts transition through pending → firing → inactive

1. Create the Data Table

Connect to GreptimeDB with a MySQL client and create the load1 table:

CREATE TABLE `load1` (
host STRING,
load1 FLOAT32,
ts TIMESTAMP TIME INDEX
) WITH ('append_mode'='true');

2. Create Trigger

Connect to GreptimeDB with MySQL client and create the load1_monitor trigger:

CREATE TRIGGER IF NOT EXISTS `load1_monitor`
ON (
SELECT
host AS label_host,
avg(load1) AS avg_load1,
max(ts) AS ts
FROM public.load1
WHERE ts >= NOW() - '1 minutes'::INTERVAL
GROUP BY host
HAVING avg(load1) > 10
) EVERY '1 minutes'::INTERVAL
FOR '3 minutes'::INTERVAL
KEEP FIRING FOR '3 minutes'::INTERVAL
LABELS (severity=warning)
ANNOTATIONS (comment='Your computer is smoking, should take a break.')
NOTIFY(
WEBHOOK alert_manager URL 'http://localhost:9093' WITH (timeout='1m')
);

This Trigger runs every minute, computes average load per host over the last 60 seconds, and produces an alert instance for each host where avg(load1) > 10.

Key parameters:

  • FOR: Specifies how long the condition must continuously hold before an alert instance enters firing state.
  • KEEP FIRING FOR: Specifies how long an alert instance stays in the firing state after the condition no longer holds.

See the trigger syntax for more detail.

3. Check Trigger Status

List all Triggers

SHOW TRIGGERS;

Output:

+---------------+
| Triggers |
+---------------+
| load1_monitor |
+---------------+

View the creation statement

SHOW CREATE TRIGGER `load1_monitor`\G

Output:

*************************** 1. row ***************************
Trigger: load1_monitor
Create Trigger: CREATE TRIGGER IF NOT EXISTS `load1_monitor`
ON (SELECT host AS label_host, avg(load1) AS avg_load1 ...) EVERY '1 minutes'::INTERVAL
FOR '3 minutes'::INTERVAL
KEEP FIRING FOR '3 minutes'::INTERVAL
LABELS (severity = 'warning')
ANNOTATIONS (comment = 'Your computer is smoking, should take a break.')
NOTIFY(
WEBHOOK `alert_manager` URL `http://localhost:9093` WITH (timeout = '1m'),
)

View Trigger details

SELECT * FROM information_schema.triggers\G

Output:

*************************** 1. row ***************************
trigger_name: load1_monitor
trigger_id: 1024
raw_sql: (SELECT host AS label_host, avg(load1) AS avg_load1, ...)
interval: 60
labels: {"severity":"warning"}
annotations: {"comment":"Your computer is smoking, should take a break."}
for: 180
keep_firing_for: 180
channels: [{"channel_type":{"Webhook":{"opts":{"timeout":"1m"}, ...}]
flownode_id: 0

See the Triggers for more details.

View alert instances

SELECT * FROM information_schema.alerts;

With no data written yet, this returns an empty set.

See the Alerts for more details.

4. Write Data and Observe Alert States

This script simulates data ingestion: normal values for the first minute, high values for 6 minutes to trigger alerts, then back to normal.

#!/usr/bin/env bash

MYSQL="mysql -h 127.0.0.1 -P 4002"

insert_normal() {
$MYSQL -e "INSERT INTO load1 (host, load1, ts) VALUES
('newyork1', 1.2, now()),
('newyork2', 1.1, now()),
('newyork3', 1.3, now());"
}

insert_high() {
$MYSQL -e "INSERT INTO load1 (host, load1, ts) VALUES
('newyork1', 1.2, now()),
('newyork2', 12.1, now()),
('newyork3', 11.5, now());"
}

# First minute: normal data
for i in {1..4}; do insert_normal; sleep 15; done

# Next 6 minutes: high values
for i in {1..24}; do insert_high; sleep 15; done

# After: back to normal
while true; do insert_normal; sleep 15; done

State Transitions

In another terminal, query alert status:

Phase 1: No alerts

SELECT * FROM information_schema.alerts\G

Output:

Empty set

Phase 2: pending (condition met, FOR duration not reached)

SELECT trigger_id, labels, active_at, fired_at, resolved_at FROM information_schema.alerts;
+------------+-----------------------------------------------------------------------+----------------------------+----------+-------------+
| trigger_id | labels | active_at | fired_at | resolved_at |
+------------+-----------------------------------------------------------------------+----------------------------+----------+-------------+
| 1024 | {"alert_name":"load1_monitor","host":"newyork3","severity":"warning"} | 2025-12-29 11:58:20.992670 | NULL | NULL |
| 1024 | {"alert_name":"load1_monitor","host":"newyork2","severity":"warning"} | 2025-12-29 11:58:20.992670 | NULL | NULL |
+------------+-----------------------------------------------------------------------+----------------------------+----------+-------------+

Phase 3: firing (FOR satisfied, notifications sent)

SELECT trigger_id, labels, active_at, fired_at, resolved_at FROM information_schema.alerts;
+------------+-----------------------------------------------------------------------+----------------------------+----------------------------+-------------+
| trigger_id | labels | active_at | fired_at | resolved_at |
+------------+-----------------------------------------------------------------------+----------------------------+----------------------------+-------------+
| 1024 | {"alert_name":"load1_monitor","host":"newyork3","severity":"warning"} | 2025-12-29 11:58:20.992670 | 2025-12-29 12:02:20.991713 | NULL |
| 1024 | {"alert_name":"load1_monitor","host":"newyork2","severity":"warning"} | 2025-12-29 11:58:20.992670 | 2025-12-29 12:02:20.991713 | NULL |
+------------+-----------------------------------------------------------------------+----------------------------+----------------------------+-------------+

Phase 4: inactive (condition cleared + KEEP FIRING FOR expired)

SELECT trigger_id, labels, active_at, fired_at, resolved_at FROM information_schema.alerts;
+------------+-----------------------------------------------------------------------+----------------------------+----------------------------+----------------------------+
| trigger_id | labels | active_at | fired_at | resolved_at |
+------------+-----------------------------------------------------------------------+----------------------------+----------------------------+----------------------------+
| 1024 | {"alert_name":"load1_monitor","host":"newyork3","severity":"warning"} | 2025-12-29 11:58:20.992670 | 2025-12-29 12:02:20.991713 | 2025-12-29 12:05:20.991750 |
| 1024 | {"alert_name":"load1_monitor","host":"newyork2","severity":"warning"} | 2025-12-29 11:58:20.992670 | 2025-12-29 12:02:20.991713 | 2025-12-29 12:05:20.991750 |
+------------+-----------------------------------------------------------------------+----------------------------+----------------------------+----------------------------+

5. Alertmanager Integration (Optional)

If you have Prometheus Alertmanager deployed, GreptimeDB automatically pushes firing and inactive alerts to it.

After each evaluation, the Trigger injects fields from the query results into labels and annotations. In this example, host is included as a label and avg_load1 is included as an annotation. These fields are propagated to Alertmanager and can be referenced in notification templates.

Since the payload is Alertmanager-compatible, you can use grouping, inhibition, silencing, and routing without adapters.

Reference