mirror of
https://github.com/block/buzz.git
synced 2026-10-28 13:01:10 +00:00
Bound media authentication retry waiters
Signed-off-by: Tom Brow <tomb@block.xyz>
This commit is contained in:
@@ -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--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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');
|
||||
|
||||
Reference in New Issue
Block a user