mirror of
https://github.com/hanzoai/traces.git
synced 2026-10-30 18:20:41 +00:00
a739cb2805
* Bump github.com/twmb/franz-go from 1.18.1 to 1.20.0 Bumps [github.com/twmb/franz-go](https://github.com/twmb/franz-go) from 1.18.1 to 1.20.0. - [Changelog](https://github.com/twmb/franz-go/blob/master/CHANGELOG.md) - [Commits](https://github.com/twmb/franz-go/compare/v1.18.1...v1.20.0) --- updated-dependencies: - dependency-name: github.com/twmb/franz-go dependency-version: 1.20.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <[email protected]> * Update kfake --------- Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Mario <[email protected]>
238 lines
5.3 KiB
Go
238 lines
5.3 KiB
Go
package kfake
|
|
|
|
import (
|
|
"net"
|
|
"strconv"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/twmb/franz-go/pkg/kerr"
|
|
"github.com/twmb/franz-go/pkg/kmsg"
|
|
)
|
|
|
|
// Behavior:
|
|
//
|
|
// * If topic does not exist, we hang
|
|
// * Topic created while waiting is not returned in final response
|
|
// * If any partition is on a different broker, we return immediately
|
|
// * Out of range fetch causes early return
|
|
// * Raw bytes of batch counts against wait bytes
|
|
|
|
func init() { regKey(1, 4, 18) }
|
|
|
|
func (c *Cluster) handleFetch(creq *clientReq, w *watchFetch) (kmsg.Response, error) {
|
|
var (
|
|
req = creq.kreq.(*kmsg.FetchRequest)
|
|
resp = req.ResponseKind().(*kmsg.FetchResponse)
|
|
)
|
|
|
|
if err := checkReqVersion(req.Key(), req.Version); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var (
|
|
nbytes int
|
|
returnEarly bool
|
|
needp tps[int]
|
|
)
|
|
if w == nil {
|
|
out:
|
|
for i, rt := range req.Topics {
|
|
if req.Version >= 13 {
|
|
rt.Topic = c.data.id2t[rt.TopicID]
|
|
req.Topics[i].Topic = rt.Topic
|
|
}
|
|
t, ok := c.data.tps.gett(rt.Topic)
|
|
if !ok {
|
|
continue
|
|
}
|
|
for _, rp := range rt.Partitions {
|
|
pd, ok := t[rp.Partition]
|
|
if !ok || pd.createdAt.After(creq.at) {
|
|
continue
|
|
}
|
|
if pd.leader != creq.cc.b && !pd.followers.has(creq.cc.b) {
|
|
returnEarly = true // NotLeaderForPartition
|
|
break out
|
|
}
|
|
i, ok, atEnd := pd.searchOffset(rp.FetchOffset)
|
|
if atEnd {
|
|
continue
|
|
}
|
|
if !ok {
|
|
returnEarly = true // OffsetOutOfRange
|
|
break out
|
|
}
|
|
pbytes := 0
|
|
for _, b := range pd.batches[i:] {
|
|
nbytes += b.nbytes
|
|
pbytes += b.nbytes
|
|
if pbytes >= int(rp.PartitionMaxBytes) {
|
|
returnEarly = true
|
|
break out
|
|
}
|
|
}
|
|
needp.set(rt.Topic, rp.Partition, int(rp.PartitionMaxBytes)-pbytes)
|
|
}
|
|
}
|
|
}
|
|
|
|
wait := time.Duration(req.MaxWaitMillis) * time.Millisecond
|
|
deadline := creq.at.Add(wait)
|
|
if w == nil && !returnEarly && nbytes < int(req.MinBytes) && time.Now().Before(deadline) {
|
|
w := &watchFetch{
|
|
need: int(req.MinBytes) - nbytes,
|
|
needp: needp,
|
|
deadline: deadline,
|
|
creq: creq,
|
|
}
|
|
w.cb = func() {
|
|
select {
|
|
case c.watchFetchCh <- w:
|
|
case <-c.die:
|
|
}
|
|
}
|
|
for _, rt := range req.Topics {
|
|
t, ok := c.data.tps.gett(rt.Topic)
|
|
if !ok {
|
|
continue
|
|
}
|
|
for _, rp := range rt.Partitions {
|
|
pd, ok := t[rp.Partition]
|
|
if !ok || pd.createdAt.After(creq.at) {
|
|
continue
|
|
}
|
|
pd.watch[w] = struct{}{}
|
|
w.in = append(w.in, pd)
|
|
}
|
|
}
|
|
w.t = time.AfterFunc(wait, w.cb)
|
|
return nil, nil
|
|
}
|
|
|
|
id2t := make(map[uuid]string)
|
|
tidx := make(map[string]int)
|
|
|
|
donet := func(t string, id uuid, errCode int16) *kmsg.FetchResponseTopic {
|
|
if i, ok := tidx[t]; ok {
|
|
return &resp.Topics[i]
|
|
}
|
|
id2t[id] = t
|
|
tidx[t] = len(resp.Topics)
|
|
st := kmsg.NewFetchResponseTopic()
|
|
st.Topic = t
|
|
st.TopicID = id
|
|
resp.Topics = append(resp.Topics, st)
|
|
return &resp.Topics[len(resp.Topics)-1]
|
|
}
|
|
donep := func(t string, id uuid, p int32, errCode int16) *kmsg.FetchResponseTopicPartition {
|
|
sp := kmsg.NewFetchResponseTopicPartition()
|
|
sp.Partition = p
|
|
sp.ErrorCode = errCode
|
|
st := donet(t, id, 0)
|
|
st.Partitions = append(st.Partitions, sp)
|
|
return &st.Partitions[len(st.Partitions)-1]
|
|
}
|
|
|
|
var includeBrokers bool
|
|
defer func() {
|
|
if includeBrokers {
|
|
for _, b := range c.bs {
|
|
sb := kmsg.NewFetchResponseBroker()
|
|
h, p, _ := net.SplitHostPort(b.ln.Addr().String())
|
|
p32, _ := strconv.Atoi(p)
|
|
sb.NodeID = b.node
|
|
sb.Host = h
|
|
sb.Port = int32(p32)
|
|
resp.Brokers = append(resp.Brokers, sb)
|
|
}
|
|
}
|
|
}()
|
|
|
|
var batchesAdded int
|
|
nbytes = 0
|
|
full:
|
|
for _, rt := range req.Topics {
|
|
for _, rp := range rt.Partitions {
|
|
pd, ok := c.data.tps.getp(rt.Topic, rp.Partition)
|
|
if !ok {
|
|
if req.Version >= 13 {
|
|
donep(rt.Topic, rt.TopicID, rp.Partition, kerr.UnknownTopicID.Code)
|
|
} else {
|
|
donep(rt.Topic, rt.TopicID, rp.Partition, kerr.UnknownTopicOrPartition.Code)
|
|
}
|
|
continue
|
|
}
|
|
if pd.leader != creq.cc.b && !pd.followers.has(creq.cc.b) {
|
|
p := donep(rt.Topic, rt.TopicID, rp.Partition, kerr.NotLeaderForPartition.Code)
|
|
p.CurrentLeader.LeaderID = pd.leader.node
|
|
p.CurrentLeader.LeaderEpoch = pd.epoch
|
|
includeBrokers = true
|
|
continue
|
|
}
|
|
sp := donep(rt.Topic, rt.TopicID, rp.Partition, 0)
|
|
sp.HighWatermark = pd.highWatermark
|
|
sp.LastStableOffset = pd.lastStableOffset
|
|
sp.LogStartOffset = pd.logStartOffset
|
|
i, ok, atEnd := pd.searchOffset(rp.FetchOffset)
|
|
if atEnd {
|
|
continue
|
|
}
|
|
if !ok {
|
|
sp.ErrorCode = kerr.OffsetOutOfRange.Code
|
|
continue
|
|
}
|
|
var pbytes int
|
|
for _, b := range pd.batches[i:] {
|
|
if nbytes = nbytes + b.nbytes; nbytes > int(req.MaxBytes) && batchesAdded > 0 {
|
|
break full
|
|
}
|
|
if pbytes = pbytes + b.nbytes; pbytes > int(rp.PartitionMaxBytes) && batchesAdded > 0 {
|
|
break
|
|
}
|
|
batchesAdded++
|
|
sp.RecordBatches = b.AppendTo(sp.RecordBatches)
|
|
}
|
|
}
|
|
}
|
|
|
|
return resp, nil
|
|
}
|
|
|
|
type watchFetch struct {
|
|
need int
|
|
needp tps[int]
|
|
deadline time.Time
|
|
creq *clientReq
|
|
|
|
in []*partData
|
|
cb func()
|
|
t *time.Timer
|
|
|
|
once sync.Once
|
|
cleaned bool
|
|
}
|
|
|
|
func (w *watchFetch) push(nbytes int) {
|
|
w.need -= nbytes
|
|
if w.need <= 0 {
|
|
w.once.Do(func() {
|
|
go w.cb()
|
|
})
|
|
}
|
|
}
|
|
|
|
func (w *watchFetch) deleted() {
|
|
w.once.Do(func() {
|
|
go w.cb()
|
|
})
|
|
}
|
|
|
|
func (w *watchFetch) cleanup(c *Cluster) {
|
|
w.cleaned = true
|
|
for _, in := range w.in {
|
|
delete(in.watch, w)
|
|
}
|
|
w.t.Stop()
|
|
}
|