nsivabalan opened a new issue, #20400:
URL: https://github.com/apache/hudi/issues/20400
### Bug Description
`HoodieStreamingSink` cannot keep an embedded timeline server alive across
micro-batches, so Spark Structured Streaming writes run without one.
`HoodieStreamer` does not have this problem. This issue proposes aligning the
sink with `HoodieStreamer`: **one embedded timeline server per sink instance
(per streaming query on the driver), reused across every commit, and stopped
when the query terminates.**
**Current behaviour (master `60caf5f4`)**
1. The write client is created on the first micro-batch inside
`HoodieSparkSqlWriter`. It starts its own embedded timeline server (in the
constructor via `BaseHoodieClient.startEmbeddedServerView`) and is therefore
its owner.
2. At the end of each write, `HoodieSparkSqlWriterInternal.closeWriteClient`
closes the client unless async compaction or async clustering is enabled.
Closing the owner stops the timeline server and resets the view-storage config.
3. The sink keeps the returned client (`writeClient = Some(client)`) and
hands it back on the next batch, including when it has already been closed. A
client only starts a timeline server in its constructor, so batch 2 onwards
runs without one.
4. To avoid that, the sink forces `hoodie.embed.timeline.server=false`
unless the user sets it (HUDI-3636, #9266), and always forces `DIRECT` markers
(comment in `addBatch`: *"timeline server is closed after first micro-batch and
subsequent micro-batches do not have timeline server running"*).
Consequences:
- Streaming writes never get the timeline server's file-system view caching
or timeline-server-based markers.
- A user who enables the server with `REMOTE_ONLY` fails on the second
micro-batch with `HoodieRemoteException: Connect to localhost:<port> ...
Connection refused` (reported in #12652).
- With async compaction or clustering enabled, the client (and any server it
owns) is never closed when the query stops. Cleanup only happens through JVM
shutdown hooks.
**How `HoodieStreamer` does it**
`StreamSync` owns the timeline server. It creates it once
(`EmbeddedTimelineServerHelper.createEmbeddedTimelineService`) and passes it
into every `SparkRDDWriteClient` it builds. When a client is rebuilt (for
example on a schema change), only the client is closed. The server keeps
running, because a client that was handed a server does not stop it. The server
is stopped once, in `StreamSync.close()` (`stopForBasePath`).
**Proposed fix**
- `HoodieStreamingSink` owns an `EmbeddedTimelineService`, started lazily on
the first batch from that batch's write config, and passed into the write
client (non-owning, as `StreamSync` does).
- In streaming mode the writer no longer closes the client after each batch.
The sink owns the client's lifecycle, and async compaction and clustering keep
sharing it.
- The server is stopped when the query terminates. A V1 `Sink` has no close
hook, so this would use a `StreamingQueryListener` keyed on the sink's query
id. It is also stopped on `reset`.
- Drop the HUDI-3636 default override and the forced `DIRECT` markers.
`hoodie.embed.timeline.server=false` and an explicit
`hoodie.write.markers.type` keep working as user opt-outs.
**Why now:** Spark Declarative Pipelines (RFC-112, #19997) runs every
streaming flow as a separate `HoodieStreamingSink` on one long-lived Spark
Connect driver. Each flow needs a correctly scoped timeline server that is
released when its query stops, not when the JVM exits.
### Environment
Behaviour verified against apache/hudi `master` (`60caf5f4`); originally
reported on Hudi 0.15.0 / Spark 3.3.0 in #12652.
### Steps to reproduce
Run any `writeStream.format("hudi")` query with
`hoodie.embed.timeline.server=true`, `hoodie.filesystem.view.type=REMOTE_ONLY`,
`hoodie.filesystem.remote.backup.view.enable=false`. Batch 0 succeeds. Batch 1
fails with `Connection refused` against the stopped server's port. Full repro
and stack trace in #12652.
### Code pointers
-
`hudi-spark-datasource/hudi-spark-common/src/main/scala/org/apache/hudi/HoodieStreamingSink.scala`:
`addBatch` (marker and timeline-server overrides, client reuse), `reset`
-
`hudi-spark-datasource/hudi-spark-common/src/main/scala/org/apache/hudi/HoodieSparkSqlWriter.scala`:
`closeWriteClient` and the `finally` in `writeInternal`
-
`hudi-client/hudi-client-common/src/main/java/org/apache/hudi/client/BaseHoodieClient.java`:
`startEmbeddedServerView` / `stopEmbeddedServerView` (ownership via
`shouldStopTimelineServer`)
-
`hudi-utilities/src/main/java/org/apache/hudi/utilities/streamer/StreamSync.java`:
`reInitWriteClient`, `close` (reference behaviour)
Related: #12652, #9266 (HUDI-3636), #20370, #19997.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]