Use the queue in a gisila app
You’ll add background tasks to a gisila web app: the HTTP server enqueues from a controller, a separate worker binary in the same package runs the tasks, and the queue’s connections open and close with the app.
Prerequisites
Section titled “Prerequisites”- A gisila app (see the framework docs).
- Redis, reachable through
REDIS_URL.
1. Add the dependency
Section titled “1. Add the dependency”dart pub add gisila_queue2. Put tasks in shared code
Section titled “2. Put tasks in shared code”lib/queue.dart:
import 'dart:io';
import 'package:gisila_queue/gisila_queue.dart' hide Router, LogLevel;
GisilaQueue buildQueue() { final redis = RedisOptions.parseUrl(Platform.environment['REDIS_URL']!); return GisilaQueue.redis( namespace: 'myapp', host: redis.host, port: redis.port, options: redis.options, );}
class AppTasks { AppTasks(GisilaQueue q) : sendWelcome = q.task<void>('emails.send_welcome', (ctx) async { await sendWelcomeEmail(ctx.arg(0) as String); return null; }, maxRetries: 5, autoRetry: true);
final TaskRef<void> sendWelcome;}3. Register the queue as a singleton
Section titled “3. Register the queue as a singleton”In your server’s main:
import 'package:gisila/gisila.dart';import 'package:my_app/queue.dart';
Future<void> main() async { final app = GisilaApp(config: AppConfig());
final queue = buildQueue(); app.registerSingleton<GisilaQueue>(queue, dispose: (q) => q.close()); app.registerSingleton<AppTasks>(AppTasks(queue));
app.registerController(attacher: UsersController().attachToApp); await app.start(host: '0.0.0.0', port: 8080);}dispose runs on app.stop(), closing the queue’s Redis connections. Redis connections
open lazily on first use.
4. Enqueue from a controller
Section titled “4. Enqueue from a controller”import 'package:gisila/gisila.dart';import 'package:my_app/queue.dart';
part 'users_controller.g.dart';
@Controller('/users')class UsersController { @Post('/') Future<Map<String, Object?>> register(RequestContext ctx) async { final body = await readJsonBody(ctx.request) as Map<String, Object?>; final email = body['email'] as String; // ... create the user ... final r = await ctx.singleton<AppTasks>().sendWelcome.delay([email]); return {'ok': true, 'welcome_task': r.id}; }}Respond right after delay(); don’t get() the result inside the request unless the task
is very short.
5. Add a worker binary
Section titled “5. Add a worker binary”bin/worker.dart in the same package:
import 'package:gisila_queue/gisila_queue_cli.dart';import 'package:my_app/queue.dart';
Future<void> main(List<String> argv) async { final cli = WorkerCli.parse(argv); if (cli.help) return print(WorkerCli.usage); final queue = cli.buildApp(); AppTasks(queue); await cli.run(queue);}Run it with the same namespace: dart run bin/worker.dart --namespace=myapp.
6. Share Redis for rate limits and cache too
Section titled “6. Share Redis for rate limits and cache too”If the web app runs several isolates or servers, gisila_redis gives it a shared rate-limit store and cache on the same Redis:
import 'package:gisila_redis/gisila_redis.dart';
final redis = GisilaRedis.fromUrl(Platform.environment['REDIS_URL']!);final app = GisilaApp( config: AppConfig( rateLimitStore: RedisRateLimitStore(redis), cache: RedisCacheProvider(redis), ),);Their keys (gisila:rl:, gisila:cache:) don’t overlap the queue’s namespace.
Verify it works
Section titled “Verify it works”dart run bin/server.dart &dart run bin/worker.dart --namespace=myapp &curl -X POST localhost:8080/users -H 'Content-Type: application/json' -d '{"email":"a@b.c"}'The response contains a welcome_task id and the worker logs
task emails.send_welcome ✓.