krabka_log/log/
verbatim_group.rs1use {
4 super::{Log, VerbatimBatch, open::DataProducerAppend},
5 crate::{DeliveryPolicy, LogError, SegmentAllocation, segment::PlacedBatch},
6 krabka_ids::Offset,
7 krabka_protocol::records::{Attributes, CRC_COVERAGE_START, HEADER_LEN, TimestampType},
8 krabka_units::prelude::{ByteSize, ByteSizeExt as _, mebibytes},
9};
10
11impl Log {
12 pub fn try_append_verbatim_group(
24 &mut self,
25 batches: &[&VerbatimBatch],
26 ) -> Option<Result<Vec<Offset>, LogError>> {
27 let config = self.config_snapshot();
28 if batches.len() < 2
29 || config.segment_allocation != SegmentAllocation::Preallocate
30 || config.delivery_policy != DeliveryPolicy::Immediate
31 || config.message_timestamp_type != TimestampType::CreateTime
32 || config.flush_on_append
33 || config.flush_messages.is_some()
34 || config.flush_interval.is_some()
35 || self.stamp_source.is_some()
36 {
37 return None;
38 }
39 let base = self.append_at_expected_offset();
40 let mut next = base;
41 let mut size = 0_u64;
42 let mut max_timestamp = i64::MIN;
43 let mut placed = Vec::with_capacity(batches.len());
44 for &batch in batches {
45 if batch.is_transactional || batch.bytes.len() < HEADER_LEN {
46 return None;
47 }
48 let &attributes = batch
49 .bytes
50 .get(CRC_COVERAGE_START..)
51 .and_then(<[u8]>::first_chunk::<2>)?;
52 let attributes = Attributes(i16::from_be_bytes(attributes));
53 if attributes.is_control_batch() || attributes.is_transactional() {
54 return None;
55 }
56 let (_, end) =
57 krabka_verified::local_append_coordinates(next.0, next.0, batch.last_offset_delta)?;
58 Self::data_producer_tail(
59 batch.producer_id,
60 batch.base_sequence,
61 batch.last_offset_delta,
62 next,
63 )
64 .ok()?;
65 placed.push(PlacedBatch { batch, base: next });
66 next = Offset(end);
67 size = size.checked_add(u64::try_from(batch.bytes.len()).ok()?)?;
68 max_timestamp = max_timestamp.max(batch.max_timestamp);
69 }
70 if size > mebibytes(1).bytes_u64() {
71 return None;
72 }
73 let active = self.active.as_ref()?;
74 if active.size() == ByteSize::ZERO
75 || active.indexes_full_after(config.segment_index_size, batches.len())
76 {
77 return None;
78 }
79 let interval = self.jittered_roll_interval(config.segment_roll_interval);
80 if self.should_roll_for_incoming(
81 ByteSize::from_bytes(size),
82 max_timestamp,
83 config.segment_size,
84 interval,
85 config.segment_index_size,
86 ) {
87 return None;
88 }
89 let active = self.active.as_mut()?;
90 active.reserve(config.segment_size);
91 active.write_direct(config.tail_cache_size);
92 if !active.writes_direct() {
93 return None;
94 }
95 Some(self.append_direct_group(&placed, config.index_interval))
96 }
97
98 fn append_direct_group(
99 &mut self,
100 batches: &[PlacedBatch<'_>],
101 index_interval: ByteSize,
102 ) -> Result<Vec<Offset>, LogError> {
103 self.ensure_unfenced()?;
104 self.rollover_flusher.check()?;
105 self.active
106 .as_mut()
107 .ok_or_else(super::no_active_segment)?
108 .append_verbatim_group(batches, index_interval)?;
109 let result = (|| {
110 for &PlacedBatch { batch, base } in batches {
111 self.flush_after_append(batch.last_offset_delta, false)?;
112 self.update_data_producer_entry(DataProducerAppend {
113 producer_id: batch.producer_id,
114 producer_epoch: batch.producer_epoch,
115 base_sequence: batch.base_sequence,
116 last_offset_delta: batch.last_offset_delta,
117 base_offset: base,
118 timestamp: batch.max_timestamp,
119 is_transactional: false,
120 })?;
121 self.assign_appended_epoch(batch.leader_epoch, base)?;
122 }
123 self.refresh_lso()
124 })();
125 match batches.first() {
126 Some(first) => self.complete_append_state(result, first.base)?,
127 None => result?,
129 }
130 Ok(batches.iter().map(|placed| placed.base).collect())
131 }
132}