Description
Add a sink connector that consumes messages from Iggy topics and writes them into Apache Fluss tables.
Fluss provides durable, offset-ordered log tables and primary-key tables with sub-millisecond lookups and lakehouse tiering. The official Rust client (fluss-rs) speaks Apache Arrow natively, which pairs well with JSON/Avro payloads on the Iggy side and Arrow-oriented egress (for example Iceberg).
Use cases:
- Land Iggy stream data into Fluss as the real-time analytics / serving layer.
- Feed Fluss hot tier from Iggy connectors pipelines (sources → transforms → Fluss).
- Use Fluss PK tables for materialized views keyed by business identifiers from Iggy events.
Affected area / component
- Connectors (new
fluss_sink plugin under core/connectors/sinks/)
- Connectors runtime configuration examples
- Integration tests (optional follow-up; may need Fluss test fixture)
Affected area / component
No response
Proposed solution
Implement iggy_connector_fluss_sink as a cdylib plugin implementing iggy_connector_sdk::Sink, following conventions from postgres_sink and iceberg_sink.
Client dependency
Use fluss-rs. Pin a released version in the workspace Cargo.toml when adding the crate.
Write modes (phased)
| Phase |
Fluss table type |
Write API |
Iggy input |
| 1 |
Log table |
AppendWriter / Arrow RecordBatch append |
Schema::Json (row objects) |
| 1b |
Log table |
Arrow IPC Schema::Raw passthrough |
Zero-copy when upstream sends Arrow batches |
| 2 |
Primary-key table |
UpsertWriter upsert/delete |
JSON with PK columns + optional op header |
Message mapping
- JSON payloads: map object fields to
GenericRow using Fluss table schema (fail batch on type mismatch; log and skip or dead-letter per existing sink patterns).
- Avro payloads: optional transform via existing
avro_convert before write.
- Arrow IPC (
Schema::Raw): decode to RecordBatch and call Fluss batch append API when payload_format = "arrow_ipc".
Routing
Phase 1: single destination (database, table) from config.
Phase 2 (optional): static or dynamic routing by message field (same pattern as iceberg_sink dynamic_route_field).
Example configuration
[plugin_config]
bootstrap_servers = "localhost:9123"
database = "mydb"
table = "events"
table_type = "log" # "log" | "primary_key"
write_mode = "append" # "append" | "upsert" | "delete"
payload_format = "json" # "json" | "arrow_ipc"
batch_size = 500
flush_interval = "1s"
# primary_key_columns = ["id"] # required when table_type = "primary_key"
# dynamic_routing = false
# dynamic_route_field = "fluss_table" # "database.table"
Files / layout (expected)
core/connectors/sinks/fluss_sink/
├── Cargo.toml
├── config.toml
├── README.md
└── src/
├── lib.rs
├── config.rs
└── writer.rs
Register in workspace Cargo.toml, core/connectors/sinks/README.md, and add a runtime example under core/connectors/runtime/example_config/connectors/.
Example plugins
- Sink lifecycle / batch consume:
postgres_sink, mongodb_sink
- JSON → typed row mapping:
postgres_sink
- Arrow batch path:
iceberg_sink (arrow_json / RecordBatch)
Acceptance criteria
References
Alternatives considered
Alternatives considered
- Write via Flink Table API only — excludes Iggy-native connector users.
- Iceberg sink only — Fluss hot tier + PK lookup is a different latency/consistency profile than object-store lake tables.
- Custom Protobuf client — duplicate of maintained
fluss-rs; avoid.
Contribution
Good first issue
Description
Add a sink connector that consumes messages from Iggy topics and writes them into Apache Fluss tables.
Fluss provides durable, offset-ordered log tables and primary-key tables with sub-millisecond lookups and lakehouse tiering. The official Rust client (
fluss-rs) speaks Apache Arrow natively, which pairs well with JSON/Avro payloads on the Iggy side and Arrow-oriented egress (for example Iceberg).Use cases:
Affected area / component
fluss_sinkplugin undercore/connectors/sinks/)Affected area / component
No response
Proposed solution
Implement
iggy_connector_fluss_sinkas acdylibplugin implementingiggy_connector_sdk::Sink, following conventions frompostgres_sinkandiceberg_sink.Client dependency
Use
fluss-rs. Pin a released version in the workspaceCargo.tomlwhen adding the crate.Write modes (phased)
AppendWriter/ ArrowRecordBatchappendSchema::Json(row objects)Schema::RawpassthroughUpsertWriterupsert/deleteopheaderMessage mapping
GenericRowusing Fluss table schema (fail batch on type mismatch; log and skip or dead-letter per existing sink patterns).avro_convertbefore write.Schema::Raw): decode toRecordBatchand call Fluss batch append API whenpayload_format = "arrow_ipc".Routing
Phase 1: single destination
(database, table)from config.Phase 2 (optional): static or dynamic routing by message field (same pattern as
iceberg_sinkdynamic_route_field).Example configuration
Files / layout (expected)
Register in workspace
Cargo.toml,core/connectors/sinks/README.md, and add a runtime example undercore/connectors/runtime/example_config/connectors/.Example plugins
postgres_sink,mongodb_sinkpostgres_sinkiceberg_sink(arrow_json/RecordBatch)Acceptance criteria
fluss_sinkplugin builds ascdyliband loads in connectors runtimeErrorvariants; permanent schema errors fail fastREADME.mdwith minimal runnable examplecargo fmt,cargo sort --no-format,cargo clippy,cargo test -p iggy_connector_fluss_sinkpassReferences
fluss-rson crates.io: https://crates.io/crates/fluss-rsAlternatives considered
Alternatives considered
fluss-rs; avoid.Contribution
Good first issue