Skip to content

Commit 147e2c5

Browse files
committed
kafka: skip input step hashing below exactly-once fault tolerance
The Kafka input connector hashed every parsed row into a per-partition step hash on its poller threads, at every fault-tolerance level. The hash is only ever read when a step is replayed, and replay requires exactly-once fault tolerance, so every pipeline below that level computed the hash and threw it away. For rows with VARIANT columns the hash walked every nested map on the hot path. The other transports (file, url, s3, nexmark) already skip hashing when the consumer reports that the pipeline cannot replay. Create the hasher only when the pipeline runs with exactly-once fault tolerance, and report each step through Resume::new_metadata_only: Replay with the hash under exactly-once, Seek otherwise. Seek still carries the partition offsets, so suspend and resume from a checkpoint work unchanged. On a benchmark that ingests 5 million JSON records into a table with six VARIANT columns (16 partitions, 8 poller threads, fault tolerance off), steady-state ingest rises from 108k to 161k records per second, and the hash disappears from the profile, where it had been the largest single item on the poller threads. Signed-off-by: Gerd Zellweger <mail@gerdzellweger.com>
1 parent c94d447 commit 147e2c5

1 file changed

Lines changed: 23 additions & 12 deletions

File tree

  • crates/adapters/src/transport/kafka/ft

crates/adapters/src/transport/kafka/ft/input.rs

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -536,19 +536,26 @@ impl KafkaFtInputReaderInner {
536536
buffers: Box<dyn StagedBuffers>,
537537
timestamp: DateTime<Utc>,
538538
amt: BufferSize,
539-
hash: u64,
539+
hash: Option<u64>,
540540
offsets: Vec<Range<i64>>,
541541
}
542542
struct Stager {
543543
buffers: VecDeque<StagedBuffer>,
544544
offsets: Vec<Range<i64>>,
545-
hasher: KafkaFtHasher,
545+
/// `Some` only with exactly-once fault tolerance: the step hash is
546+
/// verified on replay, which never happens below that level, and
547+
/// hashing every parsed row is expensive.
548+
hasher: Option<KafkaFtHasher>,
546549
inputs: Vec<Box<dyn InputBuffer>>,
547550
amt: BufferSize,
548551
timestamp: Option<DateTime<Utc>>,
549552
}
550553
impl Stager {
551-
fn new(receivers: &BTreeMap<&i32, Arc<PartitionReceiver>>, partitions: &[i32]) -> Self {
554+
fn new(
555+
receivers: &BTreeMap<&i32, Arc<PartitionReceiver>>,
556+
partitions: &[i32],
557+
hashing: bool,
558+
) -> Self {
552559
Self {
553560
buffers: VecDeque::new(),
554561
offsets: receivers
@@ -558,7 +565,7 @@ impl KafkaFtInputReaderInner {
558565
next_offset..next_offset
559566
})
560567
.collect(),
561-
hasher: KafkaFtHasher::new(partitions),
568+
hasher: hashing.then(|| KafkaFtHasher::new(partitions)),
562569
inputs: Vec::new(),
563570
amt: BufferSize::default(),
564571
timestamp: None,
@@ -576,7 +583,7 @@ impl KafkaFtInputReaderInner {
576583
buffers: parser.stage(std::mem::take(&mut self.inputs)),
577584
timestamp: self.timestamp.take().unwrap_or_else(Utc::now),
578585
amt: std::mem::take(&mut self.amt),
579-
hash: self.hasher.take(),
586+
hash: self.hasher.as_mut().map(KafkaFtHasher::take),
580587
offsets: self.take_offsets(),
581588
}
582589
}
@@ -600,7 +607,9 @@ impl KafkaFtInputReaderInner {
600607
let amt = msg.len();
601608
consumer.buffered(amt);
602609
self.amt += amt;
603-
self.hasher.add(partition, &msg);
610+
if let Some(hasher) = &mut self.hasher {
611+
hasher.add(partition, &msg);
612+
}
604613
if let Some(buffer) = msg {
605614
self.inputs.push(buffer);
606615
}
@@ -620,7 +629,13 @@ impl KafkaFtInputReaderInner {
620629
}
621630
}
622631
}
623-
let mut stager = Stager::new(&receivers, &partitions);
632+
// The step hash is only ever verified when the pipeline replays a
633+
// step, which requires exactly-once fault tolerance; below that
634+
// level, skip hashing (it walks every parsed row) and emit
635+
// `Resume::Seek`, which still carries the offsets needed for
636+
// suspend/resume.
637+
let hashing = consumer.pipeline_fault_tolerance() == Some(FtModel::ExactlyOnce);
638+
let mut stager = Stager::new(&receivers, &partitions, hashing);
624639

625640
loop {
626641
let was_running = running;
@@ -640,11 +655,7 @@ impl KafkaFtInputReaderInner {
640655
.unwrap();
641656
consumer.extended(
642657
buffer.amt,
643-
Some(Resume::Replay {
644-
hash: buffer.hash,
645-
seek: metadata.clone(),
646-
replay: rmpv::Value::Nil,
647-
}),
658+
Some(Resume::new_metadata_only(metadata.clone(), buffer.hash)),
648659
vec![Watermark::new(buffer.timestamp, Some(metadata))],
649660
);
650661
}

0 commit comments

Comments
 (0)