Skip to content

Conversation

fm3
Copy link
Member

@fm3 fm3 commented May 19, 2025

Agglomerate Mappings can now also be read from the new zarr3-based format, and from remote object storage.

  • AgglomerateFileKey, which identifies an agglomerate file, now holds a LayerAttachment (which can specify a remote URI) so we can use VaultPaths for accessing remote agglomerate files
  • interface of AgglomerateService methods changed (take the new AgglomerateFileKey)
    AgglomerateService is no longer injected, instead it is explicitly created in BinaryDataServiceHolder so we can pass it the sharedChunkContentsCache
  • AgglomerateService now delegates to either Hdf5AgglomerateService (basically the old code) or ZarrAgglomerateService (a lot of duplication from the other one, but unifying it at this time would sacrifice performance for hdf5)
  • DatasetArray has new public method ReadAsMultiArray, which does not do any axisorder but instead yields a MultiArray in the order of the DatasetArray
  • removed unused route agglomerateIdsForAllSegmentIds

URL of deployed dev instance (used for testing):

Steps to test:

TODOs:

Backend
  • open zarr agglomerate as zarr array and read contents
    • read MultiArray without caring about AxisOrder
    • test with 2D
  • Read agglomerate arrays with correct types
  • Re-Implement public functions of agglomerate service
    • applyAgglomerate
    • generate agglomerate skeleton
    • largest agglomerate id
    • generate agglomerate graph
    • segmentIdsForAgglomerateId
    • agglomerateIdsForSegmentIds
    • positionForSegmentId
  • What’s up with zarr streaming in the tests? reproduce with test-dataset, ids are wrong, also in normal data loading
  • Create indirection for selecting the zarr agglomerates OR hdf5 agglomerates
  • reduce code duplication btw hdf5 and zarr
  • Error handling (index lookups always in tryo. abstraction?)
  • Read remote (Build VaultPath for URI)
  • Discover files?
  • Adapt requests to specify which agglomerate file should be read from (type? full path?)
  • Caching / Speedup (added some caching but did not test on large-scale DS. will be follow-up)
  • Clear caches on layer/DS reload
  • Make sure the agglomerate zarr directories don’t blow up dataset exploring
  • Code Cleanup
Frontend

Issues:


  • Updated changelog
  • Removed dev-only changes like prints and application.conf edits
  • Considered common edge cases
  • Needs datastore update after deployment

@fm3 fm3 self-assigned this May 19, 2025
Copy link
Contributor

coderabbitai bot commented May 19, 2025

📝 Walkthrough

Walkthrough

This update introduces support for reading agglomerate mappings from the zarr3 format, including remote object storage. It refactors agglomerate-related services for modularity and multi-format support, updates type signatures for large-dimension compatibility, and adjusts controller and service logic to use repository lookups and new data types. Several new services and helper methods are added.

Changes

