perf(rest): cache partition decode plans - #1803
Conversation
|
Since this is a draft, i'll hold off on further review until it's marked ready |
zeroshade
left a comment
There was a problem hiding this comment.
Thanks for completing the end-to-end remote scan-planning path. I found two blocking lifecycle/response-validation issues in the newly activated path; details are inline. Separately, all 10 commits in apache/main..HEAD are missing the Signed-off-by trailer required by CONTRIBUTING.md, so the series needs DCO sign-off before merge. Please also update the PR title and description: they currently describe only partition decode-plan caching, while this patch activates and implements remote scan planning across 15 files and roughly 1,400 added lines.
| // by the server. | ||
| func (r *Catalog) SupportsRemoteScanPlanning() bool { | ||
| return false | ||
| return r.SupportsPlanTableScan() |
There was a problem hiding this comment.
Blocking: activating REST remote planning here makes the existing PlanIO owner leak reachable in normal use. planFilesRemote installs an owning planIOState, but exhausting the ReadTasks iterator releases only its reader lease; the owner is released only by replacement/local replanning through the private closePlanIO. Scan has no public Close and no finalizer, so a one-shot remote scan that reads successfully and is then discarded never invokes PlanIO.Close, leaving the cached prefix-scoped cloud filesystems/credential resources alive. Cloning an already planned scan adds more owners that public callers likewise cannot release. The package tests can clean up only by calling the private helper. Please provide a deterministic public lifetime mechanism or redesign ownership so completed/abandoned scans release their plan IO.
a8a7855 to
315d35f
Compare
zeroshade
left a comment
There was a problem hiding this comment.
The null fetch-response blocker is fixed, and all ten commits now carry the required Signed-off-by trailer. The focused REST and table lifecycle suites pass under the race detector, package tests and vet are clean, and CI is green.
The PlanIO lifecycle issue is still not fully resolved. ReadTasks acquires a reader lease before returning its lazy iterator, but that lease is released only if the iterator is invoked. If the caller abandons the returned iterator without ranging over it and then calls Scan.Close, the owner is released while the reader count remains permanently nonzero, so PlanIO.Close is never called. The inline comment includes the focused reproduction.
Please also update the PR title and description to represent the end-to-end REST remote scan-planning implementation rather than only partition decode-plan caching, and reference issue #1178 as required by CONTRIBUTING.md.
This review was drafted by an AI-assisted tool and confirmed by an Apache Iceberg Go maintainer.
More on how Apache Iceberg Go handles contributions:
https://github.com/apache/iceberg-go/blob/main/CONTRIBUTING.md
| } | ||
|
|
||
| // Close releases the plan-scoped resources owned by this scan. It is safe to | ||
| // call more than once. Active ReadTasks iterators retain their reader lease and |
There was a problem hiding this comment.
The active-iterator guarantee leaves an unreleasable edge: ReadTasks increments the reader count before returning its lazy iter.Seq2, while releasePlanIOAfter runs only if that sequence is invoked. If the caller receives the iterator, never ranges over it, and calls Scan.Close, the owner is released but the reader count remains permanently nonzero; PlanIO.Close is never called, and iter.Seq2 has no separate close operation. I reproduced this with a focused test. Please defer acquisition until iteration starts or otherwise provide deterministic abandonment cleanup.
Signed-off-by: Minh Vu <vuhoangminh97@gmail.com>
Signed-off-by: Minh Vu <vuhoangminh97@gmail.com>
Signed-off-by: Minh Vu <vuhoangminh97@gmail.com>
315d35f to
d56b266
Compare
zeroshade
left a comment
There was a problem hiding this comment.
This PR was reduced to the cache-only change, so I want to be explicit about what happened to my earlier review: my PlanIO lifetime, null fetch-response, and end-to-end title/description findings were raised against the much larger earlier version of this PR. That implementation has since landed via merged and split-out work, and none of those files or lines are in this six-file diff. Those findings are therefore no longer in scope rather than outstanding, and I'm clearing my CHANGES_REQUESTED accordingly.
On the change that is actually here, the partition decode-plan cache looks correct:
- Key correctness — all envelopes in one remote scan share the same immutable table metadata, and Iceberg partition-spec IDs uniquely identify specs within that metadata, so
specIDis a collision-free key for this scope. - Lifetime and memory —
remoteScanTaskscreates one map for the sequential envelope walk andDecodeScanTaskscreates one per call. It is not process-global: the map is discarded when the decode operation ends and holds only the distinct spec IDs actually encountered, bounded by the finite spec set. No eviction or invalidation is needed for that lifetime. - Concurrency — the cache never escapes the decode call and all access is sequential; concurrent scans get independent caches. No mutex or
sync.Maprequired. - Isolation — entries retain only immutable spec/type/logical metadata, while decoded partition values and output maps stay per-file allocations.
Benchmark evidence is present (Apple M1 Pro, GOMAXPROCS=1, five samples: 64 tasks 365 µs → 241 µs; 1024 tasks 4.23 ms → 3.82 ms; allocations 2,049 → 1,862) and the patch adds BenchmarkDecodeScanTasks. Checked-in raw output or a benchstat comparison would be a nice-to-have for reproducibility — explicitly not a condition of this approval.
CI is green across all 15 checks. Thanks for splitting this out; the narrowed scope made it much easier to review.
This review was drafted by an AI-assisted tool and confirmed by an Apache Iceberg Go maintainer, who has read the findings and signed off. If something feels off, please reply on the PR and a maintainer will follow up.
More on how to contribute to Apache Iceberg Go: CONTRIBUTING.md
Summary
Performance
Compared with the parent commit on an Apple M1 Pro, using
GOMAXPROCS=1and 5 benchmark samples:The cache is shared across inline and fetched task envelopes, so the setup cost is paid once per spec for a remote scan.
Testing