Skip to content

fix(csharp): tear down in-flight CloudFetch on connection dispose and statement cancel/dispose - #659

Open
eric-wang-1990 wants to merge 20 commits into
mainfrom
eric-wang/csharp-fix-cloudfetch-dispose-hang
Open

fix(csharp): tear down in-flight CloudFetch on connection dispose and statement cancel/dispose#659
eric-wang-1990 wants to merge 20 commits into
mainfrom
eric-wang/csharp-fix-cloudfetch-dispose-hang

Conversation

@eric-wang-1990

@eric-wang-1990 eric-wang-1990 commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

Problem

ConcurrencyStressTests.CloseConnection_DuringCloudFetch_ShouldNotHang (Thrift) flakes with:

Connection close during CloudFetch hung for more than 60000ms. Query completed: False, Dispose completed: True.

It bounced PR #654 out of the merge queue. The failure is unrelated to #654 — it's a pre-existing, intermittent hang in the CloudFetch shutdown path on main.

Root cause

The CloudFetch pipeline runs on background tasks driven by a cancellation token that CloudFetchDownloadManager.StartAsync created itself and linked to nothing external. The only thing that cancelled it was disposing the reader — owned by the blocked query task, so it can't dispose until the read loop returns. connection.Dispose() disposes the shared HttpClient and closes the session but never cancels the pipeline, so the in-flight download fails on the dead HttpClient and the retry loop spins for the retry timeout (minutes) while the reader is parked on DownloadCompletedTask. Intermittent because it only fires when Dispose() lands mid-download.

Fix — full connection ⊃ statement ⊃ cloudfetch cancel cascade, both protocols

  • Connection: DatabricksConnection (Thrift) and StatementExecutionConnection (SEA) each get a CloudFetchShutdownToken (a CancellationTokenSource cancelled at the top of Dispose()).
  • Statement: DatabricksStatement and StatementExecutionStatement each get a statement-lifetime CTS linked to their connection's shutdown token, cancelled in Cancel() and Dispose(), and refreshed per-execute (a reusable statement mustn't be poisoned by a prior Cancel()). This is distinct from the per-execute token, which is disposed when execution returns and so can't cover the CloudFetch result-fetch phase.
  • Pipeline: CloudFetchDownloadManager.StartAsync(CancellationToken) builds its source as a linked source; both reader factories pass the statement token in.
  • Prompt stop even mid-buffer: CloudFetchDownloadManager exposes PipelineToken; CloudFetchReader links it with the caller token and checks it at the top of each read-loop iteration, so a cancel stops the read immediately instead of draining the in-memory buffer.

Net: closing a connection, or cancelling/disposing a statement, tears the pipeline down and the reader unblocks in milliseconds. A healthy read that never cancels is unaffected — the tokens only fire on explicit dispose/cancel (no timers). The only wall-clock bound on CloudFetch remains the pre-existing query timeout (ThriftResultFetcher / SEA, default ~3h).

Testing (real warehouse, both Thrift and SEA)

  • New unit StartAsync_TokenCancelled_UnblocksReaderWaitingForNextFile: times out (5s) without the fix, ~0.2s with it.
  • New E2E CancelStatement_DuringCloudFetch_ShouldStopPromptly: without the statement wiring it never stops (33s timeout on Thrift; read 67M rows on SEA); with it, ~5-20s. Verified as a real guard on both protocols.
  • CloseConnection_DuringCloudFetch_ShouldNotHang: passes repeatedly on Thrift (~2-6s) and SEA (~1s).
  • Full-read CloudFetchE2ETest.TestCloudFetch (10 cases) passes on both protocols — no regression to normal reads.
  • Unit suite: 926 green.

This pull request and its description were written by Isaac.

Closing a DatabricksConnection while a CloudFetch query was streaming could
hang the reader. The CloudFetch pipeline ran on background tasks driven by a
cancellation token that CloudFetchDownloadManager.StartAsync created itself and
linked to nothing external. The only thing that cancelled it was disposing the
reader — but the reader is owned by the query task, which is blocked awaiting
the next chunk. connection.Dispose() disposed the shared HttpClient and closed
the session, but never cancelled the pipeline or reached the active reader, so
the in-flight download failed on the dead HttpClient and DownloadFileAsync
retry-spun for up to the retry timeout (minutes) while the reader stayed parked
on DownloadCompletedTask — blowing the 60s limit in
CloseConnection_DuringCloudFetch_ShouldNotHang. Intermittent because it only
fires when Dispose lands mid-download.

