Skip to content

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.

  • A gisila app (see the framework docs).
  • Redis, reachable through REDIS_URL.
Terminal window
dart pub add gisila_queue

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;
}

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.

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.

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.

Terminal window
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 ✓.