Scheduler User Guide
Kaspr Scheduler is an optional add-on component of KasprApp that provides delayed message delivery to Kafka topics.
What the Scheduler Does
Scheduler lets producers send a message now and have it delivered to a target topic at a specified time.
Typical use cases:
- Delayed notifications.
- Deferred retries.
- Time-based workflow triggers.
Enable Scheduler
Enable scheduler on KasprApp:
schedulerEnabled: true
apiVersion: kaspr.io/v1alpha1
kind: KasprApp
metadata:
name: orders-app
spec:
config:
schedulerEnabled: trueOptional tuning:
schedulerTopicPartitions: number of partitions for scheduler internal topics.schedulerDebugStatsEnabled: enable periodic scheduler stats logs.
After enabling, scheduler topics are created using your app id prefix:
<app-id>-schedule-requests<app-id>-schedule-actions<app-id>-schedule-rejections
Publish a Scheduled Message (ADD)
Send your message to:
<app-id>-schedule-requests
Required headers for ADD:
x-scheduler-deliver-to: destination topic name.x-scheduler-deliver-at: UTC timestamp in ISO format.
Optional headers for ADD:
x-scheduler-action: defaults toADDif omitted.x-scheduler-request-id: client-defined id used later for cancellation.
Example payload shape:
{
"key": "order-123",
"value": {"type": "send-reminder"},
"headers": {
"x-scheduler-deliver-to": "reminders-topic",
"x-scheduler-deliver-at": "2026-05-24T12:30:00Z",
"x-scheduler-request-id": "reminder-order-123"
}
}What to Expect
- If timestamp is in the future, message is delivered at scheduled time.
- If timestamp is in the past, message is delivered immediately.
Replace a Scheduled Message (REPLACE)
Use REPLACE when you want to update an existing scheduled message identified by request id.
To replace, publish to <app-id>-schedule-requests with:
x-scheduler-action: REPLACEx-scheduler-request-id: <same id used at schedule time>x-scheduler-deliver-to: <destination topic>x-scheduler-deliver-at: <new UTC ISO timestamp>
REPLACE requires both target fields (deliver-to, deliver-at) and identity (request-id).
Replace Semantics
- If the request id exists, scheduler removes the previous row and inserts the new row.
- If the request id does not exist, scheduler treats it as a new schedule with that id.
- If the existing schedule is strictly identical (same time, destination, key, value, headers), scheduler performs a no-op replacement.
Example headers:
x-scheduler-action=REPLACE
x-scheduler-request-id=reminder-order-123
x-scheduler-deliver-to=reminders-topic
x-scheduler-deliver-at=2026-05-24T13:00:00ZCancel a Scheduled Message (CANCEL)
To cancel, publish to <app-id>-schedule-requests with:
x-scheduler-action: CANCELx-scheduler-request-id: <same id used at schedule time>
No x-scheduler-deliver-at or x-scheduler-deliver-to is required for CANCEL.
Example headers:
x-scheduler-action=CANCEL
x-scheduler-request-id=reminder-order-123Cancel Semantics
- Cancellation is id-based and best effort.
- If the request id is unknown, no message is canceled.
- If the message is already being delivered, cancellation may be too late.
Invalid Requests and Rejections
Invalid scheduler requests are sent to:
<app-id>-schedule-rejections
Common rejection reasons:
- Missing
x-scheduler-deliver-atforADD. - Missing
x-scheduler-deliver-toforADD. - Missing
x-scheduler-deliver-atforREPLACE. - Missing
x-scheduler-deliver-toforREPLACE. - Invalid timestamp format for
x-scheduler-deliver-at. - Missing
x-scheduler-request-idforCANCEL. - Missing
x-scheduler-request-idforREPLACE.
Rejection records include original key/value/headers plus error details.
Delivery Guarantees and Expectations
- Treat delivery as at-least-once: consumers should be idempotent.
- Do not rely on global ordering across partitions.
- Ensure destination topics exist before scheduling traffic.
Production Checklist
- Use UTC timestamps end to end.
- Include
x-scheduler-request-idon all scheduled messages if you need cancellation or replacement. - Start with default partitioning, then increase
schedulerTopicPartitionsfor higher throughput. - Monitor rejections topic and scheduler lag/throughput metrics.
- Keep consumer handlers idempotent to handle duplicate deliveries safely.
Quick Troubleshooting
Message was not delivered
- Check destination topic exists.
- Check consumer group is healthy.
- Check if request was rejected in
<app-id>-schedule-rejections.
Message was rejected
- Validate required headers for selected action (
ADD,CANCEL, orREPLACE). - Validate timestamp is parseable ISO UTC.
Cancel did not stop delivery
- Confirm
x-scheduler-request-idexactly matches original scheduled request. - Confirm cancel arrived before dispatch window.
Replace had no visible effect
- Confirm
x-scheduler-request-idmatches the existing scheduled message. - Check
kms_messages_replace_noopto see if replacement was a strict no-op. - Validate replacement timestamp/topic differ from the existing entry when change is expected.
Quick Reference
Action: ADD
- Topic:
<app-id>-schedule-requests - Required headers:
x-scheduler-deliver-to,x-scheduler-deliver-at - Optional headers:
x-scheduler-action=ADD,x-scheduler-request-id
Action: CANCEL
- Topic:
<app-id>-schedule-requests - Required headers:
x-scheduler-action=CANCEL,x-scheduler-request-id
Action: REPLACE
- Topic:
<app-id>-schedule-requests - Required headers:
x-scheduler-action=REPLACE,x-scheduler-request-id,x-scheduler-deliver-to,x-scheduler-deliver-at