feat: roll Repository client to 4.2.22 and re-enable consumer L1 caching - #54
Conversation
Bumps XtremeIdiots.Portal.Repository.Api.Client.V1 from 4.2.21 to 4.2.22 and MX.Api.Client from 2.3.76 to 2.3.77. 4.2.22 consumes MX.Api 2.3.77's reflection-free SharedCacheConfiguration which scopes each cache policy to its matching typed sub-API, fixing the cross-sub-API expression fanning bug that crashed startup with 'The expression must invoke a method declared by ...IAdminActionsApi ...' on 4.2.21 (hotfix removed via PR #53). Re-applies '.WithCaching(c => c.UseLibraryDefaults())' on AddRepositoryApiClient that was removed by PR #53. Adds the new mandatory '.WithCachePartition("portal-server-events")' introduced in MX.Api 2.3.77 to isolate this consumer's cache keys. Cache safety verified: the processor's cached surface (library defaults cover GET-shaped reads on GetConfiguration/GetConfigurations, GetProtectedNames, GetAdminActions and Get/HeadPlayer variants) is disjoint from its write surface (Create*/Update*/Set*/Upsert*/Record*/Ensure*/Claim*/Complete*/Consume*). No read-after-write on the same entity in a single Service Bus message handler: HeadPlayerByGameType 404 -> CreatePlayer -> GetPlayerByGameType is a fresh GET (HEAD 404 is not a cached success payload). Configuration/ProtectedNames/AdminAction reads all happen before their respective writes in the same handler. App-owned IMemoryCache player-context is unaffected and still explicitly invalidated after writes. Updates Startup/RepositoryApiClientRegistrationTests to mirror the production registration exactly (both '.WithCachePartition' and '.WithCaching' present) so the smoke test resolves IRepositoryApiClient plus every typed sub-API .V1 the processor uses under the same DI composition path that failed on 4.2.21. Runs under the default CI test filter. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Dependency ReviewThe following issues were found:
License Issuessrc/XtremeIdiots.Portal.Server.Events.Processor.App/XtremeIdiots.Portal.Server.Events.Processor.App.csproj
OpenSSF Scorecard
Scanned Files
|
There was a problem hiding this comment.
Pull request overview
This pull request rolls the Processor Function App forward to Portal Repository client 4.2.22 / MX.Api.Client 2.3.77 and re-enables consumer-side L1 caching for Repository reads, restoring the optimization that was temporarily removed after the 4.2.21 startup crash.
Changes:
- Bump
XtremeIdiots.Portal.Repository.Api.Client.V1to4.2.22andMX.Api.Clientto2.3.77. - Re-enable Repository client caching in the Processor App DI registration, adding the required explicit cache partition.
- Update the startup DI smoke test to mirror the production Repository client registration including caching and partitioning.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/XtremeIdiots.Portal.Server.Events.Processor.App/XtremeIdiots.Portal.Server.Events.Processor.App.csproj | Dependency bumps for Repository client and MX.Api.Client to the versions that support safe caching composition. |
| src/XtremeIdiots.Portal.Server.Events.Processor.App/Program.cs | Re-introduces .WithCaching(UseLibraryDefaults) and adds mandatory .WithCachePartition("portal-server-events") to the Repository client registration. |
| src/XtremeIdiots.Portal.Server.Events.Processor.App.Tests/Startup/RepositoryApiClientRegistrationTests.cs | Updates the DI registration smoke test to include cache partition + caching so the full production composition path is exercised. |
⛔ Superseded — A newer run has replaced this result.🏗️ Terraform Plan
✅ Validate — Passed ✅ Plan
📋 Resource Details
|
PR #54 review feedback: the boot smoke test previously duplicated the production Repository client option chain inline, so Program.cs and the test could drift (especially the cache partition literal and the option order). Introduce internal AddPortalServerEventsRepositoryApiClient extension in the App project as the single source of truth for BaseUrl / EntraId audience / cache partition / L1 caching policy. Program.cs and RepositoryApiClientRegistrationTests both call it, so the test is now a real regression guard: any future change to the production chain is exercised by the DI resolution asserts automatically. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Addressed in c067139. Extracted the Repository client registration into a new internal Validation refreshed:
|
|
🏗️ Terraform Plan
✅ Validate — Passed ✅ Plan
📋 Resource Details
|
…ng (#55) Bump three client packages to their latest crash-safe releases and turn on the library-default in-memory read-only caching now that MX.Api.Client 2.3.77 scopes each captured policy via SharedCacheConfiguration: - MX.GeoLocation.Api.Client.V1 1.2.96 -> 1.2.98 (caching on) - MX.InvisionCommunity.Api.Client 1.0.61 -> 1.0.63 (caching on) - XtremeIdiots.Portal.Integrations.Servers.Api.Client.V1 4.1.9 -> 4.1.14 (currency + crash-safety; no defaults to enable) - XtremeIdiots.Portal.Integrations.Servers.Abstractions.V1 4.1.9 -> 4.1.14 (transitive dependency lockstep) Registration is extracted into GeoLocationApiClientRegistration, InvisionApiClientRegistration, and ServersApiClientRegistration helpers so Program.cs and the boot-composition tests share a single source of truth for each client's option chain — matching the RepositoryApiClient pattern re-enabled in #54. Add xUnit startup-composition tests that build the real ServiceProvider with caching enabled and resolve every typed sub-API the Processor App touches at runtime (Geo v1 + v1.1 GeoLookup, Invision Core/Downloads/ Forums, Servers Cod2/Cod4/Cod5/CoD4x Rcon + Maps). A dedicated Geo test also resolves IMxCache to guard the L1 auto-wiring. Any future regression of the 2.3.76-era 'The expression must invoke a method declared by ...' ArgumentException now fails these tests instead of the Functions host. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>



Summary
Roll the portal-server-events processor onto Portal Repository client 4.2.22 (MX.Api.Client 2.3.77) and re-enable the consumer-side L1 caching that hotfix PR #53 removed, now that the root-cause bug is fixed upstream. Adds a startup-composition regression test that would have caught the original crash.
Closes N/A (agent-driven rollout task; no tracking issue).
Type of change
Details
XtremeIdiots.Portal.Repository.Api.Client.V1:4.2.21→4.2.22MX.Api.Client:2.3.76→2.3.77Program.cs: re-applies.WithCaching(c => c.UseLibraryDefaults())onAddRepositoryApiClient(...)and adds the new mandatory.WithCachePartition("portal-server-events")(2.3.77 requires an explicit non-secret partition when caching is enabled).RepositoryApiClientRegistrationTests: the DI smoke test now mirrors production exactly (.WithBaseUrl→.WithEntraIdAuthentication→.WithCachePartition→.WithCaching(UseLibraryDefaults)). It resolvesIRepositoryApiClientand the 12 typed.V1sub-APIs the processor uses — the exact composition path that threw on 4.2.21. Runs under the default CI filter (Startupnamespace, noIntegrationTestsmarker).Cached surface — safety rationale
Library defaults in 4.2.22 apply cache policies only to GET-shaped read operations on stable reference data. The processor's Repository call surface is:
GetConfiguration,GetConfigurations,GetProtectedNames,GetAdminActions,GetPlayer,GetPlayerByGameType,HeadPlayerByGameType,GetMapCreatePlayer,UpdatePlayerIpAddress,CreateAdminAction,UpdateAdminAction,CreateChatMessage,CreateGameServerEvent,CreateGameServerStats,CreateRecentPlayers,EnsureAutomatedAction,RecordPlayerSession,SetGameServerLiveStatus,SetVpnDetectedTag,ClaimForumTopicPublication,CompleteForumTopicPublication,ConsumeConnectedPlayerActivationCode,UpsertMapVoteVerified no read-after-write on the same entity in a single Service Bus message handler that could return stale cached data:
PlayerConnectedProcessor/BanAppliedProcessor/BanFileChangedProcessor:HeadPlayerByGameType(404) →CreatePlayer→GetPlayerByGameType. HEAD returning 404 is a not-found response, not a cacheable success payload, so the follow-up GET is a fresh call.ChatModerationPipeline,ProtectedNameService, VPN/command providers: configuration / protected-names / admin-action reads all occur before their respective writes in the same handler.IMemoryCacheplayer-context is unaffected and still explicitly invalidated after writes (e.g.PlayerIpResolvedProcessor.InvalidatePlayerCache)..WithCachePartition("portal-server-events")is a stable, non-secret literal that isolates this consumer's cache keys inside its isolated Functions worker process. No collision or leakage risk.Why the 4.2.21 crash is fixed
Repository 4.2.22 consumes MX.Api.Client 2.3.77's reflection-free
SharedCacheConfiguration, which scopes each cache policy to its matching typed sub-API instead of fanning one delegate expression across all ~34 typed sub-API registrations. The exact.WithCaching(c => c.UseLibraryDefaults())expression from PR #52 is now safe to compose.Validation evidence
Local Windows note: the initial
dotnet buildfailed with an MSB3030 file-copy error under Microsoft.NET.Sdk.Functions WorkerExtensions caused by the local worktree path exceeding Windows MAX_PATH (269 chars > 260). Reproducible on unmodifiedorigin/main, so it is not a regression from this change. Worked around locally withsubst P: <worktree>; CI does not hit this limit.The
code-reviewsub-agent was run against the full diff — no High or Medium findings.Risk and rollout
dev→prdpipeline in.github/workflows/. Recommend soaking on dev to confirm no cache-hit anomalies before promoting..WithCaching. Same shape as PR fix: remove consumer-side Repository client WithCaching to prevent startup crash #53. Repository server-side caching remains active regardless.Consumer impact
None. Abstractions package, Service Bus queue contracts and DTOs are unchanged. No published contract affected.
Agent attestation
dotnet build,dotnet test(default filter), anddotnet format --verify-no-changes— all clean.IRepositoryApiClientplus every typed.V1sub-API the processor calls.code-reviewsub-agent; no High/Medium findings.version.json,.github/workflows/, orDirectory.*.props..WithCachePartition("portal-server-events")is a stable non-secret literal.