Files / Areas Changed Summary
services/AgglomerateService.scala, services/ZarrAgglomerateService.scala, services/Hdf5AgglomerateService.scala Refactored agglomerate service to modular, multi-format design; added new Zarr and HDF5 agglomerate services; updated method signatures and logic.
storage/AgglomerateFileCache.scala Replaced AgglomerateFileKey structure with new domain-specific fields.
services/BinaryDataServiceHolder.scala, DataStoreModule.scala Changed service composition and dependency injection for agglomerate services.
controllers/DataSourceController.scala, DSMeshController.scala, services/mesh/DSFullMeshService.scala Updated controllers and mesh services to use repository lookups and new agglomerate service APIs.
services/mesh/MeshMappingHelper.scala, services/mesh/MeshFileService.scala, services/mesh/NeuroglancerPrecomputedMeshFileService.scala Updated mesh mapping and file services to use new agglomerate key logic and accept Seq[Long] for segment IDs.
services/SegmentIndexFileService.scala Updated to use new agglomerate key lookup and Fox monad for async error handling.
datareaders/DatasetArray.scala, datareaders/ChunkUtils.scala, datareaders/AxisOrder.scala, datareaders/MultiArrayUtils.scala Added multi-array reading API, changed shape types to Array[Long], added helper methods for large dimensions.
datareaders/DatasetHeader.scala, dataformats/wkw/WKWHeader.scala, datareaders/n5/N5Header.scala, datareaders/precomputed/PrecomputedHeader.scala, datareaders/zarr/ZarrHeader.scala, datareaders/zarr3/Zarr3ArrayHeader.scala Updated dataset shape fields and related methods to use Array[Long] for large dimension support.
datareaders/wkw/WKWArray.scala, datareaders/zarr3/Zarr3Array.scala Adjusted to use long-based axis permutations and improved error context for Zarr3.
explore/ExploreRemoteLayerService.scala, explore/NgffExplorationUtils.scala, explore/NgffV0_4Explorer.scala, explore/NgffV0_5Explorer.scala, explore/PrecomputedExplorer.scala Updated explorers and utils for long-based shapes and type conversions.
models/datasource/DatasetLayerAttachments.scala Added default values and updated JSON format for layer attachments.
services/mesh/AdHocMeshService.scala Switched from Box to Fox monad for async handling.
storage/RemoteSourceDescriptorService.scala Refactored path/URI resolution logic with whitelist and error handling improvements.
conf/com.scalableminds.webknossos.datastore.routes, conf/messages Removed obsolete agglomerate route; added new error message for Zarr shard index failures.
unreleased_changes/8633.md Noted support for zarr3 agglomerate mappings and remote storage.
tracingstore/controllers/VolumeTracingZarrStreamingController.scala Updated Zarr header construction to use Array[Long] shapes.

Possibly related PRs

  • scalableminds/webknossos#8236: Both PRs involve mesh-related backend services and controller logic, indicating a strong connection in mesh file and chunk handling.
  • scalableminds/webknossos#8350: Both PRs address dataset path resolution and validation, including whitelist checks and real path handling.
  • scalableminds/webknossos#8598: Both PRs implement and utilize dataset layer attachments, extending the data model and database support for these features.

Suggested labels

enhancement

Poem

In the warren where data hops free,
Zarr3 agglomerates now join the spree!
With Long-shaped tunnels, and caches anew,
The rabbit refactored, as only they do.
Remote vaults explored, mappings in tow—
Oh, what a leap for the warren below!
🐇✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5920905 and bb07185.

