github.com/deanMdreon/kafka-go@v0.4.32/protocol/record_batch.go (about) 1 package protocol 2 3 import ( 4 "errors" 5 "io" 6 "time" 7 ) 8 9 // RecordReader is an interface representing a sequence of records. Record sets 10 // are used in both produce and fetch requests to represent the sequence of 11 // records that are sent to or receive from kafka brokers. 12 // 13 // RecordSet values are not safe to use concurrently from multiple goroutines. 14 type RecordReader interface { 15 // Returns the next record in the set, or io.EOF if the end of the sequence 16 // has been reached. 17 // 18 // The returned Record is guaranteed to be valid until the next call to 19 // ReadRecord. If the program needs to retain the Record value it must make 20 // a copy. 21 ReadRecord() (*Record, error) 22 } 23 24 // NewRecordReader constructs a reader exposing the records passed as arguments. 25 func NewRecordReader(records ...Record) RecordReader { 26 switch len(records) { 27 case 0: 28 return emptyRecordReader{} 29 default: 30 r := &recordReader{records: make([]Record, len(records))} 31 copy(r.records, records) 32 return r 33 } 34 } 35 36 // MultiRecordReader merges multiple record batches into one. 37 func MultiRecordReader(batches ...RecordReader) RecordReader { 38 switch len(batches) { 39 case 0: 40 return emptyRecordReader{} 41 case 1: 42 return batches[0] 43 default: 44 m := &multiRecordReader{batches: make([]RecordReader, len(batches))} 45 copy(m.batches, batches) 46 return m 47 } 48 } 49 50 func forEachRecord(r RecordReader, f func(int, *Record) error) error { 51 for i := 0; ; i++ { 52 rec, err := r.ReadRecord() 53 54 if err != nil { 55 if errors.Is(err, io.EOF) { 56 err = nil 57 } 58 return err 59 } 60 61 if err := handleRecord(i, rec, f); err != nil { 62 return err 63 } 64 } 65 } 66 67 func handleRecord(i int, r *Record, f func(int, *Record) error) error { 68 if r.Key != nil { 69 defer r.Key.Close() 70 } 71 if r.Value != nil { 72 defer r.Value.Close() 73 } 74 return f(i, r) 75 } 76 77 type recordReader struct { 78 records []Record 79 index int 80 } 81 82 func (r *recordReader) ReadRecord() (*Record, error) { 83 if i := r.index; i >= 0 && i < len(r.records) { 84 r.index++ 85 return &r.records[i], nil 86 } 87 return nil, io.EOF 88 } 89 90 type multiRecordReader struct { 91 batches []RecordReader 92 index int 93 } 94 95 func (m *multiRecordReader) ReadRecord() (*Record, error) { 96 for { 97 if m.index == len(m.batches) { 98 return nil, io.EOF 99 } 100 r, err := m.batches[m.index].ReadRecord() 101 if err == nil { 102 return r, nil 103 } 104 if !errors.Is(err, io.EOF) { 105 return nil, err 106 } 107 m.index++ 108 } 109 } 110 111 func concatRecordReader(head RecordReader, tail RecordReader) RecordReader { 112 if head == nil { 113 return tail 114 } 115 if m, _ := head.(*multiRecordReader); m != nil { 116 m.batches = append(m.batches, tail) 117 return m 118 } 119 return MultiRecordReader(head, tail) 120 } 121 122 // optimizedRecordReader is an implementation of a RecordReader which exposes a 123 // sequence 124 type optimizedRecordReader struct { 125 records []optimizedRecord 126 index int 127 buffer Record 128 headers [][]Header 129 } 130 131 func (r *optimizedRecordReader) ReadRecord() (*Record, error) { 132 if i := r.index; i >= 0 && i < len(r.records) { 133 rec := &r.records[i] 134 r.index++ 135 r.buffer = Record{ 136 Offset: rec.offset, 137 Time: rec.time(), 138 Key: rec.key(), 139 Value: rec.value(), 140 } 141 if i < len(r.headers) { 142 r.buffer.Headers = r.headers[i] 143 } 144 return &r.buffer, nil 145 } 146 return nil, io.EOF 147 } 148 149 type optimizedRecord struct { 150 offset int64 151 timestamp int64 152 keyRef *pageRef 153 valueRef *pageRef 154 } 155 156 func (r *optimizedRecord) time() time.Time { 157 return makeTime(r.timestamp) 158 } 159 160 func (r *optimizedRecord) key() Bytes { 161 return makeBytes(r.keyRef) 162 } 163 164 func (r *optimizedRecord) value() Bytes { 165 return makeBytes(r.valueRef) 166 } 167 168 func makeBytes(ref *pageRef) Bytes { 169 if ref == nil { 170 return nil 171 } 172 return ref 173 } 174 175 type emptyRecordReader struct{} 176 177 func (emptyRecordReader) ReadRecord() (*Record, error) { return nil, io.EOF } 178 179 // ControlRecord represents a record read from a control batch. 180 type ControlRecord struct { 181 Offset int64 182 Time time.Time 183 Version int16 184 Type int16 185 Data []byte 186 Headers []Header 187 } 188 189 func ReadControlRecord(r *Record) (*ControlRecord, error) { 190 if r.Key != nil { 191 defer r.Key.Close() 192 } 193 if r.Value != nil { 194 defer r.Value.Close() 195 } 196 197 k, err := ReadAll(r.Key) 198 if err != nil { 199 return nil, err 200 } 201 if k == nil { 202 return nil, Error("invalid control record with nil key") 203 } 204 if len(k) != 4 { 205 return nil, Errorf("invalid control record with key of size %d", len(k)) 206 } 207 208 v, err := ReadAll(r.Value) 209 if err != nil { 210 return nil, err 211 } 212 213 c := &ControlRecord{ 214 Offset: r.Offset, 215 Time: r.Time, 216 Version: readInt16(k[:2]), 217 Type: readInt16(k[2:]), 218 Data: v, 219 Headers: r.Headers, 220 } 221 222 return c, nil 223 } 224 225 func (cr *ControlRecord) Key() Bytes { 226 k := make([]byte, 4) 227 writeInt16(k[:2], cr.Version) 228 writeInt16(k[2:], cr.Type) 229 return NewBytes(k) 230 } 231 232 func (cr *ControlRecord) Value() Bytes { 233 return NewBytes(cr.Data) 234 } 235 236 func (cr *ControlRecord) Record() Record { 237 return Record{ 238 Offset: cr.Offset, 239 Time: cr.Time, 240 Key: cr.Key(), 241 Value: cr.Value(), 242 Headers: cr.Headers, 243 } 244 } 245 246 // ControlBatch is an implementation of the RecordReader interface representing 247 // control batches returned by kafka brokers. 248 type ControlBatch struct { 249 Attributes Attributes 250 PartitionLeaderEpoch int32 251 BaseOffset int64 252 ProducerID int64 253 ProducerEpoch int16 254 BaseSequence int32 255 Records RecordReader 256 } 257 258 // NewControlBatch constructs a control batch from the list of records passed as 259 // arguments. 260 func NewControlBatch(records ...ControlRecord) *ControlBatch { 261 rawRecords := make([]Record, len(records)) 262 for i, cr := range records { 263 rawRecords[i] = cr.Record() 264 } 265 return &ControlBatch{ 266 Records: NewRecordReader(rawRecords...), 267 } 268 } 269 270 func (c *ControlBatch) ReadRecord() (*Record, error) { 271 return c.Records.ReadRecord() 272 } 273 274 func (c *ControlBatch) ReadControlRecord() (*ControlRecord, error) { 275 r, err := c.ReadRecord() 276 if err != nil { 277 return nil, err 278 } 279 if r.Key != nil { 280 defer r.Key.Close() 281 } 282 if r.Value != nil { 283 defer r.Value.Close() 284 } 285 return ReadControlRecord(r) 286 } 287 288 func (c *ControlBatch) Offset() int64 { 289 return c.BaseOffset 290 } 291 292 func (c *ControlBatch) Version() int { 293 return 2 294 } 295 296 // RecordBatch is an implementation of the RecordReader interface representing 297 // regular record batches (v2). 298 type RecordBatch struct { 299 Attributes Attributes 300 PartitionLeaderEpoch int32 301 BaseOffset int64 302 ProducerID int64 303 ProducerEpoch int16 304 BaseSequence int32 305 Records RecordReader 306 } 307 308 func (r *RecordBatch) ReadRecord() (*Record, error) { 309 return r.Records.ReadRecord() 310 } 311 312 func (r *RecordBatch) Offset() int64 { 313 return r.BaseOffset 314 } 315 316 func (r *RecordBatch) Version() int { 317 return 2 318 } 319 320 // MessageSet is an implementation of the RecordReader interface representing 321 // regular message sets (v1). 322 type MessageSet struct { 323 Attributes Attributes 324 BaseOffset int64 325 Records RecordReader 326 } 327 328 func (m *MessageSet) ReadRecord() (*Record, error) { 329 return m.Records.ReadRecord() 330 } 331 332 func (m *MessageSet) Offset() int64 { 333 return m.BaseOffset 334 } 335 336 func (m *MessageSet) Version() int { 337 return 1 338 } 339 340 // RecordStream is an implementation of the RecordReader interface which 341 // combines multiple underlying RecordReader and only expose records that 342 // are not from control batches. 343 type RecordStream struct { 344 Records []RecordReader 345 index int 346 } 347 348 func (s *RecordStream) ReadRecord() (*Record, error) { 349 for { 350 if s.index < 0 || s.index >= len(s.Records) { 351 return nil, io.EOF 352 } 353 354 if _, isControl := s.Records[s.index].(*ControlBatch); isControl { 355 s.index++ 356 continue 357 } 358 359 r, err := s.Records[s.index].ReadRecord() 360 if err != nil { 361 if errors.Is(err, io.EOF) { 362 s.index++ 363 continue 364 } 365 } 366 367 return r, err 368 } 369 }