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?

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.

A shard is also a chunk. It is just a specific kind of chunk that contains other "inner" chunks. To specify the size of a "shard" in Zarr I say chunk_shape: [1024, 1024, 1024].

Your warning shoud be qualified to say "inner" chunk perhaps.

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.

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 would suggest either softening the warning copy to something like "may exceed some browsers' in-memory HTTP cache limits, causing slower (disk-cache or re-fetch) access" rather than stating a re-fetch as the consequence, or dropping the specific causal mechanism and just framing the threshold as a general chunk-size-for-viewing-performance heuristic (which is closer to where the 64 MB / 32 MB revision in this thread is already headed, and is more defensible than pinning it to one browser's memory-tier default).

@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.

@allison-truhlar

Copy link
Copy Markdown
Collaborator

Some minor suggestions for the wording of the warnings:

  • Only one resolution level - "...for XX GB data. Without multiple levels, viewers read the full-resolution data at every zoom level, making viewing slow. Generating...
  • Chunks may be too large for efficient viewing - "...(before compression). This may exceed your browser's cache limit and cause slow viewing due to frequent chunk file re-downloads. A final chunk size...

@mkitti

mkitti commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Where would you set that threshold without creating too many false positives?

Amazon's "Best Practices Design
Patterns: Optimizing
Amazon S3 Performance" states

Typical sizes for byte-range requests are 8 MB or 16 MB.

https://d1.awsstatic.com/whitepapers/AmazonS3BestPractices.pdf

32 MiB is the upper limit I usually recommend, especially in the absence of sharding. I typically recommend 256³ for uint16 datasets on microscope systems running Windows when I anticipate relatively high file overhead from the Windows file system and the pending remote transfer of the entire dataset.

1 MB chunk sizes is the target I recommend when on systems where the number of files is not an acute constraint.

https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-files-best-practices.html

Using larger IO sizes (1 MB or more) amortizes per-operation overhead and is more cost effective than many small reads or writes.

https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-files-best-practices.html

My main recommendation for Zarr v3 is 1 GiB shards (hard upper limit is 2 GiB to support 32-bit systems that address memory or arrays via signed 32-bit integers, such as Java and JavaScript). Scalable Minds recommends 32³ to 128³ voxel chunks within the shard, preferring smaller chunks for smaller datasets.

https://docs.webknossos.org/webknossos/data/zarr.html

The Keller Lab, Fernando Amat, specifically, found optimal chunk sizes around 96 x 96 x 8 in shape for KLB.

https://www.nature.com/articles/nprot.2015.111/figures/9

So depending on the underlying systems and intended uses we recommendations spanning 32³ to 256³ voxels and 32 KiB to 32 MiB in terms of bytes.

It would be worthwhile to do some study on this with systematic benchmarks to make sure we understand the impact of chunk size on our current systems and the attribution of this across various layers and applications.

@mkitti

mkitti commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

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.

Conversion and data copies can get expensive. If our main target application is Neuroglancer or other viewers intended for volumetric display (BDV, BigStitcher, Imaris), I do not see the cost of conversion or copies as being worthwhile here for permutations of the spatial dimensions within the lowest strided dimensions.

The original OME recommendations are based on viewing applications such as OMERO which focus on 2D display and are carried over from OME-TIFF.

My main concern here is that the chunks are 3D and isotropic. In that case, the permutation of XYZ should not strongly matter.

It seems that the main concern is for thumbnail generation. However, I find the assumption of which two dimensions to preview to be conceptually flawed. If we have a true isotropic volume, then a preview should be either 3D or allow for multiple 2D slices / projections (XY, YZ, ,XZ).

@mkitti

mkitti commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

My question here is whether these issues are actually intrinsic to the array. In many cases, I think we could adapt the display to the array properties.

Do we only have one scale level or large chunks? Zoom-in to display fewer chunks at a time.

Are the chunks not isotropic? Orient the initial display for the optimal two dimensions. Perhaps do not show all three orthogonal dimensions if this would result in loading multiple chunks. If we have XY tiles with no Z depth, then only show the XY plane perspective.

@krokicki

Copy link
Copy Markdown
Member Author

So depending on the underlying systems and intended uses we recommendations spanning 32³ to 256³ voxels and 32 KiB to 32 MiB in terms of bytes.

I will increase the chunk size threshold for warnings from 10 MB to 64 MB, which should account for compression down to ~32 MB. That would still catch the 128 MB chunk issue we saw, while accommodating the larger chunks you use in practice. I'd also be interested in understanding the cases where those larger chunk sizes are beneficial, since that could help inform the guidance we give.

I find the assumption of which two dimensions to preview to be conceptually flawed.

I agree that there are limitations in this implementation, but it does follow the existing specification, so for now I think it makes sense to keep it as-is. When the spec is changed (e.g. when RFC-3 is merged) then we will need to revisit how thumbnails are implemented.

My question here is whether these issues are actually intrinsic to the array. In many cases, I think we could adapt the display to the array properties.

For this PR I'm trying to keep the scope focused on providing practical advice that can be useful to users today. It would be helpful to capture some of the adaptive-display ideas separately, so that we can consider them independent of this PR.

@mkitti

mkitti commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Here's the OME-Zarr to roadmap to v1.0:
https://forum.image.sc/t/our-proposed-roadmap-to-ome-zarr-1-0/121995/1

image

RFC-3 related comments:

  1. "RFC-3 (Relaxing dimension restrictions) almost ready for review"
  2. "For example, using soon-to-be-released versions of napari-ome-zarr and napari, you can now read example RFC-3 data into napari and get accurate axis metadata, and work is ongoing on bioformats2raw to accurately export additional axes to RFC-3 compliant OME-Zarr."

As shown in the roadmap, this is the focus of primary activity in the community right now. The objective is to finalize RFC-3 this month.

For this PR I'm trying to keep the scope focused on providing practical advice that can be useful to users today.

This is what is confusing me. I do not see the axis order advice as practical. It does not affect Neuroglancer. I do not think people should waste time or resources on axis order issues.

Even worse, I think the axis order advice is suboptimal for many applications. For example, if you have a true RGB image or RGBA image often these are expected to be the last dimension as exemplified by these libraries:

  1. Matplotlib: matplotlib.pylot.imshow takes arrays in (M, N, 3) or (M, N, 4) order.
  2. Tensorflow: tf.nn.conv2d - With the default format "NHWC", the data is stored in the order of: batch_shape + [height, width, channels].
  3. scikit.image skimage.color
  4. ImageJ "Interleaved RGB images have pixels stored contiguously (rgbrgbrgb...) in a single image plane. Planar RGB images have the red, green and blue image data stored in separate 8-bit sample planes. ImageJ saves RGB images (both TIFF and raw) in interleaved format."

While it is true that many images coming our way are from fluoresence microscopy are not in RGB format, fluorescence microscopy is the exception. Most histology images (e.g. H&E sections) for example are in RGB format.

Regarding ome-zarr.js, I do see a lot of code that does not assume the order of the dimensions:

  1. https://github.com/BioNGFF/ome-zarr.js/blob/37c4ed5037902d0a4999bf207debf53710e4f4e6/src/render.ts#L153
  2. https://github.com/BioNGFF/ome-zarr.js/blob/37c4ed5037902d0a4999bf207debf53710e4f4e6/src/image.ts#L237

I submitted a pull request to address the parts of the code where the order of the dimensions is assumed:
BioNGFF/ome-zarr.js#47

I would particularly focus on the chunk issues for the warnings since there really is not any solution that we can implement short of rewriting the entire array. The single resolution issue can be resolved by adding the additional scales by materializing them to disk or calculating them on the fly in some cases, which is a service we might be able to provide.

The dimension order issue is at best superfluous and at worse deleterious to potential applications. The dimension order issue should just be left to a OME-NGFF validator and hopefully removed from the spec as soon as possible. If a tool assumes the last two dimensions are Y and X despite clear metadata to contrary, it is a bug that we can and should fix.

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.

3 participants