Skip to content

[c++] Add write callbacks with batch completion dispatch - #4328

Open
naivedogger wants to merge 17 commits into
apache:mainfrom
naivedogger:feature/rust-batch-write-callbacks
Open

naivedogger wants to merge 17 commits into
apache:mainfrom
naivedogger:feature/rust-batch-write-callbacks

Conversation

@naivedogger

Copy link
Copy Markdown
Contributor

Purpose

Linked issue: close #4276

Add C++ write callbacks so applications can handle write completion without maintaining a separate WriteResult waiting queue.

Brief change log

  • Add callback overloads for Append, AppendArrowBatch, Upsert, and Delete.
  • Register callbacks directly on Rust write batches and dispatch them through shared workers, avoiding per-record async waiting tasks.
  • Add coverage for registration races, error propagation, result ordering, and callback lifetime and exceptions.

Tests

  • Rust core: 794 tests passed; Rust C++ bindings: 9 tests passed.
  • Existing C++ test binary: 4 non-cluster tests passed.
  • cargo fmt, cargo clippy, and git diff --check passed.
  • No live-cluster tests or full Maven verification for this revision.

API and Format

  • Add C++ callback overloads while preserving existing Wait/await result semantics and retry behavior. Shared completion allocation and notification scheduling change.
  • Return submission status synchronously and report completion through callbacks. Submission errors do not register callbacks; multi-bucket ArrowBatch writes remain non-atomic.
  • Callbacks may execute concurrently and out of order. The completion queue is unbounded, and Flush/destruction do not drain callbacks.
  • The existing Rust shutdown-retry issue, delivery deadlines, and durable recovery are outside this PR's scope.
  • No wire or storage format changes.

Documentation

Update the C++ API reference with callback examples, compatibility notes, partial-failure semantics, and application responsibilities for memory limits and shutdown.

@naivedogger
naivedogger force-pushed the feature/rust-batch-write-callbacks branch from db30250 to e0d7dd6 Compare September 17, 2026 05:56
@naivedogger

Copy link
Copy Markdown
Contributor Author

@fresh-borzoni @loserwang1024 @leekeiabstraction, Appreciate a review here, thanks! 🙏

@leonardBang
leonardBang self-requested a review September 18, 2026 06:41

@fresh-borzoni fresh-borzoni left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@naivedogger Thank you, left a comment, PTAL

Comment thread fluss-rust/bindings/cpp/src/write_callback.hpp Outdated
@naivedogger

Copy link
Copy Markdown
Contributor Author

@fresh-borzoni thanks for the earlier review. I pushed three commits on top. enqueue_timeout now bounds the whole callback submission, both callback capacity and buffer backpressure, in the max.block.ms style, and a zero timeout makes submission non-blocking. The callback executor thread count is now overridable through an advanced FLUSS_CALLBACK_WORKERS variable, with the default of four unchanged.

I also expanded the docs and example around callback behavior: callbacks run on a small shared pool and should stay short and non-blocking, failures should be recorded and handled outside the callback with dedup by id, and crash recovery should come from a replayable source that advances only after Flush. When you have time, another look would be appreciated.

@naivedogger
naivedogger force-pushed the feature/rust-batch-write-callbacks branch 2 times, most recently from 77113f3 to ac3775e Compare September 20, 2026 12:48
Make WriteCallbackOptions::enqueue_timeout cover the whole submission,
both callback capacity and buffer backpressure, following the Kafka
max.block.ms model. The callback path passes a submit budget through the
FFI so the Rust buffer-memory wait is bounded by the remaining budget,
while the public overloads keep the writer's configured buffer wait
timeout. A zero timeout makes submission non-blocking.
Read the process-wide callback executor thread count from the advanced
FLUSS_CALLBACK_WORKERS environment variable, falling back to the default
when it is unset, invalid, or zero. This is a rarely needed escape hatch;
the default of four workers is unchanged.
Explain that callbacks run on a small shared executor pool and must stay
short and non-blocking, and note the advanced FLUSS_CALLBACK_WORKERS knob.
Clarify failure handling: record the outcome and either stop or retry
outside the callback, deduplicating by identifier, and drive crash
recovery from a replayable source that advances only after Flush. Update
the enqueue_timeout wording and the example comments to match.
@naivedogger
naivedogger force-pushed the feature/rust-batch-write-callbacks branch from ac3775e to f332fe3 Compare September 21, 2026 02:20
@loserwang1024
loserwang1024 self-requested a review September 21, 2026 11:32
@loserwang1024

loserwang1024 commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

pushed three commits on top. enqueue_timeout now bounds the whole callback submission, both callback capacity and buffer backpressure,

I do think client.writer.buffer.wait-timeout is enough, no need top. enqueue_timeout anymore

Remove the WriteCallbackOptions enqueue_timeout field and route the whole
callback submit through the connection's client.writer.buffer.wait-timeout.
The capacity reservation and the buffer-backpressure wait now share one
deadline sourced from that setting, so a submit returns within a single
timeout instead of two. UINT64_MAX keeps the default unbounded and a zero
timeout makes submission non-blocking.
…bmit

Set writer_buffer_wait_timeout_ms in the example config so its role is
visible: it bounds both the write-buffer wait and the whole callback
submission. Add an end-to-end test that fills a writer's capacity with a
blocking callback and asserts the next submit returns with a timeout error
after the configured budget, not after the callback finally releases the slot.
@naivedogger

Copy link
Copy Markdown
Contributor Author

@loserwang1024 I think you’re right. I’ve updated it to use client.writer.buffer.wait-timeout.

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.

[Feature] C++ client: provide async/callback API for write result instead of blocking Wait()

3 participants