feat(query+observability): Infinite queries + observability alignment - #68
Merged
Conversation
…e observability stack Brings the infinite-query implementation up to the query lifecycle and telemetry model introduced on main (#62, #63). InfiniteQuery was written against the older Query, so merging the branches cleanly required reconciling both sides. Cache: - Generalize QueryCache.GetOrCreate over a new internal ICacheEntry rather than Query<TArgs, TData>, so infinite queries share one cache with regular queries. ICacheEntry carries the Subscribed/Unsubscribed signals the cache uses to drive eviction; IQueryInspector stays the DevTools-facing surface, which observers also implement. - Keep main's typed-entry guard, now reported per cache-entry shape. - Infinite queries consequently take part in subscriber-driven eviction, which they previously did not — only an explicit Detach() removed them. InfiniteQuery: - Adopt the current instrumentation API: MetricName tagging, trigger tags, and attempt counts. Retries are accumulated per page, so a refetch-all of N pages reports 1 attempt when every page succeeds first try. - Tag the fetch span with the new `direction` tag (refetch_all / next / previous). - Route RefetchInterval through Invalidate() so polling respects StaleTime and defers to stale-while-revalidate when no subscribers are attached. - Swap in a fresh CancellationTokenSource on Cancel(), so cancelling no longer permanently poisons the query. - Only roll state back on the explicit Cancel() path; a fetch superseded by Switch() no longer stomps the newer fetch's state. - Guard Refetch/FetchNextPage/FetchPreviousPage against disposal, and read _lastSuccessAt under the lock. Also adds an optional Name to InfiniteQueryOptions for metric tagging, and documents the direction tag and refetch-all attempt semantics. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Correct the formatter command: csharpier 1.3.0 requires the `format` subcommand, bare `dotnet csharpier .` only prints help. CI's `check .` was already correct. - Document infinite queries across the architecture notes: the client factory method, the InfiniteQueryObserver/InfiniteQuery pair, and the Blazor components. - Document the ICacheEntry vs IQueryInspector split and the metric-name tagging rules, since both are easy to get wrong when adding a query type. - Ignore ./coverage, which the CI test command writes to. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…vability - Constrain TData to reference types across the infinite query surface; an unconstrained struct page made InitialData silently seed default(TData) - Implement DataComparer: unchanged pages keep their previous instance on refetch, and Success suppresses re-emissions via element-wise comparison - Resolve page params once per run (replaces ShouldFetch) so user delegates run once, and only record fetch success when a fetch actually ran - Return a snapshot from InfiniteQuery.CurrentData instead of a live view - Decrement dotnetquery.cache.entries on cache dispose so scoped SSR clients no longer leak into the process-wide gauge - Record duration histograms in seconds per OTel semantic conventions (logs stay in ms); add query.name to fetch spans and query.pages to infinite fetch spans - Correct InfiniteSuspense failure-slot docs; add bUnit tests for InfiniteSuspense/InfiniteTransition and tests for the new behaviors Full review and resolution notes in REVIEW-infinite-query-observability.md Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #68 +/- ##
==========================================
- Coverage 94.27% 92.69% -1.58%
==========================================
Files 22 29 +7
Lines 995 1602 +607
Branches 179 275 +96
==========================================
+ Hits 938 1485 +547
- Misses 21 37 +16
- Partials 36 80 +44 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Codecov's patch coverage for this PR was 84.72%, with InfiniteQuery.cs at 86.18%, InfiniteQueryObserver.cs at 76.14%, and QueryClient.cs's new CreateInfiniteQuery lines at 0% patch coverage (never exercised through the client, only through the observer/query directly). Add tests that target the specific miss/partial lines Codecov's API reported: - QueryClientTests: exercise CreateInfiniteQuery directly (fetch, cache-sharing) instead of only via InfiniteQueryObserver - InfiniteQueryObserverTests: cover the previously-untested observer surface (Key, CacheTime, Status, CurrentData, LastUpdatedAt, ObserverCount, MetricName, StateChanged, SetEnabled, FetchPreviousPage, Failure, Cancel, Invalidate, Detach, idempotent Dispose, and the candidate.Dispose() cache-hit branch in SetArgs) - InfiniteQueryTests: Status/LastUpdatedAt/ObserverCount property access, CurrentData null with no pages, a retrying IRetryHandler to cover the FetchPageAsync retry-counting branch, and a TCS-controlled fetch to hit the disposed-mid-fetch "didFetch = false" path without throwing - InfiniteSuspenseTests/InfiniteTransitionTests: add the OnParametersSet_SameQueryInstance_DoesNotResubscribe regression test already present for Suspense/Transition, covering the early-return branch when the Query parameter reference is unchanged 417 tests passing (was 391), verified stable across repeated runs. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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.
What's new
Core:
IInfiniteQuery<TArgs, TData, TPageParam>IQueryClient.CreateInfiniteQuery, following the same proxy-plus-cache-entry split as regular queries:InfiniteQueryObserveris the key-switching proxy handed to callers,InfiniteQueryis the cached entry, and both share the oneQueryCachewith regular queries (same key held by a different entry shape throws).Refetch(re-fetches every loaded page),FetchNextPage,FetchPreviousPage— with cursors derived byGetNextPageParam/GetPreviousPageParamreturningPageParam<T>.Some(...)or.None.Switch()-based cancellation of superseded fetches, stale-while-revalidate (invalidation with no subscribers defers the fetch to the first subscriber),Cancel()without poisoning the entry,MaxPagestrimming,InitialDataseeding, and retry viaIRetryHandler.TDatais constrained to reference types, matchingIQuery<TArgs, TData>(nullmeans "no data yet").DataComparerpreserves page instances across refetches when pages are structurally unchanged, andSuccesssuppresses duplicate emissions via element-wise page comparison.Blazor:
<InfiniteSuspense>/<InfiniteTransition>IInfiniteQuerycounterparts of<Suspense>/<Transition>. TheirContentslot receives the wholeInfiniteQueryState, so templates can renderPagesalongsideHasNextPage/IsFetchingNextPage/IsFetchingPreviousPage.Observability
query.fetchspan as regular queries, tagged withdirection(refetch_all/next/previous) andquery.pages; a refetch-all runs under a single span with retry-awareattemptsaccounting.query.namefor trace↔metric correlation.dotnetquery.query.duration,dotnetquery.mutation.duration) now record seconds per OTel semantic conventions (previously milliseconds). Dashboards and alert thresholds need rescaling. Log messages still report ms.dotnetquery.cache.entriesis now decremented on cache dispose, so scoped SSR clients no longer permanently inflate the process-wide gauge.