feat(dag): add --local-only to dag export and import#11229
Merged
lidel merged 18 commits intoMay 25, 2026
Conversation
- Export: only export blocks present locally; skip missing (partial CAR). --local-only with --offline. Support both binary and base58 link keys. - Import: support partial CARs; --local-only with -- pin-roots=false (error if both --pin-roots and --local-only set). - Fix cidFromBinString to accept base58 key format from link implementations. Signed-off-by: Chayan Das <01chayandas@gmail.com>
2e5e224 to
9ad0c3b
Compare
- remove local replace directive for go-car/v2 - upgrade to v2.16.1-0.20260306172652-7d2f4aceb070
lidel
requested changes
Mar 8, 2026
Member
lidel
left a comment
There was a problem hiding this comment.
Thank you @ChayanDass, quick first pass review below.
Also, we need to wait for go-car release with your fix before this can be merged.
Signed-off-by: Chayan Das <01chayandas@gmail.com>
Signed-off-by: Chayan Das <01chayandas@gmail.com>
Contributor
Author
|
@lidel i’ve added the tests ,PTAL |
Contributor
|
Triage: need to review again and check test coverage. |
…ort-local-only # Conflicts: # core/commands/dag/dag.go # docs/examples/kubo-as-a-library/go.mod # docs/examples/kubo-as-a-library/go.sum # go.mod # go.sum
Pass --local-only without pairing it with --offline (export) or --pin-roots=false (import); the companion is now implicit. Explicit opposites (--offline=false, --pin-roots=true) are rejected so the intent stays unambiguous. * export: imply --offline so missing blocks are not fetched over the network, which would defeat --local-only * import: imply --pin-roots=false since a partial CAR has no full DAG to pin * tests: cover the new implications and the rejected explicit-opposite combinations; drop the brittle exec.CommandContext path in favor of the existing harness
The --local-only branch now uses walker.WalkDAG with WithLocality(bs.Has) and carstorage.NewWritable, matching the MFS+unique provider in core/node/provider.go. Semantics: any input-side read error during the walk (missing block, decode failure, post-locality race) is treated as "not available locally" and the block plus its subtree are skipped. Output-side errors (writable.Put) are still surfaced. --help is updated to call out the best-effort nature. The non-local-only path is unchanged.
Pin chunker and max-file-links via a shared shallowDAGArgs so block counts are deterministic regardless of Import.* defaults or active profiles. Tighten existing assertions: * TestDagExportLocalOnly: assert exact fullCount=3 and partialCount=fullCount-1 instead of partialCount<fullCount * TestDagExportLocalOnlyImpliesOffline: assert exact partial block count, not just file Size > 0 (proves --offline was applied) Add TestDagExportLocalOnlySkipsSubtree: builds a 259-block DAG with depth>1 (256 chunks under 2 intermediates), removes an intermediate, and verifies the partial CAR is missing the intermediate plus all 174 of its descendants. Existing tests only exercised leaf removal. Extract countCARBlocks and makePartialDAG helpers used across tests.
Replace the req.Options["offline"] = true mutation with an explicit api.WithOptions(options.Api.Offline(true)) wrap after GetApi, matching the pattern already used in core/commands/dag/import.go. Clarify in comments that the walker reads from the raw blockstore (not via the kubo CoreAPI or DAGService) and therefore cannot trigger a network fetch by construction. The --offline implication exists for api.Block().Stat path resolution, not for the DAG walk itself.
ResetCids returns ctx.Err() straight from its ctx-done select, so a shutdown-during-sync surfaces as err="context canceled" while the outer ctx.Err() check at the classifier sometimes races behind the propagation and logs at Error. Classify context.Canceled the same way as keystore.ErrClosed so the message lands at Debug. Applied to both the startup and periodic classifiers. DeadlineExceeded is intentionally not included: nothing in the current call chain imposes a deadline, and a future timeout would be a real failure worth logging at Error. Closes the flake in TestProviderKeystoreSyncShutdownQuiet (10/10 local soak now green; CI hit the race 3 reruns in a row).
lidel
approved these changes
May 25, 2026
Member
lidel
left a comment
There was a problem hiding this comment.
Thanks @ChayanDass for getting this off the ground! 🙏
I pushed a few follow-ups on top:
- swapped the export walk to
boxo/dag/walkerso missing intermediates skip their whole subtree cleanly --local-onlynow auto-implies its companion flag (--offlineon export,--pin-roots=falseon import) instead of asking users to pass both- tightened the tests (deterministic DAG shape, exact block counts, subtree-skip case)
- small unrelated drive-by: quieted
context.Cancelednoise from the provider keystore sync on shutdown (fixes flaky test)
Planning to ship this in 0.42.0-rc1.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add
--local-onlytodag exportanddag importSummary
Adds support for partial CAR export and import: export only blocks that exist locally (skipping missing blocks), and import such partial CARs without pinning roots.
Changes
dag export--local-only(bool): When set, only blocks present in the local blockstore are exported; missing blocks are skipped (partial CAR). User must pass--offlineexplicitly for a local-only DAG walk; the command does not set offline automatically.dag import--pin-roots(bool, default true): Pin optional roots listed in the CAR headers after importing.--pin-roots=falseand--local-only(not both with pin-roots true).Usage
Export a partial CAR (local blocks only; user must pass both flags):
Import that partial CAR without pinning roots:
ipfs dag import --pin-roots=false partial.car # or ipfs dag import --local-only --pin-roots=false partial.carReferences
ipfs dag export --ignore-missing-blocks#10826 helps with Support import of partial CARs without prefetching the full DAG ipfs-webui#2380%wwhen wrapping errors to preserve error chain ipld/go-car#658