serverpod-offline-sync/packages/serverpod_offline_sync/test/utils/once_test.dart
Tizian 「ティツィアーン」 f062725a16
fix: Rebuild projections once per context after the registry changed
Every ephemeral OfflineSyncDatabase shares one context but initializes a
recorder of its own, and each rebuilt the projections of every space
when the context's first initialize had changed the registry: on every
session and every merge, until the process stopped (upstream #150). A
new device, or one whose app changed its synced tables, spent about 3 s
of every inbound merge on it with a library of 8,000 cards, a server
after such a migration as much on every request.

The context now runs the rebuild once (Once): wrappers initializing
while it runs await the same run, later ones skip it, and a failed run
is tried again by the next.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 19:12:03 +02:00

47 lines
1.1 KiB
Dart

import 'dart:async';
import 'package:serverpod_offline_sync/src/utils/once.dart';
import 'package:test/test.dart';
void main() {
group('Given an action run once,', () {
late Once once;
late int runs;
late Completer<void> gate;
setUp(() {
once = Once();
runs = 0;
gate = Completer<void>();
});
Future<void> action() async {
runs++;
await gate.future;
}
test('when asked while it runs and after, then it runs once and every '
'caller awaits that run.', () async {
final first = once.run(action);
final meanwhile = once.run(action);
gate.complete();
await Future.wait([first, meanwhile]);
await once.run(action);
expect(runs, 1);
});
test('when its run fails, then the next caller runs it again.', () async {
final failing = once.run(() async {
runs++;
throw StateError('failed');
});
await expectLater(failing, throwsStateError);
gate.complete();
await once.run(action);
expect(runs, 2);
});
});
}