|
|
@@ -1,127 +1,238 @@
|
|
|
package sarama
|
|
|
|
|
|
-type fetchRequestBlock struct {
|
|
|
- Version int16
|
|
|
- currentLeaderEpoch int32
|
|
|
- fetchOffset int64
|
|
|
- logStartOffset int64
|
|
|
- maxBytes int32
|
|
|
+// FetchPartition contains the partitions to fetch.
|
|
|
+type FetchPartition struct {
|
|
|
+ // Version defines the protocol version to use for encode and decode
|
|
|
+ Version int16
|
|
|
+ // PartitionIndex contains the partition index.
|
|
|
+ PartitionIndex int32
|
|
|
+ // CurrentLeaderEpoch contains the current leader epoch of the partition.
|
|
|
+ CurrentLeaderEpoch int32
|
|
|
+ // FetchOffset contains the message offset.
|
|
|
+ FetchOffset int64
|
|
|
+ // LogStartOffset contains the earliest available offset of the follower replica. The field is only used when the request is sent by the follower.
|
|
|
+ LogStartOffset int64
|
|
|
+ // MaxBytes contains the maximum bytes to fetch from this partition. See KIP-74 for cases where this limit may not be honored.
|
|
|
+ MaxBytes int32
|
|
|
}
|
|
|
|
|
|
-func (b *fetchRequestBlock) encode(pe packetEncoder, version int16) error {
|
|
|
- b.Version = version
|
|
|
- if b.Version >= 9 {
|
|
|
- pe.putInt32(b.currentLeaderEpoch)
|
|
|
+func (f *FetchPartition) encode(pe packetEncoder, version int16) (err error) {
|
|
|
+ f.Version = version
|
|
|
+ pe.putInt32(f.PartitionIndex)
|
|
|
+
|
|
|
+ if f.Version >= 9 {
|
|
|
+ pe.putInt32(f.CurrentLeaderEpoch)
|
|
|
}
|
|
|
- pe.putInt64(b.fetchOffset)
|
|
|
- if b.Version >= 5 {
|
|
|
- pe.putInt64(b.logStartOffset)
|
|
|
+
|
|
|
+ pe.putInt64(f.FetchOffset)
|
|
|
+
|
|
|
+ if f.Version >= 5 {
|
|
|
+ pe.putInt64(f.LogStartOffset)
|
|
|
}
|
|
|
- pe.putInt32(b.maxBytes)
|
|
|
+
|
|
|
+ pe.putInt32(f.MaxBytes)
|
|
|
+
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func (b *fetchRequestBlock) decode(pd packetDecoder, version int16) (err error) {
|
|
|
- b.Version = version
|
|
|
- if b.Version >= 9 {
|
|
|
- if b.currentLeaderEpoch, err = pd.getInt32(); err != nil {
|
|
|
+func (f *FetchPartition) decode(pd packetDecoder, version int16) (err error) {
|
|
|
+ f.Version = version
|
|
|
+ if f.PartitionIndex, err = pd.getInt32(); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ if f.Version >= 9 {
|
|
|
+ if f.CurrentLeaderEpoch, err = pd.getInt32(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
}
|
|
|
- if b.fetchOffset, err = pd.getInt64(); err != nil {
|
|
|
+
|
|
|
+ if f.FetchOffset, err = pd.getInt64(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
- if b.Version >= 5 {
|
|
|
- if b.logStartOffset, err = pd.getInt64(); err != nil {
|
|
|
+
|
|
|
+ if f.Version >= 5 {
|
|
|
+ if f.LogStartOffset, err = pd.getInt64(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
}
|
|
|
- if b.maxBytes, err = pd.getInt32(); err != nil {
|
|
|
+
|
|
|
+ if f.MaxBytes, err = pd.getInt32(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
+
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-// FetchRequest (API key 1) will fetch Kafka messages. Version 3 introduced the MaxBytes field. See
|
|
|
-// https://issues.apache.org/jira/browse/KAFKA-2063 for a discussion of the issues leading up to that. The KIP is at
|
|
|
-// https://cwiki.apache.org/confluence/display/KAFKA/KIP-74%3A+Add+Fetch+Response+Size+Limit+in+Bytes
|
|
|
-type FetchRequest struct {
|
|
|
- MaxWaitTime int32
|
|
|
- MinBytes int32
|
|
|
- MaxBytes int32
|
|
|
- Version int16
|
|
|
- Isolation IsolationLevel
|
|
|
- SessionID int32
|
|
|
- SessionEpoch int32
|
|
|
- blocks map[string]map[int32]*fetchRequestBlock
|
|
|
- forgotten map[string][]int32
|
|
|
- RackID string
|
|
|
+// FetchableTopic contains the topics to fetch.
|
|
|
+type FetchableTopic struct {
|
|
|
+ // Version defines the protocol version to use for encode and decode
|
|
|
+ Version int16
|
|
|
+ // Name contains the name of the topic to fetch.
|
|
|
+ Name string
|
|
|
+ // FetchPartitions contains the partitions to fetch.
|
|
|
+ FetchPartitions []FetchPartition
|
|
|
+}
|
|
|
+
|
|
|
+func (t *FetchableTopic) encode(pe packetEncoder, version int16) (err error) {
|
|
|
+ t.Version = version
|
|
|
+ if err := pe.putString(t.Name); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ if err := pe.putArrayLength(len(t.FetchPartitions)); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ for _, block := range t.FetchPartitions {
|
|
|
+ if err := block.encode(pe, t.Version); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (t *FetchableTopic) decode(pd packetDecoder, version int16) (err error) {
|
|
|
+ t.Version = version
|
|
|
+ if t.Name, err = pd.getString(); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ if numFetchPartitions, err := pd.getArrayLength(); err != nil {
|
|
|
+ return err
|
|
|
+ } else {
|
|
|
+ t.FetchPartitions = make([]FetchPartition, numFetchPartitions)
|
|
|
+ for i := 0; i < numFetchPartitions; i++ {
|
|
|
+ var block FetchPartition
|
|
|
+ if err := block.decode(pd, t.Version); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ t.FetchPartitions[i] = block
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
}
|
|
|
|
|
|
-type IsolationLevel int8
|
|
|
+// ForgottenTopic contains in an incremental fetch request, the partitions to remove.
|
|
|
+type ForgottenTopic struct {
|
|
|
+ // Version defines the protocol version to use for encode and decode
|
|
|
+ Version int16
|
|
|
+ // Name contains the partition name.
|
|
|
+ Name string
|
|
|
+ // ForgottenPartitionIndexes contains the partitions indexes to forget.
|
|
|
+ ForgottenPartitionIndexes []int32
|
|
|
+}
|
|
|
+
|
|
|
+func (f *ForgottenTopic) encode(pe packetEncoder, version int16) (err error) {
|
|
|
+ f.Version = version
|
|
|
+ if f.Version >= 7 {
|
|
|
+ if err := pe.putString(f.Name); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if f.Version >= 7 {
|
|
|
+ if err := pe.putInt32Array(f.ForgottenPartitionIndexes); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (f *ForgottenTopic) decode(pd packetDecoder, version int16) (err error) {
|
|
|
+ f.Version = version
|
|
|
+ if f.Version >= 7 {
|
|
|
+ if f.Name, err = pd.getString(); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if f.Version >= 7 {
|
|
|
+ if f.ForgottenPartitionIndexes, err = pd.getInt32Array(); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
|
|
|
const (
|
|
|
- ReadUncommitted IsolationLevel = iota
|
|
|
+ ReadUncommitted int8 = iota
|
|
|
ReadCommitted
|
|
|
)
|
|
|
|
|
|
+type FetchRequest struct {
|
|
|
+ // Version defines the protocol version to use for encode and decode
|
|
|
+ Version int16
|
|
|
+ // ReplicaID contains the broker ID of the follower, of -1 if this request is from a consumer.
|
|
|
+ ReplicaID int32
|
|
|
+ // MaxWait contains the maximum time in milliseconds to wait for the response.
|
|
|
+ MaxWait int32
|
|
|
+ // MinBytes contains the minimum bytes to accumulate in the response.
|
|
|
+ MinBytes int32
|
|
|
+ // MaxBytes contains the maximum bytes to fetch. See KIP-74 for cases where this limit may not be honored.
|
|
|
+ MaxBytes int32
|
|
|
+ // IsolationLevel contains a This setting controls the visibility of transactional records. Using READ_UNCOMMITTED (isolation_level = 0) makes all records visible. With READ_COMMITTED (isolation_level = 1), non-transactional and COMMITTED transactional records are visible. To be more concrete, READ_COMMITTED returns all data from offsets smaller than the current LSO (last stable offset), and enables the inclusion of the list of aborted transactions in the result, which allows consumers to discard ABORTED transactional records
|
|
|
+ IsolationLevel int8
|
|
|
+ // SessionID contains the fetch session ID.
|
|
|
+ SessionID int32
|
|
|
+ // Epoch contains the epoch of the partition leader as known to the follower replica or a consumer.
|
|
|
+ Epoch int32
|
|
|
+ // Topics contains the topics to fetch.
|
|
|
+ Topics []FetchableTopic
|
|
|
+ // Forgotten contains in an incremental fetch request, the partitions to remove.
|
|
|
+ Forgotten []ForgottenTopic
|
|
|
+ // RackID contains a Rack ID of the consumer making this request
|
|
|
+ RackID string
|
|
|
+}
|
|
|
+
|
|
|
func (r *FetchRequest) encode(pe packetEncoder) (err error) {
|
|
|
- pe.putInt32(-1) // replica ID is always -1 for clients
|
|
|
- pe.putInt32(r.MaxWaitTime)
|
|
|
+ pe.putInt32(r.ReplicaID)
|
|
|
+
|
|
|
+ pe.putInt32(r.MaxWait)
|
|
|
+
|
|
|
pe.putInt32(r.MinBytes)
|
|
|
+
|
|
|
if r.Version >= 3 {
|
|
|
pe.putInt32(r.MaxBytes)
|
|
|
}
|
|
|
+
|
|
|
if r.Version >= 4 {
|
|
|
- pe.putInt8(int8(r.Isolation))
|
|
|
+ pe.putInt8(r.IsolationLevel)
|
|
|
}
|
|
|
+
|
|
|
if r.Version >= 7 {
|
|
|
pe.putInt32(r.SessionID)
|
|
|
- pe.putInt32(r.SessionEpoch)
|
|
|
}
|
|
|
- err = pe.putArrayLength(len(r.blocks))
|
|
|
- if err != nil {
|
|
|
+
|
|
|
+ if r.Version >= 7 {
|
|
|
+ pe.putInt32(r.Epoch)
|
|
|
+ }
|
|
|
+
|
|
|
+ if err := pe.putArrayLength(len(r.Topics)); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
- for topic, blocks := range r.blocks {
|
|
|
- err = pe.putString(topic)
|
|
|
- if err != nil {
|
|
|
+ for _, block := range r.Topics {
|
|
|
+ if err := block.encode(pe, r.Version); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
- err = pe.putArrayLength(len(blocks))
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- for partition, block := range blocks {
|
|
|
- pe.putInt32(partition)
|
|
|
- err = block.encode(pe, r.Version)
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- }
|
|
|
}
|
|
|
+
|
|
|
if r.Version >= 7 {
|
|
|
- err = pe.putArrayLength(len(r.forgotten))
|
|
|
- if err != nil {
|
|
|
+ if err := pe.putArrayLength(len(r.Forgotten)); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
- for topic, partitions := range r.forgotten {
|
|
|
- err = pe.putString(topic)
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- err = pe.putArrayLength(len(partitions))
|
|
|
- if err != nil {
|
|
|
+ for _, block := range r.Forgotten {
|
|
|
+ if err := block.encode(pe, r.Version); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
- for _, partition := range partitions {
|
|
|
- pe.putInt32(partition)
|
|
|
- }
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
if r.Version >= 11 {
|
|
|
- err = pe.putString(r.RackID)
|
|
|
- if err != nil {
|
|
|
+ if err := pe.putString(r.RackID); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
}
|
|
|
@@ -131,99 +242,72 @@ func (r *FetchRequest) encode(pe packetEncoder) (err error) {
|
|
|
|
|
|
func (r *FetchRequest) decode(pd packetDecoder, version int16) (err error) {
|
|
|
r.Version = version
|
|
|
-
|
|
|
- if _, err = pd.getInt32(); err != nil {
|
|
|
+ if r.ReplicaID, err = pd.getInt32(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
- if r.MaxWaitTime, err = pd.getInt32(); err != nil {
|
|
|
+
|
|
|
+ if r.MaxWait, err = pd.getInt32(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
+
|
|
|
if r.MinBytes, err = pd.getInt32(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
+
|
|
|
if r.Version >= 3 {
|
|
|
if r.MaxBytes, err = pd.getInt32(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
if r.Version >= 4 {
|
|
|
- isolation, err := pd.getInt8()
|
|
|
- if err != nil {
|
|
|
+ if r.IsolationLevel, err = pd.getInt8(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
- r.Isolation = IsolationLevel(isolation)
|
|
|
}
|
|
|
+
|
|
|
if r.Version >= 7 {
|
|
|
- r.SessionID, err = pd.getInt32()
|
|
|
- if err != nil {
|
|
|
+ if r.SessionID, err = pd.getInt32(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
- r.SessionEpoch, err = pd.getInt32()
|
|
|
- if err != nil {
|
|
|
+ }
|
|
|
+
|
|
|
+ if r.Version >= 7 {
|
|
|
+ if r.Epoch, err = pd.getInt32(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
}
|
|
|
- topicCount, err := pd.getArrayLength()
|
|
|
- if err != nil {
|
|
|
+
|
|
|
+ if numTopics, err := pd.getArrayLength(); err != nil {
|
|
|
return err
|
|
|
- }
|
|
|
- if topicCount == 0 {
|
|
|
- return nil
|
|
|
- }
|
|
|
- r.blocks = make(map[string]map[int32]*fetchRequestBlock)
|
|
|
- for i := 0; i < topicCount; i++ {
|
|
|
- topic, err := pd.getString()
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- partitionCount, err := pd.getArrayLength()
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- r.blocks[topic] = make(map[int32]*fetchRequestBlock)
|
|
|
- for j := 0; j < partitionCount; j++ {
|
|
|
- partition, err := pd.getInt32()
|
|
|
- if err != nil {
|
|
|
+ } else {
|
|
|
+ r.Topics = make([]FetchableTopic, numTopics)
|
|
|
+ for i := 0; i < numTopics; i++ {
|
|
|
+ var block FetchableTopic
|
|
|
+ if err := block.decode(pd, r.Version); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
- fetchBlock := &fetchRequestBlock{}
|
|
|
- if err = fetchBlock.decode(pd, r.Version); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- r.blocks[topic][partition] = fetchBlock
|
|
|
+ r.Topics[i] = block
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if r.Version >= 7 {
|
|
|
- forgottenCount, err := pd.getArrayLength()
|
|
|
- if err != nil {
|
|
|
+ if numForgotten, err := pd.getArrayLength(); err != nil {
|
|
|
return err
|
|
|
- }
|
|
|
- r.forgotten = make(map[string][]int32)
|
|
|
- for i := 0; i < forgottenCount; i++ {
|
|
|
- topic, err := pd.getString()
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- partitionCount, err := pd.getArrayLength()
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- r.forgotten[topic] = make([]int32, partitionCount)
|
|
|
-
|
|
|
- for j := 0; j < partitionCount; j++ {
|
|
|
- partition, err := pd.getInt32()
|
|
|
- if err != nil {
|
|
|
+ } else {
|
|
|
+ r.Forgotten = make([]ForgottenTopic, numForgotten)
|
|
|
+ for i := 0; i < numForgotten; i++ {
|
|
|
+ var block ForgottenTopic
|
|
|
+ if err := block.decode(pd, r.Version); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
- r.forgotten[topic][j] = partition
|
|
|
+ r.Forgotten[i] = block
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if r.Version >= 11 {
|
|
|
- r.RackID, err = pd.getString()
|
|
|
- if err != nil {
|
|
|
+ if r.RackID, err = pd.getString(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
}
|
|
|
@@ -269,27 +353,3 @@ func (r *FetchRequest) requiredVersion() KafkaVersion {
|
|
|
return MaxVersion
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
-func (r *FetchRequest) AddBlock(topic string, partitionID int32, fetchOffset int64, maxBytes int32) {
|
|
|
- if r.blocks == nil {
|
|
|
- r.blocks = make(map[string]map[int32]*fetchRequestBlock)
|
|
|
- }
|
|
|
-
|
|
|
- if r.Version >= 7 && r.forgotten == nil {
|
|
|
- r.forgotten = make(map[string][]int32)
|
|
|
- }
|
|
|
-
|
|
|
- if r.blocks[topic] == nil {
|
|
|
- r.blocks[topic] = make(map[int32]*fetchRequestBlock)
|
|
|
- }
|
|
|
-
|
|
|
- tmp := new(fetchRequestBlock)
|
|
|
- tmp.Version = r.Version
|
|
|
- tmp.maxBytes = maxBytes
|
|
|
- tmp.fetchOffset = fetchOffset
|
|
|
- if r.Version >= 9 {
|
|
|
- tmp.currentLeaderEpoch = int32(-1)
|
|
|
- }
|
|
|
-
|
|
|
- r.blocks[topic][partitionID] = tmp
|
|
|
-}
|