Repository navigation
feat(connectors): Add Iggy source connector #3886
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
jiengup
wants to merge
15
commits into
apache:master
Choose a base branch
from
jiengup:iggy-source-connector
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
5a4597c
feat(connectors): Add Iggy source connector
jiengup a5ddffb
feat(connectors): commit Iggy source state after batch ACK
jiengup 53c38d6
fix(connectors): reject malformed Iggy source batches
jiengup 8a0217a
fix(connectors): back off after invalid source offsets
jiengup 1546b9b
docs(connectors): clarify latest offset retry risk
jiengup a8735f2
test(connectors): expand Iggy source coverage
jiengup ffcb38b
build(connectors): include Iggy source in release artifacts
jiengup cb2aacd
fix(connectors): isolate Iggy source poll failures
jiengup 2f03c15
fix(connectors): validate Iggy source batch size
jiengup 4bd2c50
fix(connectors): adapt Iggy source retry backoff
jiengup 6a9184c
fix(connectors): reset source offsets after topic recreation
jiengup 1da9666
fix(connectors): harden Iggy source startup and polling
jiengup ca59797
fix(connectors): address Iggy source review feedback
jiengup 8f536d7
fix(connectors): cap Iggy source poll cycles
jiengup b6baf62
fix(connectors): pass stream options in Iggy source
jiengup File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
49 changes: 49 additions & 0 deletions
49
core/connectors/runtime/example_config/connectors/iggy_source.toml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| # Licensed to the Apache Software Foundation (ASF) under one | ||
| # or more contributor license agreements. See the NOTICE file | ||
| # distributed with this work for additional information | ||
| # regarding copyright ownership. The ASF licenses this file | ||
| # to you under the Apache License, Version 2.0 (the | ||
| # "License"); you may not use this file except in compliance | ||
| # with the License. You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, | ||
| # software distributed under the License is distributed on an | ||
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
| # KIND, either express or implied. See the License for the | ||
| # specific language governing permissions and limitations | ||
| # under the License. | ||
|
|
||
| type = "source" | ||
| key = "iggy" | ||
| enabled = true | ||
| version = 0 | ||
| name = "Iggy source" | ||
| path = "target/release/libiggy_connector_iggy_source" | ||
| verbose = false # Log message processing at info level instead of debug (default: false) | ||
|
|
||
| [[streams]] | ||
| stream = "downstream_stream" | ||
| topic = "downstream_topic" | ||
| schema = "raw" | ||
| batch_length = 100 | ||
| linger_time = "5ms" | ||
|
|
||
| [plugin_config] | ||
| # Connection string of the UPSTREAM Iggy cluster to replicate from | ||
| connection_string = "iggy+tcp://iggy:iggy@127.0.0.1:8090" | ||
| upstream_stream = "upstream_stream" | ||
| upstream_topic = "upstream_topic" | ||
| poll_interval = "1s" | ||
| batch_size = 100 | ||
| # "earliest", "latest", or a numeric offset; used for partitions without a | ||
| # saved offset, including after the upstream topic is recreated with the same name. | ||
| initial_offset = "earliest" | ||
| include_user_headers = true | ||
| # "block" preserves malformed messages for retry; "drop_headers" forwards | ||
| # their payload without user headers. | ||
| malformed_message_policy = "block" | ||
| retry_interval = "1s" | ||
| max_retry_interval = "60s" | ||
| verbose_logging = false |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| # Licensed to the Apache Software Foundation (ASF) under one | ||
| # or more contributor license agreements. See the NOTICE file | ||
| # distributed with this work for additional information | ||
| # regarding copyright ownership. The ASF licenses this file | ||
| # to you under the Apache License, Version 2.0 (the | ||
| # "License"); you may not use this file except in compliance | ||
| # with the License. You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, | ||
| # software distributed under the License is distributed on an | ||
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
| # KIND, either express or implied. See the License for the | ||
| # specific language governing permissions and limitations | ||
| # under the License. | ||
|
|
||
| [package] | ||
| name = "iggy_connector_iggy_source" | ||
| version = "0.5.0-edge.1" | ||
| description = "Iggy source connector replicating a topic from an upstream Apache Iggy cluster" | ||
| edition = "2024" | ||
| license = "Apache-2.0" | ||
| keywords = ["iggy", "messaging", "streaming", "replication"] | ||
| categories = ["command-line-utilities", "database", "network-programming"] | ||
| homepage = "https://iggy.apache.org" | ||
| documentation = "https://iggy.apache.org/docs" | ||
| repository = "https://github.com/apache/iggy" | ||
| readme = "../../README.md" | ||
| publish = false | ||
|
|
||
| [package.metadata.cargo-machete] | ||
| ignored = ["dashmap"] | ||
|
|
||
| [lib] | ||
| crate-type = ["cdylib", "lib"] | ||
|
|
||
| [dependencies] | ||
| async-trait = { workspace = true } | ||
| dashmap = { workspace = true } | ||
| iggy = { workspace = true } | ||
| iggy_common = { workspace = true } | ||
| iggy_connector_sdk = { workspace = true } | ||
| rmp-serde = { workspace = true } | ||
| secrecy = { workspace = true } | ||
| serde = { workspace = true } | ||
| tokio = { workspace = true } | ||
| tracing = { workspace = true } | ||
|
|
||
| [dev-dependencies] | ||
| serde_json = { workspace = true } | ||
|
|
||
| [lints] | ||
| workspace = true | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,137 @@ | ||
| # Iggy Source Connector | ||
|
|
||
| The Iggy source connector replicates a topic from an upstream Apache Iggy | ||
| cluster into a stream and topic managed by the connectors runtime. It polls | ||
| every upstream partition and persists the last acknowledged offset for each | ||
| partition so replication can resume after a restart. | ||
|
|
||
| ## Features | ||
|
|
||
| - Polls every partition of one upstream topic over successive cycles. | ||
| - Preserves payload bytes and non-zero message IDs. | ||
| - Preserves user headers by default, with an option to omit them. | ||
| - Stores per-partition offsets in the connector runtime's state storage. | ||
| - Retries fully failed polling cycles with exponential backoff and jitter. | ||
| - Creates the configured upstream stream or topic when it does not exist. | ||
|
|
||
| ## Configuration | ||
|
|
||
| ```toml | ||
| type = "source" | ||
| key = "iggy" | ||
| enabled = true | ||
| version = 0 | ||
| name = "Iggy source" | ||
| path = "target/release/libiggy_connector_iggy_source" | ||
| verbose = false | ||
| benchmark = false | ||
|
|
||
| [[streams]] | ||
| stream = "downstream_stream" | ||
| topic = "downstream_topic" | ||
| schema = "raw" | ||
| batch_length = 100 | ||
| linger_time = "5ms" | ||
|
|
||
| [plugin_config] | ||
| connection_string = "iggy+tcp://iggy:iggy@127.0.0.1:8090" | ||
| upstream_stream = "upstream_stream" | ||
| upstream_topic = "upstream_topic" | ||
| poll_interval = "1s" | ||
| batch_size = 100 | ||
| initial_offset = "earliest" | ||
| include_user_headers = true | ||
| malformed_message_policy = "block" | ||
| retry_interval = "1s" | ||
| max_retry_interval = "60s" | ||
| verbose_logging = false | ||
| ``` | ||
|
|
||
| Use `schema = "raw"` on the downstream stream to replicate payload bytes | ||
| without decoding or re-encoding them. The runtime-level `batch_length` controls | ||
| downstream producer batching, while `plugin_config.batch_size` limits each | ||
| upstream partition poll. A poll cycle forwards at most 10,000 messages across | ||
| all partitions and resumes with another partition on the next cycle. | ||
|
|
||
| ### Plugin Fields | ||
|
|
||
| | Field | Required | Default | Description | | ||
| | --- | --- | --- | --- | | ||
| | `connection_string` | yes | none | Connection string for the upstream Iggy cluster. | | ||
| | `upstream_stream` | yes | none | Name of the stream to replicate from. | | ||
| | `upstream_topic` | yes | none | Name of the topic to replicate from. | | ||
| | `poll_interval` | no | `2s` | Delay before each upstream poll cycle. | | ||
| | `batch_size` | no | `100` | Maximum messages requested from each upstream partition per poll. Must be between `1` and `10,000`. | | ||
| | `initial_offset` | no | `earliest` | Starting position for a partition without a saved offset, including after the upstream topic is recreated with the same name. Accepts `earliest`, `latest`, or an absolute numeric offset. | | ||
| | `include_user_headers` | no | `true` | Copy user headers to downstream messages. | | ||
| | `malformed_message_policy` | no | `block` | Handling for messages with unparsable user headers: `block` retries from the failed offset; `drop_headers` forwards the payload without those headers. | | ||
| | `retry_interval` | no | `1s` | Base delay used for exponential backoff when every partition poll in a cycle fails. | | ||
| | `max_retry_interval` | no | `60s` | Maximum delay between poll retries. | | ||
| | `verbose_logging` | no | `false` | Log per-cycle connector details at info level instead of debug level. | | ||
|
|
||
| The `connection_string` can use any transport supported by the Rust client, | ||
| including TCP, QUIC, HTTP, and WebSocket. It may contain credentials, so keep | ||
| the connector configuration private or supply the value through the | ||
| `IGGY_CONNECTORS_SOURCE_IGGY_PLUGIN_CONFIG_CONNECTION_STRING` environment | ||
| variable. | ||
|
|
||
| ## Offset and Delivery Semantics | ||
|
|
||
| For each partition, the connector resumes at one offset after the last saved | ||
| offset. `initial_offset` is used only when that partition has no saved offset: | ||
|
|
||
| - `earliest` starts with the oldest available message. | ||
| - `latest` requests up to `batch_size` of the most recent messages. | ||
| - A numeric value starts at that absolute offset. | ||
|
|
||
| If the upstream topic is recreated with the same name, the connector clears | ||
| its saved offsets and uses `initial_offset` for the new topic. | ||
|
|
||
| The connector stages new offsets while polling. It commits them only after the | ||
| runtime sends the complete downstream batch and saves the connector state. If | ||
| the batch is rejected, the previous offsets remain committed and the messages | ||
| are eligible for replay. Consumers should therefore tolerate duplicate | ||
| delivery after failures or crashes. | ||
|
jiengup marked this conversation as resolved.
Resolved
|
||
|
|
||
| After five consecutive downstream send or state-save failures, the source | ||
| stops. An operator must restart it before it can replay the unacknowledged | ||
| messages. | ||
|
|
||
| > When `initial_offset = "latest"` and no offset has been committed yet, each | ||
| > retry recalculates the current tail of the upstream partition. If a first | ||
| > batch is rejected while new messages arrive, messages from that failed batch | ||
| > can be skipped. Use `earliest` or a numeric offset when replay continuity is | ||
| > required. | ||
|
|
||
| ## Message Mapping | ||
|
|
||
| | Upstream field | Downstream behavior | | ||
| | --- | --- | | ||
| | Payload | Copied without modification when the downstream schema is `raw`. | | ||
| | Message ID | Copied when the upstream ID is non-zero. | | ||
| | User headers | Copied when `include_user_headers = true`. | | ||
| | Offset and partition | Reassigned by the downstream Iggy topic. | | ||
| | Timestamp and checksum | Generated for the downstream message. | | ||
|
|
||
| ## Operational Notes | ||
|
|
||
| - The connector discovers new upstream partitions automatically on each poll cycle. | ||
| - A `batch_size` outside `1..=10,000` prevents the connector from starting. | ||
| - The 10,000-message cycle cap bounds message count, not payload bytes. Large | ||
| messages can still use substantial memory while the batch crosses the runtime. | ||
| - If the upstream stream is missing, the connector creates it. If the topic is | ||
| missing, it creates the topic with one partition, no compression, persisted | ||
| durability, and a one-message save threshold. | ||
| - Invalid `initial_offset` values produce a warning and fall back to | ||
| `earliest`. | ||
| - If user headers cannot be decoded, `malformed_message_policy = "block"` | ||
| forwards the valid prefix before the malformed message, leaves that | ||
| partition at the failed offset, and continues polling other partitions at | ||
| the normal `poll_interval`. Record conversion errors do not activate the | ||
| connector-wide polling backoff. | ||
| - `malformed_message_policy = "drop_headers"` forwards a malformed message's | ||
| payload without its user headers and advances the partition offset. Setting | ||
| `include_user_headers = false` bypasses header parsing for every message. | ||
| - A polling error on one partition is logged and counted without skipping the | ||
| remaining partitions. Connector-wide backoff is activated only when no | ||
| partition poll succeeds during the cycle. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| # Licensed to the Apache Software Foundation (ASF) under one | ||
| # or more contributor license agreements. See the NOTICE file | ||
| # distributed with this work for additional information | ||
| # regarding copyright ownership. The ASF licenses this file | ||
| # to you under the Apache License, Version 2.0 (the | ||
| # "License"); you may not use this file except in compliance | ||
| # with the License. You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, | ||
| # software distributed under the License is distributed on an | ||
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
| # KIND, either express or implied. See the License for the | ||
| # specific language governing permissions and limitations | ||
| # under the License. | ||
|
|
||
| type = "source" | ||
| key = "iggy" | ||
| enabled = true | ||
| version = 0 | ||
| name = "Iggy source" | ||
| path = "../../target/release/libiggy_connector_iggy_source" | ||
| verbose = false | ||
|
|
||
| [[streams]] | ||
| stream = "downstream_stream" | ||
| topic = "downstream_topic" | ||
| schema = "raw" | ||
| batch_length = 100 | ||
|
|
||
| [plugin_config] | ||
| connection_string = "iggy+tcp://iggy:iggy@127.0.0.1:8090" | ||
| upstream_stream = "upstream_stream" | ||
| upstream_topic = "upstream_topic" | ||
| poll_interval = "1s" | ||
| batch_size = 100 | ||
| # Starting position when a partition has no committed offset, including after | ||
| # the upstream topic is recreated with the same name: | ||
| # "earliest", "latest", or an absolute numeric offset. | ||
| # With "latest", if the first batch is NACKed before an offset is committed, | ||
| # retrying recalculates the moving tail and may skip messages from the failed | ||
| # batch when the upstream topic advances. | ||
| initial_offset = "earliest" | ||
| include_user_headers = true | ||
| # "block" preserves malformed messages for retry; "drop_headers" forwards | ||
| # their payload without user headers. | ||
| malformed_message_policy = "block" | ||
| retry_interval = "1s" | ||
| max_retry_interval = "60s" | ||
| verbose_logging = false |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.