Skip to content

Commit 2228dbd

Browse files
test(audience): concurrent FlushAsync must serialise on _sendInFlight
Pin the H2-A fix (commit f58260d5): two parallel FlushAsync calls must not both issue HTTP POSTs against the same on-disk batch. BlockingHandler blocks in SendAsync until released. First FlushAsync enters; second starts and must wait on the gate; RequestCount stays at 1. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 5191a29 commit 2228dbd

1 file changed

Lines changed: 33 additions & 0 deletions

File tree

src/Packages/Audience/Tests/Runtime/ImmutableAudienceTests.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -894,6 +894,39 @@ public void SendBatch_ConcurrentTicks_OnlyOneReachesTransport()
894894
"overlapping tick must not issue a second HTTP request");
895895
}
896896

897+
[Test]
898+
public void FlushAsync_ConcurrentCallers_OnlyOneReachesTransport()
899+
{
900+
// Two parallel FlushAsync calls must serialise on _sendInFlight so
901+
// ReadBatch/POST pairs do not double-send. Sabotage: remove the
902+
// gate in FlushAsync and this test sees RequestCount > 1.
903+
var handler = new BlockingHandler();
904+
var config = MakeConfig();
905+
config.HttpHandler = handler;
906+
907+
ImmutableAudience.Init(config);
908+
ImmutableAudience.Track("event_to_send");
909+
ImmutableAudience.FlushQueueToDiskForTesting();
910+
911+
// First caller enters SendAsync and blocks on handler.Release.
912+
var flush1 = Task.Run(() => ImmutableAudience.FlushAsync());
913+
Assert.IsTrue(handler.EnteredSendAsync.Wait(TimeSpan.FromSeconds(2)),
914+
"first FlushAsync should reach the HTTP handler");
915+
916+
// Second caller starts while the first holds the gate — it must
917+
// wait, not issue a second request.
918+
var flush2 = Task.Run(() => ImmutableAudience.FlushAsync());
919+
920+
// Give the second caller a moment to try (and back off).
921+
Thread.Sleep(200);
922+
Assert.AreEqual(1, handler.RequestCount,
923+
"second FlushAsync must not issue a second HTTP request while the first is in-flight");
924+
925+
handler.Release.Set();
926+
Assert.IsTrue(Task.WaitAll(new[] { flush1, flush2 }, TimeSpan.FromSeconds(10)),
927+
"both FlushAsync calls should complete after release");
928+
}
929+
897930
private class BlockingHandler : HttpMessageHandler
898931
{
899932
public readonly ManualResetEventSlim EnteredSendAsync = new ManualResetEventSlim(false);

0 commit comments

Comments
 (0)