Fix ODSP file-version base selection and harden the version manager#27695
Fix ODSP file-version base selection and harden the version manager#27695lindsnguyen wants to merge 2 commits into
Conversation
|
Hi! Thank you for opening this PR. Want me to review it? Based on the diff (466 lines, 5 files), I've queued these reviewers:
How this works
|
Fleet Review — CleanNo issues found across the reviewer fleet for this run. |
24c34c9 to
7003041
Compare
7003041 to
4bd31d9
Compare
4bd31d9 to
0eece2d
Compare
|
🔗 No broken links found! ✅ Your attention to detail is admirable. linkcheck output |
Deep ReviewReviewed commit Readiness: 8/10 — ALMOST READY The load-bearing base-selection concern is fully settled: the tip-drop was reverted, Path to Ready
Context for Reviewers
For human reviewer
Review history (2 prior reviews)
|
| public async listVersions(): Promise<ResolvedVersion[]> { | ||
| const versions = await this.getVersions(); | ||
| // Resolution order does not matter here, so resolve concurrently; the newest-first array order is | ||
| // Resolution order does not matter, so resolve concurrently; the newest-first array order is |
There was a problem hiding this comment.
Deep Review: The rewrite drops the concurrent-fetch coalescing #27689 added. findBaseForSeq/listVersions now call this.fetcher.listFileVersions() unconditionally, so N concurrent callers issue N overlapping /versions enumerations and interleave mutations of the shared lastKnownVersionIds/seqCache. This is not a wrong-base bug — each caller acts on the concrete list it fetched, and failed seq resolutions auto-evict via PromiseCache — but it is extra round-trips plus an untested interleaving edge on the lastKnownVersionIds prune/repopulate path.
Related resolved thread: r3590146089 — anthony-murphy asked to "store the fetch promise, ensure only a single fetch promise is ever active"; the seq cache moved to PromiseCache, but the list fetch itself is no longer coalesced.
Wrap listFileVersions() in a single-flight promise (e.g. this.pendingFetch) cleared on settle so simultaneous callers join one in-flight enumeration before lastKnownVersionIds is updated — preserving per-query freshness for sequential calls while restoring coalescing for concurrent bursts. If concurrent-query volume is provably low, add an explicit "concurrent callers not expected" note in DEV.md instead. Are concurrent findBaseForSeq/listVersions calls on a single instance expected in practice?
| loaded *content* — not the list. Page History's `#getOdspVersions` likewise dedups and drops the tip | ||
| (`slice(1)`), the same two rules applied here. | ||
|
|
||
| - **Re-resolving a sequence number across calls?** Each version's number is cached, so a later query |
There was a problem hiding this comment.
Deep Review: M-CACHE-03 is repurposed here to document the new "version leaves the list" behavior and M-CACHE-02 is removed, but DEV.md's own convention (lines 7-13) states scenario IDs are append-only and are never renumbered — reusing or removing IDs breaks traceability between the doc and the tests that reference these IDs.
Keep the old IDs (M-CACHE-02, M-CACHE-03) reserved/deprecated rather than renumbering, and assign new, unused IDs for the added cache behaviors so existing references stay valid — or drop the append-only clause if it is no longer intended.
| * list is re-enumerated on each query rather than cached, since new versions are cut over time. The | ||
| * resolution strategy (eager, newest-to-oldest, stopping at the first usable base) is hidden behind | ||
| * {@link findBaseForSeq} and can change without affecting callers. | ||
| */ |
There was a problem hiding this comment.
Deep Review: In #27689 anthony-murphy asked to export a factory rather than the class — "you should export a function which returns the interface and internally constructs the class. this keeps the implementation off our exposed api surface." The follow-up added createOdspVersionManager returning IOdspVersionManager, but export class OdspVersionManager remains — because tests construct it directly with a mock fetcher (new OdspVersionManager(fetcher) at test/odspVersionManager.spec.ts:59, 214, 249), so a naive "just drop export" would break the suite. The class never reaches the folder barrel (odspVersionManager/index.ts) nor the package public index, so this is narrow, not a public-API leak.
Add a test-injection factory overload accepting IOdspFileVersionFetcher so tests inject the mock without importing the class, then drop export; or add a one-line comment documenting that the class export is retained deliberately for test injection.
|
Deep Review: The new Swap the plain |
Bundle size comparisonBase commit: Notable changesNo bundles changed by ≥ 500 bytes parsed. Per-bundle deltas
|
Description
Follow-up to #27689 addressing review feedback on the internal ODSP file-version manager in
@fluidframework/odsp-driver. As before, everything here is internal to the package (not exported fromindex.ts, not wired into a call site), so there is no consumer-facing change.Base selection — tip exclusion retained. An earlier iteration of this PR removed the tip-drop
(
versions.slice(1)) and scanned all versions. Per review feedback (anthony-murphy), that was reverted:the tip (newest version, index 0) is the one version whose sequence number is not yet static, so it cannot
serve as a stable base.
findBaseForSeqscans the sealed versions (index 1+) newest-first and returns theclosest one with
seq <= target; a file whose only version is the tip returnsnoBaseVersion, and aconsumer loads the live file instead (
M-TIP-01/M-TIP-02).Robustness — failed fetches.
refresh()is removed. The version list is re-enumerated on every query(it changes as new versions are cut), pruning cached sequence numbers for versions that have left the list
(
lastKnownVersionIds). Resolved sequence numbers are cached viaPromiseCache, which evicts rejectedentries by default, so a transient 5xx/429 no longer memoizes a permanent failure and a later call retries
(
M-ERR-02,M-CACHE-04). This mirrors how Page History pulls the version list fresh per navigation.Robustness — content-type.
resolveSequenceNumbernow handlesapplication/jsonandapplication/ms-fluidexplicitly and throws a clear error on anything else (e.g. an HTML error page)rather than mis-parsing (
F-RESOLVE-04).Test fidelity. Manager fixtures exercise the real
/versionsshape (a distinct tip plus sealedversions). Added coverage for percent-encoding of version labels in the snapshot URL (
F-RESOLVE-05).DEV.mdis updated to match.Work item: https://dev.azure.com/fluidframework/internal/_workitems/edit/77292