Thanks Anton for the deep review — great catches, all points are now updated in the FIP-49. 1. the new cooperative backpressure / request timeout on writes Both need client-side capabilities that fluss-rust doesn't have yet. I've filed issues to implement them — #3855 <https://github.com/apache/fluss/issues/3855>(cooperative backpressure, client side of #3463) and #3861 <https://github.com/apache/fluss/issues/3861> (a delivery.timeout.ms-style delivery bound) — under the umbrella #3854 <https://github.com/apache/fluss/issues/3854>, and the REST gateway will build on these capabilities.On top of them, the FIP-49 will run every write under an end-to-end deadline (gateway.rest.write.request-timeout, default 30s), with client delivery bounded below it. Pressure shows up as bounded latency, observable could via re-exported client metrics; hard rejections surface as a retriable entry-level storage_backpressure, so callers retry just those entries. At the deadline, in-flight entries are reported timeout (indeterminate); never-sent entries fail deterministically.
2. connections in service mode / per-user authorization The FIP-49 design is simply two identity modes. service: one shared connection per cluster — so you're right, connection.max can't fire there; it and idle-timeout are scoped to user mode only. user: multiple connections per cluster, one per user id, using impersonation — the connection authenticates as a super-user and carries the end user as the SASL/PLAIN authorization id, so Fluss authorizes each request as that user against its own ACLs. That is the per-user authorization path; prerequisite is #3857 <https://github.com/apache/fluss/issues/3837>, and user mode fails at startup until it's integrated. 4.which tables get per-row results? That was a wording error in the FIP — the REST surface is JSON-only and all writes are row-by-row, so every table gets per-row results. Corrected: the append_arrow_batch path is dropped from this FIP (deferred to a future columnar content type); the accumulator still batches per bucket, so nothing is lost. 5.duplicates on client retry The FIP-49 design is at-least-once — HTTP retries are not deduplicated. KV tables converge per key; for log tables dedup belongs downstream (and retry only the entries in failures). Same stance as Kafka REST Proxy. 6. type names Good catch — that was an oversight. Fixed: the FIP now uses the DataTypeJsonSerde vocabulary verbatim (INTEGER,TIMESTAMP_WITH_LOCAL_TIME_ZONE, ...), so REST schema objects are identical to the persisted serialization and no mapping exists anywhere. All changes are updated on the Confluence page.Thanks again Anton for the thorough review — these suggestions were really helpful. Best regards, Junbo Wang > On 4 Aug 2026, at 14:04, Junbo Wang <[email protected]> wrote: > > Thanks for the MKO's advice — good point! We started with Prometheus pull for > the MVP mainly for simplicity — it works out of the box and aligns with how > Fluss servers expose metrics today. That said, it doesn't preclude OTel: a > Collector can scrape the /metrics endpoint and re-export via OTLP, and we > plan to keep the config extensible so a native OTLP exporter can be added > later. > > > Thanks for the great questions, Liting Liu. > > > Why Rust instead of Java/Netty? > Three reasons: (a) the gateway is a proxy-style component where tail latency > and footprint matter — Rust gives GC-free predictable latency and a much > smaller memory footprint as a single static binary; (b) ecosystem fit — Fluss > data is Arrow-native, and the Rust stack (arrow-rs, DataFusion, pgwire) keeps > a zero-copy path end to end; (c) the gateway also helps drive the fluss-rust > client to production readiness and grow the Rust ecosystem. > > > a query failure or OOM could potentially affect the entire Gateway. > > Query execution will run on a dedicated tokio runtime with a bounded thread > pool, so it can't starve the write path. For memory, DataFusion's built-in > MemoryPool sets per-query budgets — an over-limit query fails alone, not the > process. We'll also add concurrency limits and query timeouts. If heavy > workloads ever emerge, a separate query worker remains an option. > > > > > a lightweight lookup REST API backed directly by the Fluss client, > > Agreed — the initial read path is a lightweight lookup API backed directly by > the Fluss client, no DataFusion. DataFusion comes later during FIP-32 > implementation, and only for lightweight queries (filter/projection/limit). > > > -- > Best regards, > Junbo Wang > > > Anton Borisov <[email protected] <mailto:[email protected]>> > 于2026年8月4日周二 10:30写道: >> Hi Junbo, >> >> Nice proposal, thank you for driving it! >> I like the direction overall, +1 >> >> My questions below: >> >> 1. Have you seen the new cooperative backpressure? >> #3463 [1] went in on 31 July, a day after this thread started. Tablet >> servers now >> send a per-bucket pressure value on PutKv responses, the client throttles on >> that by up to 3 seconds by default, and the server can reject with a >> retriable >> StorageBackpressureException when RocksDB is close to stalling. >> >> Inside an HTTP request that becomes invisible latency, the caller waits and >> isn't told why. It also looks like the thing your rate limiting wants to be >> driven by. A 429 with Retry-After says "slow down" in a way HTTP clients >> already understand, and a fixed requests/sec limit can't track what the >> cluster is actually doing. Worth pulling into the FIP? >> >> 2. What is a "connection" in service mode, and how many are there? >> The page says service mode uses "one shared pool per cluster", and >> connection.max is a "total connection cap (exceeded -> 429)". But it doesn't >> say what a connection is, or how many service mode opens. >> >> It matters because a Fluss Connection isn't a socket. It's documented as >> heavy-weight to create, thread-safe and meant to be shared, and each one >> carries a 64 MB write buffer, a sender task, a writer id and sockets to every >> tablet server. If service mode really is one shared connection, then the 512 >> cap and its 429 never fire, and it'd be clearer to say so. If it's many, >> they'll batch worse than one would, since each accumulator sees only a >> fraction of the traffic. Kafka REST Proxy shares a single producer across >> requests for that reason, and only goes per-principal when identities differ. >> >> 3. Is there a request timeout on writes? >> The client retries forever by default, and "leader not available" is one of >> the errors it retries. So the row in your example wouldn't fail, it would >> wait. And if the gateway stops waiting and reports a failure, does the row >> stay queued and land later? That would make "failed" sometimes mean >> "succeeded, just later". >> >> 4. Which tables get per-row results? >> For Log tables without a bucket key you use append_arrow_batch, which returns >> one result for the whole batch and stops at the first failure. I don't think >> per-row reporting is possible on that path. Is it meant to be all-or-nothing >> there? >> >> 5. What stops a duplicate when a client retries? >> Our idempotence catches the writer resending its own batch. A retried HTTP >> request is a different thing - the rows arrive fresh, so nothing links them >> to >> the first attempt. The writer id also belongs to one process, so a second >> gateway can't pick up where the first left off. >> >> 6. Is per-user authorization on the roadmap? >> The page says Fluss-side ACLs see only the gateway account, so every request >> reaches the cluster as gateway_svc and the server can't tell one caller from >> another. Is something planned for later: which principals may write to which >> tables, or is the gateway meant only for deployments where one shared >> identity is fine? >> >> Last one, the type names. Fluss spells types two ways: INT and TIMESTAMP_LTZ >> in SQL, INTEGER and TIMESTAMP_WITH_LOCAL_TIME_ZONE in DataTypeJsonSerde. The >> FIP uses the SQL names with the serde's JSON shape, so I don't think the >> examples parse as written. >> >> Which did you intend? If it's the SQL names, the gateway has to convert them >> before calling CreateTable, and that mapping is worth writing into the FIP. >> >> Thank you! >> >> -- Anton >> >> [1] https://github.com/apache/fluss/pull/3463 >> >> пн, 3 авг. 2026 г. в 16:02, David Reger <[email protected] >> <mailto:[email protected]>>: >> > >> > I think this also relates to one of the architectural discussions we've >> > recently had around FIP-32 [1]. >> > >> > My understanding is that there is a useful distinction between *native >> > Fluss operations* and *SQL/query execution*. >> > >> > For example, writes, metadata operations, and potentially lightweight >> > native reads (such as point lookups) could continue to build directly on >> > the fluss-rust foundation, whereas DataFusion would primarily serve the >> > SQL/query path. This would allow the REST gateway to remain lightweight >> > while keeping the query layer reusable for SQL-based protocols in the >> > future. >> > >> > I don't think this necessarily changes the scope of FIP-49, but I do think >> > it's an important architectural distinction as we evolve the gateway beyond >> > the initial REST write MVP. >> > >> > [1] https://lists.apache.org/thread/sf0qfxhr9j2g0dtxvp586p9zknxvpx6x >> > >> > Best regards, >> > >> > David >> > >> > Am Fr., 31. Juli 2026 um 05:02 Uhr schrieb Liting Liu (litiliu) via dev < >> > [email protected] <mailto:[email protected]>>: >> > >> > > Hi, Junbo >> > > >> > > >> > > Thanks for the detailed proposal. I have a couple of questions about the >> > > technology choice and the future SQL path. >> > > >> > > For the current scope—REST writes and metadata/DDL—have we considered >> > > implementing the Gateway with a Java/Netty REST stack backed by the >> > > existing Fluss Java client? This would be similar in spirit to how Flink >> > > SQL Gateway reuses Flink’s native Java stack and Netty-based REST >> > > infrastructure. The Fluss Java client already supports append, upsert, >> > > delete, partial update, metadata operations, and lookup. It would be >> > > helpful if the FIP could compare this alternative with the proposed >> > > >> > > I also have a question about the future DataFusion integration. Is >> > > DataFusion expected to execute SQL queries inside the same Gateway >> > > process? >> > > If so, complex or resource-intensive queries could compete with REST >> > > ingestion and metadata operations for CPU and memory, and a query failure >> > > or OOM could potentially affect the entire Gateway. Do we plan to provide >> > > admission control, memory and concurrency limits, query >> > > timeout/cancellation, spilling, or process-level isolation? >> > > >> > > If the initial read use case is mainly primary-key or prefix lookup, >> > > could >> > > we expose a lightweight lookup REST API backed directly by the Fluss >> > > client, without introducing DataFusion? DataFusion seems more valuable >> > > when >> > > the intended scope includes general SQL capabilities such as scans, >> > > filters, aggregations, and joins? If that is the long-term goal, would it >> > > be preferable to keep complex SQL execution in a separate optional SQL >> > > service or worker process, so that it does not increase the failure >> > > domain >> > > of the REST write Gateway? >> > > >> > > >> > > From: Michael Koepf <[email protected] >> > > <mailto:[email protected]>> >> > > Date: Thursday, July 30, 2026 at 22:15 >> > > To: [email protected] <mailto:[email protected]> >> > > <[email protected] <mailto:[email protected]>> >> > > Subject: Re: [DISCUSS] FIP-49: Fluss Gateway REST API >> > > >> > > Hi Juno, >> > > >> > > Thanks for the FIP, great initiative. >> > > >> > > A question that comes to my mind: Why not expose metrics via vendor >> > > neutral OTEL instead of limiting it to Prometheus? >> > > -- >> > > Best, >> > > MKO >> > > >> > > >> > > > On 30.07.2026, at 12:23, Forward Xu <[email protected] >> > > > <mailto:[email protected]>> wrote: >> > > > >> > > > Thanks junbo, >> > > > >> > > > The proposal looks good to me in general. +1 >> > > > >> > > > Best, >> > > > Foward >> > > > >> > > > Junbo Wang <[email protected] <mailto:[email protected]>> >> > > > 于2026年7月30日周四 11:49写道: >> > > > >> > > >> Hi all, I'd like to start a discussion on FIP-49: Fluss Gateway REST >> > > API. >> > > >> >> > > >> The proposal is available here: >> > > >> >> > > https://cwiki.apache.org/confluence/spaces/FLUSS/pages/444334561/FIP-49+Fluss+Gateway+REST+API >> > > >> >> > > >> This FIP is an extension of FIP-32 (Multiprotocol Query Gateway) [1] >> > > >> and >> > > >> does not conflict with it. Following the earlier suggestion to split >> > > >> the >> > > >> gateway work into focused FIPs, FIP-49 defines the REST write path and >> > > DDL >> > > >> / metadata management in detail, while REST read and the SQL path >> > > remain in >> > > >> the scope of FIP-32. >> > > >> >> > > >> In short, it proposes a standalone, stateless fluss-gateway process >> > > (Rust, >> > > >> axum + tokio, built on fluss-rust) providing: >> > > >> >> > > >> HTTP/JSON ingestion into KV and Log tables, at-least-once, with >> > > >> per-row >> > > >> success/failure reporting >> > > >> Full database / table / partition DDL >> > > >> Pluggable client authentication, and a service / user (act-as) >> > > >> identity >> > > >> mode towards Fluss >> > > >> Multi-cluster routing, Prometheus metrics, a unified error model >> > > >> >> > > >> No Fluss server-side changes are required— the gateway accesses >> > > >> existing >> > > >> clusters purely as a fluss-rust client. >> > > >> >> > > >> Looking forward to your feedback and suggestions. >> > > >> >> > > >> >> > > >> >> > > >> [1] https://lists.apache.org/thread/sf0qfxhr9j2g0dtxvp586p9zknxvpx6x >> > > >> >> > > >> >> > > >> Best regards, >> > > >> Junbo Wang >> > > >> >> > > >> >> > > > > > > >
