Skip to Content
Tares

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 uses p99_ms.
  • An aggregate. max fires when the slowest value in the window crosses the threshold. The other aggregates are count, sum, avg, min and any. Use max for early warning, because one slow reading is enough. avg would 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 as 1m keeps 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_field names 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

  1. 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.
  2. Add the trigger on the project’s page (+ next to Triggers), under Triggers → Add trigger, or in the catalog YAML. Replace db_metrics, service and p99_ms with 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
  1. Set the threshold from your own baseline. The 1000 here is the guide’s latency example, not a recommendation for your database.
  2. Narrow the events if needed with filters. Each filter has a field, an op and a value, and op is one of eq, neq, contains, gt, lt, gte, lte. For example, count only production events by filtering on an env label.
  3. 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 subscribe tool, which returns a subscription id. An agent can also create the trigger itself over MCP with create_trigger and update_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 count with a filter instead of max.
  • If no source produces a numeric latency field yet, add one first. A trigger can only aggregate a typed field that a source carries.

Still stuck? Get help

Last updated on