📒 Files selected for processing (1)
  • unreleased_changes/8633.md (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • unreleased_changes/8633.md
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: build-smoketest-push
  • GitHub Check: backend-tests
  • GitHub Check: frontend-tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@fm3 fm3 mentioned this pull request May 28, 2025
8 tasks
Copy link
Contributor

@MichaelBuessemeyer MichaelBuessemeyer left a comment

Choose a reason for hiding this comment

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

Thanks for all your work 🙏.The code looks pretty good to me. I only found a few suggestion / questions.
The only question stretching over multiple files is: Why did you change the type to Long for some arguments / members of DatasetArray (I think it was)?

Besides this, I'll leave testing for tomorrow :)

@fm3 fm3 mentioned this pull request Jun 18, 2025
16 tasks
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/AgglomerateService.scala (1)

36-36: Variable name still implies “only‐HDF5” support

hdf5AgglomerateFileExtension suggests this is the only supported agglomerate format, which is no longer true. Consider renaming to something like legacyHdf5AgglomerateFileExtension or at least agglomerateHdf5FileExtension to avoid confusion.

🧹 Nitpick comments (3)
webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/AgglomerateService.scala (1)

198-200: unsupportedDataFormat does not use the ExecutionContext

unsupportedDataFormat receives an implicit ExecutionContext that it never uses. Drop the parameter to avoid accidental shadowing and to make the signature cleaner.

webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/DatasetArray.scala (2)

304-307: Guard Long → Int cast against out-of-range values.

(globalOffset(dim) - (chunkIndex(dim).toLong * chunkShape(dim).toLong)).toInt
assumes the intra-chunk offset always fits into Int. While logically true, an out-of-range result would truncate without warning.

Add an explicit range assertion before the cast:

val diff = globalOffset(dim) - chunkIndex(dim).toLong * chunkShape(dim)
require(diff >= Int.MinValue && diff <= Int.MaxValue,
        s"Offset $diff overflows Int for dim=$dim")
diff.toInt

310-312: toString truncates 64-bit shapes.

printAsInner(s.map(_.toInt)) narrows potentially huge dimensions to Int, leading to negative or wrong values in logs.

Introduce a printAsInner(values: Array[Long]) overload (or call .mkString) to preserve accuracy.

- shape=${header.datasetShape.map(s => printAsInner(s.map(_.toInt)))}
+ shape=${header.datasetShape.map(_.mkString("inner(", ",", ")"))}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 74a0bc3 and e170ebe.

📒 Files selected for processing (8)
  • CHANGELOG.unreleased.md (1 hunks)
  • conf/messages (1 hunks)
  • webknossos-datastore/app/com/scalableminds/webknossos/datastore/DataStoreModule.scala (1 hunks)
  • webknossos-datastore/app/com/scalableminds/webknossos/datastore/controllers/DataSourceController.scala (9 hunks)
  • webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/DatasetArray.scala (8 hunks)
  • webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/zarr3/Zarr3Array.scala (1 hunks)
  • webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/AgglomerateService.scala (1 hunks)
  • webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/ZarrAgglomerateService.scala (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • conf/messages
🚧 Files skipped from review as they are similar to previous changes (5)
  • CHANGELOG.unreleased.md
  • webknossos-datastore/app/com/scalableminds/webknossos/datastore/DataStoreModule.scala
  • webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/zarr3/Zarr3Array.scala
  • webknossos-datastore/app/com/scalableminds/webknossos/datastore/controllers/DataSourceController.scala
  • webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/ZarrAgglomerateService.scala
🧰 Additional context used
🧠 Learnings (1)
webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/DatasetArray.scala (1)
Learnt from: MichaelBuessemeyer
PR: scalableminds/webknossos#8352
File: app/models/organization/CreditTransactionService.scala:0-0
Timestamp: 2025-01-27T12:06:42.865Z
Learning: In Scala's for-comprehension with Fox (Future-like type), the `<-` operator ensures sequential execution. If any step fails, the entire chain short-circuits and returns early, preventing subsequent operations from executing. This makes it safe to perform validation checks before database operations.
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: frontend-tests
  • GitHub Check: backend-tests
  • GitHub Check: build-smoketest-push
🔇 Additional comments (4)
webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/AgglomerateService.scala (1)

45-58: Listing local files may still fail (or be slow) for remote layers

PathUtils.listFiles is invoked unconditionally on a path that can be non-existent (remote dataset) or reside on network storage. While the silent flag prevents exceptions, the call can still block and degrade request latency.

Please verify whether listAgglomerates is executed on the request path and, if so, guard the listing with Files.exists(layerDir) (or a cheap metadata check) to avoid unnecessary I/O for remote layers.

webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/DatasetArray.scala (3)

157-161: Mixed Int/Long arithmetic – verify no silent narrowing.

totalOffset.map(_.toLong) feeds a Seq[Long] into computeChunkIndices, but the shape argument remains Array[Int].
Double-check that computeChunkIndices internally lifts shape to Long before doing multiplications; otherwise offsets ≥ 2³¹ will silently overflow.


296-298: Nice micro-cleanup on zero-offset check.

Replacing Arrays.equals with the clearer offset.forall(_ == 0) eliminates allocation and improves readability. 👍


51-55: ```shell
#!/bin/bash
set -e

echo "=== Find permuteIndicesArrayToWkLong ==="
rg -n "permuteIndicesArrayToWkLong" -A2 webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/AxisOrder.scala


</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

@fm3 fm3 requested a review from MichaelBuessemeyer June 18, 2025 13:14
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/DatasetArray.scala (1)

310-312: Avoid toInt when logging very large shapes

toString converts Long dimensions to Int, which will print negative numbers for shapes > 2 147 483 647.

- shape=${header.datasetShape.map(s => printAsInner(s.map(_.toInt)))}
+ shape=${header.datasetShape.map(s => printAsInner(s.map(_.toString.toIntOption.getOrElse(Int.MaxValue))))}

Or simply build the string from Longs directly to prevent misleading logs.

webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/AgglomerateService.scala (1)

198-200: Remove unused implicit ExecutionContext

unsupportedDataFormat does not use the passed execution context, yet requires one implicitly, forcing callers to have it in scope.

-  private def unsupportedDataFormat(agglomerateFileKey: AgglomerateFileKey)(implicit ec: ExecutionContext) =
+  private def unsupportedDataFormat(agglomerateFileKey: AgglomerateFileKey) =
     Fox.failure(
       s"Trying to load agglomerate file with unsupported data format ${agglomerateFileKey.attachment.dataFormat}")
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e170ebe and 1360184.

📒 Files selected for processing (2)
  • webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/DatasetArray.scala (8 hunks)
  • webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/AgglomerateService.scala (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/DatasetArray.scala (1)
Learnt from: MichaelBuessemeyer
PR: scalableminds/webknossos#8352
File: app/models/organization/CreditTransactionService.scala:0-0
Timestamp: 2025-01-27T12:06:42.865Z
Learning: In Scala's for-comprehension with Fox (Future-like type), the `<-` operator ensures sequential execution. If any step fails, the entire chain short-circuits and returns early, preventing subsequent operations from executing. This makes it safe to perform validation checks before database operations.
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: build-smoketest-push
  • GitHub Check: frontend-tests
  • GitHub Check: backend-tests
🔇 Additional comments (6)
webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/DatasetArray.scala (5)

189-199: Mixed Long/Int API – please double-check

readAsMultiArray(offset: Array[Long], shape: Array[Int]) mixes numeric widths. Offsets > Int.MaxValue are expected, so why is shape still Int while the very next call hands it to computeChunkIndices, which already takes Long?
Either

  1. keep both as Long for consistency, or
  2. document the invariant that individual chunk shapes always fit into an Int.

This prevents subtle truncation bugs when somebody passes a shape bigger than 2 147 483 647 by mistake.


269-277: Fast-path predicate can give false positives with large offsets

partialCopyingIsNotNeededForMultiArray calls computeOffsetInChunkIgnoringAxisOrder, which currently truncates to Int. If overflow occurs, isZeroOffset may erroneously return true, skipping the partial-copy logic and returning a wrong slice.

Once the overflow in computeOffsetInChunkIgnoringAxisOrder is fixed (see above), please re-run the tests to verify this branch still behaves as intended.


297-297: Simplified zero-offset check looks good

Switching to offset.forall(_ == 0) is clearer and avoids potential overflow on product.


51-55: datasetShape now Array[Long] – ensure all downstream helpers are updated

The value feeds into:

  • fullAxisOrder.permuteIndicesArrayToWkLong (nice!), and
  • computeChunkIndices.

Please verify that every overload of these helpers now accepts Long; otherwise an implicit down-cast will re-introduce the very size limitation this PR tries to remove.


156-162: Compile-time check: new helper permuteIndicesArrayToWkLong must exist

The call to

fullAxisOrder.permuteIndicesArrayToWkLong

assumes a freshly added overload. Please ensure it is defined for all FullAxisOrder implementations; otherwise this file will not compile.

webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/AgglomerateService.scala (1)

42-59: ```bash
#!/usr/bin/env bash
set -e

Locate the PathUtils source file

PATHUTILS_FILE=$(rg -l "^object PathUtils" | head -n1)
if [[ -z "$PATHUTILS_FILE" ]]; then
echo "❌ PathUtils object not found."
exit 1
fi
echo "🗂 Found PathUtils at: $PATHUTILS_FILE"

Show definitions for listFiles, listDirs, and fileExtensionFilter

echo -e "\n🔍 Checking for listFiles definition:"
rg -n "def listFiles" "$PATHUTILS_FILE" || echo "⚠️ listFiles not defined here"

echo -e "\n🔍 Checking for listDirs definition:"
rg -n "def listDirs" "$PATHUTILS_FILE" || echo "⚠️ listDirs not defined here"

echo -e "\n🔍 Checking for fileExtensionFilter:"
rg -n "fileExtensionFilter" "$PATHUTILS_FILE" || echo "⚠️ fileExtensionFilter not found"

Search for any usage of listDirs across the repo

echo -e "\n🔎 Global search for listDirs usage:"
rg -n "listDirs" || echo "⚠️ No usages of listDirs in the repo"


</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Copy link
Contributor

@MichaelBuessemeyer MichaelBuessemeyer left a comment

Choose a reason for hiding this comment

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

Awesome thanks for applying my feedback. I left some reply on open conversions.

Will test now

Comment on lines +216 to +219
})
for {
_ <- copiedFuture
} yield targetMultiArray
Copy link
Contributor

