Apache Iggyのコネクター変更を複数の専門視点から厳しくレビューし、指摘ごとに独立した検証を行って、修正が必要な問題を整理します。
- コネクターのPRをマージ前に確認したいとき
- ブランチの変更を専門視点でレビューしたいとき
- 指摘の妥当性を独立して検証したいとき
データベース、API、キューなど外部システムをポーリングし、Apache Iggyのストリームへメッセージを送るSourceプラグインの作成・変更を支援します。
Author a new Apache Iggy connector source plugin under core/connectors/sources/. Sources poll an external system (DB, API, queue) and produce messages into Apache Iggy streams. Load when creating, modifying, or reviewing a source crate. Use for source plugin authoring. NOT for runtime internals (see connector-runtime).
実行スコア 未検証
インストール方法を見るデータベース、API、キューなどの外部システムをポーリングし、Apache Iggyのストリームへメッセージを送るSourceプラグインの作成・変更・レビューを支援します。ポーリング間隔、スキーマ選択、メッセージIDや時刻の扱いに加え、カーソルなどの状態をバッチ送信の結果に合わせて確定する方法を確認できます。ACK(送信と状態保存の完了通知)とNACK(完了を確認できなかった通知)の処理、再配信、外部データの削除や更新を行うタイミングも扱います。
外部のデータソースからApache Iggyへデータを取り込むプラグインを作るときに向いています。再起動後の再開位置を保ちたい場合や、送信失敗時にデータを失わないよう状態管理を見直したい場合にも役立ちます。
実装対象はRustのcdylibプラグインです。外部インフラを使うSourceには統合テストの手順が示されています。Sourceトレイト自体の変更やランタイム内部の変更は対象外です。
この紹介文は、公開されている SKILL.md をもとに AI(OpenAI Codex)が作成しました。正確な仕様は下の原文を確認してください。
インストールする前に、エージェントに与えられる指示の中身を確認できます。
A source is a Rust cdylib that implements
iggy_connector_sdk::Source and exposes FFI symbols via the
source_connector! macro. The runtime calls poll() in a loop,
applies transforms, encodes via the configured Schema, sends to
Apache Iggy, and persists the returned ConnectorState after every
successful send.
Universal connector rules (SecretString, benchmark, verbose flag, drop accounting, filter contract, exemplar patterns) live in connectors-overview. This skill covers only what's source-specific.
Source::open / poll / on_batch_result / close) - that's an SDK change.close(). Sanctioned only where the source is itself a server the runtime cannot drive, as in http_source's listener, and then only with an explicit shutdown in the last close() that awaits its tasks before returning.State is rewritten every batch.random_source (minimal + canonical state tests), postgres_source (cursor / delete-after-read / processed-column modes, restart-survives-state tests), elasticsearch_source (scroll cursor), influxdb_source (time-series scan).poll() signature is &selfThe macro shares the source as Arc<T> across the FFI callback and forwarding loop. Signature: async fn poll(&self) -> ... - any mutable state behind tokio::sync::Mutex. Single most common new-contributor mistake.
Never hold the state Mutex across upstream I/O. Build a candidate from committed
state, then stage it until the runtime reports the batch result:
let mut candidate = self.state.lock().await.clone();
let rows = client.query(&sql, &[&candidate.cursor]).await?;
candidate.cursor = Some(new_cursor);
let persisted = ConnectorState::serialize(&candidate, CONNECTOR_NAME, self.id)
.ok_or_else(|| Error::Serialization("failed to serialize source state".into()))?;
*self.pending.lock().await = Some(candidate);
on_batch_result (added by #3855) is how a source learns what happened to the batch it just
returned. The SDK keeps exactly one batch in flight: it will not call poll() again until this
returns. By default it stops after MAX_CONSECUTIVE_NACKS (5) consecutive NACKs; a source can
override batch_policy() to disable the limit when its accepted input cannot be replayed after
restart. A stop is reported to the runtime only when the plugin exports the current SDK's stop
callback, so rebuild older source plugins.
Ack means the runtime sent the batch and persisted its state. Nack means it could not
confirm both, which is not the same as neither happening: a batch that reached the topic but
whose state save failed is NACKed. A missing runtime result leaves the batch pending; it does not
produce a synthetic NACK. A source that replays on Nack is at-least-once, not exactly-once.poll() advances a cursor, deletes rows, or drains an in-memory buffer,
omitting this loses data silently and nothing will tell you. random_source and http_source
implement it; the other shipped sources take the default and skip rows on a NACK.poll(), apply on Ack, discard or replay on Nack. A source whose input is pushed to
it, rather than re-readable upstream, has to hold the batch itself: see http_source's staging.Err from it stops the source immediately, so it is not a retry signal.ConnectorState is Vec<u8> via MessagePack (rmp_serde). Use
ConnectorState::serialize(&state, NAME, id) and
ConnectorState::deserialize::<State>(NAME, id).poll() must not commit cursors or destructive work. Return messages with candidate state and
keep the corresponding work staged.{state_path}/source_{key}.state, then calls on_batch_result(Ack). Commit staged in-memory
state and external delete or mark operations only on ACK. A NACK discards the candidate so the
same data can be polled again.poll() returning and state persistence leaves the prior cursor for the next
poll, so downstream must tolerate at-least-once delivery.on_batch_result can attach state to the corresponding
message batch. Control-plane state that is independent of a publish can ride an empty batch
instead.state: None for an empty poll when no watermark changed. If an empty poll advances a
watermark, stage and return the new state through the same ACK handshake.on_batch_result reports the outcome.State small - rewritten every batch. No unbounded vecs.The SDK allows one in-flight batch. Five consecutive NACKs stop a source using
the default policy and require a manual restart. Returning Err from
on_batch_result is fatal regardless of the NACK limit, so
retry transient backend failures inside the callback before returning an error.
If the runtime has not reported ACK or NACK after 30 seconds, the SDK warns
and keeps waiting for that result without polling or replaying the batch.
Once the result is received, the SDK waits for on_batch_result to finish,
so the callback must bound its own connection acquisition and retry budget.
poll() must sleep(self.poll_interval).await before any work. Without it, an empty source spins a CPU.
Match ProducedMessages.schema to the bytes in messages[i].payload:
Schema::JsonSchema::ProtoSchema::AvroSchema::RawProducedMessage.id: Option<u128> - set when a natural ID exists (DB PK, document id). It rides the wire as the message id, which is what a consumer can dedupe an at-least-once duplicate on.core/server/src/http/wire.rs, which mints a fresh uuid when the incoming id is 0 and otherwise passes it through. Its dedup path is a per-client request-id watermark (dedup_clients_max) that never looks at this field.origin_timestamp: Option<u64> - source-system event time in nanoseconds. Lets downstream sinks reason about lag.timestamp and checksum are Iggy-side - leave None.poll() task per source. No concurrent poll().http_source is the worked example, and it shuts its tasks down in the last close() rather than leaving them to outlive the connector.| Scenario | Variant |
|---|---|
Bad config in new()/open() | Error::InitError |
| Cannot reach external system at startup | Error::InitError or Error::Connection |
| Transient fetch failure (retry-worthy) | Error::Connection or Error::HttpRequestFailed |
| Permanent fetch failure (auth, schema gone) | Error::PermanentHttpError |
| Row failed to serialize | Error::Serialization(...) |
| State serialization failed | Error::Serialization(...) |
Returning Err from poll() is only logged by the SDK's FFI bridge
(sdk/src/source.rs::handle_messages) - the loop continues, the next
poll() runs. Connector status does NOT flip to Error from a poll
failure. Status Error is set by the runtime only on transform/encode
failure, Iggy send failure, or state save failure
(runtime/src/source.rs::source_forwarding_loop calls to
context.sources.set_error). To surface a poll failure as unhealth,
raise it through the metric counter or escalate to Error::InitError
from open().
info!("Opened <connector> connector ID: {}, endpoint: {}", self.id, ...);
info!("Restored state for <connector> ID: {id}, cursor: {:?}", ...);
debug!("Polled {} rows for <connector> ID: {}", rows.len(), self.id);
warn!("Transient fetch failure for <connector> ID: {}, will retry: {error}", self.id);
error!("Failed to <op> for <connector> ID: {}, error: {error}", self.id);
info!("Closed <connector> connector ID: {}, total produced: {}", self.id, ...);
Iggy consumer-loop labels use literal API names (offset=, current_offset=).
async fn poll(&mut self) - won't compile. Use &self + Mutex<State>.state.lock() across the fetch I/O - blocks close(), causes shutdown timeouts.poll() - stage it and wait for ACK.State - rewritten every batch. keep O(constant).std::sync::Mutex - blocks the executor. Use tokio::sync::Mutex.ProducedMessage.id when a stable ID exists - leaves a consumer nothing to dedupe a replayed duplicate on. It does not make the write idempotent server-side, because nothing there reads it.close() that awaits its tasks.Mandatory four canonical source state tests (see connector-testing for the full pattern). Copy from sources/random_source/src/lib.rs::tests. Plus config defaults, payload building, schema selection.
Integration tests under core/integration/tests/connectors/<backend>/ for any source backed by external infra. Use #[iggy_harness] + a TestFixture backed by testcontainers-modules. Reference: core/integration/tests/connectors/postgres/postgres_source.rs (multi-mode tests) + restart.rs (state survives restart). Exercise both ACK and NACK paths when the source stages cursors or destructive work.
cargo fmt --all
cargo sort --no-format --workspace
cargo clippy -p iggy_connector_<name>_source --all-targets -- -D warnings
cargo test -p iggy_connector_<name>_source
# Integration tests:
cargo test -p integration -- connectors::<backend>::<test_name>
Update core/connectors/sources/README.md and add a sample TOML under core/connectors/runtime/example_config/connectors/.
Discussion / help: see AGENTS.md.
まだレビューはありません。使ってみた感想をお寄せください。
ログインなしで書き込めます。使ってみた感想、質問、動かなかった環境などを気軽にどうぞ。
まだコメントはありません。最初のコメントを書いてみませんか。
Apache Iggyのコネクター変更を複数の専門視点から厳しくレビューし、指摘ごとに独立した検証を行って、修正が必要な問題を整理します。
Apache Iggyとコネクタープラグインをつなぐランタイムの開発に使います。読み込みやデータ転送、状態保存、設定、HTTP API、メトリクスなどの変更箇所を把握できます。
Apache Iggy Connectors SDKのスキーマ、エンコーダー、デコーダー、エラー型やFFIなどを追加・変更するときに、関連箇所と互換性への影響を整理するスキルです。
Apache Iggyのストリームからメッセージを受け取り、データベースやHTTPなど外部システムへ送るSinkプラグインの作成・変更を支援します。
Apache Iggyのコネクター向けに、ユニットテストや実サービスを使う統合テストを設計・作成し、命名規則や状態復元などのテスト慣例に沿って確認します。
Apache Iggyコネクターのメッセージにフィールド追加・削除や形式変換などの処理を加える変換機能を設計し、設定連携やテストまで整えます。