Skip to content

fix(asset): address review feedback from #827 (streaming follow-ups)#880

Merged
icellan merged 1 commit into
mainfrom
fix/asset-subtree-streaming-followups
May 18, 2026
Merged

fix(asset): address review feedback from #827 (streaming follow-ups)#880
icellan merged 1 commit into
mainfrom
fix/asset-subtree-streaming-followups

Conversation

@icellan

@icellan icellan commented May 15, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #827 (which was merged before all review feedback could be folded in).
Addresses comments from @oskarszoon and @ordishs.

Changes

  • Subtree page (JSON)readSubtreePageFromReader now returns an empty page when the requested offset is past the end of the subtree (matching readSubtreeNodesPageFromReader) instead of silently wrapping back to offset 0. The wrap was confusing for paginated clients. (review item [FEAT] Peer service should sanity check asset_httpPublicAddress #2)
  • settings.conf — Documented the actual non-zero defaults for the asset subtree concurrency keys (asset_concurrency_get_subtree_data=2, ..._data_reader=4, ..._transactions=2, asset_subtreeDataStreamingConcurrency=2). The previous block claimed 0 = unlimited defaults, which silently became 2 / 4 / 2 / 2 in Stream asset subtree responses to reduce memory use #827. (review item Why can mining nodes run without downloading the entire blockchain? #3)
  • Streaming benchmark + alloc regression test — Added an un-gated BenchmarkSubtreeNodeHashesReader and a regression test (TestSubtreeNodeHashesReaderAllocsBound) that runs it via testing.Benchmark and asserts AllocsPerOp <= 16. The original benchmarks were behind RUN_SUBTREE_BENCHMARK=true and never ran in CI, so the streaming memory savings claim was not machine-verified. (review item Is it possible to develop a mining pool for the testnet using Teranode? #4)
  • scheduleSubtreeChunkFetches error masking — Prefer waitErr (real worker error) over readErr (which is the context.Canceled we observe when the errgroup cancels gCtx). Falls back to readErr when waitErr is nil or itself a cancellation, so genuine read errors are still surfaced. (review item fix(model): correct BlockHeaderMeta serialization and add ProcessedAt support #6)
  • GetSubtree handler — Collapsed the trailing dead switch + unreachable return nil into a direct if/else; mode is already validated above. (review item Is it possible to develop a mining pool for the testnet using Teranode? #4)
  • Deployment docsassetServer.md gains a section explaining that the gzip-skipped /subtree* endpoints require upstream rate-limiting / ACL in a reverse proxy (nginx limit_req_zone / limit_conn_zone or equivalent). The asset service does not yet do per-IP rate limiting; that is a planned defense-in-depth follow-up. (review item ops note)
  • ConflictingNodes paginated-response semantics (documented here)ConflictingNodes is now only populated when the page covers the whole subtree (offset==0 && pageSize==totalNodes). Partial pages return an empty ConflictingNodes array. This semantic was introduced in 827's b08a88e3 to avoid scanning all 1M nodes when paginating a large subtree; calling it out explicitly so any downstream consumer can adapt. (review item Why can mining nodes run without downloading the entire blockchain? #3)
  • Tests — Targeted unit-test coverage for the new subtree_stream.go paths (negative args, offset past end, zero limit, page truncation, empty subtree, truncated header / conflict count, zero-length Read, context cancel, source.Close on header read failure), plus micro-tests for drainChunkResults and sendChunkResult. Boosts subtree_stream.go function coverage to ~75–82% and clears the SonarQube new-code-coverage gap for these files. (ordishs review item 1, 4)

Items not addressed here

  • FeeHash JSON value — Confirmed not a regression; old behaviour also emitted zero-value FeeHash in /subtree/:hash/json. No code change needed. (review item #1)
  • CodeQL findings at subtree_stream.go:118 and :178 — False positives; pageSize is provably bounded to ≤ 100 by both the HTTP-layer clamp (helpers.go:107-109) and the repository-layer defense-in-depth clamps (subtree_stream.go:86-88, 142-144). Max allocation ~4.8 KB per request. Suggest dismissing in the Security tab with that rationale rather than adding dead defensive code.
  • claude[bot] finding at http.go:435 — Already refuted in the original PR review (parts[i+1:] cannot panic; Go allows slicing to len, and the len(tail)==1/2 guards prevent tail[1] OOB).

Test plan

  • go test -race ./services/asset/... passes locally
  • go vet ./services/asset/... clean
  • New TestSubtreeNodeHashesReaderAllocsBound passes (locks in the streaming memory claim)
  • make test — all asset-related packages pass; remaining failures are unrelated testcontainer (Docker daemon) issues in util/kafka and util/usql
  • CI green (full pipeline)

- subtree page (json): return empty page when offset is past totalNodes
  instead of silently wrapping to 0; aligns with readSubtreeNodesPageFromReader
  and avoids surprising paginated clients.
- settings.conf: document the actual non-zero defaults for the asset subtree
  concurrency keys (2 / 4 / 2 / 2) so operators don't believe the documented
  "0 = unlimited" comment after these were silently bounded.
- bench: add an un-gated BenchmarkSubtreeNodeHashesReader plus a regression
  test that runs it via testing.Benchmark and asserts AllocsPerOp <= 16,
  so a future regression to non-streaming buffering fails CI.
- GetLegacyBlock: in scheduleSubtreeChunkFetches, prefer waitErr (the real
  worker error) over readErr (which is the context.Canceled propagated when
  the errgroup cancelled gCtx). Falls back to readErr when waitErr is nil or
  itself a cancellation, so genuine read errors are still surfaced.
- GetSubtree handler: collapse the trailing dead switch + unreachable
  return nil into an if/else (mode is already validated above).
- docs: assetServer.md gains a Deployment section documenting that the
  gzip-skipped /subtree* endpoints require upstream rate-limiting / ACL
  in a reverse proxy, since per-IP rate limiting is not yet implemented
  in the asset service.
- tests: targeted coverage for the new subtree_stream paths (negative
  args, offset past end, zero limit, page truncation, empty subtree,
  truncated header / conflict count, zero-length Read, context cancel,
  source.Close on header failure), plus micro-tests for the new
  drainChunkResults and sendChunkResult helpers.

Notes for reviewers:
- ConflictingNodes pagination semantics: partial pages return empty
  ConflictingNodes; only a page that covers the whole subtree
  (offset==0 && pageSize==totalNodes) reads them. This is the change
  introduced in b08a88e and now documented here.
- FeeHash zero-value in /subtree/:hash/json is unchanged from prior
  behaviour; no regression.
@icellan icellan requested review from Copilot, ordishs and oskarszoon May 15, 2026 16:23
@github-actions

github-actions Bot commented May 15, 2026

Copy link
Copy Markdown
Contributor

🤖 Claude Code Review

Status: Complete


Current Review:

This PR successfully addresses review feedback from #827. The changes are well-structured with appropriate tests and documentation. No issues found.

Key improvements verified:

  1. Pagination fixreadSubtreePageFromReader now returns an empty page when offset exceeds total nodes (instead of wrapping to 0). Test coverage confirms the behavior at repository_test.go:237-247.

  2. Settings documentationsettings.conf:214-226 now correctly documents the actual non-zero defaults (2/4/2/2) matching the implementation in settings/settings.go:199-209.

  3. Error masking fixGetLegacyBlock.go:360-372 now prefers the real worker error over context.Canceled, improving debuggability.

  4. Control flow cleanupGetSubtree.go:185-194 replaced dead switch/unreachable return with direct if/else.

  5. Test coverage — Comprehensive tests added for edge cases (negative args, offset past end, zero limit, empty subtrees, truncated streams, context cancellation). Allocation regression test TestSubtreeNodeHashesReaderAllocsBound locks in the streaming memory claim (≤16 allocs per op).

  6. Deployment docsassetServer.md:837-852 accurately describes the gzip-skipped endpoints and correctly warns operators to add upstream rate limiting. The documented default values (2/4/2/2) match the code.

Documentation accuracy:

All documentation changes accurately reflect the implementation. The assetServer.md deployment section correctly identifies the streaming endpoints and their security trade-offs. The settings.conf defaults match the code at settings/settings.go and settings/asset_settings.go.

Copilot AI 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.

Pull request overview

This PR is a follow-up to #827 that folds in outstanding review feedback around asset subtree streaming semantics, configuration documentation, error propagation, and test verification of the streaming allocation claims.

Changes:

  • Adjust subtree JSON paging to return an empty page when offset >= totalNodes (instead of wrapping to offset 0) and clarify ConflictingNodes population semantics for full-subtree pages only.
  • Improve scheduleSubtreeChunkFetches error reporting by preferring the actual worker error over context.Canceled caused by errgroup cancellation.
  • Add always-on benchmark + alloc regression test coverage for the streaming node-hashes reader, plus additional unit tests for subtree streaming helpers/edge-cases; update docs/settings to reflect bounded defaults and operational guidance.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
settings.conf Documents the actual non-zero defaults for subtree-related asset concurrency settings and fixes formatting.
services/asset/repository/subtree_stream.go Updates subtree JSON paging behavior to return empty pages past end and adjusts when conflicting nodes are read.
services/asset/repository/subtree_stream_test.go Adds extensive unit tests plus a benchmark and an allocation regression test for the streaming reader.
services/asset/repository/repository_test.go Updates repository-level test expectations for the new “offset past end returns empty page” behavior.
services/asset/repository/GetLegacyBlock.go Ensures worker errors are surfaced instead of masking them with context.Canceled.
services/asset/repository/GetLegacyBlock_test.go Adds micro-tests for drainChunkResults and sendChunkResult.
services/asset/httpimpl/GetSubtree.go Simplifies the handler control flow by removing an unreachable/dead switch tail.
docs/topics/services/assetServer.md Adds deployment guidance requiring upstream rate limiting/ACLs for large streaming subtree endpoints.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread services/asset/repository/subtree_stream_test.go
@sonarqubecloud

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown
Contributor

Benchmark Comparison Report

Baseline: main (unknown)

Current: PR-880 (3166694)

Summary

  • Regressions: 0
  • Improvements: 0
  • Unchanged: 142
  • Significance level: p < 0.05
All benchmark results (sec/op)
Benchmark Baseline Current Change p-value
_NewBlockFromBytes-4 1.930µ 1.686µ ~ 0.700
SplitSyncedParentMap_SetIfNotExists/256_buckets-4 61.62n 61.58n ~ 0.700
SplitSyncedParentMap_SetIfNotExists/16_buckets-4 61.49n 61.95n ~ 0.200
SplitSyncedParentMap_SetIfNotExists/1_bucket-4 61.70n 61.65n ~ 1.000
SplitSyncedParentMap_ConcurrentSetIfNotExists/256_buckets... 29.99n 30.47n ~ 0.700
SplitSyncedParentMap_ConcurrentSetIfNotExists/16_buckets_... 52.78n 51.15n ~ 0.200
SplitSyncedParentMap_ConcurrentSetIfNotExists/1_bucket_pa... 112.8n 110.6n ~ 0.400
MiningCandidate_Stringify_Short-4 262.1n 262.6n ~ 0.500
MiningCandidate_Stringify_Long-4 1.885µ 1.898µ ~ 0.100
MiningSolution_Stringify-4 986.6n 990.4n ~ 0.100
BlockInfo_MarshalJSON-4 1.755µ 1.762µ ~ 0.100
NewFromBytes-4 122.2n 121.8n ~ 0.300
Mine_EasyDifficulty-4 60.35µ 60.43µ ~ 0.700
Mine_WithAddress-4 7.346µ 6.771µ ~ 0.400
DirectSubtreeAdd/4_per_subtree-4 59.57n 63.25n ~ 0.400
DirectSubtreeAdd/64_per_subtree-4 31.47n 31.84n ~ 0.100
DirectSubtreeAdd/256_per_subtree-4 30.46n 30.54n ~ 1.000
DirectSubtreeAdd/1024_per_subtree-4 29.10n 29.22n ~ 0.200
DirectSubtreeAdd/2048_per_subtree-4 28.68n 28.71n ~ 0.700
SubtreeProcessorAdd/4_per_subtree-4 283.5n 286.4n ~ 0.400
SubtreeProcessorAdd/64_per_subtree-4 278.6n 278.1n ~ 1.000
SubtreeProcessorAdd/256_per_subtree-4 276.9n 280.9n ~ 0.100
SubtreeProcessorAdd/1024_per_subtree-4 268.5n 273.7n ~ 0.100
SubtreeProcessorAdd/2048_per_subtree-4 268.5n 272.3n ~ 0.100
SubtreeProcessorRotate/4_per_subtree-4 273.1n 275.5n ~ 0.200
SubtreeProcessorRotate/64_per_subtree-4 274.1n 273.8n ~ 0.800
SubtreeProcessorRotate/256_per_subtree-4 272.5n 272.7n ~ 0.700
SubtreeProcessorRotate/1024_per_subtree-4 272.0n 275.5n ~ 0.100
SubtreeNodeAddOnly/4_per_subtree-4 54.13n 54.33n ~ 0.700
SubtreeNodeAddOnly/64_per_subtree-4 34.52n 34.31n ~ 0.100
SubtreeNodeAddOnly/256_per_subtree-4 33.45n 33.37n ~ 0.700
SubtreeNodeAddOnly/1024_per_subtree-4 32.70n 32.63n ~ 0.200
SubtreeCreationOnly/4_per_subtree-4 115.8n 116.1n ~ 0.700
SubtreeCreationOnly/64_per_subtree-4 433.3n 411.1n ~ 0.700
SubtreeCreationOnly/256_per_subtree-4 1.335µ 1.367µ ~ 0.100
SubtreeCreationOnly/1024_per_subtree-4 4.338µ 4.429µ ~ 0.100
SubtreeCreationOnly/2048_per_subtree-4 8.002µ 8.186µ ~ 0.200
SubtreeProcessorOverheadBreakdown/64_per_subtree-4 271.9n 272.0n ~ 0.700
SubtreeProcessorOverheadBreakdown/1024_per_subtree-4 274.4n 273.2n ~ 0.400
ParallelGetAndSetIfNotExists/1k_nodes-4 597.8µ 821.5µ ~ 0.100
ParallelGetAndSetIfNotExists/10k_nodes-4 1.329m 1.566m ~ 0.100
ParallelGetAndSetIfNotExists/50k_nodes-4 6.788m 6.722m ~ 0.100
ParallelGetAndSetIfNotExists/100k_nodes-4 13.69m 13.34m ~ 0.100
SequentialGetAndSetIfNotExists/1k_nodes-4 654.9µ 651.9µ ~ 0.700
SequentialGetAndSetIfNotExists/10k_nodes-4 2.836m 2.925m ~ 0.400
SequentialGetAndSetIfNotExists/50k_nodes-4 10.54m 10.59m ~ 0.700
SequentialGetAndSetIfNotExists/100k_nodes-4 20.32m 20.27m ~ 1.000
ProcessOwnBlockSubtreeNodesParallel/1k_nodes-4 643.9µ 638.4µ ~ 0.700
ProcessOwnBlockSubtreeNodesParallel/10k_nodes-4 4.196m 4.224m ~ 0.200
ProcessOwnBlockSubtreeNodesParallel/100k_nodes-4 16.80m 16.72m ~ 0.700
ProcessOwnBlockSubtreeNodesSequential/1k_nodes-4 713.2µ 699.2µ ~ 0.100
ProcessOwnBlockSubtreeNodesSequential/10k_nodes-4 5.877m 5.881m ~ 1.000
ProcessOwnBlockSubtreeNodesSequential/100k_nodes-4 38.92m 38.28m ~ 0.100
DiskTxMap_SetIfNotExists-4 3.552µ 3.569µ ~ 1.000
DiskTxMap_SetIfNotExists_Parallel-4 3.311µ 3.358µ ~ 0.700
DiskTxMap_ExistenceOnly-4 312.1n 299.6n ~ 0.400
Queue-4 191.0n 187.6n ~ 0.100
AtomicPointer-4 4.315n 4.422n ~ 0.400
ReorgOptimizations/DedupFilterPipeline/Old/10K-4 856.8µ 834.9µ ~ 0.100
ReorgOptimizations/DedupFilterPipeline/New/10K-4 802.5µ 798.3µ ~ 0.700
ReorgOptimizations/AllMarkFalse/Old/10K-4 101.4µ 101.4µ ~ 1.000
ReorgOptimizations/AllMarkFalse/New/10K-4 61.96µ 61.83µ ~ 0.200
ReorgOptimizations/HashSlicePool/Old/10K-4 55.37µ 56.02µ ~ 1.000
ReorgOptimizations/HashSlicePool/New/10K-4 11.33µ 11.47µ ~ 0.700
ReorgOptimizations/NodeFlags/Old/10K-4 4.708µ 5.019µ ~ 0.100
ReorgOptimizations/NodeFlags/New/10K-4 1.610µ 2.032µ ~ 0.100
ReorgOptimizations/DedupFilterPipeline/Old/100K-4 9.314m 9.219m ~ 0.700
ReorgOptimizations/DedupFilterPipeline/New/100K-4 9.636m 9.437m ~ 0.200
ReorgOptimizations/AllMarkFalse/Old/100K-4 1.052m 1.047m ~ 0.700
ReorgOptimizations/AllMarkFalse/New/100K-4 678.9µ 686.8µ ~ 0.200
ReorgOptimizations/HashSlicePool/Old/100K-4 552.6µ 529.7µ ~ 0.100
ReorgOptimizations/HashSlicePool/New/100K-4 302.2µ 302.4µ ~ 0.400
ReorgOptimizations/NodeFlags/Old/100K-4 49.75µ 50.09µ ~ 0.200
ReorgOptimizations/NodeFlags/New/100K-4 17.55µ 17.43µ ~ 0.200
TxMapSetIfNotExists-4 52.55n 51.69n ~ 0.200
TxMapSetIfNotExistsDuplicate-4 38.73n 38.72n ~ 1.000
ChannelSendReceive-4 590.7n 610.6n ~ 0.100
BlockAssembler_AddTx-4 0.02979n 0.02971n ~ 1.000
AddNode-4 10.41 11.09 ~ 0.100
AddNodeWithMap-4 10.78 10.54 ~ 1.000
CalcBlockWork-4 487.9n 487.1n ~ 0.700
CalculateWork-4 669.4n 667.8n ~ 1.000
BuildBlockLocatorString_Helpers/Size_10-4 1.281µ 1.309µ ~ 0.100
BuildBlockLocatorString_Helpers/Size_100-4 12.50µ 12.28µ ~ 0.700
BuildBlockLocatorString_Helpers/Size_1000-4 148.3µ 144.4µ ~ 1.000
CatchupWithHeaderCache-4 104.3m 104.1m ~ 0.100
_BufferPoolAllocation/16KB-4 3.526µ 3.351µ ~ 1.000
_BufferPoolAllocation/32KB-4 8.584µ 9.442µ ~ 0.700
_BufferPoolAllocation/64KB-4 16.52µ 16.39µ ~ 1.000
_BufferPoolAllocation/128KB-4 28.40µ 32.60µ ~ 0.100
_BufferPoolAllocation/512KB-4 105.7µ 110.6µ ~ 0.200
_BufferPoolConcurrent/32KB-4 16.79µ 18.85µ ~ 0.100
_BufferPoolConcurrent/64KB-4 26.15µ 29.50µ ~ 0.100
_BufferPoolConcurrent/512KB-4 132.5µ 135.3µ ~ 0.100
_SubtreeDeserializationWithBufferSizes/16KB-4 608.0µ 592.6µ ~ 0.100
_SubtreeDeserializationWithBufferSizes/32KB-4 600.7µ 589.5µ ~ 0.100
_SubtreeDeserializationWithBufferSizes/64KB-4 608.2µ 588.4µ ~ 0.100
_SubtreeDeserializationWithBufferSizes/128KB-4 609.6µ 590.8µ ~ 0.100
_SubtreeDeserializationWithBufferSizes/512KB-4 629.1µ 602.7µ ~ 0.100
_SubtreeDataDeserializationWithBufferSizes/16KB-4 36.43m 35.48m ~ 0.200
_SubtreeDataDeserializationWithBufferSizes/32KB-4 36.11m 35.63m ~ 0.400
_SubtreeDataDeserializationWithBufferSizes/64KB-4 35.76m 35.63m ~ 1.000
_SubtreeDataDeserializationWithBufferSizes/128KB-4 35.63m 35.64m ~ 1.000
_SubtreeDataDeserializationWithBufferSizes/512KB-4 35.25m 35.16m ~ 0.400
_PooledVsNonPooled/Pooled-4 737.7n 735.6n ~ 0.400
_PooledVsNonPooled/NonPooled-4 6.199µ 7.278µ ~ 0.100
_MemoryFootprint/Current_512KB_32concurrent-4 6.734µ 6.571µ ~ 0.400
_MemoryFootprint/Proposed_32KB_32concurrent-4 9.039µ 8.900µ ~ 0.700
_MemoryFootprint/Alternative_64KB_32concurrent-4 8.695µ 8.629µ ~ 1.000
_prepareTxsPerLevel-4 411.0m 419.4m ~ 0.700
_prepareTxsPerLevelOrdered-4 3.568m 3.692m ~ 0.100
_prepareTxsPerLevel_Comparison/Original-4 420.1m 418.6m ~ 0.400
_prepareTxsPerLevel_Comparison/Optimized-4 3.746m 3.732m ~ 1.000
SubtreeSizes/10k_tx_4_per_subtree-4 1.341m 1.351m ~ 0.700
SubtreeSizes/10k_tx_16_per_subtree-4 318.2µ 313.0µ ~ 0.700
SubtreeSizes/10k_tx_64_per_subtree-4 75.58µ 75.95µ ~ 1.000
SubtreeSizes/10k_tx_256_per_subtree-4 18.91µ 18.80µ ~ 0.400
SubtreeSizes/10k_tx_512_per_subtree-4 9.376µ 9.322µ ~ 1.000
SubtreeSizes/10k_tx_1024_per_subtree-4 4.627µ 4.595µ ~ 0.200
SubtreeSizes/10k_tx_2k_per_subtree-4 2.313µ 2.327µ ~ 0.400
BlockSizeScaling/10k_tx_64_per_subtree-4 73.60µ 73.65µ ~ 1.000
BlockSizeScaling/10k_tx_256_per_subtree-4 18.60µ 18.83µ ~ 0.400
BlockSizeScaling/10k_tx_1024_per_subtree-4 4.642µ 4.591µ ~ 0.400
BlockSizeScaling/50k_tx_64_per_subtree-4 395.1µ 394.7µ ~ 1.000
BlockSizeScaling/50k_tx_256_per_subtree-4 93.16µ 92.47µ ~ 1.000
BlockSizeScaling/50k_tx_1024_per_subtree-4 23.09µ 22.72µ ~ 0.200
SubtreeAllocations/small_subtrees_exists_check-4 159.6µ 160.1µ ~ 1.000
SubtreeAllocations/small_subtrees_data_fetch-4 161.8µ 160.6µ ~ 0.200
SubtreeAllocations/small_subtrees_full_validation-4 320.4µ 319.3µ ~ 1.000
SubtreeAllocations/medium_subtrees_exists_check-4 9.203µ 9.317µ ~ 0.700
SubtreeAllocations/medium_subtrees_data_fetch-4 9.410µ 9.425µ ~ 1.000
SubtreeAllocations/medium_subtrees_full_validation-4 18.67µ 18.64µ ~ 0.700
SubtreeAllocations/large_subtrees_exists_check-4 2.214µ 2.233µ ~ 0.600
SubtreeAllocations/large_subtrees_data_fetch-4 2.298µ 2.290µ ~ 1.000
SubtreeAllocations/large_subtrees_full_validation-4 4.687µ 4.675µ ~ 0.400
StoreBlock_Sequential/BelowCSVHeight-4 320.5µ 315.5µ ~ 1.000
StoreBlock_Sequential/AboveCSVHeight-4 326.3µ 331.1µ ~ 0.100
GetUtxoHashes-4 258.4n 260.1n ~ 0.200
GetUtxoHashes_ManyOutputs-4 42.71µ 42.46µ ~ 0.100
_NewMetaDataFromBytes-4 237.9n 238.1n ~ 1.000
_Bytes-4 616.9n 612.2n ~ 1.000
_MetaBytes-4 562.5n 556.6n ~ 0.200

Threshold: >10% with p < 0.05 | Generated: 2026-05-15 16:37 UTC

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

All six items from my #827 review addressed at HEAD.

  1. Empty-page consistency: both readers share limit == 0 || offset >= totalNodes early-exit.
  2. settings.conf concurrency keys documented 2/4/2/2.
  3. TestSubtreeNodeHashesReaderAllocsBound runs unconditionally; 16-alloc ceiling meaningful (per-node regression would push AllocsPerOp to ~1024).
  4. scheduleSubtreeChunkFetches precedence: non-cancellation waitErrreadErr → nil. Correct, no deadlock.
  5. GetSubtree dead-switch collapsed; mode validation intact.
  6. assetServer.md names correct nginx directives (limit_req_zone, limit_conn_zone).

Items deliberately not addressed (rationale verified):

  • FeeHash JSON was always zero-value; not a regression.
  • CodeQL :118/:178 — two independent clamps (HTTP layer + repository subtreePageMaxNodes) confirm the false-positive rationale.
  • claude[bot] http.go:435 — parts[i+1:] cannot panic (slice to len returns empty).

Local: 696 tests pass, race-clean, vet/lint clean.

@icellan icellan merged commit 5d3286d into main May 18, 2026
34 checks passed
@icellan icellan deleted the fix/asset-subtree-streaming-followups branch May 18, 2026 09:16
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.

4 participants