diff --git a/.changeset/grumpy-penguins-lick.md b/.changeset/grumpy-penguins-lick.md new file mode 100644 index 00000000..084a659e --- /dev/null +++ b/.changeset/grumpy-penguins-lick.md @@ -0,0 +1,6 @@ +--- +"@zarrita/storage": patch +"@zarrita/core": patch +--- + +Use a counter to prioritize v2/v3 when opening. diff --git a/packages/core/src/open.ts b/packages/core/src/open.ts index 3caa1fc4..4e51dab9 100644 --- a/packages/core/src/open.ts +++ b/packages/core/src/open.ts @@ -13,6 +13,25 @@ import { v2_to_v3_group_metadata, } from "./util.js"; +function create_version_counter() { + let version_counts = new WeakMap(); + function get_counts(store: Readable) { + let counts = version_counts.get(store) ?? { v2: 0, v3: 0 }; + version_counts.set(store, counts); + return counts; + } + return { + increment(store: Readable, version: "v2" | "v3") { + get_counts(store)[version] += 1; + }, + version_max(store: Readable): "v2" | "v3" { + let counts = get_counts(store); + return counts.v3 > counts.v2 ? "v3" : "v2"; + }, + }; +} +let VERSION_COUNTER = create_version_counter(); + async function load_attrs( location: Location, ): Promise { @@ -43,6 +62,7 @@ async function open_v2( let loc = "store" in location ? location : new Location(location); let attrs = {}; if (options.attrs ?? true) attrs = await load_attrs(loc); + VERSION_COUNTER.increment(loc.store, "v2"); if (options.kind === "array") return open_array_v2(loc, attrs); if (options.kind === "group") return open_group_v2(loc, attrs); return open_array_v2(loc, attrs).catch((err) => { @@ -130,6 +150,7 @@ async function open_v3( ): Promise | Group> { let loc = "store" in location ? location : new Location(location); let node = await _open_v3(loc); + VERSION_COUNTER.increment(loc.store, "v3"); if (options.kind === undefined) return node; if (options.kind === "array" && node instanceof Array) return node; if (options.kind === "group" && node instanceof Group) return node; @@ -159,9 +180,16 @@ export async function open( location: Location | Store, options: { kind?: "array" | "group" } = {}, ): Promise | Group> { - return open_v3(location, options as any).catch((err) => { + const store = "store" in location ? location.store : location; + const version_max = VERSION_COUNTER.version_max(store); + // Use the open function for the version with the most successful opens. + // Note that here we use the dot syntax to access the open functions + // because this enables us to use vi.spyOn during testing. + let open_primary = version_max === "v2" ? open.v2 : open.v3; + let open_secondary = version_max === "v2" ? open.v3 : open.v2; + return open_primary(location, options as any).catch((err) => { if (err instanceof NodeNotFoundError) { - return open_v2(location, options as any); + return open_secondary(location, options as any); } throw err; }); diff --git a/packages/storage/__tests__/fetch.test.ts b/packages/storage/__tests__/fetch.test.ts index acfafe6f..657640b3 100644 --- a/packages/storage/__tests__/fetch.test.ts +++ b/packages/storage/__tests__/fetch.test.ts @@ -1,10 +1,13 @@ import { afterEach, describe, expect, it, vi } from "vitest"; +import { open, root } from "@zarrita/core"; + import FetchStore from "../src/fetch.js"; // `vitest --api` exposes the port 51204 // ref: https://vitest.dev/config/#api let href = "http://localhost:51204/fixtures/v3/data.zarr"; +let href_v2 = "http://localhost:51204/fixtures/v2/data.zarr"; describe("FetchStore", () => { afterEach(() => { @@ -138,4 +141,20 @@ describe("FetchStore", () => { '"tributes\\": {}, \\"zarr_format\\": 3, \\"node_type\\": \\"gro"', ); }); + + it("prioritizes v2 over v3 based on count of successful opens by version", async () => { + let storeRoot = root(new FetchStore(href_v2)); + + const v2_spy = vi.spyOn(open, "v2"); + const v3_spy = vi.spyOn(open, "v3"); + + let arr1 = await open(storeRoot.resolve("1d.chunked.i2"), { + kind: "array", + }); + let arr2 = await open(storeRoot.resolve("1d.chunked.ragged.i2"), { + kind: "array", + }); + expect(v2_spy).toHaveBeenCalledTimes(2); + expect(v3_spy).toHaveBeenCalledTimes(0); + }); }); diff --git a/packages/storage/package.json b/packages/storage/package.json index 1fe11cfa..47b0ee6b 100644 --- a/packages/storage/package.json +++ b/packages/storage/package.json @@ -21,6 +21,9 @@ "reference-spec-reader": "^0.2.0", "unzipit": "^1.3.6" }, + "devDependencies": { + "@zarrita/core": "workspace:^" + }, "publishConfig": { "main": "dist/src/index.js", "exports": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2f87e016..a9de4d25 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -115,6 +115,10 @@ importers: unzipit: specifier: ^1.3.6 version: 1.3.6 + devDependencies: + '@zarrita/core': + specifier: workspace:^ + version: link:../core packages/typedarray: {}