option('dry-run'); $execute = (bool) $this->option('execute'); if ($dryRun === $execute) { $this->error('Pass exactly one of --dry-run or --execute.'); return self::FAILURE; } $scanCount = $this->intOption('scan-count', (int) config('traffic.online_visitors.prune_scan_count', 500)); $maxBatches = $this->intOption('max-batches', (int) config('traffic.online_visitors.prune_max_batches', 200)); $maxMembers = $this->intOption('max-members', (int) config('traffic.online_visitors.prune_max_members', 100000)); $sleepMs = $this->intOption('sleep-ms', (int) config('traffic.online_visitors.prune_sleep_ms', 25)); $timeLimit = $this->intOption('time-limit', 0); $scard = (int) Redis::scard(OnlineVisitorRepository::INDEX_KEY); $this->info(sprintf( '[prune-online-visitor-index] key=%s prefix=%s scard=%d scan_count=%d max_batches=%d max_members=%d sleep_ms=%d %s', OnlineVisitorRepository::INDEX_KEY, (string) config('database.redis.options.prefix'), $scard, $scanCount, $maxBatches, $maxMembers, $sleepMs, $dryRun ? 'dry-run' : 'execute' )); $result = $pruner->prune( $scanCount, $maxBatches, $maxMembers, $sleepMs, $dryRun, $timeLimit > 0 ? $timeLimit : null, ); $this->info(sprintf( 'scanned=%d stale=%d live=%d removed=%d batches=%d', $result['scanned'], $result['stale'], $result['live'], $result['removed'], $result['batches'] )); Log::info('[prune-online-visitor-index] completed', $result + ['scard_before' => $scard]); return self::SUCCESS; } private function intOption(string $name, int $default): int { $raw = $this->option($name); if ($raw === null || $raw === '') { return $default; } return max(0, (int) $raw); } }