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.
Prerequisites
Section titled “Prerequisites”- A gisila project and a Redis server (
REDIS_URLor host/port). gisila_queueadded to your dependencies.
1. Declare tasks in shared code
Section titled “1. Declare tasks in shared code”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.
2. Register the queue as a singleton
Section titled “2. Register the queue as a singleton”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.
3. Enqueue from a service
Section titled “3. Enqueue from a service”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.
4. Run a worker process
Section titled “4. Run a worker process”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.
Verify it works
Section titled “Verify it works”- Start Redis, the web server and the worker.
curl -X POST localhost:8080/signup -H 'content-type: application/json' -d '{...}'returns at once.- The worker’s log shows
emails.send_welcomerunning.
For tests, build the queue with an in-memory broker so no Redis is needed:
final jobs = Jobs(GisilaQueue(broker: InMemoryBroker(), backend: InMemoryResultBackend()));Next steps
Section titled “Next steps”- gisila_queue documentation: retries, scheduling, routing, Beat, monitoring.
- Services and dependency scopes