Apache Iggyのコネクター変更を複数の専門視点から厳しくレビューし、指摘ごとに独立した検証を行って、修正が必要な問題を整理します。
- コネクターのPRをマージ前に確認したいとき
- ブランチの変更を専門視点でレビューしたいとき
- 指摘の妥当性を独立して検証したいとき
Apache Iggyのストリームからメッセージを受け取り、データベースやHTTPなど外部システムへ送るSinkプラグインの作成・変更を支援します。
Author a new Apache Iggy connector sink plugin under core/connectors/sinks/. Sinks consume messages from Apache Iggy streams and push them to an external system (DB, search engine, HTTP endpoint, object store). Load when creating, modifying, or reviewing a sink crate. Use for sink plugin authoring. NOT for runtime internals (see connector-runtime).
実行スコア 未検証
インストール方法を見るApache Iggyのストリームから受け取ったメッセージを、データベース、検索エンジン、HTTPエンドポイント、オブジェクトストアなどへ送るSinkプラグインの作成・変更・レビューを支援します。初期化時の接続確認、設定値の扱い、ペイロードやヘッダーの変換、バッチ送信、リトライ方針、終了時のフラッシュなど、Sink固有の実装上の要点を確認できます。メッセージの再配信に備えた冪等性や、外部サービスを使う統合テストも扱います。
Apache Iggyから外部サービスへデータを連携するプラグインを新しく作るときや、既存のSinkの設定・送信処理を変更するときに向いています。失敗時の再試行やバッチ処理を含めて、実装の確認をしたい場合にも使えます。
実装対象はRustのcdylibプラグインです。外部サービスと連携するプラグインには、Dockerを使う統合テストの手順が示されています。SDKのSinkトレイト自体の変更や、ランタイム内部の変更は対象外です。
この紹介文は、公開されている SKILL.md をもとに AI(OpenAI Codex)が作成しました。正確な仕様は下の原文を確認してください。
インストールする前に、エージェントに与えられる指示の中身を確認できます。
A sink is a Rust cdylib that implements iggy_connector_sdk::Sink
and exposes FFI symbols via the sink_connector! macro. The runtime
loads it as .so/.dylib/.dll, polls Apache Iggy on its behalf,
runs the transform chain, decodes via the configured Schema, and
hands the sink batches of ConsumedMessage. The sink is responsible
for getting them to the external system reliably and efficiently.
Universal connector rules (SecretString, benchmark, verbose flag, drop accounting, filter contract, exemplar patterns) live in connectors-overview. This skill covers only what's sink-specific.
Sink::open / consume / close) - that's an SDK change, not a plugin one.batch_size, retry caps, or backoff defaults baked into existing in-tree sinks.Err variant to retryable when it previously was not - retrying bad data trips circuit breakers.stdout_sink (minimal), postgres_sink (DB + transient detection), http_sink (validation, batch modes, retry middleware), mongodb_sink (atomic counters), elasticsearch_sink / iceberg_sink (backend-specific idioms).Sink::consume takes &self - never &mut self. Mutable state goes behind tokio::sync::Mutex / AtomicU64. Prefer atomics for hot counters - mongodb_sink uses messages_processed: AtomicU64 + insertion_errors: AtomicU64 and avoids a state lock on the hot path entirely.Sink::open builds the client AND tests connectivity. Fail fast with Error::InitError.Sink::close flushes + drops resources. Take ownership via client: Option<Client> + .take(). sqlx pools have .close().await. reqwest/mongodb/elasticsearch clients just drop. Log final stats either way.Option<T> with a default applied in new(). Forward-compat: adding a field doesn't break existing TOML.Option<String>, parsed via humantime::Duration in new(). Two equivalent in-tree idioms - inline HumanDuration::from_str(&raw).map(|d| *d).unwrap_or_else(..) (random_source, postgres_source) or a local parse_duration helper (http_sink, sdk/src/retry.rs). Fall back to a default on parse failure with warn! - never panic. We do not use humantime_serde.new() + warn! - don't error. http_sink swaps retry_delay and max_retry_delay when reversed to avoid an ExponentialBackoff panic downstream.new(), connectivity in open(), never in consume().Payload variant. Handle Json, Raw, Text and Proto at minimum. Proto holds text that is a JSON document when proto_convert had no descriptor or could not encode, and arbitrary text otherwise: call Payload::json_document() first and fall back to the text handling when it returns None. Unsupported -> Error::InvalidPayloadType or fall back to base64 (elasticsearch_sink).Payload::Json, call try_to_bytes(&self) - single-pass serialization without cloning the OwnedValue tree (sdk/src/lib.rs::Payload::try_to_bytes). Never payload.clone().try_into_vec().ConsumedMessage without cloning: std::mem::replace(&mut message.payload, Payload::Raw(vec![])). Standard pattern across http_sink::send_individual / send_ndjson / send_json_array.Vec::with_capacity(messages.len()) (or * factor for serialized bodies). Used across postgres_sink, mongodb_sink, quickwit_sink, elasticsearch_sink, http_sink.message.headers: Option<BTreeMap<HeaderKey, HeaderValue>>. Check .is_empty() before iterating - runtime treats empty as None.v.as_raw() + base64. for text use v.to_string_value(). See http_sink::EncodedHeader.BTreeMap for deterministic ordering. Don't collect into HashMap.| Scenario | SDK variant | Retry? |
|---|---|---|
| Init / connectivity failure | Error::InitError | n/a (fails open) |
| Bad config value | Error::InvalidConfigValue(field) | n/a |
| Single-message validation failure | Error::InvalidRecordValue(reason) | no - skip + log |
| Transient backend (5xx, conn reset, ...) | Error::HttpRequestFailed / Error::Connection / Error::CannotStoreData | yes |
| Permanent 4xx, schema mismatch, constraint | Error::PermanentHttpError / Error::SchemaMismatch | no - retrying trips circuit breakers |
| I/O failure mid-write (parquet/iceberg) | Error::WriteFailure | caller decides |
| Catalog commit failure (Iceberg) | Error::CatalogCommitError | no - not idempotent |
iggy_connector_sdk::retry::check_connectivity_with_retry(...) for open(), HttpRetryMiddleware for default 429/5xx/network policy on reqwest clients.reqwest-middleware + reqwest_retry::RetryTransientMiddleware::new_with_policy_and_strategy. http_sink defines its own HttpSinkRetryStrategy (honors success_status_codes, per-status decisions).is_transient_error(&e) mapping driver-specific codes. postgres_sink::is_transient_error maps SQLSTATEs 40001, 40P01, 57P01-03, 08000/03/06.iggy_connector_sdk::retry::retry_async(policy, context, should_retry, op) for anything that fails as Err. It owns attempt counting, backoff and the per-retry log, and returns RetryFailure { error, attempts, exhausted }; the caller logs the terminal failure.Ok response, carries a deadline, or reconnects between attempts): retry_backoff(base, retry, max), where retry is 1-based. exponential_backoff is the 0-based primitive underneath, and it applies no jitter; call it directly only when the delay must be exact, as sdk/src/source.rs::nack_retry_delay needs for its tests.Runtime retries on any Err - same batch can be redelivered. In-tree guarantees:
postgres_sink::build_batch_insert_query): PK on id, but plain INSERT (no ON CONFLICT). Duplicate retry triggers 23505, is_transient_error does NOT mark it transient -> error propagates. Adding ON CONFLICT (id) DO NOTHING is a known improvement._id from stream:topic:partition:message_id, via insert_many(docs).ordered(false). Per-document duplicate-key errors don't abort the batch - effective idempotency under retry.bulk_index_documents): bulk action specifies only _index, no _id -> ES auto-generates ids -> NOT idempotent. Extensions should add "_id".Error::CatalogCommitError documents that the txn is consumed on commit(). Rebuild-then-retry can duplicate data - callers must check the catalog first.When writing a new sink, prefer dedup-on-write using the message id field. Mongo's upsert-style is the cleanest reference.
batch_size: 100. Tune per-backend.messages.chunks(self.batch_size). Do not buffer across consume() - runtime already batches at the consumer-poll boundary.http_sink::BatchMode.info!("Opened <connector> connector ID: {}, endpoint: <redacted>", self.id);
debug!("Processing batch of {} messages, offset: {}", batch.len(), offset);
warn!("Retry {attempt}/{max} for <connector> ID: {}, reason: {error}", self.id);
error!("Failed to <op> for <connector> ID: {}, error: {error}", self.id);
info!("Closed <connector> connector ID: {}, processed: {}", self.id, count);
Use literal API field names as labels (offset=, current_offset=).
[lib] crate-type = ["cdylib", "lib"] - runtime can't dlopen a regular rlib.Payload::Json to serialize - use try_to_bytes(&self).Mutex across .await when the locked region does I/O.&mut self on consume - won't compile. you need interior mutability.last_err, process all, return.PermanentHttpError - retries hammer the backend with bad data.consume() - config is a snapshot from new(). Reload requires runtime restart.http_sink::sanitize_url_for_log populates HttpSink.log_url.Unit tests at EOF of src/lib.rs. See connector-testing for BDD naming + test_config() helper.
Real-infra integration tests live under core/integration/tests/connectors/<backend>/ using #[iggy_harness] + a backend fixture that spins up Docker via testcontainers-modules. Plugins integrating with external services must have an integration test. Reference: core/integration/tests/connectors/postgres/postgres_sink.rs + fixtures/postgres/.
cargo fmt --all
cargo sort --no-format --workspace
cargo clippy -p iggy_connector_<name>_sink --all-targets -- -D warnings
cargo test -p iggy_connector_<name>_sink
# Integration tests (filter by name; integration crate has no per-area target):
cargo test -p integration -- connectors::<backend>::<test_name>
Update core/connectors/sinks/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などを追加・変更するときに、関連箇所と互換性への影響を整理するスキルです。
データベース、API、キューなど外部システムをポーリングし、Apache Iggyのストリームへメッセージを送るSourceプラグインの作成・変更を支援します。
Apache Iggyのコネクター向けに、ユニットテストや実サービスを使う統合テストを設計・作成し、命名規則や状態復元などのテスト慣例に沿って確認します。
Apache Iggyコネクターのメッセージにフィールド追加・削除や形式変換などの処理を加える変換機能を設計し、設定連携やテストまで整えます。