What trigger condition catches slow database queries early?
Tares catches slow database queries early with a trigger that takes the max of a numeric latency field over a short window, per entity, with a predicate and a cooldown. For example, max(p99_ms) > 1000 over 1m, grouped by key_value, fires as soon as one database crosses the line. The condition is checked as events are ingested, so you do not wait for a polling cycle.
How it works
A trigger watches one or more sources and evaluates its condition as events arrive. Four parts of the condition matter for slow queries:
- A typed field. Triggers aggregate a numeric
field, so one of the trigger’s sources must produce your query latency as a number. The guide’s latency example usesp99_ms. - An aggregate.
maxfires when the slowest value in the window crosses the threshold. The other aggregates arecount,sum,avg,minandany. Usemaxfor early warning, because one slow reading is enough.avgwould smooth that reading away. - A predicate and a window. The predicate, such as
> 1000, is tested against the aggregate over the window. A short window such as1mkeeps the check close to what is happening now. - A per-entity grouping.
group_by: [key_value]evaluates the condition per entity, so each database or service fires on its own.key_fieldnames the entity label. If you leave it out, it defaults to the first source’s primary label.
cooldown is the minimum gap between firings for the same entity. A latency number that hovers around the threshold fires once, not on every event.
Set it up
- Make sure a source carries your query latency as a number, with a label that names the database or service. Tares aggregates a typed field, so the field must be numeric.
- Add the trigger on the project’s page (+ next to Triggers), under Triggers → Add trigger, or in the catalog YAML. Replace
db_metrics,serviceandp99_mswith your own source, label and field:
triggers:
- name: slow_queries
sources: [db_metrics]
key_field: service
condition:
aggregate: max
field: p99_ms
predicate: "> 1000" # fire when max(p99_ms) over the window exceeds 1000
window: 1m
group_by: [key_value] # evaluated per entity
emit:
kind: slow_queries
context_window: 15m # how much timeline to hand the agent
cooldown: 5m- Set the threshold from your own baseline. The
1000here is the guide’s latency example, not a recommendation for your database. - Narrow the events if needed with
filters. Each filter has afield, anopand avalue, andopis one ofeq,neq,contains,gt,lt,gte,lte. For example, count only production events by filtering on anenvlabel. - Subscribe an agent. A Tares agent is woken by the trigger in a project that wires it to that trigger. An external agent registers a webhook over MCP with the
subscribetool, which returns a subscription id. An agent can also create the trigger itself over MCP withcreate_triggerandupdate_trigger, which take the same fields.
For the general walkthrough of sources, subscribers and dispatches, see Watch with triggers. For the wider picture of using Tares as an SRE assistant, see AI SRE.
What you get
- A firing per slow entity. Each database or service that crosses the threshold fires on its own, independent of the others.
- Context with the alert. Each subscriber gets one JSON body with the entity’s correlated timeline across the trigger’s sources over
context_window. That can include the deploy, the logs and the metrics around the spike, so the agent needs no follow-up call. - No alert storms. The cooldown holds back repeat firings for the same entity.
- A record of every firing. Firings are logged even with zero subscribers. You see them in the console under Firings, on the trigger’s page and on the project’s Activity, with the exact payload and the delivery outcome of each dispatch.
- Reliable delivery. Delivery is at-least-once, so dedupe on
dispatch_id. Acknowledge with a 2xx. Tares retries 5xx responses and transport errors with backoff. A 4xx is recorded as failed and not retried.
When something else fits
- If you want a regular review of the database rather than a threshold, give the trigger a schedule with
condition: {every: 10m}. It fires once per interval and hands over a summary of the window. See Watch, then escalate. - If you care about how many slow events happen rather than how slow the worst one is, use
countwith a filter instead ofmax. - If no source produces a numeric latency field yet, add one first. A trigger can only aggregate a typed field that a source carries.