Give connection shutdown a way to cancel the pipeline: add a connection-scoped
CancellationTokenSource cancelled at the top of Dispose (before the HttpClient
is torn down), and have CloudFetchDownloadManager.StartAsync link the caller's
token into its own source. On dispose the download loop exits and completes the
result queue (unblocking the reader's Take) and any in-flight download faults
DownloadCompletedTask (unblocking the reader's await), so the read task ends
promptly instead of spinning on retries. Wired on the Thrift path; SEA uses a
separate connection and already passed, so its call keeps the default token.

Add a deterministic unit test that cancels the StartAsync token and asserts a
reader parked in GetNextDownloadedFileAsync unblocks within a short bound
(times out without the fix, passes with it).

Co-authored-by: Isaac

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: 1 Low

Looks good — a well-scoped, correctly-implemented fix for the CloudFetch dispose hang. The linked-token approach is sound, the SEA default-token path stays behavior-equivalent (non-breaking), per-query registrations don't accumulate on the connection token (linked CTS is disposed in the manager's Stop/Dispose), and the new regression test is deterministic (the parked read cannot complete without cancellation, so its timing checks aren't flaky). Only one minor, non-blocking ordering note filed inline.

Comment thread csharp/src/DatabricksConnection.cs Outdated
@eric-wang-1990 eric-wang-1990 added the engineer-bot engineer-bot may fix this issue / take over this PR label Aug 28, 2026
Addresses:
  - #3876787273 at csharp/src/DatabricksConnection.cs:1241

Signed-off-by: peco-engineer-bot[bot] <peco-engineer-bot[bot]@users.noreply.github.com>

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ No issues identified by the review bot.

…cade)

Extends the connection-level CloudFetch cancellation to the statement level, so
the full connection ⊃ statement ⊃ cloudfetch cancel cascade holds:

- DatabricksStatement gets a statement-lifetime CancellationTokenSource created
  linked to the connection's shutdown token, cancelled in Cancel() and Dispose()
  and disposed in Dispose(). It is distinct from the base
  HiveServer2Statement._executeTokenSource, which is disposed when ExecuteQuery()
  returns and so cannot cover the later CloudFetch result-fetch phase.
- The Thrift reader factory passes this statement token into the pipeline instead
  of the raw connection token. Because the statement token is linked to the
  connection token, connection dispose still cancels every statement's downloads
  (preserving the prior fix), and cancelling/disposing a single statement now
  stops just its downloads.

Also make the reader observe cancellation so a cancel stops the read promptly
even while draining already-buffered chunks, not only when it next blocks for a
download: CloudFetchDownloadManager exposes the pipeline token (PipelineToken),
and CloudFetchReader links it with the caller's token and checks it at the top of
each read-loop iteration. Without this, a statement cancel during a large stream
would keep returning buffered rows until the in-memory buffer drained.

Add E2E CancelStatement_DuringCloudFetch_ShouldStopPromptly: reads the first
CloudFetch batch of a huge RANGE, cancels the statement, and asserts the read
ends well within the window (it does not without the fix — the read keeps going
until the timeout). Full unit suite (925) and full-read CloudFetch E2E pass.

Co-authored-by: Isaac
@eric-wang-1990 eric-wang-1990 changed the title fix(csharp): cancel in-flight CloudFetch pipeline on connection dispose fix(csharp): tear down in-flight CloudFetch on connection dispose and statement cancel/dispose Aug 28, 2026

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: 1 Low

Looks good — a well-scoped fix for the CloudFetch shutdown hang; the connection⊃statement⊃pipeline linked-CTS cascade is sound, the PipelineToken getter handles the disposed-CTS race, and the reader now observes cancellation while draining buffered chunks. One low-severity consistency issue: the statement's CTS Dispose() is placed after (unguarded by) the telemetry emit, unlike the connection which the author deliberately guarded against exactly that throw.

Comment thread csharp/src/DatabricksStatement.cs Outdated
Addresses:
  - #3877046084 at csharp/src/DatabricksStatement.cs:1497

Signed-off-by: peco-engineer-bot[bot] <peco-engineer-bot[bot]@users.noreply.github.com>

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: 1 Medium · 1 Low

Solid, well-motivated fix for the CloudFetch teardown hang, with good tests. One medium concern: the statement-lifetime CloudFetch CTS is never recreated, so Cancel() permanently poisons the CloudFetch path and breaks statement reuse-after-cancel (F1). Also flagging that the connection-dispose cascade is Thrift-only — the SEA reader path is left untokenized (F2).

Comment thread csharp/src/DatabricksStatement.cs Outdated
Comment thread csharp/src/Reader/CloudFetch/CloudFetchReaderFactory.cs Outdated
Addresses:
  - #3877068667 at csharp/src/DatabricksStatement.cs:1525
  - #3877068674 at csharp/src/Reader/CloudFetch/CloudFetchReaderFactory.cs:119

Signed-off-by: peco-engineer-bot[bot] <peco-engineer-bot[bot]@users.noreply.github.com>

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: 1 Medium · 1 Low

Solid, well-reasoned fix for the CloudFetch teardown hang — the linked-CTS cascade, ODE-guarded PipelineToken, and dispose ordering all check out, and the download manager disposes its linked source so no token registration leaks. Two concerns: Cancel() cancels the pipeline CTS after the throwing base.Cancel() RPC (medium — teardown is skipped in the very failure path the fix targets), and RefreshCloudFetchStatementCts disposing the previous CTS can sever a still-open reader's link to the connection shutdown token on re-execute (low).

Comment thread csharp/src/DatabricksStatement.cs Outdated
Comment thread csharp/src/DatabricksStatement.cs Outdated
Addresses:
  - #3877106059 at csharp/src/DatabricksStatement.cs:1553
  - #3877106064 at csharp/src/DatabricksStatement.cs:170

Signed-off-by: peco-engineer-bot[bot] <peco-engineer-bot[bot]@users.noreply.github.com>

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ No issues identified by the review bot.

CreateThriftReader is Thrift-only and already hard-casts statement.Connection to
DatabricksConnection, so the statement is always a DatabricksStatement there. The
`statement is DatabricksStatement ? ... : connection.CloudFetchShutdownToken`
fallback was unreachable — replace it with a direct cast, matching the existing
connection cast.

Co-authored-by: Isaac

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: 1 Low

Looks good — solid, well-documented fix for the CloudFetch teardown hang; the cancel cascade (connection ⊃ statement ⊃ pipeline), the per-execute CTS refresh, and the reader-level prompt-stop are all sound, and the two documented gaps (open-reader re-execute detaching from the connection cascade; SEA path left unlinked) are honestly called out in the code. One Low thread-safety note on the unsynchronized _cloudFetchStatementCts field swap racing a cross-thread Cancel() is filed inline.

Comment thread csharp/src/DatabricksStatement.cs Outdated
Addresses:
  - #3882435357 at csharp/src/DatabricksStatement.cs:171

Signed-off-by: peco-engineer-bot[bot] <peco-engineer-bot[bot]@users.noreply.github.com>

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: 1 Low

Looks good — a clean, well-documented fix for the CloudFetch shutdown hang. The connection ⊃ statement ⊃ cloudfetch cancel cascade is coherent: I confirmed the downloader's retry loop (CloudFetchDownloader.cs:595/:771) actually observes the linked pipeline token, so the cancel truly breaks the retry-spin, and the reader/refresh/lock discipline all hold. Only one minor robustness note: the new CloudFetchStatementToken getter isn't defensive against ObjectDisposedException the way its sibling PipelineToken is (currently unreachable in valid usage).

Comment thread csharp/src/DatabricksStatement.cs Outdated
Addresses:
  - #3882499934 at csharp/src/DatabricksStatement.cs:134

Signed-off-by: peco-engineer-bot[bot] <peco-engineer-bot[bot]@users.noreply.github.com>

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: 1 Low

Looks good — a solid, well-documented fix for the CloudFetch shutdown hang with the connection ⊃ statement ⊃ cloudfetch cancel cascade, good test coverage (unit + E2E), and correct linked-CTS lifecycle handling. One low-severity consistency gap: the new CloudFetchShutdownToken getter lacks the post-dispose ObjectDisposedExceptionNone guard that its two sibling token getters intentionally have. The SEA path being left out of the cascade is clearly documented and reasonable to defer.

Comment thread csharp/src/DatabricksConnection.cs Outdated
Addresses:
  - #3882538923 at csharp/src/DatabricksConnection.cs:137

Signed-off-by: peco-engineer-bot[bot] <peco-engineer-bot[bot]@users.noreply.github.com>

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ No issues identified by the review bot.

The Thrift path got the connection ⊃ statement ⊃ cloudfetch cancel cascade;
the SEA (StatementExecution) path had the same latent gap — statement.Cancel()
during CloudFetch streaming was a no-op (its _executeCts is disposed once
results stream, and the pipeline was started with no token), so a cancel left
downloads running (verified: an E2E cancel test read 67M rows and never stopped
on SEA). Mirror the Thrift wiring so both protocols behave identically:

- StatementExecutionConnection gets a CloudFetchShutdownToken, cancelled at the
  top of Dispose() and disposed at the end.
- StatementExecutionStatement gets a statement-lifetime CTS linked to that token,
  refreshed per-execute (ExecuteQueryAsync/ExecuteUpdateAsync) so a reused
  statement isn't poisoned by a prior Cancel(), cancelled in Cancel() and
  Dispose() (before the no-statement early-return so the linked registration is
  always freed).
- CreateStatementExecutionReader passes the statement token into StartAsync,
  replacing the no-token call (and the comment that scoped SEA out).

Verified on a real warehouse: SEA CancelStatement_DuringCloudFetch now stops
promptly (~17s vs never), SEA CloseConnection still passes (~1s), SEA full-read
CloudFetchE2ETest.TestCloudFetch (10 cases) unaffected; Thrift unchanged; unit
suite 926 green.

Co-authored-by: Isaac

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: 1 Low

Looks good — a well-constructed connection ⊃ statement ⊃ cloudfetch cancel cascade with sound locking, per-execute CTS refresh, symmetric Thrift/SEA wiring, and defensive post-dispose token guards; the reader's linked-CTS + top-of-loop ThrowIfCancellationRequested correctly delivers the mid-buffer prompt-stop, and normal reads remain unaffected. One low-severity note inline about the narrow catch (ObjectDisposedException) around CancellationTokenSource.Cancel() in the Dispose teardown paths. Nit (not inline): the new E2E test CancelStatement_DuringCloudFetch_ShouldStopPromptly never disposes its ManualResetEventSlim queryStarted.

Comment thread csharp/src/DatabricksConnection.cs
Addresses:
  - #3888803204 at csharp/src/DatabricksConnection.cs:1221

Signed-off-by: peco-engineer-bot[bot] <peco-engineer-bot[bot]@users.noreply.github.com>

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: 1 Low

Looks good — a well-constructed, defensively-documented fix for the CloudFetch shutdown hang. The connection ⊃ statement ⊃ pipeline linked-token cascade is correct: per-execute CTS refresh keeps statements reusable after cancel, teardown cancels are best-effort so HttpClient/session cleanup can't be skipped, read-after-dispose is guarded to CancellationToken.None, and the reader now observes the pipeline token plus passes it into GetNextDownloadedFileAsync (I confirmed the parked DownloadCompletedTask await still unblocks because the downloader faults the task on cancel). Only one minor nit: the SEA connection's shutdown-cancel telemetry tag shape diverges from the sibling paths (see inline).

Comment thread csharp/src/StatementExecution/StatementExecutionConnection.cs
Addresses:
  - #3888821600 at csharp/src/StatementExecution/StatementExecutionConnection.cs:1216

Signed-off-by: peco-engineer-bot[bot] <peco-engineer-bot[bot]@users.noreply.github.com>

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: 1 Low

Looks good — solid, well-documented fix for the CloudFetch shutdown hang. The connection ⊃ statement ⊃ cloudfetch linked-token cascade is correctly wired: the download manager's linked CTS is disposed in StopAsync/Dispose, token getters guard ObjectDisposedException, the per-execute refresh swaps under a lock, and both production StartAsync callers pass the statement token so the cascade is never silently dropped. One low-severity note inline about misleading error telemetry on repeated dispose. Nit (no anchor needed): StartAsync(CancellationToken = default) makes an unlinked pipeline the silent default — fine today since both callers pass a token, but a future caller that omits it would quietly regress the cascade; consider making the token required.

Comment thread csharp/src/DatabricksStatement.cs
Addresses:
  - #3888832882 at csharp/src/DatabricksStatement.cs:1538

Signed-off-by: peco-engineer-bot[bot] <peco-engineer-bot[bot]@users.noreply.github.com>

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: 1 Low

Solid, carefully-implemented fix — the connection ⊃ statement ⊃ cloudfetch cancel cascade is wired correctly (linked CTS in StartAsync, in-flight downloads faulted on cancel, guarded per-execute CTS refresh, unconditional disposal), and I found no correctness or resource-leak defects in the cancellation/disposal paths. One low-severity behavioral note: a statement.Cancel() while the reader is parked waiting for the next file surfaces as a silent clean EOF (partial results, no exception) rather than an OperationCanceledException, so cancellation is surfaced inconsistently depending on reader timing. Nit (not inline): the new E2E CancelStatement_DuringCloudFetch_ShouldStopPromptly has a fairly tight margin (reported 5–20s vs a 30s timeout) on the SEA path, though it's a live-workspace SkippableFact not run in ordinary CI.

Comment thread csharp/src/Reader/CloudFetch/CloudFetchReader.cs
Addresses:
  - #3888848391 at csharp/src/Reader/CloudFetch/CloudFetchReader.cs:163

Signed-off-by: peco-engineer-bot[bot] <peco-engineer-bot[bot]@users.noreply.github.com>

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: 1 Low

Looks good — a well-structured, heavily-documented fix for the CloudFetch shutdown hang, with a correct connection ⊃ statement ⊃ cloudfetch cancel cascade mirrored on both Thrift and SEA. I verified token-field locking is consistent per class, dispose ordering frees linked-token registrations unconditionally, the reader's cancelled-vs-EOF null distinction is sound, and the ICloudFetchDownloadManager change is backward-compatible (no hand-written test doubles). Only one low-severity note about a newly-introduced concrete cast whose safety rests on the current single caller.

Comment thread csharp/src/Reader/CloudFetch/CloudFetchReaderFactory.cs Outdated
Addresses:
  - #3888866814 at csharp/src/Reader/CloudFetch/CloudFetchReaderFactory.cs:218

Signed-off-by: peco-engineer-bot[bot] <peco-engineer-bot[bot]@users.noreply.github.com>

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: 1 Low

Looks good — a well-engineered, carefully-documented fix for the CloudFetch dispose/cancel hang, with one low-severity robustness note. The connection⊃statement⊃cloudfetch cancel cascade, cancel-before-teardown ordering, ObjectDisposedException-tolerant token getters, per-execute CTS refresh, and lock-guarded field swaps all check out on both Thrift and SEA paths; the StartAsync(CancellationToken = default) signature change is backward compatible and the new unit/E2E tests target the real regression.

Comment thread csharp/src/Reader/CloudFetch/CloudFetchReader.cs
Addresses:
  - #3888880902 at csharp/src/Reader/CloudFetch/CloudFetchReader.cs:171

Signed-off-by: peco-engineer-bot[bot] <peco-engineer-bot[bot]@users.noreply.github.com>

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: 1 Low

Looks good — a well-structured connection ⊃ statement ⊃ cloudfetch cancel cascade with careful locking around the per-statement CTS refresh/swap and correct linked-token teardown; AwaitWithCancellationAsync handles the completed/faulted/cancelled cases correctly and the manager disposes its linked CTS. One low-severity inconsistency: the connection dispose paths catch only generic Exception around the shutdown-CTS Cancel(), so a double-dispose emits a spurious error telemetry event, whereas the statement paths deliberately swallow ObjectDisposedException. Nit: CreateThriftReader casts (DatabricksStatement)statement unconditionally while CreateStatementExecutionReader guards the cast with a clear-message throw — harmless but asymmetric.

Comment thread csharp/src/DatabricksConnection.cs
Addresses:
  - #3888896539 at csharp/src/DatabricksConnection.cs:1225

Signed-off-by: peco-engineer-bot[bot] <peco-engineer-bot[bot]@users.noreply.github.com>

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: 1 Low

Looks good — a solid, thoroughly-documented fix for the CloudFetch dispose/cancel hang. The connection⊃statement⊃cloudfetch token cascade is wired correctly on both protocols (both StartAsync callers pass the statement token; RefreshCloudFetchStatementCts runs on every execute path; token getters defensively map ObjectDisposedExceptionNone), the reader's cancelled-null vs EOF distinction prevents silent truncation, and AwaitWithCancellationAsync correctly avoids leaking the delay timer. One low-severity note: a cross-thread Cancel() racing Dispose() emits a spurious error telemetry event for a benign ObjectDisposedException, since Cancel() (unlike Dispose()) doesn't special-case it.

Comment thread csharp/src/DatabricksStatement.cs
Addresses:
  - #3888907934 at csharp/src/DatabricksStatement.cs:1642

Signed-off-by: peco-engineer-bot[bot] <peco-engineer-bot[bot]@users.noreply.github.com>

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: 1 Low

Solid, carefully-documented fix for the CloudFetch teardown hang — the connection ⊃ statement ⊃ cloudfetch cancel cascade, linked-CTS refresh-per-execute, and the AwaitWithCancellationAsync netstandard shim all look correct, and the lock scoping around the per-statement CTS swap is sound. One low-severity edge case: the cancel path now abandons the in-flight DownloadCompletedTask, which can leave a later download fault unobserved.

Comment thread csharp/src/Reader/CloudFetch/CloudFetchReader.cs
Addresses:
  - #3888924976 at csharp/src/Reader/CloudFetch/CloudFetchReader.cs:262

Signed-off-by: peco-engineer-bot[bot] <peco-engineer-bot[bot]@users.noreply.github.com>

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ No issues identified by the review bot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

engineer-bot engineer-bot may fix this issue / take over this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant