Skip to content

Background jobs with gisila_queue

You’ll send a welcome email after sign-up without making the client wait: the request enqueues a task with gisila_queue and returns, and a worker process sends the mail. You’ll also see the lighter option for small periodic jobs inside the web process.

  • A gisila project and a Redis server (REDIS_URL or host/port).
  • gisila_queue added to your dependencies.

Producers (the web app) and workers must register the same task names. Put them in one library both import, lib/jobs.dart:

import 'package:gisila_queue/gisila_queue.dart';
GisilaQueue buildQueue() => GisilaQueue.redis(namespace: 'notes', host: 'localhost');
class Jobs {
Jobs(this.queue)
: sendWelcome = queue.task<void>(
'emails.send_welcome',
(ctx) async {
final userId = ctx.arg(0) as String;
await sendWelcomeEmail(userId); // your mailer
return null;
},
maxRetries: 5,
);
final GisilaQueue queue;
final TaskRef<void> sendWelcome;
}

Use one namespace per app on a shared Redis; producers and workers of one app must use the same one.

import 'package:gisila/gisila.dart';
import 'package:notes_api/jobs.dart';
Future<void> main() async {
final app = GisilaApp(config: AppConfig());
final jobs = Jobs(buildQueue());
app.registerSingleton<Jobs>(jobs, dispose: (j) => j.queue.close());
app.registerService<AccountService>(AccountService.new);
app.registerController(attacher: AccountController().attachToApp);
await app.start(port: 8080, stopOnSignals: true);
}

The queue is closed on app.stop(), after in-flight requests have finished.

class AccountService extends Service {
Future<User> signUp(SignUpForm form) async {
final user = await _users.create(form);
await singleton<Jobs>().sendWelcome.delay([user.id]);
return user;
}
}

delay returns as soon as the task is on the broker. Arguments must be JSON-encodable; pass ids, not objects, and let the worker load fresh data.

bin/worker.dart:

import 'package:notes_api/jobs.dart';
Future<void> main() async {
final jobs = Jobs(buildQueue());
await jobs.queue.worker(concurrency: 4).run();
}

Run it next to the web process (dart run bin/worker.dart), as a second app or process in your deployment. gisila_queue also ships WorkerCli (package:gisila_queue/gisila_queue_cli.dart) for flag parsing and graceful shutdown; see the queue documentation.

5. Small periodic jobs inside the web process

Section titled “5. Small periodic jobs inside the web process”

For light work that doesn’t need retries or a separate process (cleaning expired sessions every few minutes), tie a loop to the app’s lifecycle with onStart and onStop:

var running = true;
Future<void>? loop;
app.onStart(() {
loop = () async {
while (running) {
try {
await purgeExpiredSessions(app.singleton<CacheProvider>());
} catch (e, st) {
app.config.logger.error('session purge failed', error: e, stackTrace: st);
}
await Future<void>.delayed(const Duration(minutes: 5));
}
}();
});
app.onStop(() async {
running = false;
await loop;
});

Every instance runs its own copy, so use gisila_queue’s Beat scheduler when a job must run exactly once across instances.

  1. Start Redis, the web server and the worker.
  2. curl -X POST localhost:8080/signup -H 'content-type: application/json' -d '{...}' returns at once.
  3. The worker’s log shows emails.send_welcome running.

For tests, build the queue with an in-memory broker so no Redis is needed:

final jobs = Jobs(GisilaQueue(broker: InMemoryBroker(), backend: InMemoryResultBackend()));