Repository navigation
test: freeze the metadata budget clock in the global-budget tests - #1258
Merged
Merged
Conversation
Both tests drain ClientMetadataDocuments' global budget, a fixed minute aligned to the wall clock, on a counter reading TimeProvider.System. A run that crossed a minute boundary started the budget over, so the unapproved and unknown URLs were fetched too: run 38181977796 failed at 23:01:00.06 with 63 expected and 65 actual. The counter now runs on a FakeTimeProvider that stands still, as OAuthFailClosedTests.FrozenHost already does; expiry still reads the real clock.
This was referenced Oct 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two tests turned PRs red at random. One is fixed here. The other needs no code change, and the evidence for that is below. No issue was filed for either.
The OAuth metadata request count
ApprovedApp_IsRefreshed_PastADrainedGlobalBudgetfailed once in run 38181977796 (PR #1247) withExpected: 63,Actual: 65.The cause is the global metadata budget, 60 fetches per minute (
ClientMetadataDocuments.cs:43).InProcessFixedWindowCounterbuckets windows on the wall clock (InProcessFixedWindowCounter.cs:115), and the test's own resolver gave its counterTimeProvider.System. The failing run ended at 23:01:00.06 after 2 s. A minute boundary fell after the drain, so the budget started over. The unapproved and unknown URLs were then fetched too: 63 + 2 = 65. No background fetch was involved. Every setup request is awaited, and the host never refreshes documents on its own.ManyUrls_ShareOneGlobalBudgetbuilds the same resolver and has the same exposure.The fix: both tests build the resolver through one helper,
BudgetResolver. The helper gives the counter the suite'sFakeTimeProvider, a clock that does not move.OAuthFailClosedTests.FrozenHostalready does this for the OAuth failure budget. The expiry check still reads the real clock, so the two stored copies the test expires are really expired. No production code changes.Proof:
FullyQualifiedName~OAuthFailClosedTests: 81 of 81 passed, before and after merging main.The Playwright sign-in step
The one observed sign-in failure is run 38170366723, attempt 1, shard 1/3, at
a11y-live-regions.spec.ts:370. A census of 1,000 e2e-smoke runs since 2026-09-08 found this failure once. The other red runs were branch defects or Docker Hub 429 and 504 errors.This failure coincided with a database and service stall of several minutes, plus a Playwright teardown timeout. In the logs:
The logs point to a transient stall in the infrastructure or its resources. They do not show what caused it. This PR adds no sign-in code and no retries that would hide a failure.
Not fixed here
These problems already existed and are rated P3. The full list is in the review notes.
MetadataHostAsync:OAuthFailClosedTests.ClientMetadataDocuments.cs:199(FailingUrl_IsFetchedOnlyWithinItsBudget) and:274(RemovedDocument_IsNotRevived_ByDrainingTheBudget). A five-minute boundary during their loops allows one more fetch.DistributedRateLimiterWiringTests.cs:66and:83(assertions at:149,:202,:223) andOAuthClientRegistrationTests.cs:280(assertion at:238).OAuthFailClosedTests.cs:265,:524and:561,RateLimitingTests.cs:74,AuthRateLimitLoggingTests.cs:105,ClientErrorReportTests.cs:153,ForwardedHeaderTrustKestrelTests.cs:131,MultiInstanceRateLimitTests.cs:121,LoginCounterKeyProbeTests.cs:139,SharedState/SharedStateNamespaceWiringTests.cs:25, andSharedState/RedisFixedWindowCounterContractTests.cs:37and:58.DailyEntryLockSweepraisedDbUpdateConcurrencyExceptionfrom inside EF's retrying execution strategy at 20:06:04.486.LockDueDailyEntriesHandler.cs:32saves versioned entries with retries enabled. A replay after a commit whose acknowledgement was lost would fail this way, which is the Add transient-DB resilience (EnableRetryOnFailure) on the request path #269 pattern. That replay is the most likely explanation, but it is not proven, because a real competing writer gives the same stack. The impact: the pass reports wrong counts, a misleadingTenant.WriteRefusedByDatabasealarm fires, andSimulationDataSeeder.cs:2051treats a false zero as fully drained, so it can stop draining early.Tests run locally
FullyQualifiedName~ApprovedApp_IsRefreshed_PastADrainedGlobalBudget|FullyQualifiedName~ManyUrls_ShareOneGlobalBudget, 20 runsFullyQualifiedName~OAuthFailClosedTests, before and after mergingorigin/main