serveWith static method

Future<int> serveWith(
  1. ApplicationFactory factory, {
  2. String? host,
  3. int? port,
  4. int? isolates,
  5. bool? embedQueue,
  6. bool? embedScheduler,
  7. int? maxInFlight,
  8. Duration? maxQueueDelay,
  9. ScheduleCommandRunner? commandRunner,
  10. int? processes,
  11. List<String> childArguments = const <String>[],
})

Serves with factory: in-process when isolates <= 1, otherwise a Supervisor spawns isolates workers that each build their own application. Returns the process exit code.

With embedQueue / embedScheduler (or SERVER_EMBED_QUEUE / SERVER_EMBED_SCHEDULER), a queue worker and the scheduler run in their own isolates in the same process and stop with the server. commandRunner lets the embedded scheduler run console commands (the CLI passes its own); without it, scheduled commands fail and are logged.

Implementation

static Future<int> serveWith(
  ApplicationFactory factory, {
  String? host,
  int? port,
  int? isolates,
  bool? embedQueue,
  bool? embedScheduler,
  int? maxInFlight,
  Duration? maxQueueDelay,
  ScheduleCommandRunner? commandRunner,
  int? processes,
  List<String> childArguments = const <String>[],
}) async {
  final probe = factory();
  await probe.boot();
  // ADR-027: several processes on consecutive ports behind a proxy. A
  // child (DARAVEL_PROCESS_INDEX set) serves normally.
  final processCount = processes ?? probe.env.integer('APP_PROCESSES', 1);
  if (processCount > 1 && !probe.env.has('DARAVEL_PROCESS_INDEX')) {
    final options = ServeOptions.fromEnv(probe.env, host: host, port: port);
    probe._ensureProcessesShareState(processCount);
    final logger = probe.logger;
    await probe.shutdown();
    return await ProcessSupervisor(
      executable: Platform.resolvedExecutable,
      arguments: selfArguments(childArguments),
      options: options,
      processes: processCount,
      logger: logger,
      environment: <String, String>{
        if (isolates != null) 'APP_ISOLATES': '$isolates',
        if (embedQueue != null) 'SERVER_EMBED_QUEUE': '$embedQueue',
        if (embedScheduler != null)
          'SERVER_EMBED_SCHEDULER': '$embedScheduler',
        if (maxInFlight != null) 'HTTP_MAX_IN_FLIGHT': '$maxInFlight',
        if (maxQueueDelay != null)
          'HTTP_MAX_QUEUE_DELAY': '${maxQueueDelay.inMilliseconds}ms',
      },
    ).run();
  }
  final options = ServeOptions.fromEnv(
    probe.env,
    host: host,
    port: port,
    isolates: isolates,
    embedQueue: embedQueue,
    embedScheduler: embedScheduler,
    maxInFlight: maxInFlight,
    maxQueueDelay: maxQueueDelay,
  );
  probe._ensureServeable(options);
  // Once, here in the main isolate, rather than in every worker.
  if (probe.config.section<DatabaseConfig>().sharesSqliteFile(
    options.isolates,
  )) {
    probe.logger.warning(
      'SQLite with ${options.isolates} isolates: SQLite calls are '
      'synchronous, so a worker waiting for another isolate\'s write lock '
      '(busy_timeout, up to 5 s) blocks its whole event loop. Use '
      'PostgreSQL for production with APP_ISOLATES > 1 (Deployment guide, '
      '"Concurrency").',
    );
  }
  final sizing = isolateSizingAdvice(
    production: probe.config.section<AppConfig>().isProduction,
    isolates: options.isolates,
  );
  if (sizing != null) {
    probe.logger.notice(sizing);
  }
  final roles = [
    if (options.embedQueue) EmbeddedRole.queue,
    if (options.embedScheduler) EmbeddedRole.scheduler,
  ];
  // One coordinator for the whole process: the server's isolates and the
  // embedded workers share its process cache, rate limits and locks.
  final Coordinator? coordinator;
  if (options.isolates <= 1) {
    final channel = probe.container.make<CoordinatorChannel>();
    coordinator = channel is LocalCoordinatorChannel
        ? channel.coordinator
        : null;
  } else {
    coordinator = createStandardCoordinator(
      processCacheMaxEntries: probe.config
          .section<CacheConfig>()
          .processMaxEntries,
    );
  }
  final embedded = roles.isEmpty
      ? null
      : EmbeddedWorkers(
          factory,
          roles: roles,
          logger: probe.logger,
          commandRunner: commandRunner,
          coordinator: coordinator?.listen(),
        );
  await embedded?.start();
  // A child of APP_PROCESSES: drain and stop when the parent goes away.
  Supervisor? supervisor;
  watchSupervisorParent(Platform.environment, () async {
    probe.logger.warning('Parent process is gone; stopping.');
    await (supervisor?.stop() ?? probe._engine?.stop() ?? Future.value())
        .timeout(options.shutdownTimeout, onTimeout: () {});
    exit(0);
  });
  try {
    if (options.isolates <= 1) {
      await probe.serve(options: options);
      return 0;
    }
    final logger = probe.logger;
    await probe.shutdown();
    supervisor = Supervisor(
      factory,
      options,
      logger: logger,
      coordinator: coordinator,
    );
    return await supervisor.run();
  } finally {
    await embedded?.stop(timeout: options.shutdownTimeout);
  }
}