Bound media authentication retry waiters

Signed-off-by: Tom Brow <tomb@block.xyz>
This commit is contained in:
Tom Brow
2026-10-18 14:29:05 -07:00
parent 93c1359b12
commit aee8135424
2 changed files with 42 additions and 3 deletions

View File

@@ -50,6 +50,8 @@ class MediaGetAuthService {
// Includes the running job. Bound retained work during rapid history scrolls.
static const _maxPendingSignatures = 64;
int _pendingSignatures = 0;
static const _maxPendingRetries = 64;
int _pendingRetries = 0;
Future<void> _signingTail = Future<void>.value();
MediaGetAuthService({
@@ -107,13 +109,20 @@ class MediaGetAuthService {
/// Keeps an in-flight consumer pending through one queue-saturation retry.
///
/// Waits for the existing queue snapshot, then competes for normal admission.
/// A second rejection propagates; no retry loop or extra signing queue exists.
/// At most 64 consumers may wait or retry in addition to the 64 signing jobs.
/// Excess waiters and second rejections fail without a retry loop.
Future<Map<String, String>> headersForAsyncWithRetry(String url) async {
try {
return await headersForAsync(url);
} on MediaAuthQueueFull catch (error) {
await error.drained;
return headersForAsync(url);
if (_pendingRetries >= _maxPendingRetries) rethrow;
_pendingRetries++;
try {
await error.drained;
return await headersForAsync(url);
} finally {
_pendingRetries--;
}
}
}

View File

@@ -61,6 +61,36 @@ class _VideoPlatform extends VideoPlayerPlatform {
}
void main() {
test('bounds retry waiters before signing capacity drains', () async {
final auth = _SaturatedAuth(alwaysReject: true);
var rejected = 0;
final requests = List.generate(
80,
(_) => auth
.headersForAsyncWithRetry('https://example.com/media/a')
.then<Object>(
(headers) => headers,
onError: (Object error) {
rejected++;
return error;
},
),
);
await Future<void>.delayed(Duration.zero);
expect(auth.calls, 80);
expect(rejected, 16);
auth.drained.complete();
await Future.wait(requests);
expect(auth.calls, 144);
expect(rejected, 80);
// Retry permits are released even when the second admission fails.
await expectLater(
auth.headersForAsyncWithRetry('https://example.com/media/a'),
throwsA(isA<MediaAuthQueueFull>()),
);
expect(auth.calls, 146);
});
test('retries saturation only once', () async {
final auth = _SaturatedAuth(alwaysReject: true);
final result = auth.headersForAsyncWithRetry('https://example.com/media/a');