Skip to content

Commit 1040be2

Browse files
committed
fix: Connection pooling logic and limit check calculations
Previously, when the m3u-proxy API was slow or timed out (which happens when under heavy load), the reconciliation would: - Get empty results ([]) - Think there were 0 active streams - Reset all profile connection counts to 0 - Allow unlimited new connections - Overwhelm the provider with too many connections - Cause editor to be blocked Now: - If API fails → returns null instead of [] - Reconciliation skips that playlist when it gets null - Connection counts stay accurate - Provider limits are respected No more blocking!
1 parent 9193078 commit 1040be2

File tree

3 files changed

+68
-26
lines changed

3 files changed

+68
-26
lines changed

app/Console/Commands/ReconcileProfileConnections.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,20 @@ protected function reconcilePlaylist(Playlist $playlist, M3uProxyService $proxyS
6868
// Get active streams from m3u-proxy for this playlist
6969
$activeStreams = M3uProxyService::getPlaylistActiveStreams($playlist);
7070

71+
// CRITICAL: If API call failed (returned null), skip reconciliation
72+
// This prevents incorrectly zeroing out connection counts on timeout
73+
if ($activeStreams === null) {
74+
$this->error(' Failed to fetch streams from m3u-proxy - SKIPPING reconciliation for this playlist');
75+
$this->warn(' This prevents incorrectly resetting connection counts to zero');
76+
77+
return;
78+
}
79+
80+
// If we got an empty array, that's valid - there are legitimately no streams
81+
if (empty($activeStreams)) {
82+
$this->info(' No active streams found - will reset profile counts to 0');
83+
}
84+
7185
// Build a map of profile_id => active stream count
7286
$profileStreamCounts = [];
7387

app/Services/M3uProxyService.php

Lines changed: 53 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ public static function getPlaylistActiveStreamsCount($playlist): int
159159

160160
try {
161161
$endpoint = $service->apiBaseUrl.'/streams/by-metadata';
162-
$response = Http::timeout(3)->acceptJson()
162+
$response = Http::timeout(10)->acceptJson()
163163
->withHeaders($service->apiToken ? [
164164
'X-API-Token' => $service->apiToken,
165165
] : [])
@@ -187,41 +187,69 @@ public static function getPlaylistActiveStreamsCount($playlist): int
187187

188188
/**
189189
* Get active streams for a specific playlist using metadata filtering
190+
* Returns null on failure to distinguish from legitimately empty results
190191
*/
191-
public static function getPlaylistActiveStreams($playlist): array
192+
public static function getPlaylistActiveStreams($playlist, int $retries = 2): ?array
192193
{
193194
$service = new self;
194195

195196
if (empty($service->apiBaseUrl)) {
196-
return [];
197+
Log::warning('Cannot fetch playlist streams: m3u-proxy API URL not configured');
198+
199+
return null;
197200
}
198201

199-
try {
200-
$endpoint = $service->apiBaseUrl.'/streams/by-metadata';
201-
$response = Http::timeout(3)->acceptJson()
202-
->withHeaders($service->apiToken ? [
203-
'X-API-Token' => $service->apiToken,
204-
] : [])
205-
->get($endpoint, [
206-
'field' => 'playlist_uuid',
207-
'value' => $playlist->uuid,
208-
'active_only' => true,
209-
]);
202+
$endpoint = $service->apiBaseUrl.'/streams/by-metadata';
203+
$attempt = 0;
210204

211-
if ($response->successful()) {
212-
$data = $response->json();
205+
while ($attempt < $retries) {
206+
try {
207+
$response = Http::timeout(10)->acceptJson()
208+
->withHeaders($service->apiToken ? [
209+
'X-API-Token' => $service->apiToken,
210+
] : [])
211+
->get($endpoint, [
212+
'field' => 'playlist_uuid',
213+
'value' => $playlist->uuid,
214+
'active_only' => true,
215+
]);
213216

214-
return $data['matching_streams'] ?? [];
215-
}
217+
if ($response->successful()) {
218+
$data = $response->json();
216219

217-
Log::warning('Failed to fetch playlist streams from m3u-proxy: HTTP '.$response->status());
220+
return $data['matching_streams'] ?? [];
221+
}
218222

219-
return [];
220-
} catch (Exception $e) {
221-
Log::warning('Failed to fetch playlist streams from m3u-proxy: '.$e->getMessage());
223+
Log::warning('Failed to fetch playlist streams from m3u-proxy: HTTP '.$response->status(), [
224+
'attempt' => $attempt + 1,
225+
'max_attempts' => $retries,
226+
]);
227+
228+
$attempt++;
229+
if ($attempt < $retries) {
230+
sleep(1); // Wait 1 second before retry
231+
}
222232

223-
return [];
233+
} catch (Exception $e) {
234+
Log::warning('Failed to fetch playlist streams from m3u-proxy: '.$e->getMessage(), [
235+
'attempt' => $attempt + 1,
236+
'max_attempts' => $retries,
237+
]);
238+
239+
$attempt++;
240+
if ($attempt < $retries) {
241+
sleep(1); // Wait 1 second before retry
242+
}
243+
}
224244
}
245+
246+
// All retries failed
247+
Log::error('All attempts to fetch playlist streams from m3u-proxy failed', [
248+
'playlist_uuid' => $playlist->uuid,
249+
'attempts' => $retries,
250+
]);
251+
252+
return null;
225253
}
226254

227255
/**
@@ -237,7 +265,7 @@ public static function isChannelActive(Channel $channel): bool
237265

238266
try {
239267
$endpoint = $service->apiBaseUrl.'/streams/by-metadata';
240-
$response = Http::timeout(2)->acceptJson()
268+
$response = Http::timeout(10)->acceptJson()
241269
->withHeaders($service->apiToken ? [
242270
'X-API-Token' => $service->apiToken,
243271
] : [])
@@ -283,7 +311,7 @@ public static function getActiveStreamsCountByMetadata(string $field, string $va
283311

284312
try {
285313
$endpoint = $service->apiBaseUrl.'/streams/by-metadata';
286-
$response = Http::timeout(3)->acceptJson()
314+
$response = Http::timeout(10)->acceptJson()
287315
->withHeaders($service->apiToken ? [
288316
'X-API-Token' => $service->apiToken,
289317
] : [])

routes/console.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454

5555
// Reconcile profile connection counts
5656
Schedule::command('profiles:reconcile')
57-
->everyMinute()
57+
->everyFiveMinutes()
5858
->withoutOverlapping();
5959

6060
// Refresh provider profile info (every 15 minutes)

0 commit comments

Comments
 (0)