GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | ||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | ||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — perf(thrift): prefetch CloudFetch links while earlier files download by vuanhphung · Pull Request #1038 · databricks/databricks-sql-python Conversation
|
| Workload | FetchResults | fetch p50 | fetch p95 | peak RSS |
|---|---|---|---|---|
lineitem, 4.9 GB, fetchall_arrow, n=5 |
26 → 41 | 13.27 → 10.66 s | 14.59 → 15.28 s | 5648 → 5663 MB |
⅓ of lineitem, 1.6 GB, fetchall_arrow, n=5 |
8 → 13 | 4.40 → 3.29 s | 7.87 → 3.77 s | 2399 → 2379 MB |
⅓ of lineitem, 1.6 GB, fetchmany_arrow(1M), n=3 |
8 → 13 | 4.55 → 3.59 s | 4.66 → 3.68 s | 1244 → 1304 MB |
The 4.9 GB p95 comes from one 16.4 s run; the other four were 9.4–10.8 s. An earlier run of the same A/B, before the chunked reads, measured 13.96 → 9.63 s p50 and 18.06 → 10.63 s p95. Chunked reads keep connections in reuse: draining 1.6 GB (85 files) opened 6 new HTTPS connections on both #1030 and this PR.
Early close after 500k rows of the 1.6 GB result (n=3 medians):
| main | #1030 | this PR | |
|---|---|---|---|
| downloads in flight at close | 0 | 6 | 8 |
| MB read in the 5 s after close | 0 | 45 | 6 |
| RSS 5 s after close | 312 MB | 514 MB | 292 MB |
close() |
0.14 s | 0.13 s | 0.12 s |
| exit lag when the process exits right after close | 0.20 s | 0.47 s | 0.09 s |
cancel() 1.5 s into fetchall_arrow made the fetch fail 0.29 / 0.44 / 0.19 s later on main / #1030 / this PR. This PR raises OperationalError, or RequestError when the next FetchResults fails first.
With #1032 applied on top (locally, n=3, 1.6 GB): peak RSS 1312 → 642 MB for fetchmany_arrow(1M) and 2376 → 1860 MB for fetchall_arrow. Fetch time stayed within noise: 4.69 → 3.34 s and 3.56 → 5.71 s, the latter with one 10 s outlier; an earlier run measured 3.96 → 3.13 s.
New unit tests in test_cloudfetch_prefetch.py cover the refill rule: links are requested only when a slot is free and nothing is pending, there are never more than max_download_threads tasks, and row order is kept across refills. They also check that an empty reply doesn't cause a busy loop, the link budget and its clamp, the offset check, the handoff, and the kill switch. On the cancel side they cover:
cancel() during a link fetch, and a scheduled download cancelled before it starts;Cursor.cancel() cancelling the active result set's downloads. perf(thrift): request about one CloudFetch file per download thread #1030's per-batch tests now run with the kill switch. The full unit suite passes, and perf(cloudfetch): cut peak memory of large CloudFetch results #1032's tests pass with both PRs applied. Against a live warehouse, the test_driver.py e2e tests for large wide/narrow results, async large results, fetchone/fetchall/fetchmany/iteration, Arrow fetches, row limits and cancel pass (31 passed, Thrift and SEA).Manual live checks on a 30M-row, 32-link result verified exact row counts and order across batches for fetchall_arrow, fetchmany_arrow, mixed fetchmany/fetchone, and fetchall. They also covered close() and cancel() mid-stream, and use_cloud_fetch=False, with prefetch on and with the kill switch.
Stacked on #1030. Related: #1032 (CloudFetch decompression into private mmaps).
This pull request and its description were written by Isaac.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
Each FetchResults batch waited for its slowest file before the next batch was requested, so on average only about half of the download threads were busy. The download manager now asks the result set for more links whenever a slot frees up and no links are pending (one per free slot plus half a pool), using one thread pool for the whole result. _cloudfetch_prefetch=False restores per-batch fetching. Closing a result set now sets a cancel event that downloads check before the GET and before decompression, and cancels queued downloads. Signed-off-by: Vu Anh Phung <[email protected]> Co-authored-by: Isaac <[email protected]>
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Verdict: 1 Medium · 2 Low
Looks good overall, with one medium concern: the prefetch path discards any part of a FetchResults reply that isn't a CloudFetch link. Ordering, the memory bound and cancel-on-close look right. The kill switch is wired through every result-set constructor and has tests.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
fetch_result_links raises DataError if a reply carries rows that are not CloudFetch links, instead of dropping them. set_link_source refuses a manager that is already shut down, so such a result keeps per-batch fetching. Pin that a failed link fetch reaches the caller and that fetching can resume. Signed-off-by: Vu Anh Phung <[email protected]> Co-authored-by: Isaac <[email protected]>
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Verdict: 2 Low
Looks good overall, with two low-severity notes: a re-poll edge case in _schedule_downloads, and no end-to-end unit test of the result set handing link fetching to the queue. Ordering, the memory bound, cancel-on-close and the kill switch all behave as the PR describes. Nit: the link source is attached only after ThriftCloudFetchQueue.__init__ waits for the first file, so the first prefetch can't start until that download finishes.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Base the early exit on whether a download was scheduled, so a reply whose links all have zero rows waits for the next call like an empty reply. Add end-to-end tests that run ThriftResultSet over a real CloudFetch queue and download manager across several link fetches. Signed-off-by: Vu Anh Phung <[email protected]> Co-authored-by: Isaac <[email protected]>
|
On the review-body nit (the first prefetch waits until |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Verdict: 1 Low
Looks good overall, with one low concern: the manager does not check whether it was cancelled after the link-fetch call returns. Prefetch is wired correctly through _start_prefetch and the has_more_rows hand-off. The kill switch keeps the old per-batch path, and the tests cover ordering, empty replies, errors, budget clamping and cancel-on-close.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
A read after cancel() now fails only when the cancel left the result incomplete (links pending, more on the server, or a download it stopped). Cancelling a statement whose result had already arrived no longer turns the end of the result into an error. A link whose submit races with cancel() is kept, so it still counts as missing. Signed-off-by: Vu Anh Phung <[email protected]> Co-authored-by: Isaac <[email protected]>
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Verdict: 2 Low
Looks good overall, with two low-severity notes on the downloader's new chunked read and retry loop. The download manager's prefetch, cancel-race handling (keeping links whose submit failed, keeping the link source attached after a cancel) and row-order logic hold up when traced through, and the new unit tests cover the race paths.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Signed-off-by: Vu Anh Phung <[email protected]> Co-authored-by: Isaac <[email protected]>
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Verdict: 1 Medium · 1 Low
The prefetch design looks solid, with one medium concern: an error from the mid-stream link fetch can silently drop rows when the caller retries. Cancel/close handling, the retry of interrupted body reads, and the kill switch all check out. Unit coverage is generally thorough, apart from the kill-switch forwarding noted in the second finding.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
A link fetch that raises part-way through a read loses the rows that read had collected, so a retry would continue past them. The download manager now raises the same error on every later read. Test the kill switch at all six ThriftResultSet entry points. Signed-off-by: Vu Anh Phung <[email protected]> Co-authored-by: Isaac <[email protected]>
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Verdict: 4 Low
Looks good overall. The prefetch, cancel and retry logic is careful and well tested, including the cancel and shutdown races. I found only low-severity issues: the meaning of has_more_rows has changed, the stored link error is re-raised as the same object, a secondary error in Cursor.cancel() can hide the original one, and body-read retries have no backoff.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Interrupted body reads wait 0.5 s, then 1 s, before retrying; a cancel ends the wait. After a failed mid-stream link fetch, later reads raise a fresh OperationalError chained to it that says to re-execute. A failure while cancelling downloads no longer hides the cancel RPC's own error. Signed-off-by: Vu Anh Phung <[email protected]> Co-authored-by: Isaac <[email protected]>
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Verdict: 1 Low
Looks good: one low-severity issue with how the kill switch reads _cloudfetch_prefetch=None. I checked the prefetch scheduling loop, what happens on cancel or shutdown (including the submit() racing cancel()), chunked body reads and retries in UnifiedHttpClient.request_context, and the has_more_rows handoff in ThriftResultSet. All of them hold up, and tests/unit covers them well.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Document what has_more_rows means after the handoff to the download manager. Signed-off-by: Vu Anh Phung <[email protected]> Co-authored-by: Isaac <[email protected]>
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Verdict: 1 Low · 2 Nit
Looks good overall, with 3 low findings: lenient kill-switch parsing, retry backoff counted in the download-speed metrics, and a new result-set/download-manager reference cycle. The prefetch, cancel and retry logic hangs together well and has thorough unit tests, including the cancel races and the case where a failed link fetch must not let a retry skip rows.
start_time is taken before the retry loop. When a body read is retried, download_duration therefore also covers the failed attempt(s) and the 0.5 s / 1 s backoff waits. _log_download_metrics then reports a lower MB/s and can log the CloudFetch download slower than threshold warning for a file whose successful attempt was fast. To fix it, reset start_time at the start of each attempt, or log the retry separately.set_link_source(self._fetch_more_links) creates a reference cycle: result set → results queue → download_manager._link_source (a bound method) → result set. Before this change, a ThriftResultSet dropped without close() was freed by refcounting, and its ThreadPoolExecutor (up to max_download_threads idle workers) was torn down right away. Now the whole graph, including the executor and its threads, stays alive until the cyclic GC runs. A weakref.WeakMethod (or clearing _link_source once has_more is False, which already happens, plus on close) would avoid holding the result set strongly while a stream is still in progress.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
… attempt Unrecognized string values keep prefetch on but log a warning that names the value. Download speed metrics now cover only the attempt that succeeded, not earlier failed attempts or the backoff. Signed-off-by: Vu Anh Phung <[email protected]> Co-authored-by: Isaac <[email protected]>
|
On the two review-body nits:
|
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Verdict: 1 Low
Looks good, with one low concern: after a failed link fetch, downloads already in flight keep running until close(). The hand-off, cancel/close, retry and kill-switch paths look correct to me. Tests cover them well, including the races with cancel and the sticky error after a failed link fetch.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Signed-off-by: Vu Anh Phung <[email protected]> Co-authored-by: Isaac <[email protected]>
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Verdict: 1 Low
Looks good, with one low concern: what the internal kill switch does with an unrecognized value. I checked the cancel and close races, the order of link fetches against the shutdown and cancel events, the body-read retry and connection release in downloader.py, the handoff of has_more_rows, and how SEA and the per-batch path share the download manager. I found no correctness problems, and the tests cover the new states well.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
A kill switch should fail safe: an unparsable value falls back to per-batch fetching and logs a warning. None still means unset (prefetch on). Signed-off-by: Vu Anh Phung <[email protected]> Co-authored-by: Isaac <[email protected]>
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Verdict: 1 Medium · 2 Low
Solid change overall: prefetching, ordering, the cancel/close paths and the kill switch are carefully done and well tested. One medium concern: a failed link fetch now aborts reads of files that are already downloaded. There are also two minor robustness gaps.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Signed-off-by: Vu Anh Phung <[email protected]> Co-authored-by: Isaac <[email protected]>
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Verdict: 2 Low
Looks good overall: the prefetch hand-off, the cancel and close semantics, and the chunked body read with retries are carefully designed and well tested. Two low findings: the first link prefetch waits until the first file has downloaded and the caller reads again, and the hot-loop case (no downloads in flight, server keeps returning no links) has no test.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
A failed mid-stream link fetch now stops scheduling and is raised only once the files already downloading run out, so a reader that never needs the missing rows still succeeds, as on main. The first read that needs them gets the original error and later reads fail too. Signed-off-by: Vu Anh Phung <[email protected]> Co-authored-by: Isaac <[email protected]>
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Verdict: 2 Low
Looks solid overall. The cancel and close paths, the link-error latching and the ordering guarantees are well thought through and well tested. I found two low-severity issues: the meaning of the public has_more_rows attribute has changed, and the chunked body read briefly doubles memory per download.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
a83f50f7
Deployed
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
There was an error while loading. Please reload this page.