Skip to content

Warn about Zarr layouts that make viewing awkward - #430

Open
krokicki wants to merge 1 commit into
mainfrom
dataset-warnings
Open

Warn about Zarr layouts that make viewing awkward#430
krokicki wants to merge 1 commit into
mainfrom
dataset-warnings

Conversation

@krokicki

@krokicki krokicki commented Aug 15, 2026

Copy link
Copy Markdown
Member

Adds three advisory warnings to the Zarr preview panel, flagging layout choices that make a dataset expensive to view. They run client-side on metadata fileglancer already parses, so there are no extra requests on the common path.

Motivation is the production incident that occurred on Friday, August 14th. The IO thread pools were starved by a handful of viewing sessions on a 91 GB image published with a single resolution level. Nothing here changes server behaviour — the aim is to tell data creators before they publish.

The checks

Only one resolution level. A multiscales group declaring a single dataset provides no downsampled data, so every zoom reads full resolution. Keyed on the dataset count rather than the number of shapes, because a plain zarr array also has exactly one shape while claiming nothing about being multiscale — an early version keyed on shapes and fired on healthy pyramid levels that users had browsed into. Gated at 1 GB.

Chunks too large. Chunks past the browser's cache entry limit are re-fetched on every access. Sizes come from shape and dtype, so they are logical: exact for a raw array, an upper bound for a compressed one. Limit is 10 MB with no compressor and 32 MB with one, since the compression ratio is a property of the pixels and is not in the metadata. Unknown or unfetched codec info counts as compressed, so uncertainty costs a missed warning rather than a false one.

Codec classification reuses capability-manifest's classifyCodec, plus a walk over the pipeline it does not do itself: a sharded v3 array lists only the structural sharding_indexed codec at the top level and carries the real compressor in its configuration, so a flat scan calls every sharded array uncompressed.

Axis order. OME-Zarr requires t, c, z, y, x, and many tools take the last two axes to be the image plane. A dataset ordered x, y, z, c renders as a cross-section in those tools while Neuroglancer, which reads axis names, looks fine — a confusing failure worth naming. Datasets with custom axis names are left alone.

Screenshot

Screenshot 2026-08-15 at 9 31 41 AM

Notes for review

  • Nothing is withheld. All three are advisory; viewer links and thumbnails are untouched. Neuroglancer fetches only the chunks intersecting the current view, so it opens these datasets fine.
  • No thumbnail check. ome-zarr.js already refuses when the lowest level exceeds its own limit. Modelling its behaviour a second time proved unreliable: it renders from the level nearest the requested size rather than the smallest, and reads a single plane rather than a whole level.
  • zarrQueries.ts now populates codec info for plain arrays too — the v3 branch reads it from the zarr.json it already parsed, the v2 branch fetches .zarray as the group path does.
  • __tests__/mocks/omezarrHelper.ts is a whole-module mock, so it needs every new export listed or the component tests fail.

Testing

21 unit tests covering both compression paths, nested sharding codecs, unknown codecs, each axis-order case, and the plain-array cases that earlier versions got wrong. Full frontend suite passes (321). Verified end to end against a pair of synthetic fixtures that reproduce the original dataset's defects and the corrected version — the broken one returns all three warnings, the fixed one returns none.

@StephanPreibisch @JaneliaSciComp/fileglancer @dchen116

Check the dataset layout client-side from metadata that is already parsed, and
surface the result through the MetadataHint notifications used for broken
metadata. All three checks are advisory - nothing is withheld, and viewer links
are untouched.

Resolution levels: a multiscales group declaring a single dataset provides no
downsampled data, so every zoom reads full-resolution chunks. This was the root
cause of the production incident behind these checks. Keyed on the dataset count
rather than the number of shapes, because a plain zarr array also has exactly one
shape while claiming nothing about being multiscale.

Chunk size: chunks past the browser's cache entry limit are re-fetched on every
access. Sizes are computed from shape and dtype, so they are logical: exact for a
raw array, an upper bound for a compressed one. The limit is 10 MB when no
compressor is present and 32 MB when one is, since the compression ratio is a
property of the pixels and cannot be derived from metadata. Unknown or unfetched
codec info counts as compressed, so uncertainty costs a missed warning rather
than a false one.

Codec classification reuses capability-manifest's classifyCodec, plus a walk over
the pipeline it does not do itself: a sharded v3 array lists only the structural
sharding_indexed codec at the top level and carries the real compressor in its
configuration. Plain arrays now carry codec info too - the v3 branch reads it
from the zarr.json it already parsed, the v2 branch fetches .zarray as the group
path does.

Axis order: OME-Zarr requires T, C, Z, Y, X order, and many tools take the last
two axes to be the image plane. A dataset ordered C, X, Y, Z therefore renders as
a Y-Z cross-section in those tools while Neuroglancer, which reads the axis
names, looks fine - a confusing failure worth naming explicitly. Datasets using
custom axis names are left alone, since their intent cannot be judged.

No thumbnail check: ome-zarr.js already refuses when the lowest level exceeds its
own limit, and modelling its behaviour a second time proved unreliable - it
renders from the level nearest the requested size rather than the smallest, and
reads a single plane rather than the whole level.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@krokicki
krokicki requested a review from mkitti August 15, 2026 13:46
@mkitti

