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]>
468 lines
15 KiB
Go
468 lines
15 KiB
Go
// Package kmsg contains Kafka request and response types and autogenerated
|
|
// serialization and deserialization functions.
|
|
//
|
|
// This package may bump major versions whenever Kafka makes a backwards
|
|
// incompatible protocol change, per the types chosen for this package. For
|
|
// example, Kafka can change a field from non-nullable to nullable, which would
|
|
// require changing a field from a non-pointer to a pointer. We could get
|
|
// around this by making everything an opaque struct and having getters, but
|
|
// that is more tedious than having a few rare major version bumps.
|
|
//
|
|
// If you are using this package directly with kgo, you should either always
|
|
// use New functions, or Default functions after creating structs, or you
|
|
// should pin the max supported version. If you use New functions, you will
|
|
// have safe defaults as new fields are added. If you pin versions, you will
|
|
// avoid new fields being used. If you do neither of these, you may opt in to
|
|
// new fields that do not have safe zero value defaults, and this may lead to
|
|
// errors or unexpected results.
|
|
//
|
|
// Thus, whenever you initialize a struct from this package, do the following:
|
|
//
|
|
// struct := kmsg.NewFoo()
|
|
// struct.Field = "value I want to set"
|
|
//
|
|
// Most of this package is generated, but a few things are manual. What is
|
|
// manual: all interfaces, the RequestFormatter, record / message / record
|
|
// batch reading, and sticky member metadata serialization.
|
|
package kmsg
|
|
|
|
import (
|
|
"context"
|
|
"sort"
|
|
|
|
"github.com/twmb/franz-go/pkg/kmsg/internal/kbin"
|
|
)
|
|
|
|
//go:generate cp ../kbin/primitives.go internal/kbin/
|
|
|
|
// Requestor issues requests. Notably, the kgo.Client and kgo.Broker implements
|
|
// Requestor. All Requests in this package have a RequestWith function to have
|
|
// type-safe requests.
|
|
type Requestor interface {
|
|
// Request issues a Request and returns either a Response or an error.
|
|
Request(context.Context, Request) (Response, error)
|
|
}
|
|
|
|
// Request represents a type that can be requested to Kafka.
|
|
type Request interface {
|
|
// Key returns the protocol key for this message kind.
|
|
Key() int16
|
|
// MaxVersion returns the maximum protocol version this message
|
|
// supports.
|
|
//
|
|
// This function allows one to implement a client that chooses message
|
|
// versions based off of the max of a message's max version in the
|
|
// client and the broker's max supported version.
|
|
MaxVersion() int16
|
|
// SetVersion sets the version to use for this request and response.
|
|
SetVersion(int16)
|
|
// GetVersion returns the version currently set to use for the request
|
|
// and response.
|
|
GetVersion() int16
|
|
// IsFlexible returns whether the request at its current version is
|
|
// "flexible" as per the KIP-482.
|
|
IsFlexible() bool
|
|
// AppendTo appends this message in wire protocol form to a slice and
|
|
// returns the slice.
|
|
AppendTo([]byte) []byte
|
|
// ReadFrom parses all of the input slice into the response type.
|
|
//
|
|
// This should return an error if too little data is input.
|
|
ReadFrom([]byte) error
|
|
// ResponseKind returns an empty Response that is expected for
|
|
// this message request.
|
|
ResponseKind() Response
|
|
}
|
|
|
|
// AdminRequest represents a request that must be issued to Kafka controllers.
|
|
type AdminRequest interface {
|
|
// IsAdminRequest is a method attached to requests that must be
|
|
// issed to Kafka controllers.
|
|
IsAdminRequest()
|
|
Request
|
|
}
|
|
|
|
// GroupCoordinatorRequest represents a request that must be issued to a
|
|
// group coordinator.
|
|
type GroupCoordinatorRequest interface {
|
|
// IsGroupCoordinatorRequest is a method attached to requests that
|
|
// must be issued to group coordinators.
|
|
IsGroupCoordinatorRequest()
|
|
Request
|
|
}
|
|
|
|
// TxnCoordinatorRequest represents a request that must be issued to a
|
|
// transaction coordinator.
|
|
type TxnCoordinatorRequest interface {
|
|
// IsTxnCoordinatorRequest is a method attached to requests that
|
|
// must be issued to transaction coordinators.
|
|
IsTxnCoordinatorRequest()
|
|
Request
|
|
}
|
|
|
|
// Response represents a type that Kafka responds with.
|
|
type Response interface {
|
|
// Key returns the protocol key for this message kind.
|
|
Key() int16
|
|
// MaxVersion returns the maximum protocol version this message
|
|
// supports.
|
|
MaxVersion() int16
|
|
// SetVersion sets the version to use for this request and response.
|
|
SetVersion(int16)
|
|
// GetVersion returns the version currently set to use for the request
|
|
// and response.
|
|
GetVersion() int16
|
|
// IsFlexible returns whether the request at its current version is
|
|
// "flexible" as per the KIP-482.
|
|
IsFlexible() bool
|
|
// AppendTo appends this message in wire protocol form to a slice and
|
|
// returns the slice.
|
|
AppendTo([]byte) []byte
|
|
// ReadFrom parses all of the input slice into the response type.
|
|
//
|
|
// This should return an error if too little data is input.
|
|
ReadFrom([]byte) error
|
|
// RequestKind returns an empty Request that is expected for
|
|
// this message request.
|
|
RequestKind() Request
|
|
}
|
|
|
|
// UnsafeReadFrom, implemented by all requests and responses generated in this
|
|
// package, switches to using unsafe slice-to-string conversions when reading.
|
|
// This can be used to avoid a lot of garbage, but it means to have to be
|
|
// careful when using any strings in structs: if you hold onto the string, the
|
|
// underlying response slice will not be garbage collected.
|
|
type UnsafeReadFrom interface {
|
|
UnsafeReadFrom([]byte) error
|
|
}
|
|
|
|
// ThrottleResponse represents a response that could have a throttle applied by
|
|
// Kafka. Any response that implements ThrottleResponse also implements
|
|
// SetThrottleResponse.
|
|
//
|
|
// Kafka 2.0.0 switched throttles from being applied before responses to being
|
|
// applied after responses.
|
|
type ThrottleResponse interface {
|
|
// Throttle returns the response's throttle millis value and
|
|
// whether Kafka applies the throttle after the response.
|
|
Throttle() (int32, bool)
|
|
}
|
|
|
|
// SetThrottleResponse sets the throttle in a response that can have a throttle
|
|
// applied. Any kmsg interface that implements ThrottleResponse also implements
|
|
// SetThrottleResponse.
|
|
type SetThrottleResponse interface {
|
|
// SetThrottle sets the response's throttle millis value.
|
|
SetThrottle(int32)
|
|
}
|
|
|
|
// TimeoutRequest represents a request that has a TimeoutMillis field.
|
|
// Any request that implements TimeoutRequest also implements SetTimeoutRequest.
|
|
type TimeoutRequest interface {
|
|
// Timeout returns the request's timeout millis value.
|
|
Timeout() int32
|
|
}
|
|
|
|
// SetTimeoutRequest sets the timeout in a request that can have a timeout
|
|
// applied. Any kmsg interface that implements ThrottleRequest also implements
|
|
// SetThrottleRequest.
|
|
type SetTimeoutRequest interface {
|
|
// SetTimeout sets the request's timeout millis value.
|
|
SetTimeout(timeoutMillis int32)
|
|
}
|
|
|
|
// RequestFormatter formats requests.
|
|
//
|
|
// The default empty struct works correctly, but can be extended with the
|
|
// NewRequestFormatter function.
|
|
type RequestFormatter struct {
|
|
clientID *string
|
|
}
|
|
|
|
// RequestFormatterOpt applys options to a RequestFormatter.
|
|
type RequestFormatterOpt interface {
|
|
apply(*RequestFormatter)
|
|
}
|
|
|
|
type formatterOpt struct{ fn func(*RequestFormatter) }
|
|
|
|
func (opt formatterOpt) apply(f *RequestFormatter) { opt.fn(f) }
|
|
|
|
// FormatterClientID attaches the given client ID to any issued request,
|
|
// minus controlled shutdown v0, which uses its own special format.
|
|
func FormatterClientID(id string) RequestFormatterOpt {
|
|
return formatterOpt{func(f *RequestFormatter) { f.clientID = &id }}
|
|
}
|
|
|
|
// NewRequestFormatter returns a RequestFormatter with the opts applied.
|
|
func NewRequestFormatter(opts ...RequestFormatterOpt) *RequestFormatter {
|
|
a := new(RequestFormatter)
|
|
for _, opt := range opts {
|
|
opt.apply(a)
|
|
}
|
|
return a
|
|
}
|
|
|
|
// AppendRequest appends a full message request to dst, returning the updated
|
|
// slice. This message is the full body that needs to be written to issue a
|
|
// Kafka request.
|
|
func (f *RequestFormatter) AppendRequest(
|
|
dst []byte,
|
|
r Request,
|
|
correlationID int32,
|
|
) []byte {
|
|
dst = append(dst, 0, 0, 0, 0) // reserve length
|
|
k := r.Key()
|
|
v := r.GetVersion()
|
|
dst = kbin.AppendInt16(dst, k)
|
|
dst = kbin.AppendInt16(dst, v)
|
|
dst = kbin.AppendInt32(dst, correlationID)
|
|
if k == 7 && v == 0 {
|
|
return dst
|
|
}
|
|
|
|
// Even with flexible versions, we do not use a compact client id.
|
|
// Clients issue ApiVersions immediately before knowing the broker
|
|
// version, and old brokers will not be able to understand a compact
|
|
// client id.
|
|
dst = kbin.AppendNullableString(dst, f.clientID)
|
|
|
|
// The flexible tags end the request header, and then begins the
|
|
// request body.
|
|
if r.IsFlexible() {
|
|
var numTags uint8
|
|
dst = append(dst, numTags)
|
|
if numTags != 0 {
|
|
// TODO when tags are added
|
|
}
|
|
}
|
|
|
|
// Now the request body.
|
|
dst = r.AppendTo(dst)
|
|
|
|
kbin.AppendInt32(dst[:0], int32(len(dst[4:])))
|
|
return dst
|
|
}
|
|
|
|
// StringPtr is a helper to return a pointer to a string.
|
|
func StringPtr(in string) *string {
|
|
return &in
|
|
}
|
|
|
|
// ReadFrom provides decoding various versions of sticky member metadata. A key
|
|
// point of this type is that it does not contain a version number inside it,
|
|
// but it is versioned: if decoding v1 fails, this falls back to v0.
|
|
func (s *StickyMemberMetadata) ReadFrom(src []byte) error {
|
|
return s.readFrom(src, false)
|
|
}
|
|
|
|
// UnsafeReadFrom is the same as ReadFrom, but uses unsafe slice to string
|
|
// conversions to reduce garbage.
|
|
func (s *StickyMemberMetadata) UnsafeReadFrom(src []byte) error {
|
|
return s.readFrom(src, true)
|
|
}
|
|
|
|
func (s *StickyMemberMetadata) readFrom(src []byte, unsafe bool) error {
|
|
b := kbin.Reader{Src: src}
|
|
numAssignments := b.ArrayLen()
|
|
if numAssignments < 0 {
|
|
numAssignments = 0
|
|
}
|
|
need := numAssignments - int32(cap(s.CurrentAssignment))
|
|
if need > 0 {
|
|
s.CurrentAssignment = append(s.CurrentAssignment[:cap(s.CurrentAssignment)], make([]StickyMemberMetadataCurrentAssignment, need)...)
|
|
} else {
|
|
s.CurrentAssignment = s.CurrentAssignment[:numAssignments]
|
|
}
|
|
for i := int32(0); i < numAssignments; i++ {
|
|
var topic string
|
|
if unsafe {
|
|
topic = b.UnsafeString()
|
|
} else {
|
|
topic = b.String()
|
|
}
|
|
numPartitions := b.ArrayLen()
|
|
if numPartitions < 0 {
|
|
numPartitions = 0
|
|
}
|
|
a := &s.CurrentAssignment[i]
|
|
a.Topic = topic
|
|
need := numPartitions - int32(cap(a.Partitions))
|
|
if need > 0 {
|
|
a.Partitions = append(a.Partitions[:cap(a.Partitions)], make([]int32, need)...)
|
|
} else {
|
|
a.Partitions = a.Partitions[:numPartitions]
|
|
}
|
|
for i := range a.Partitions {
|
|
a.Partitions[i] = b.Int32()
|
|
}
|
|
}
|
|
if len(b.Src) > 0 {
|
|
s.Generation = b.Int32()
|
|
} else {
|
|
s.Generation = -1
|
|
}
|
|
return b.Complete()
|
|
}
|
|
|
|
// AppendTo provides appending various versions of sticky member metadata to dst.
|
|
// If generation is not -1 (default for v0), this appends as version 1.
|
|
func (s *StickyMemberMetadata) AppendTo(dst []byte) []byte {
|
|
dst = kbin.AppendArrayLen(dst, len(s.CurrentAssignment))
|
|
for _, assignment := range s.CurrentAssignment {
|
|
dst = kbin.AppendString(dst, assignment.Topic)
|
|
dst = kbin.AppendArrayLen(dst, len(assignment.Partitions))
|
|
for _, partition := range assignment.Partitions {
|
|
dst = kbin.AppendInt32(dst, partition)
|
|
}
|
|
}
|
|
if s.Generation != -1 {
|
|
dst = kbin.AppendInt32(dst, s.Generation)
|
|
}
|
|
return dst
|
|
}
|
|
|
|
// TagReader has is a type that has the ability to skip tags.
|
|
//
|
|
// This is effectively a trimmed version of the kbin.Reader, with the purpose
|
|
// being that kmsg cannot depend on an external package.
|
|
type TagReader interface {
|
|
// Uvarint returns a uint32. If the reader has read too much and has
|
|
// exhausted all bytes, this should set the reader's internal state
|
|
// to failed and return 0.
|
|
Uvarint() uint32
|
|
|
|
// Span returns n bytes from the reader. If the reader has read too
|
|
// much and exhausted all bytes this should set the reader's internal
|
|
// to failed and return nil.
|
|
Span(n int) []byte
|
|
}
|
|
|
|
// SkipTags skips tags in a TagReader.
|
|
func SkipTags(b TagReader) {
|
|
for num := b.Uvarint(); num > 0; num-- {
|
|
_, size := b.Uvarint(), b.Uvarint()
|
|
b.Span(int(size))
|
|
}
|
|
}
|
|
|
|
// internalSkipTags skips tags in the duplicated inner kbin.Reader.
|
|
func internalSkipTags(b *kbin.Reader) {
|
|
for num := b.Uvarint(); num > 0; num-- {
|
|
_, size := b.Uvarint(), b.Uvarint()
|
|
b.Span(int(size))
|
|
}
|
|
}
|
|
|
|
// ReadTags reads tags in a TagReader and returns the tags.
|
|
func ReadTags(b TagReader) Tags {
|
|
var t Tags
|
|
for num := b.Uvarint(); num > 0; num-- {
|
|
key, size := b.Uvarint(), b.Uvarint()
|
|
t.Set(key, b.Span(int(size)))
|
|
}
|
|
return t
|
|
}
|
|
|
|
// internalReadTags reads tags in a reader and returns the tags from a
|
|
// duplicated inner kbin.Reader.
|
|
func internalReadTags(b *kbin.Reader) Tags {
|
|
var t Tags
|
|
for num := b.Uvarint(); num > 0; num-- {
|
|
key, size := b.Uvarint(), b.Uvarint()
|
|
t.Set(key, b.Span(int(size)))
|
|
}
|
|
return t
|
|
}
|
|
|
|
// Tags is an opaque structure capturing unparsed tags.
|
|
type Tags struct {
|
|
keyvals map[uint32][]byte
|
|
}
|
|
|
|
// Len returns the number of keyvals in Tags.
|
|
func (t *Tags) Len() int { return len(t.keyvals) }
|
|
|
|
// Each calls fn for each key and val in the tags.
|
|
func (t *Tags) Each(fn func(uint32, []byte)) {
|
|
if len(t.keyvals) == 0 {
|
|
return
|
|
}
|
|
// We must encode keys in order. We expect to have limited (no) unknown
|
|
// keys, so for now, we take a lazy approach and allocate an ordered
|
|
// slice.
|
|
ordered := make([]uint32, 0, len(t.keyvals))
|
|
for key := range t.keyvals {
|
|
ordered = append(ordered, key)
|
|
}
|
|
sort.Slice(ordered, func(i, j int) bool { return ordered[i] < ordered[j] })
|
|
for _, key := range ordered {
|
|
fn(key, t.keyvals[key])
|
|
}
|
|
}
|
|
|
|
// Set sets a tag's key and val.
|
|
//
|
|
// Note that serializing tags does NOT check if the set key overlaps with an
|
|
// existing used key. It is invalid to set a key used by Kafka itself.
|
|
func (t *Tags) Set(key uint32, val []byte) {
|
|
if t.keyvals == nil {
|
|
t.keyvals = make(map[uint32][]byte)
|
|
}
|
|
t.keyvals[key] = val
|
|
}
|
|
|
|
// AppendEach appends each keyval in tags to dst and returns the updated dst.
|
|
func (t *Tags) AppendEach(dst []byte) []byte {
|
|
t.Each(func(key uint32, val []byte) {
|
|
dst = kbin.AppendUvarint(dst, key)
|
|
dst = kbin.AppendUvarint(dst, uint32(len(val)))
|
|
dst = append(dst, val...)
|
|
})
|
|
return dst
|
|
}
|
|
|
|
////////////////////////
|
|
// DEPRECATED RENAMES //
|
|
////////////////////////
|
|
|
|
// Deprecated: this was renamed to ControlRecordKeyTypeSnapshotHeader.
|
|
const ControlRecordKeyTypeLeaderChange = ControlRecordKeyTypeSnapshotHeader
|
|
|
|
type (
|
|
// Deprecated: this was renamed to ListConfigResourcesRequest.
|
|
ListClientMetricsResourcesRequest = ListConfigResourcesRequest
|
|
// Deprecated: this was renamed to ListConfigResourcesResponse.
|
|
ListClientMetricsResourcesResponse = ListConfigResourcesResponse
|
|
// Deprecated: this was renamed to ListConfigResourcesResponseConfigResource.
|
|
ListClientMetricsResourcesResponseClientMetricsResource = ListConfigResourcesResponseConfigResource
|
|
)
|
|
|
|
// Deprecated: this was renamed to ListConfigResources.
|
|
var ListClientMetricsResources Key = 74
|
|
|
|
// Deprecated: this was renamed to NewPtrListConfigResourcesRequest.
|
|
func NewPtrListClientMetricsResourcesRequest() *ListClientMetricsResourcesRequest {
|
|
return NewPtrListConfigResourcesRequest()
|
|
}
|
|
|
|
// Deprecated: this was renamed to NewListConfigResourcesRequest.
|
|
func NewListClientMetricsResourcesRequest() ListClientMetricsResourcesRequest {
|
|
return NewListConfigResourcesRequest()
|
|
}
|
|
|
|
// Deprecated: this was renamed to NewListConfigResourcesResponseConfigResource.
|
|
func NewListClientMetricsResourcesResponseClientMetricsResource() ListClientMetricsResourcesResponseClientMetricsResource {
|
|
return NewListConfigResourcesResponseConfigResource()
|
|
}
|
|
|
|
// Deprecated: this was renamed to NewPtrListConfigResourcesResponse.
|
|
func NewPtrListClientMetricsResourcesResponse() *ListClientMetricsResourcesResponse {
|
|
return NewPtrListConfigResourcesResponse()
|
|
}
|
|
|
|
// Deprecated: this was renamed to NewListConfigResourcesResponse.
|
|
func NewListClientMetricsResourcesResponse() ListClientMetricsResourcesResponse {
|
|
return NewListConfigResourcesResponse()
|
|
}
|