RealtimeTrigger icon
OutputValues icon
If icon
SlackIncomingWebhook icon
Log icon

JMS Live Queue Message Latency Sentinel

Fire on live JMS queue messages, alert Slack on stale message age past your SLA, and log the result for messaging platform teams.

Categories
Infrastructure

A JMS consumer that is quietly falling behind does not page anyone; messages pile up on the queue and the only visible symptom is a growing backlog nobody notices until it is big enough to show up on a broker dashboard. This blueprint fires on every message as it lands, reads the broker's own JMS timestamp, and treats a message older than your SLA as a governance event the moment it is observed, not after it has already been redelivered past a poison-message threshold into the dead-letter queue.

This blueprint was created by Mohit Jeswani.

How it works

  1. live_queue_watch (io.kestra.plugin.jms.RealtimeTrigger) listens on the live production queue and starts one execution per message, using CLIENT_ACKNOWLEDGE for at-least-once delivery.
  2. evaluate (io.kestra.plugin.core.output.OutputValues) subtracts the message's own JMS timestamp (set by the broker when the message was sent) from the current time to get age_seconds, then compares it against max_age_seconds.
  3. route (io.kestra.plugin.core.flow.If):
    • compliant: logs the message id and its age, within SLA.
    • alert: posts the message id and age to Slack.
  4. summary logs the age and severity for every message, closing the governance cycle.
  5. errors alerts Slack if the flow itself fails, for example a broker connection drop.

What you get

  • Per-message latency measured from the broker's own JMS timestamp, not an inferred queue depth or a scheduled scan, so a slow message is caught the instant it is consumed.
  • A real-time signal distinct from a dead-letter audit: this watches the live queue a message is actually meant to be on, before it is ever redelivered into a DLQ.
  • A read-only check: this flow only consumes the triggering message, it never republishes, purges, or otherwise mutates the queue.

Who it's for

  • Messaging platform and backend teams running JMS-compliant brokers (ActiveMQ, Artemis, IBM MQ) who want consumer lag surfaced per message, not discovered as a backlog days later.
  • Teams evaluating the JMS plugin who want a worked example of RealtimeTrigger used as a standing per-message SLA gate, distinct from this repo's existing JMS blueprint, which scans the dead-letter queue on a schedule rather than watching the live queue in real time.

Why orchestrate this with Kestra

The broker stamps a timestamp on every message; it does not compare that timestamp against an SLA you declare or notify a Slack channel when a message is slow. Kestra keeps max_age_seconds as a flow input, turns a stale message into a Slack page naming the exact message id and age, and keeps a full execution history of every message observed.

Prerequisites

  • A JMS-compliant broker (ActiveMQ, Artemis, IBM MQ, or similar) reachable from the Kestra worker, with a live queue to watch.
  • The broker's JMS provider client JAR, either placed under the plugin's jms-libs folder on the worker or referenced via providerJarPaths.
  • A Slack incoming webhook URL.

A local ActiveMQ Artemis broker can be run via the official apache/activemq-artemis Docker image for a fully local end-to-end test, no managed broker required.

Secrets

  • SLACK_WEBHOOK_URL: used by alert and the errors block. In Kestra OSS, provide it as the base64-encoded environment variable SECRET_SLACK_WEBHOOK_URL.

Inputs

Input Default Purpose
max_age_seconds 300 Message age, in seconds, above which the Slack alert fires

Outputs

  • {{ trigger.messageId }} / .timestamp / .data / .headers: the observed message, as RealtimeTrigger reports it.
  • {{ outputs.evaluate.values.age_seconds }} / .severity: how long the message sat before observation, and OK or STALE_MESSAGE.

Pitfalls

  • connectionFactoryConfig and destination are trigger-level configuration, not flow inputs; a realtime trigger has no per-execution input overrides the way a Schedule trigger does, so pointing this at a different broker or queue means editing the trigger block directly.
  • This fires once per message, so a burst of slow messages produces one execution and one possible alert per message, not a single aggregated alert; route the Slack channel accordingly if a burst is expected to be noisy.
  • age_seconds depends on the producer's and broker's clocks being reasonably in sync with the Kestra worker; significant clock skew on either side shows up as a false stale reading or a false compliant one.

Quick start

  1. Add the SLACK_WEBHOOK_URL secret.
  2. Run a local ActiveMQ Artemis broker and set connectionFactoryConfig/destination on live_queue_watch to point at it.
  3. Enable the trigger, publish a message, and confirm compliant logs its age.
  4. Publish a message with a backdated JMS timestamp (or lower max_age_seconds below a real message's age) to confirm alert fires.

How to extend

  • Add a messageSelector to scope this sentinel to a specific message type or priority instead of every message on the queue.
  • Aggregate age_seconds into a KV entry or external metric over a rolling window to chart consumer lag trend, not just flag individual slow messages.
  • Route STALE_MESSAGE alerts to the on-call channel for the consumer service that reads this queue, since a growing age usually means that specific consumer is falling behind.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.