mkitti commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

At some point, we may want to provide virtualization for Zarr datasets like this as well.

@mkitti mkitti left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure how well these warnings are grounded in the spec and perhaps more specifically in Neuroglancer's implementation. I'm not sure where the 10 MiB cache limit comes from. Could we check the cache limits and add citations when possible to clearly state whether these are browser or middleware issues.

I suspect that the dimension order could be relaxed here especially in light of RFC-3. The main relevant ordering is probably the clustering of the spatial dimensions at the end but the order of the spatial dimensions is not really a problem for Neuroglancer.

Another warning I would add here is the dimensionality of the chunks versus the dimensionality of the array. If we have 2D chunks and a 3D spatial array, we will have issues for viewing in some of the orthogonal views. If the array is nD, then we want the dimension of the chunks to have non-singleton dimensions for at leastmin(n, 3) dimensions.

Also chunks that are too small might be an issue as well. In the extreme a chunk that is a single voxel is highly problematic. I would suggest a minimum element count of 256 elements if there are more elements than this in the array.

return {
kind: 'warning',
title: 'Axes are not in the order OME-Zarr specifies',
description: `The axes are ordered ${variant.axisOrder}, but the spec requires T, C, Z, Y, X order - here that would be ${variant.expectedOrder}. Many tools take the last two axes to be the image plane, so they will show a cross-section rather than the expected view. Rewriting the dataset with the axes in spec order avoids this.`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RFC 3 will likely make this advice obsolete with respect to the spec statement:

https://ngff.openmicroscopy.org/rfc/3/

@krokicki krokicki Aug 16, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know that, but in the meantime it is creating issues, specifically with ome-zarr.js (and I would bet there are other cases).

For example: https://github.com/BioNGFF/ome-zarr.js/blob/main/src/image.ts#L336-L337

return {
kind: 'warning',
title: 'Chunks may be too large for efficient viewing',
description: `This dataset uses ${variant.size} chunks ${variant.compressed ? '(before compression)' : '(without compression)'}. Chunk files larger than the browser's cache limit are re-downloaded on every access, which makes viewing slow. A final chunk size of 1-10 MB works best.`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may need to qualify this for shards. Shards are also large top-level chunks.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I follow. Shards are on the server side, the client side deals with chunks. Why would shard size matter to the client?

Comment on lines +36 to +41
/**
* Chunks above this defeat the browser cache. Applies when the array is stored
* without compression, so the size we compute is the size that transfers.
*/
export const MAX_CHUNK_BYTES = 10 * 1024 ** 2;
/**

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this limit is the one that would apply to Zarr chunks.

https://claude.ai/share/2c1c2b4f-c9a3-43d3-a24d-9de809b04062

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some browsers do have a limit on the size of a file they will cache. For example, Firefox will only cache up to 5 MB files in memory (browser.cache.memory.max_entry_size) and 50 MB on disk (browser.cache.disk.max_entry_size). This 10 MB threshold is uncompressed, and assumes 2-3x compression will put the chunk under the 5 MB threshold. It's just a heuristic.

Of course, there are other reasons to favor smaller chunk sizes, but not too small. Maybe there are better heuristics or rules we can use here.

@krokicki

Copy link
Copy Markdown
Member Author

I am not sure how well these warnings are grounded in the spec and perhaps more specifically in Neuroglancer's implementation. I'm not sure where the 10 MiB cache limit comes from. Could we check the cache limits and add citations when possible to clearly state whether these are browser or middleware issues.

Browsers have limits at 5 MB, as explained in the code comment. This thread suggests that reasonable chunk file sizes are between 1-10MB.

What we want to do here is to provide a soft warning to people when they use chunk sizes that are clearly not ideal. Where would you set that threshold without creating too many false positives? I don't expect that this threshold can be a perfect classifier for correct/wrong, since there are many different (and often opposing) forces acting upon it.

I suspect that the dimension order could be relaxed here especially in light of RFC-3. The main relevant ordering is probably the clustering of the spatial dimensions at the end but the order of the spatial dimensions is not really a problem for Neuroglancer.

But RFC-3 is not a part of any official specification version, and I'm not sure when it would be merged. Even after it is released, it will take time for libraries (such as ome-zarr.js) to catch up with it. In the meantime, it creates issues when the spec is not followed. Although there may be good reasons for deviating from the spec (and maybe some of those inspired RFC-3), I don't see the problem with alerting people that they are deviating.

Another warning I would add here is the dimensionality of the chunks versus the dimensionality of the array. If we have 2D chunks and a 3D spatial array, we will have issues for viewing in some of the orthogonal views. If the array is nD, then we want the dimension of the chunks to have non-singleton dimensions for at leastmin(n, 3) dimensions.

Yes, but that seems out of scope for this PR, which is trying to address concrete issues that happened in production.

Also chunks that are too small might be an issue as well. In the extreme a chunk that is a single voxel is highly problematic. I would suggest a minimum element count of 256 elements if there are more elements than this in the array.

Yes, but also out of scope here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants