Ship production optimization M1-M12.5A: queues, metrics, HTTP observability, and vector search reliability.
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.
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Predis\Client as PredisClient;
|
||||
|
||||
class RedisCleanupLegacyPrefixCommand extends Command
|
||||
{
|
||||
protected $signature = 'skinbase:redis-cleanup-legacy-prefix
|
||||
{--dry-run : SCAN and report only}
|
||||
{--execute : UNLINK matching obsolete keys}
|
||||
{--max-keys=500 : Cap keys this run}';
|
||||
|
||||
protected $description = 'UNLINK obsolete skinbasenova-database-* and skinbasenova_horizon:* keys only';
|
||||
|
||||
private const LEGACY_PREFIXES = [
|
||||
'skinbasenova-database-',
|
||||
'skinbasenova_horizon:',
|
||||
];
|
||||
|
||||
private const PROTECTED_PREFIXES = [
|
||||
'skinbase-database-',
|
||||
'skinbase_horizon:',
|
||||
];
|
||||
|
||||
public function handle(): 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;
|
||||
}
|
||||
|
||||
$maxKeys = max(1, (int) $this->option('max-keys'));
|
||||
$currentPrefix = (string) config('database.redis.options.prefix');
|
||||
$horizonPrefix = (string) config('horizon.prefix');
|
||||
|
||||
$this->info(sprintf(
|
||||
'[redis-cleanup-legacy-prefix] current_prefix=%s horizon_prefix=%s max_keys=%d %s',
|
||||
$currentPrefix,
|
||||
$horizonPrefix,
|
||||
$maxKeys,
|
||||
$dryRun ? 'dry-run' : 'execute'
|
||||
));
|
||||
|
||||
if (in_array($currentPrefix, self::LEGACY_PREFIXES, true) || in_array($horizonPrefix, self::LEGACY_PREFIXES, true)) {
|
||||
$this->error('Current process still uses a legacy prefix. Aborting.');
|
||||
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
$client = $this->unprefixedClient();
|
||||
$found = [];
|
||||
$cursor = '0';
|
||||
do {
|
||||
[$cursor, $keys] = $client->scan($cursor, ['COUNT' => 200, 'MATCH' => '*']);
|
||||
foreach ($keys as $key) {
|
||||
$key = (string) $key;
|
||||
if ($this->isProtected($key)) {
|
||||
continue;
|
||||
}
|
||||
if (! $this->isLegacy($key)) {
|
||||
continue;
|
||||
}
|
||||
$found[] = $key;
|
||||
if (count($found) >= $maxKeys) {
|
||||
$cursor = '0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while ($cursor !== '0' && $cursor !== 0);
|
||||
|
||||
$this->info('matched='.count($found));
|
||||
foreach (array_slice($found, 0, 20) as $key) {
|
||||
$this->line('key='.$key);
|
||||
}
|
||||
if (count($found) > 20) {
|
||||
$this->line('... truncated listing');
|
||||
}
|
||||
|
||||
$unlinked = 0;
|
||||
if (! $dryRun && $found !== []) {
|
||||
foreach (array_chunk($found, 50) as $chunk) {
|
||||
$unlinked += (int) $client->unlink(...$chunk);
|
||||
}
|
||||
}
|
||||
|
||||
$this->info('unlinked='.$unlinked);
|
||||
|
||||
Log::info('[redis-cleanup-legacy-prefix] completed', [
|
||||
'matched' => count($found),
|
||||
'unlinked' => $unlinked,
|
||||
'dry_run' => $dryRun,
|
||||
]);
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
private function isLegacy(string $key): bool
|
||||
{
|
||||
foreach (self::LEGACY_PREFIXES as $prefix) {
|
||||
if (str_starts_with($key, $prefix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function isProtected(string $key): bool
|
||||
{
|
||||
foreach (self::PROTECTED_PREFIXES as $prefix) {
|
||||
if (str_starts_with($key, $prefix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function unprefixedClient(): PredisClient
|
||||
{
|
||||
$redis = config('database.redis.default', []);
|
||||
|
||||
return new PredisClient([
|
||||
'scheme' => 'tcp',
|
||||
'host' => $redis['host'] ?? '127.0.0.1',
|
||||
'port' => (int) ($redis['port'] ?? 6379),
|
||||
'password' => $redis['password'] ?? null,
|
||||
'database' => (int) ($redis['database'] ?? 0),
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user