krishvishal commented on code in PR #3784: URL: https://github.com/apache/iggy/pull/3784#discussion_r3719634810
########## core/connectors/sinks/iggy_sink/src/lib.rs: ########## @@ -0,0 +1,232 @@ +// 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. + +use std::mem; + +use async_trait::async_trait; +use bytes::Bytes; +use iggy::clients::client::IggyClient; +use iggy_common::{Client, Identifier, IggyMessage, MessageClient, Partitioning}; +use iggy_connector_sdk::{ + ConsumedMessage, Error, MessagesMetadata, Payload, Sink, TopicMetadata, sink_connector, +}; +use serde::{Deserialize, Serialize}; +use tracing::{debug, info}; + +sink_connector!(IggySink); + +#[derive(Debug, Serialize, Deserialize)] +pub struct IggySinkConfig { + pub server_address: String, + pub stream_id: Option<String>, + pub topic_id: Option<String>, +} + +#[derive(Debug)] +pub struct IggySink { + id: u32, + config: IggySinkConfig, + client: Option<IggyClient>, +} + +impl IggySink { + pub fn new(id: u32, config: IggySinkConfig) -> Self { + IggySink { + id, + config, + client: None, + } + } +} + +#[async_trait] +impl Sink for IggySink { + async fn open(&mut self) -> Result<(), Error> { + info!( + "Opened iggy_sink with ID: {}, connecting to: {}", + self.id, self.config.server_address + ); + + let client = IggyClient::from_connection_string(&self.config.server_address) + .map_err(|e| Error::InitError(format!("Failed to build client: {e}")))?; + + client + .connect() + .await + .map_err(|e| Error::InitError(format!("Failed to connect: {e}")))?; + + info!("Successfully connected to downstream Iggy cluster"); + + self.client = Some(client); + Ok(()) + } + + async fn consume( + &self, + topic_metadata: &TopicMetadata, + messages_metadata: MessagesMetadata, + messages: Vec<ConsumedMessage>, + ) -> Result<(), Error> { + let client = self + .client + .as_ref() + .ok_or_else(|| Error::InitError("Client not initialized".to_string()))?; + + let stream = self + .config + .stream_id + .as_ref() + .unwrap_or(&topic_metadata.stream); + let topic = self + .config + .topic_id + .as_ref() + .unwrap_or(&topic_metadata.topic); + + let stream_id: Identifier = stream + .as_str() + .try_into() + .map_err(|_| Error::InvalidConfigValue(format!("Invalid stream: {stream}")))?; + let topic_id: Identifier = topic + .as_str() + .try_into() + .map_err(|_| Error::InvalidConfigValue(format!("Invalid topic: {topic}")))?; + + let partitioning = Partitioning::partition_id(messages_metadata.partition_id); Review Comment: Multi-partition source topics can have their messages silently misrouted: (Partitioning::partition_id(messages_metadata.partition_id)) forwards the last message's source partition ID as the routing key for the entire flushed batch. Traced in runtime/src/sink.rs's consume_messages: partition_id is reassigned every loop iteration from whatever partition the last poll returned, and a single sink consumer (one consumer-group member) round-robins across all partitions assigned to it. A batch can end up containing messages from partitions 0 and 1, tagged and routed entirely under partition 1's ID. This label-only field is harmless for every other sink (none of them route by it); iggy_sink is the first to turn it into an actual write-path decision. Reachability: any source topic with >1 partition (the norm), not exercised by this PR's own single-partition example/test config. Fix: group ConsumedMessages by originating partition before building the target batch (one send_messages call per source partition) — sink-local, reduces batching efficiency slightly when a poll straddles partitions. A structural fix (guarantee single-partition batches at the runtime level) affects every sink and needs separate sign-off. ########## core/connectors/sinks/iggy_sink/src/lib.rs: ########## @@ -0,0 +1,232 @@ +// 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. + +use std::mem; + +use async_trait::async_trait; +use bytes::Bytes; +use iggy::clients::client::IggyClient; +use iggy_common::{Client, Identifier, IggyMessage, MessageClient, Partitioning}; +use iggy_connector_sdk::{ + ConsumedMessage, Error, MessagesMetadata, Payload, Sink, TopicMetadata, sink_connector, +}; +use serde::{Deserialize, Serialize}; +use tracing::{debug, info}; + +sink_connector!(IggySink); + +#[derive(Debug, Serialize, Deserialize)] +pub struct IggySinkConfig { + pub server_address: String, Review Comment: Credentials logged in plaintext; not `SecretString`. logged unredacted at :59-62 `(info!("Opened iggy_sink with ID: {}, connecting to: {}", self.id, self.config.server_address))`. Every config shipped in this PR embeds credentials (iggy://iggy:[email protected]:8090). -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
