fix(sms): parallel SA failover and clear sticky cache

This commit is contained in:
2026-09-01 12:04:04 +03:30
parent d12b38a1e8
commit 00610246ce
@@ -3,6 +3,7 @@
namespace App\Services\SmsBilling;
use App\Http\Controllers\Controller;
use Illuminate\Http\Client\Pool;
use Illuminate\Http\Client\Response;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
@@ -201,6 +202,27 @@ class SuperAdminSmsClient
$method = strtoupper($method);
$lastError = null;
if ($method === 'GET' && count($bases) > 1) {
$parallel = $this->requestJsonParallel(
$bases,
$canonicalPath,
$relativeUrl,
$query,
$timeout,
$label
);
if ($parallel !== null) {
return $parallel;
}
$this->forgetSuccessfulBase();
Log::warning("SuperAdmin SMS {$label} unavailable on all bases (parallel GET)", [
'bases' => $bases,
]);
return null;
}
foreach ($bases as $index => $base) {
try {
$pending = Http::withHeaders($this->authHeaders($method, $canonicalPath))
@@ -256,6 +278,8 @@ class SuperAdminSmsClient
}
}
$this->forgetSuccessfulBase();
Log::warning("SuperAdmin SMS {$label} unavailable on all bases", [
'bases' => $bases,
'last_error' => $lastError,
@@ -264,6 +288,88 @@ class SuperAdminSmsClient
return null;
}
/**
* Fan-out GET to all SuperAdmin hosts in parallel (max wait = one timeout, not N×timeout).
*
* @param list<string> $bases
* @param array<string, mixed> $query
* @return array<string, mixed>|null
*/
private function requestJsonParallel(
array $bases,
string $canonicalPath,
string $relativeUrl,
array $query,
int $timeout,
string $label
): ?array {
$headers = $this->authHeaders('GET', $canonicalPath);
$options = Controller::httpClientOptions([], (float) $timeout);
try {
/** @var array<string, Response|\Throwable> $responses */
$responses = Http::pool(function (Pool $pool) use ($bases, $headers, $relativeUrl, $query, $timeout, $options) {
foreach ($bases as $index => $base) {
$pool->as((string) $index)
->withHeaders($headers)
->acceptJson()
->connectTimeout(2)
->timeout($timeout)
->withOptions($options)
->get($this->urlForBase($base, $relativeUrl), $query);
}
});
} catch (\Throwable $e) {
Log::warning("SuperAdmin SMS {$label} parallel pool failed", [
'error' => $e->getMessage(),
]);
return null;
}
foreach ($bases as $index => $base) {
$key = (string) $index;
if (! array_key_exists($key, $responses)) {
continue;
}
$response = $responses[$key];
if ($response instanceof \Throwable) {
Log::warning("SuperAdmin SMS {$label} parallel exception", [
'base' => $base,
'error' => $response->getMessage(),
]);
continue;
}
if (! $response->successful()) {
Log::warning("SuperAdmin SMS {$label} parallel failed", [
'base' => $base,
'status' => $response->status(),
'body' => $response->body(),
]);
continue;
}
$data = $response->json();
if (! is_array($data)) {
Log::warning("SuperAdmin SMS {$label} parallel invalid JSON", [
'base' => $base,
]);
continue;
}
$this->rememberSuccessfulBase($base);
return $data;
}
return null;
}
/**
* @return list<string>
*/
@@ -340,6 +446,17 @@ class SuperAdminSmsClient
}
}
private function forgetSuccessfulBase(): void
{
try {
Cache::forget(self::SUCCESS_BASE_CACHE_KEY);
} catch (\Throwable $e) {
Log::debug('SuperAdmin SMS sticky base cache forget failed', [
'error' => $e->getMessage(),
]);
}
}
private function readSuccessfulBase(): ?string
{
try {