Connectors move data between Apache Iggy streams and external systems. A source reads data from an external system and produces messages to an Iggy stream. A sink consumes messages from an Iggy stream and writes them to an external system. Each connector is a Rust plugin, and the connectors runtime loads the plugins and runs them.
This repository holds the runtime, the plugin SDK, the sinks and the sources. The connectors started in apache/iggy and moved here with their git history. The Iggy server, the client SDKs, the CLI and the Web UI stay in apache/iggy.
Documentation | Apache Iggy | Discussions | Discord | Docker image
- Plugins: each connector is a shared library (
.so,.dylib,.dll). It implements theSinkor theSourcetrait of the SDK, and the runtime loads it at startup. - Typed configuration: each plugin has its own configuration type, and the runtime passes the configuration from TOML to the plugin.
- Transforms: configured transforms add, change, delete or filter the fields of each message between Iggy and the plugin.
- Formats: decoders and encoders for JSON, raw bytes, text, Protocol Buffers, FlatBuffers and Avro.
- Configuration providers: connector configurations come from local TOML files or from an HTTP API.
- Delivery: sources keep their state across restarts, sinks can deliver at least once, and sources can write each batch exactly once.
- Observability: Prometheus metrics with per-stage latency histograms, and an opt-in per-batch timing log.
- Logging: text or JSON logs, selected in the
[logging]configuration.
The runtime ships as the Docker image apache/iggy-connect. Run docker pull apache/iggy-connect:edge to fetch the image of the latest master commit.
Run these commands from the root of this repository. You need Docker and the Rust toolchain.
-
Build the runtime and the two plugins of this example:
cargo build --release -p iggy-connectors \ -p iggy_connector_random_source -p iggy_connector_stdout_sink
-
Start an Iggy server with the credentials of the example configuration:
docker run -d --name iggy \ --cap-add=SYS_NICE --security-opt seccomp=unconfined --ulimit memlock=-1:-1 \ -e IGGY_ROOT_USERNAME=iggy -e IGGY_ROOT_PASSWORD=iggy \ -e IGGY_TCP_ADDRESS=0.0.0.0:8090 -e IGGY_HTTP_ADDRESS=0.0.0.0:3000 \ -e IGGY_NODE_ADVERTISED_ADDRESS=localhost \ -p 8090:8090 -p 3000:3000 \ apache/iggy
The Docker page of the Iggy documentation explains the options.
-
Create the stream and the topic of the example with the
iggyCLI of the server image:docker exec iggy iggy -u iggy -p iggy stream create example_stream docker exec iggy iggy -u iggy -p iggy topic create example_stream example_topic 1 none 1d --durability persisted
-
Start the runtime with the example configuration:
IGGY_CONNECTORS_CONFIG_PATH=runtime/example_config/config.toml \ cargo run --release --bin iggy-connectors
The Random source produces messages to example_stream, and the Stdout sink prints them. The transforms of both connectors add a field to each message. The Web UI of Iggy shows the stream too.
The example directory runtime/example_config/connectors/ also enables other connectors, for example ClickHouse, Delta Lake and Quickwit. Without their plugin or their backend, the runtime reports them with the Error status, and the other connectors continue. Set enabled = false in their files to skip them.
For a debug build, remove --release from the commands and change target/release to target/debug in the path of the connector files.
The Quickwit sink of the example reads the qw stream. To try it, you need a checkout of apache/iggy for its example producer sink-data-producer.
-
Build the plugin:
cargo build --release -p iggy_connector_quickwit_sink
-
In the apache/iggy checkout, start Quickwit:
docker compose -f examples/rust/src/sink-data-producer/docker-compose.yml up -d
-
Create the stream and the topic that the sink reads:
docker exec iggy iggy -u iggy -p iggy stream create qw docker exec iggy iggy -u iggy -p iggy topic create qw records 1 none 1d
-
In the apache/iggy checkout, send 100 batches of records to the topic:
cargo run --example sink-data-producer --release
-
If the runtime runs, stop it. Then start it again, as in step 4 of the quick start.
The Quickwit sink writes the records to the events index. The Quickwit UI at http://localhost:7280 shows the index.
| Path | Package | Content |
|---|---|---|
runtime/ |
iggy-connectors |
The runtime binary and its example configuration |
sdk/ |
iggy_connector_sdk |
Traits, FFI macros, decoders, encoders and transforms |
sinks/<name>/ |
iggy_connector_<name> |
One sink plugin with its integration tests |
sources/<name>/ |
iggy_connector_<name> |
One source plugin with its integration tests |
testing/ |
iggy_connector_testing |
Test fixtures that several test targets share |
scripts/ |
CI checks, test server build and the core pin bump | |
.claude/skills/ |
Skills for AI agents, linked from .agents/skills/ |
Only iggy_connector_sdk goes to crates.io. The runtime ships only as the Docker image, and the sinks and sources ship as source code.
This repository builds against one commit of apache/iggy, called the core pin. The root Cargo.toml holds the core pin as the rev of each core crate that comes from git: iggy, iggy_common, configs, configs_derive, system_stats and the test crate integration. The core pin is the last core release, unless a connector needs an unreleased core change, and scripts/bump-core-pin.sh moves it.
cargo build # runtime and all plugins, into target/debug/
cargo build --release # the same, into target/release/The runtime binary is target/<profile>/iggy-connectors. On Linux, each plugin builds to target/<profile>/libiggy_connector_<name>.so.
Unit tests live in src/ of each crate and need no services:
cargo nextest run --workspace --lib --binsIntegration tests live in tests/ of each package. They need Docker, an Iggy server built at the core pin, and fresh builds of the runtime and the plugins in target/debug/:
./scripts/build-test-server.sh
cargo build
cargo nextest run -p iggy_connector_postgres_sinkscripts/build-test-server.sh builds iggy-server from apache/iggy at the core pin and copies it to target/debug/iggy-server. Run it again after the core pin changes. The tests start that server and target/debug/iggy-connectors, but they never build them. After a change to the runtime or a plugin, run cargo build again.
cargo nextest run --workspace runs all tests, as CI does. The Doris tests need sudo sysctl -w vm.max_map_count=2000000 on Linux. Per-test logs go to test_logs/.
The runtime manages the lifecycle of the connectors and gives them the infrastructure that they need to run. The runtime documentation describes its configuration, the HTTP API, the state storage and the delivery guarantees.
The path field in connector configs points to the shared library (.so, .dylib, .dll). The runtime resolves it as follows:
-
Extension: if the path has no known extension, the runtime adds the extension of the OS. That is
.soon Linux,.dylibon macOS and.dllon Windows. -
Absolute paths: used as they are.
-
Relative paths: the runtime tries these locations in order and uses the first match:
- the literal relative path, from the working directory
- the file name in the directory of the runtime binary
- the file name in the working directory
- the file name in
/usr/lib,/usr/lib64,/lib,/lib64,/usr/local/liband/usr/local/lib64
Examples:
# Relative: resolved against the search directories, extension added on Linux
path = "target/release/libiggy_connector_stdout_sink"
# Absolute: used directly
path = "/opt/iggy/plugins/libiggy_connector_stdout_sink.so"If the runtime does not find the library, it logs all the paths that it tried.
A sink consumes the messages from the configured streams and topics and sends them to its destination. For example, the Quickwit sink sends the messages to the Quickwit indexer. The sink documentation describes the configuration and a sample implementation.
A Sink implementation uses the sink_connector! macro, which exposes the FFI interface that the runtime calls. The macro also exports the version of the connector from its Cargo.toml, and the runtime reports it in its /stats endpoint. Each sink has its own configuration type, and its new() method receives that configuration with the unique plugin ID.
| Sink | Destination |
|---|---|
| BigQuery | a Google BigQuery table, through the Storage Write API |
| ClickHouse | ClickHouse tables |
| Delta Lake | Delta Lake tables |
| Apache Doris | Apache Doris tables, through the Stream Load HTTP API |
| Elasticsearch | Elasticsearch indices |
| HTTP | any HTTP endpoint, for example a webhook or a REST API |
| Apache Iceberg | Apache Iceberg tables, through a REST catalog |
| InfluxDB | InfluxDB V2 and V3, as line-protocol points |
| Meilisearch | a Meilisearch index |
| MongoDB | a MongoDB collection |
| PostgreSQL | PostgreSQL tables |
| Quickwit | a Quickwit index |
| RabbitMQ | RabbitMQ exchanges, through AMQP 0.9.1 |
| Amazon Redshift | Redshift tables, with Parquet files in S3 as the staging area |
| Amazon S3 | Amazon S3 and S3-compatible stores (MinIO, R2, B2, DigitalOcean Spaces) |
| Stdout | the standard output, for debugging and development |
| SurrealDB | SurrealDB, with deterministic record IDs for an idempotent replay |
A source produces messages to an Iggy stream and topic. Configure one [[streams]] entry for each source instance, because the runtime keeps only the last configured producer. For example, the Random source generates messages for its stream and topic. The source documentation describes the configuration and a sample implementation.
| Source | Origin |
|---|---|
| Elasticsearch | documents of Elasticsearch indices, by polling with timestamp tracking |
| HTTP | POST bodies on the paths of an embedded webhook server, with bearer or HMAC authentication |
| InfluxDB | InfluxDB V2 (Flux) and V3 (SQL), by polling |
| MQTT | the topics of an MQTT 3.1.1 or MQTT 5 broker, with QoS 0, 1 or 2 |
| PostgreSQL | PostgreSQL tables, by table polling or Change Data Capture |
| Random | random test messages, for testing and development |
A new connector implements the Sink or the Source trait. Read the sink or the source documentation, and use the existing plugins in sinks/ and sources/ as examples. Add the new crate to the members of the root Cargo.toml.
Field transformations can change messages before a source sends them to a topic, or before a sink consumes them. The payload format decides which transformations apply. To add a transformation, implement the Transform trait in the SDK, add its TransformType variant and extend transforms::from_config. Each transform can have its own configuration.
The SDK documentation describes the transforms, the stream decoders and the encoders.
Read CONTRIBUTING.md before you open a pull request. Report security issues as SECURITY.md describes, and never in public.
- Discussions of this repository
- Discord
- Mailing lists
- Documentation
Apache Iggy Connectors is licensed under the Apache License 2.0.