Keep similar-ai from tripping the global circuit on a lone URL 502, clamp Qdrant search to 100, and add Server-Timing plus slow-request logging. Studio shared props, Academy S3 exists caching, heat chunking, and Redis/scheduler hygiene stay in this rollout.
80 lines
2.9 KiB
PHP
80 lines
2.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use App\Support\Redis\OrphanQueueCleanup;
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Support\Facades\Log;
|
|
use RuntimeException;
|
|
|
|
class RedisCleanupOrphansCommand extends Command
|
|
{
|
|
protected $signature = 'skinbase:redis-cleanup-orphans
|
|
{target : forum-moderation, forum-security, or collections}
|
|
{--dry-run : Inspect and report without UNLINK}
|
|
{--execute : UNLINK the waiting queue and notify list}
|
|
{--force : Allow unexpected sampled job classes}';
|
|
|
|
protected $description = 'UNLINK an orphan Redis queue after producer and class checks (operator only)';
|
|
|
|
public function handle(OrphanQueueCleanup $cleanup): int
|
|
{
|
|
$dryRun = (bool) $this->option('dry-run');
|
|
$execute = (bool) $this->option('execute');
|
|
if ($dryRun === $execute) {
|
|
$this->error('Pass exactly one of --dry-run or --execute.');
|
|
|
|
return self::FAILURE;
|
|
}
|
|
|
|
$target = (string) $this->argument('target');
|
|
$force = (bool) $this->option('force');
|
|
|
|
$this->info(sprintf(
|
|
'[redis-cleanup-orphans] prefix=%s connection=default target=%s force=%s mode=%s',
|
|
(string) config('database.redis.options.prefix'),
|
|
$target,
|
|
$force ? 'yes' : 'no',
|
|
$dryRun ? 'dry-run' : 'execute'
|
|
));
|
|
|
|
try {
|
|
$plan = $execute
|
|
? $cleanup->execute($target, $force)
|
|
: $cleanup->plan($target, $force);
|
|
} catch (RuntimeException $e) {
|
|
$this->error($e->getMessage());
|
|
Log::warning('[redis-cleanup-orphans] refused', ['target' => $target, 'error' => $e->getMessage()]);
|
|
|
|
return self::FAILURE;
|
|
}
|
|
|
|
$this->line('logical_key='.$plan['logical_key']);
|
|
$this->line('notify_key='.$plan['notify_key']);
|
|
$this->line('llen='.$plan['llen'].' reserved='.$plan['reserved'].' delayed='.$plan['delayed']);
|
|
$this->line('sampled='.$plan['sampled'].' est_bytes='.$plan['est_bytes']);
|
|
$this->line('classes='.json_encode($plan['classes']));
|
|
if ($plan['unexpected_classes'] !== []) {
|
|
$this->warn('unexpected_classes='.json_encode($plan['unexpected_classes']));
|
|
}
|
|
if ($plan['errors'] !== []) {
|
|
$this->error('errors='.implode(',', $plan['errors']));
|
|
}
|
|
if ($execute) {
|
|
$this->info('unlinked='.($plan['unlinked'] ?? 0));
|
|
}
|
|
|
|
Log::info('[redis-cleanup-orphans] completed', [
|
|
'target' => $target,
|
|
'dry_run' => $dryRun,
|
|
'llen' => $plan['llen'],
|
|
'errors' => $plan['errors'],
|
|
'unlinked' => $plan['unlinked'] ?? 0,
|
|
]);
|
|
|
|
return ($plan['errors'] === [] || $dryRun) ? self::SUCCESS : self::FAILURE;
|
|
}
|
|
}
|