Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions Lite.Tests/Lite.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,24 @@
<PackageReference Include="xunit.v3" />
</ItemGroup>

<ItemGroup>
<!-- #3134: LocalDataService.OpenWriteConnectionAsync bounds its write-lock wait so a WPF dispatcher
cannot freeze behind an archival. There is no dispatcher in this host, and the lock on
DuckDbInitializer is process-wide - so a store test waits behind every other class in the suite,
including the test methods that take the exclusive lock deliberately and hold it for seconds
while they assert on timing. A budget sized for a dispatcher, expiring inside a hold taken by a
neighbour, is a failure with no user behind it; so this host states a budget orders of magnitude
larger and the acquisition waits the hold out. Short of forever on purpose - a wedged lock still
fails, with the same message, rather than hanging the suite. The shipped app declares nothing
here and keeps LocalDataService.DefaultWriteLockBudget.

This comment carries no apostrophe, deliberately. CrossAppGuardCiGateTests reads this file with
a regex that treats one as an attribute delimiter, so an unbalanced pair swallows the quoted
attribute values after it - including the linked cross-app Compile below, whose absence the
guard reports as a moved blind spot rather than as a comment. -->
<RuntimeHostConfigurationOption Include="PerformanceMonitorLite.WriteLockBudgetSeconds" Value="120" />
</ItemGroup>

<ItemGroup>
<!-- The shared C# source walk (#2913), compiled into this assembly rather than copied into it. Lite.Tests
cannot ProjectReference Darling.Tests - both are xunit executables and CI would discover the Darling
Expand Down
23 changes: 15 additions & 8 deletions Lite.Tests/SharedDuckDbFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,13 @@ namespace PerformanceMonitorLite.Tests;
/// the write sites — archival, compaction, CHECKPOINT, the mute and alert-history stores —
/// rather than a flat tax on every database call.</para>
///
/// <para>That distinction decides what a fix could even look like: a collection fixture
/// would serialize the READERS as well, so it does not address writer-driven contention and
/// would cost more than it saves. Nobody has measured how much of the suite's ~190-220s is
/// this, and the honest answer is that it may be very little.</para>
/// <para>That distinction decides what a fix could even look like, and it rules out the
/// obvious one. A shared collection over the classes that use this fixture serializes the
/// READERS as well, which is where the wall clock goes, and it still would not bound the
/// contention: the test methods that hold this lock EXCLUSIVELY for seconds at a time — the
/// ones asserting on how a writer, a status-bar read or a cancellable read behaves while a
/// writer holds it — build their own <c>DuckDbInitializer</c> and are not classes of this
/// fixture at all, so a collection they are not in cannot stop them running alongside it.</para>
///
/// <para>That lock is correct and must not be narrowed to fix this: production creates
/// several <c>DuckDbInitializer</c> instances over the same <c>App.DatabasePath</c>
Expand All @@ -50,10 +53,14 @@ namespace PerformanceMonitorLite.Tests;
/// suite for a real data race.</para>
///
/// <para>The practical consequence is worth knowing when a test here fails oddly: a
/// scheduling-pressure window can starve the 5-second write-lock acquisition in
/// <c>LocalDataService.GetDatabaseStateDeviationsAsync</c>, whose maintenance block is
/// best-effort and simply SKIPS on timeout. That is #2374 — a test that assumed the
/// maintenance had run, rather than waiting for it, failed a nightly.</para>
/// scheduling-pressure window can starve <c>LocalDataService.OpenWriteConnectionAsync</c>'s
/// acquisition past a budget sized for a UI thread, which is #2374 —
/// <c>GetDatabaseStateDeviationsAsync</c>'s maintenance block is best-effort and simply SKIPS
/// on timeout, and a test that assumed the maintenance had run rather than waiting for it
/// failed a nightly. This host therefore states its own budget
/// (<c>LocalDataService.WriteLockBudgetConfigKey</c> in <c>Lite.Tests.csproj</c>): the wait a
/// dispatcher cannot afford is one there is nothing here to protect, so an acquisition queued
/// behind a neighbour's deliberate hold waits it out instead of expiring inside it.</para>
/// </summary>
public sealed class SharedDuckDbFixture : IAsyncLifetime
{
Expand Down
Loading
Loading