Schedule periodic tasks
You’ll run a beat process that enqueues a nightly report, a heartbeat every minute and an office-hours sync, and that stays correct if you start it twice or restart it.
Prerequisites
Section titled “Prerequisites”- Redis and a worker consuming the queues the tasks go to.
- The task names registered in the worker.
1. Write bin/beat.dart
Section titled “1. Write bin/beat.dart”import 'package:gisila_queue/gisila_queue.dart';import 'package:gisila_queue/gisila_queue_cli.dart';
Future<void> main(List<String> argv) async { final cli = BeatCli.parse(argv); if (cli.help) return print(BeatCli.usage); final app = cli.buildApp(); final beat = cli.buildBeat(app) ..schedule('nightly-report', 'reports.nightly', Schedule.dailyAt(2, minute: 30)) ..schedule('heartbeat', 'health.ping', Schedule.every(const Duration(minutes: 1))) ..schedule('crm-sync', 'sync.crm', Schedule.cron('*/15 9-17 * * 1-5'), kwargs: {'full': false}, queue: 'low'); await cli.run(beat);}Beat only sends task names; it doesn’t need the handlers. BeatCli
wires a RedisBeatLock and RedisBeatStateStore.
2. Or build Beat on your app
Section titled “2. Or build Beat on your app”With GisilaQueue.redis the lock and state
store come with the app:
import 'package:gisila_queue/gisila_queue.dart';import 'package:gisila_queue/gisila_queue_cli.dart' show installShutdownHooks;
final app = GisilaQueue.redis(namespace: 'shop');final beat = Beat(app) ..schedule('heartbeat', 'health.ping', Schedule.every(const Duration(minutes: 1)));installShutdownHooks(beat.stop);await beat.run();await app.close();3. Pick the right schedule
Section titled “3. Pick the right schedule”| Need | Schedule |
|---|---|
| Roughly every N | Schedule.every(Duration) (fires on first start, then every N) |
| Once a day at a time | Schedule.dailyAt(hour, minute: m) |
| Calendar rules | Schedule.cron('min hour dom month dow') |
Times are the beat host’s local time. Set the process TZ if it matters.
4. Make retries work
Section titled “4. Make retries work”Beat sends with maxRetries: 0. If a periodic job needs retries, schedule a tiny task
that enqueues the real one through its TaskRef:
final nightly = app.task<void>('reports.nightly.run', buildReport, maxRetries: 5, autoRetry: true);app.task<void>('reports.nightly', (ctx) async { await nightly.delay(); return null;});5. Run it
Section titled “5. Run it”dart run bin/beat.dart --namespace=shop --lock-ttl=30Starting a second copy is safe: it waits until the first stops or dies (after
--lock-ttl), then takes over with the persisted last-run times.
Verify it works
Section titled “Verify it works”redis-cli GET shop:beat:lock # the active beat's id (host:pid)redis-cli HGETALL shop:beat:lastrun # entry name → last fire timeThe worker should log task health.ping ✓ once a minute.
Next steps
Section titled “Next steps”- Beat reference
- Deploy workers (run beat as its own process)