fix(sms): parallel SA failover and clear sticky cache
This commit is contained in:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user