Choose a reason for hiding this comment

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

Well, ok. I am not that much of a fan but I am also fine with your decision

Comment on lines 29 to 31
bind(classOf[ZarrAgglomerateService]).asEagerSingleton()
bind(classOf[Hdf5AgglomerateService]).asEagerSingleton()
bind(classOf[AgglomerateService]).asEagerSingleton()
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this is necessary as the BinaryDataServiceHolder is already a singleton and does only create one of each manually. So these three classes should therefore also be singletons.

But having this might also make sure that no strange things happen. So might keep this

Copy link
Contributor

Choose a reason for hiding this comment

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

this is acutally making the code not compile as there is no injectable constructor for AgglomerateService. I deleted it locally to proceed with testing

Copy link
Member Author

Choose a reason for hiding this comment

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

You’re right, I confused myself here. In the next iteration (MeshFileService) I make them injectable again. I’ll move this change there.

NodeDefaults.createInstance.copy(
id = idx + nodeIdStartAtOneOffset,
position = Vec3IntProto(pos(0).toInt, pos(1).toInt, pos(2).toInt)
def lookUpAgglomerateFile(dataSourceId: DataSourceId, dataLayer: DataLayer, mappingName: String)(
Copy link
Contributor

Choose a reason for hiding this comment

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

Well, I see your point and it is valid. I only wanted to avoid confusion as this makes the code read:
agglomerateFileKey <- agglomerateService.lookUpAgglomerateFile(...) and not
agglomerateFileKey <- agglomerateService.lookUpAgglomerateFileKey(...),
And to me the first reads like a logical mismatch on the first glance as on a conceptual level a "file key" and a "file" are different things. Maybe you mean something like "file handle" or so? 🤔

If you still want to keep lookUpAgglomerateFile feel free to do so

Copy link
Contributor

@MichaelBuessemeyer MichaelBuessemeyer left a comment

Choose a reason for hiding this comment

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

Sadly, testing did not fully work

When I tried to merge 2 segments in an annotation with agglomerate view 5 turned on, I ended up with a broken annotation where I could no longer load agglomerate meshes.
These are the errors in the console:
image
And here is the frontend log of actions I performed:

Start new bucket watcher for layer af681321-9141-4c88-93c1-3786f947029b
Don't load mesh for segment 32 because it already exists.
Cancel old bucket watcher
Start new bucket watcher for layer af681321-9141-4c88-93c1-3786f947029b
Accessing mapping for proofreading
dispatch setMappingAction in proofreading saga
Accessing mapping for proofreading
Merging agglomerate 211 with 5 and segment ids 226 172

And the backend error causing this is I think:

backend: 2025-06-18 18:46:59,772 [error] com.scalableminds.webknossos.datastore.rpc.RPCRequest - Failed RPC 313: GET http://localhost:9000/data/datasets/sample_organization/test-agglomerate-file-zarr/layers/segmentation/agglomerates/agglomerate_view_5/agglomerateGraph/211, Status: 400.() RequestBody: '' ResponseBody: '{"messages":[{"error":"agglomerateGraph.failed"},{"chain":"[Server Time 2025-06-18T16:46:59.771Z] class [I cannot be cast to class [F ([I and [F are in module java.base of loader 'bootstrap') java.lang.ClassCastException: class [I cannot be cast to class [F ([I and [F are in module java.base of loader 'bootstrap')"}]}'

Comment on lines 29 to 31
bind(classOf[ZarrAgglomerateService]).asEagerSingleton()
bind(classOf[Hdf5AgglomerateService]).asEagerSingleton()
bind(classOf[AgglomerateService]).asEagerSingleton()
Copy link
Contributor

Choose a reason for hiding this comment

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

this is acutally making the code not compile as there is no injectable constructor for AgglomerateService. I deleted it locally to proceed with testing

@fm3
Copy link
Member Author

fm3 commented Jun 19, 2025

Thanks so much for testing and the detailed steps to reproduce! This bug should be fixed now. I also re-tested the position query (where previously we requested shape 3,1 instead of 1,3. That seems to work now too.

Also, you convinced me to rename the function to lookUpAgglomerateFileKey.

Please have another look :)

@fm3 fm3 requested a review from MichaelBuessemeyer June 19, 2025 06:40
Copy link
Contributor

@MichaelBuessemeyer MichaelBuessemeyer left a comment

Choose a reason for hiding this comment

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

Awesome, the latest changes are looking good.
Couldn't find any bugs anymore 🚫 🐛

Should be good to go 🐎

@fm3 fm3 enabled auto-merge (squash) June 23, 2025 07:18
@fm3 fm3 merged commit 3d93c5c into master Jun 23, 2025
5 checks passed
@fm3 fm3 deleted the agglomerates-zarr branch June 23, 2025 07:18
@fm3 fm3 mentioned this pull request Jun 23, 2025
5 tasks
fm3 added a commit that referenced this pull request Jun 30, 2025
- Similar to #8633 we now support reading mesh files in zarr format.
Note that there are also neuroglancerPrecomputed meshes, so this time
there are three services the meshFileService delegates to.

- The frontend no longer needs to specially handle
neuroglancerPrecomputed meshes, since the lookUpMeshFileKey function
abstracts from that. Because of that, I also simplified the frontend
types.

- Note that zarr meshfiles must be registered in the
datasource-properties.json, they are not explored.

- Also fixed a small error in job result status reporting

- Also fixed a small error in mag selection for animation job.

### Steps to test:
- Load some precomputed meshes (both zarr, hdf5,
neuroglancerPrecomputed; talk to me for example datasets), should show
meshes correctly
- Create animations via wk worker, should also work correctly (I tested
this locally)
- Note that neuroglancerPrecomputed meshes now need to be registered as
attachments in the datasource-properties.json. You may need to
re-explore the relevant datasets or edit the json.

### TODOs:
- [x] introduce and look up MeshFileKey
- [x] delegate to zarr, hdf5 or neuroglancer meshfile services
- [x] explore remote neuroglancer meshes
- [x] parse new meshfile attributes zarr group format
- [x] do we still need the null vs none meshfile-mapping attribute?
- [x] test with frontend (zarr,hdf5,neuro)
- [x] adapt frontend to no longer need path + fileType for meshfiles
- [x] clear caches
- [x] unify fullMeshStl route
- [x] unify spelling (meshfile vs meshFile)
- [x] When exploring remote neuroglancer meshes, add credentialId, and
use it? (or create follow-up issue)
- [x] re-add singletons to module after merge of zarr-agglomerate’s
latest changes
- [x] unify function names with zarr-agglomerate’s latest changes

### Issues:
- contributes to #8618
- contributes to #8567

------
- [x] Updated
[changelog](../blob/master/CHANGELOG.unreleased.md#unreleased)
- [x] Considered [common edge
cases](../blob/master/.github/common_edge_cases.md)
- [x] Needs datastore update after deployment

---------

Co-authored-by: MichaelBuessemeyer <[email protected]>
@coderabbitai coderabbitai bot mentioned this pull request Jun 30, 2025
8 tasks
@coderabbitai coderabbitai bot mentioned this pull request Jul 7, 2025
12 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants