Skip to content

Commit bfe1bde

Browse files
authored
Batching for zarr get requests (#412)
* Batching for zarr get requests * Lint
1 parent d0cf33f commit bfe1bde

File tree

1 file changed

+62
-0
lines changed

1 file changed

+62
-0
lines changed

src/vitessce/widget.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,11 +235,56 @@ def get_uid_str(uid):
235235
let pluginFileTypes = [];
236236
let pluginJointFileTypes = [];
237237
238+
let pending = [];
239+
let batchId = 0;
240+
241+
async function processBatch(prevPendingArr) {
242+
const [dataArr, buffersArr] = await view.experimental.invoke("_zarr_get_multi", prevPendingArr.map(d => d.params), {
243+
signal: AbortSignal.timeout(invokeTimeout),
244+
});
245+
prevPendingArr.forEach((prevPendingItem, i) => {
246+
const data = dataArr[i];
247+
const bufferData = buffersArr[i];
248+
const { params, resolve, reject } = prevPendingItem;
249+
const [storeUrl, key] = params;
250+
251+
if (!data.success) {
252+
resolve(undefined);
253+
}
254+
255+
if (key.includes("spatialdata_attrs") && key.endsWith("0") && !ArrayBuffer.isView(bufferData.buffer)) {
256+
// For some reason, the Zarrita.js UnicodeStringArray does not seem to work with
257+
// ArrayBuffers (throws a TypeError), so here we convert to Uint8Array if needed.
258+
// This error is occurring specifically for the arr.getChunk call within the AnnDataSource._loadString function.
259+
// TODO: figure out a more long-term solution.
260+
resolve(new Uint8Array(bufferData.buffer));
261+
}
262+
263+
resolve(bufferData.buffer);
264+
});
265+
}
266+
267+
function run() {
268+
processBatch(pending);
269+
pending = [];
270+
batchId = 0;
271+
}
272+
273+
function enqueue(params) {
274+
batchId = batchId || requestAnimationFrame(() => run());
275+
let { promise, resolve, reject } = Promise.withResolvers();
276+
pending.push({ params, resolve, reject });
277+
return promise;
278+
}
279+
280+
238281
const stores = Object.fromEntries(
239282
storeUrls.map(storeUrl => ([
240283
storeUrl,
241284
{
242285
async get(key) {
286+
return enqueue([storeUrl, key]);
287+
/*
243288
const [data, buffers] = await view.experimental.invoke("_zarr_get", [storeUrl, key], {
244289
signal: AbortSignal.timeout(invokeTimeout),
245290
});
@@ -254,6 +299,7 @@ def get_uid_str(uid):
254299
}
255300
256301
return buffers[0].buffer;
302+
*/
257303
},
258304
}
259305
])),
@@ -577,6 +623,22 @@ def _zarr_get(self, params, buffers):
577623
buffers = []
578624
return {"success": len(buffers) == 1}, buffers
579625

626+
@anywidget.experimental.command
627+
def _zarr_get_multi(self, params_arr, buffers):
628+
# This variant of _zarr_get supports batching.
629+
result_dicts = []
630+
result_buffers = []
631+
for params in params_arr:
632+
[store_url, key] = params
633+
store = self._stores[store_url]
634+
try:
635+
result_buffers.append(store[key.lstrip("/")])
636+
result_dicts.append({"success": True})
637+
except KeyError:
638+
result_buffers.append(b'')
639+
result_dicts.append({"success": False})
640+
return result_dicts, result_buffers
641+
580642
@anywidget.experimental.command
581643
def _plugin_command(self, params, buffers):
582644
[command_name, command_params] = params

0 commit comments

Comments
 (0)