import 'dart:async'; import 'dart:io'; import 'package:meta/meta.dart'; import '../pipeline/question_pipeline.dart'; import '../trading/trading_orchestrator.dart'; import 'market_history_scheduler.dart'; /// Runs [QuestionPipeline.runMaintenanceCycle] on a fixed interval, and /// optionally [TradingOrchestrator.runMaintenanceCycle] right after when /// trading is enabled. /// /// When [marketHistoryScheduler] is set, [MarketHistoryScheduler.runIfDue] /// runs at the start of each tick (before the question pipeline). class QuestionBackgroundWorker { QuestionBackgroundWorker({ required QuestionPipeline pipeline, required Duration interval, TradingOrchestrator? tradingOrchestrator, MarketHistoryScheduler? marketHistoryScheduler, Future Function()? tradingMaintenanceRunner, DateTime Function()? clock, }) : _pipeline = pipeline, _interval = interval, _tradingOrchestrator = tradingOrchestrator, _marketHistoryScheduler = marketHistoryScheduler, _tradingMaintenanceRunner = tradingMaintenanceRunner, _clock = clock ?? DateTime.now; final QuestionPipeline _pipeline; final TradingOrchestrator? _tradingOrchestrator; final MarketHistoryScheduler? _marketHistoryScheduler; final Future Function()? _tradingMaintenanceRunner; final DateTime Function() _clock; final Duration _interval; Timer? _timer; bool _running = false; void start() { if (_timer != null) { return; } stdout.writeln( 'Question background worker started (interval ${_interval.inSeconds}s, ' 'trading=${_tradingOrchestrator != null || _tradingMaintenanceRunner != null}, ' 'marketHistory=${_marketHistoryScheduler != null})', ); _timer = Timer.periodic(_interval, (_) => _tick()); unawaited(_tick()); } void stop() { _timer?.cancel(); _timer = null; _pipeline.close(); } @visibleForTesting Future runTickForTest() => _tick(); Future _tick() async { if (_running) { return; } _running = true; if (_marketHistoryScheduler != null) { try { await _marketHistoryScheduler.runIfDue(_clock()); } catch (e, st) { stderr.writeln('Market history scheduler tick failed: $e\n$st'); } } try { await _pipeline.runMaintenanceCycle(); } catch (e, st) { stderr.writeln('Question background worker tick failed: $e\n$st'); } if (_tradingMaintenanceRunner != null) { try { await _tradingMaintenanceRunner(); } catch (e, st) { stderr.writeln('Trading orchestrator tick failed: $e\n$st'); } } else if (_tradingOrchestrator != null) { try { await _tradingOrchestrator.runMaintenanceCycle(); } catch (e, st) { stderr.writeln('Trading orchestrator tick failed: $e\n$st'); } } _running = false; } }