Skip to content

feat: EventBridge Scheduler support (one-time and recurring schedules) - #106

Open
psantus wants to merge 1 commit into
brefphp:masterfrom
psantus:feat/scheduler
Open

psantus wants to merge 1 commit into
brefphp:masterfrom
psantus:feat/scheduler

Conversation

@psantus

@psantus psantus commented Mar 24, 2026 •

Copy link
Copy Markdown

EventBridge Scheduler

Messages can be scheduled for future delivery using EventBridge Scheduler. This supports one-time and recurring schedules.

Design

sequenceDiagram
    participant App as Application
    participant Bus as Symfony Messenger Bus
    participant T as EventBridgeTransport
    participant EB as EventBridgeClient
    participant SC as SchedulerClient

    App->>Bus: dispatch(message, [SchedulerStamp])
    Bus->>T: send(envelope)
    alt Has SchedulerStamp
        T->>SC: createSchedule(at(...), Target{Arn, Input, RoleArn})
        SC-->>T: ScheduleArn
    else No SchedulerStamp
        T->>EB: putEvents(...)
        EB-->>T: PutEventsResponse
    end
    T-->>Bus: envelope
Loading

Installation

composer require async-aws/scheduler

Configuration

Add target_arn and role_arn to your EventBridge transport DSN:

# config/packages/messenger.yaml
framework:
    messenger:
        transports:
            async: 'eventbridge://myapp?target_arn=arn:aws:lambda:us-east-1:123456789:function:my-worker&role_arn=arn:aws:iam::123456789:role/scheduler-role&schedule_group=my-group'
  • target_arn — The ARN of the Lambda function (or EventBridge bus) to invoke when the schedule fires
  • role_arn — The IAM role ARN that EventBridge Scheduler assumes to invoke the target
  • schedule_group — (optional) The schedule group name (defaults to AWS default group)

Usage

One-time schedule

use Bref\Symfony\Messenger\Service\EventBridge\SchedulerStamp;

// Schedule a message at 10am Paris time on April 1st
$envelope = $bus->dispatch(new MyMessage(), [
    SchedulerStamp::oneTime(new \DateTimeImmutable('2026-04-01 10:00:00'), 'Europe/Paris'),
]);

Recurring schedule

use Bref\Symfony\Messenger\Service\EventBridge\SchedulerStamp;

// Every hour
$envelope = $bus->dispatch(new MyMessage(), [
    new SchedulerStamp('rate(1 hour)'),
]);

// Cron: every day at 9am UTC
$envelope = $bus->dispatch(new MyMessage(), [
    new SchedulerStamp('cron(0 9 * * ? *)', timezone: 'UTC'),
]);

Retrieving the schedule ID

use Bref\Symfony\Messenger\Service\EventBridge\ScheduledStamp;

$envelope = $bus->dispatch(new MyMessage(), [$stamp]);

$scheduled = $envelope->last(ScheduledStamp::class);
$scheduled->getScheduleArn();   // arn:aws:scheduler:...
$scheduled->getScheduleName();  // bref-msg-...

Cancelling a schedule

use Bref\Symfony\Messenger\Service\EventBridge\ScheduleDeleter;

/** @var ScheduleDeleter $deleter */
$deleter->delete($scheduleName, 'my-group');

Overriding target per message

The SchedulerStamp can override the DSN-level target_arn and role_arn:

$bus->dispatch(new MyMessage(), [
    new SchedulerStamp(
        scheduleExpression: 'at(2026-04-01T10:00:00)',
        targetArn: 'arn:aws:lambda:us-east-1:123456789:function:other-worker',
        roleArn: 'arn:aws:iam::123456789:role/other-role',
        timezone: 'Europe/Paris',
    ),
]);

How it works

When a SchedulerStamp is attached to a message, the transport creates an EventBridge Scheduler schedule instead of calling putEvents. The serialized message (including Symfony Messenger headers) is set as the schedule's target input, so the existing Bref consumer can decode it.

  • One-time schedules (at(...)) are automatically deleted by AWS after execution
  • Recurring schedules (rate(...), cron(...)) persist until explicitly deleted
  • Without a SchedulerStamp, the transport behaves exactly as before (no breaking change)

Key design decisions

  • async-aws/scheduler is a suggested dependency (not required) — the package works without it; a clear LogicException is thrown if someone uses SchedulerStamp without installing it
  • ActionAfterCompletion = DELETE for one-time schedules, NONE for recurring
  • DSN-level defaults (target_arn, role_arn, schedule_group) can be overridden per-message via the stamp
  • Tested against real AWS EventBridge Scheduler in eu-west-3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant