github.com/hoveychen/kafka-go@v0.4.42/offsetfetch.go (about) 1 package kafka 2 3 import ( 4 "bufio" 5 "context" 6 "fmt" 7 "net" 8 "time" 9 10 "github.com/hoveychen/kafka-go/protocol/offsetfetch" 11 ) 12 13 // OffsetFetchRequest represents a request sent to a kafka broker to read the 14 // currently committed offsets of topic partitions. 15 type OffsetFetchRequest struct { 16 // Address of the kafka broker to send the request to. 17 Addr net.Addr 18 19 // ID of the consumer group to retrieve the offsets for. 20 GroupID string 21 22 // Set of topic partitions to retrieve the offsets for. 23 Topics map[string][]int 24 } 25 26 // OffsetFetchResponse represents a response from a kafka broker to an offset 27 // fetch request. 28 type OffsetFetchResponse struct { 29 // The amount of time that the broker throttled the request. 30 Throttle time.Duration 31 32 // Set of topic partitions that the kafka broker has returned offsets for. 33 Topics map[string][]OffsetFetchPartition 34 35 // An error that may have occurred while attempting to retrieve consumer 36 // group offsets. 37 // 38 // The error contains both the kafka error code, and an error message 39 // returned by the kafka broker. Programs may use the standard errors.Is 40 // function to test the error against kafka error codes. 41 Error error 42 } 43 44 // OffsetFetchPartition represents the state of a single partition in a consumer 45 // group. 46 type OffsetFetchPartition struct { 47 // ID of the partition. 48 Partition int 49 50 // Last committed offsets on the partition when the request was served by 51 // the kafka broker. 52 CommittedOffset int64 53 54 // Consumer group metadata for this partition. 55 Metadata string 56 57 // An error that may have occurred while attempting to retrieve consumer 58 // group offsets for this partition. 59 // 60 // The error contains both the kafka error code, and an error message 61 // returned by the kafka broker. Programs may use the standard errors.Is 62 // function to test the error against kafka error codes. 63 Error error 64 } 65 66 // OffsetFetch sends an offset fetch request to a kafka broker and returns the 67 // response. 68 func (c *Client) OffsetFetch(ctx context.Context, req *OffsetFetchRequest) (*OffsetFetchResponse, error) { 69 topics := make([]offsetfetch.RequestTopic, 0, len(req.Topics)) 70 71 for topicName, partitions := range req.Topics { 72 indexes := make([]int32, len(partitions)) 73 74 for i, p := range partitions { 75 indexes[i] = int32(p) 76 } 77 78 topics = append(topics, offsetfetch.RequestTopic{ 79 Name: topicName, 80 PartitionIndexes: indexes, 81 }) 82 } 83 84 m, err := c.roundTrip(ctx, req.Addr, &offsetfetch.Request{ 85 GroupID: req.GroupID, 86 Topics: topics, 87 }) 88 89 if err != nil { 90 return nil, fmt.Errorf("kafka.(*Client).OffsetFetch: %w", err) 91 } 92 93 res := m.(*offsetfetch.Response) 94 ret := &OffsetFetchResponse{ 95 Throttle: makeDuration(res.ThrottleTimeMs), 96 Topics: make(map[string][]OffsetFetchPartition, len(res.Topics)), 97 Error: makeError(res.ErrorCode, ""), 98 } 99 100 for _, t := range res.Topics { 101 partitions := make([]OffsetFetchPartition, len(t.Partitions)) 102 103 for i, p := range t.Partitions { 104 partitions[i] = OffsetFetchPartition{ 105 Partition: int(p.PartitionIndex), 106 CommittedOffset: p.CommittedOffset, 107 Metadata: p.Metadata, 108 Error: makeError(p.ErrorCode, ""), 109 } 110 } 111 112 ret.Topics[t.Name] = partitions 113 } 114 115 return ret, nil 116 } 117 118 type offsetFetchRequestV1Topic struct { 119 // Topic name 120 Topic string 121 122 // Partitions to fetch offsets 123 Partitions []int32 124 } 125 126 func (t offsetFetchRequestV1Topic) size() int32 { 127 return sizeofString(t.Topic) + 128 sizeofInt32Array(t.Partitions) 129 } 130 131 func (t offsetFetchRequestV1Topic) writeTo(wb *writeBuffer) { 132 wb.writeString(t.Topic) 133 wb.writeInt32Array(t.Partitions) 134 } 135 136 type offsetFetchRequestV1 struct { 137 // GroupID holds the unique group identifier 138 GroupID string 139 140 // Topics to fetch offsets. 141 Topics []offsetFetchRequestV1Topic 142 } 143 144 func (t offsetFetchRequestV1) size() int32 { 145 return sizeofString(t.GroupID) + 146 sizeofArray(len(t.Topics), func(i int) int32 { return t.Topics[i].size() }) 147 } 148 149 func (t offsetFetchRequestV1) writeTo(wb *writeBuffer) { 150 wb.writeString(t.GroupID) 151 wb.writeArray(len(t.Topics), func(i int) { t.Topics[i].writeTo(wb) }) 152 } 153 154 type offsetFetchResponseV1PartitionResponse struct { 155 // Partition ID 156 Partition int32 157 158 // Offset of last committed message 159 Offset int64 160 161 // Metadata client wants to keep 162 Metadata string 163 164 // ErrorCode holds response error code 165 ErrorCode int16 166 } 167 168 func (t offsetFetchResponseV1PartitionResponse) size() int32 { 169 return sizeofInt32(t.Partition) + 170 sizeofInt64(t.Offset) + 171 sizeofString(t.Metadata) + 172 sizeofInt16(t.ErrorCode) 173 } 174 175 func (t offsetFetchResponseV1PartitionResponse) writeTo(wb *writeBuffer) { 176 wb.writeInt32(t.Partition) 177 wb.writeInt64(t.Offset) 178 wb.writeString(t.Metadata) 179 wb.writeInt16(t.ErrorCode) 180 } 181 182 func (t *offsetFetchResponseV1PartitionResponse) readFrom(r *bufio.Reader, size int) (remain int, err error) { 183 if remain, err = readInt32(r, size, &t.Partition); err != nil { 184 return 185 } 186 if remain, err = readInt64(r, remain, &t.Offset); err != nil { 187 return 188 } 189 if remain, err = readString(r, remain, &t.Metadata); err != nil { 190 return 191 } 192 if remain, err = readInt16(r, remain, &t.ErrorCode); err != nil { 193 return 194 } 195 return 196 } 197 198 type offsetFetchResponseV1Response struct { 199 // Topic name 200 Topic string 201 202 // PartitionResponses holds offsets by partition 203 PartitionResponses []offsetFetchResponseV1PartitionResponse 204 } 205 206 func (t offsetFetchResponseV1Response) size() int32 { 207 return sizeofString(t.Topic) + 208 sizeofArray(len(t.PartitionResponses), func(i int) int32 { return t.PartitionResponses[i].size() }) 209 } 210 211 func (t offsetFetchResponseV1Response) writeTo(wb *writeBuffer) { 212 wb.writeString(t.Topic) 213 wb.writeArray(len(t.PartitionResponses), func(i int) { t.PartitionResponses[i].writeTo(wb) }) 214 } 215 216 func (t *offsetFetchResponseV1Response) readFrom(r *bufio.Reader, size int) (remain int, err error) { 217 if remain, err = readString(r, size, &t.Topic); err != nil { 218 return 219 } 220 221 fn := func(r *bufio.Reader, size int) (fnRemain int, fnErr error) { 222 item := offsetFetchResponseV1PartitionResponse{} 223 if fnRemain, fnErr = (&item).readFrom(r, size); err != nil { 224 return 225 } 226 t.PartitionResponses = append(t.PartitionResponses, item) 227 return 228 } 229 if remain, err = readArrayWith(r, remain, fn); err != nil { 230 return 231 } 232 233 return 234 } 235 236 type offsetFetchResponseV1 struct { 237 // Responses holds topic partition offsets 238 Responses []offsetFetchResponseV1Response 239 } 240 241 func (t offsetFetchResponseV1) size() int32 { 242 return sizeofArray(len(t.Responses), func(i int) int32 { return t.Responses[i].size() }) 243 } 244 245 func (t offsetFetchResponseV1) writeTo(wb *writeBuffer) { 246 wb.writeArray(len(t.Responses), func(i int) { t.Responses[i].writeTo(wb) }) 247 } 248 249 func (t *offsetFetchResponseV1) readFrom(r *bufio.Reader, size int) (remain int, err error) { 250 fn := func(r *bufio.Reader, withSize int) (fnRemain int, fnErr error) { 251 item := offsetFetchResponseV1Response{} 252 if fnRemain, fnErr = (&item).readFrom(r, withSize); fnErr != nil { 253 return 254 } 255 t.Responses = append(t.Responses, item) 256 return 257 } 258 if remain, err = readArrayWith(r, size, fn); err != nil { 259 return 260 } 261 262 return 263 }