mirror of
https://github.com/hanzoai/traces.git
synced 2026-10-30 18:20:41 +00:00
+6



![dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>](/assets/img/avatar_default.png)





Mario
GitHub
Javier Molina Reyes
Zach Leslie
Joe Elliott
dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Ryan Perry
Kim Nylander
Suraj Nath
Alex Bikfalvi
Andrey Karpov
Jennifer Villa
Martin Disibio
Markus Toivonen
Daniel Strobusch
Carles Garcia
835e1fd250
* Block-builder PoC * Add unit test for block-builder (#4289) * Add unit test for block-builder * fmt * Update tests * cmon * Deterministically build blocks for partition sections (#4327) * Pull main (#4342) * chore: remove gofakeit dependency (#4274) * Further reduce Labes() calls in the metrics registry (#4283) * Respect passed headers in read path requests (#4287) * Ingester: Validate completed blocks (#4256) * Add validate method to block Signed-off-by: Joe Elliott <[email protected]> * Add Validate usage in the ingester Signed-off-by: Joe Elliott <[email protected]> * changelog Signed-off-by: Joe Elliott <[email protected]> * add test and fix replay Signed-off-by: Joe Elliott <[email protected]> * increment metric Signed-off-by: Joe Elliott <[email protected]> --------- Signed-off-by: Joe Elliott <[email protected]> * Add `invalid_utf8` to reasons spans could be rejected (#4293) * Add `invalid_utf8` to reasons spans could be rejected * Update changelog * Update docs * Ensure test covers invalid UTF-8 and not slack time * add signals for duplicate rf1 data (#4296) Signed-off-by: Joe Elliott <[email protected]> * Bump anchore/sbom-action from 0.17.5 to 0.17.7 (#4307) Bumps [anchore/sbom-action](https://github.com/anchore/sbom-action) from 0.17.5 to 0.17.7. - [Release notes](https://github.com/anchore/sbom-action/releases) - [Changelog](https://github.com/anchore/sbom-action/blob/main/RELEASE.md) - [Commits](https://github.com/anchore/sbom-action/compare/v0.17.5...v0.17.7) --- updated-dependencies: - dependency-name: anchore/sbom-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * docs: Update readme with explore traces info (#4263) * docs: Update readme with explore traces info Co-authored-by: Kim Nylander <[email protected]> * chore: remove spanlogger (#4312) * chore: remove spanlogger * Query-Frontend: Add middleware to drop headers (#4298) * header strip ware Signed-off-by: Joe Elliott <[email protected]> * comment Signed-off-by: Joe Elliott <[email protected]> * changelog Signed-off-by: Joe Elliott <[email protected]> * remove header strip wear from metrics summary Signed-off-by: Joe Elliott <[email protected]> --------- Signed-off-by: Joe Elliott <[email protected]> * Increase length of time compactions have to fail (#4315) * increase length of time compactions have to fail Signed-off-by: Joe Elliott <[email protected]> * gen Signed-off-by: Joe Elliott <[email protected]> --------- Signed-off-by: Joe Elliott <[email protected]> * docs: mark serverless as deprecated (#4017) * docs: mark serverless as deprecated * Changelog + readme * docs: Remove duplicated examples (#4295) This removes duplicates examples from the Configure TraceQL metrics page. Signed-off-by: Alex Bikfalvi <[email protected]> * tempo-cli: support dropping multiple traces in a single operation (#4266) * tempo-cli: support dropping multiple traces in a single operation * update final log message --------- Co-authored-by: Suraj Nath <[email protected]> * [DOC] Add clarification for metrics summary and traceQL metrics (#4316) * Add clarification for metrics summary and traceQL metrics * Apply suggestions from code review Co-authored-by: Jennifer Villa <[email protected]> * Update docs/sources/tempo/api_docs/metrics-summary.md --------- Co-authored-by: Jennifer Villa <[email protected]> * TraceQL metrics time range fixes (#4325) * Disconnect job time range filtering from step, so that results in split backend/recent range is accurate * changelog * Fix to assert metrics query range before alignment because alignment may increase it, which is not the responsibility of the caller to account for (#4331) * Add doc about configuring TLS with Helm (#4328) * Add doc about configuring TLS with Helm * Add memberlist and readinessProbe to example * Include server config for listening on TLS * Add note about scraping * Update docs/sources/tempo/configuration/network/tls.md Co-authored-by: Markus Toivonen <[email protected]> * Update docs/sources/tempo/configuration/network/tls.md Co-authored-by: Kim Nylander <[email protected]> * Update docs/sources/tempo/configuration/network/tls.md Co-authored-by: Kim Nylander <[email protected]> * Add memcached config for TLS --------- Co-authored-by: Markus Toivonen <[email protected]> Co-authored-by: Kim Nylander <[email protected]> * [DOC] Add TLS info to Helm chart doc (#4334) --------- Signed-off-by: Joe Elliott <[email protected]> Signed-off-by: dependabot[bot] <[email protected]> Signed-off-by: Alex Bikfalvi <[email protected]> Co-authored-by: Javier Molina Reyes <[email protected]> Co-authored-by: Zach Leslie <[email protected]> Co-authored-by: Joe Elliott <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ryan Perry <[email protected]> Co-authored-by: Kim Nylander <[email protected]> Co-authored-by: Suraj Nath <[email protected]> Co-authored-by: Alex Bikfalvi <[email protected]> Co-authored-by: Andrey Karpov <[email protected]> Co-authored-by: Jennifer Villa <[email protected]> Co-authored-by: Martin Disibio <[email protected]> Co-authored-by: Markus Toivonen <[email protected]> * WIP: Rhythm ingest path (#4314) * Validate distributor config. Finish encoder/decoder tests * Repair tests * Make SingleBinary work out of the box by defaulting to partition 0 * Fix first time startup where blockbuilder fails before ingester can create topic * Fix initial startup cycle time and delay * Add more failure modes to the block-builder (#4345) * Add more tests to the block-builder * stuff * Add comments * [Rhythm] Metrics generator read from kafka first pass (#4359) * Metrics generator read from kafka first pass * review feedback * Multiple fixes in block-builder (#4364) * [rhythm] git merge origin/main (#4376) * chore: remove gofakeit dependency (#4274) * Further reduce Labes() calls in the metrics registry (#4283) * Respect passed headers in read path requests (#4287) * Ingester: Validate completed blocks (#4256) * Add validate method to block Signed-off-by: Joe Elliott <[email protected]> * Add Validate usage in the ingester Signed-off-by: Joe Elliott <[email protected]> * changelog Signed-off-by: Joe Elliott <[email protected]> * add test and fix replay Signed-off-by: Joe Elliott <[email protected]> * increment metric Signed-off-by: Joe Elliott <[email protected]> --------- Signed-off-by: Joe Elliott <[email protected]> * Add `invalid_utf8` to reasons spans could be rejected (#4293) * Add `invalid_utf8` to reasons spans could be rejected * Update changelog * Update docs * Ensure test covers invalid UTF-8 and not slack time * add signals for duplicate rf1 data (#4296) Signed-off-by: Joe Elliott <[email protected]> * Bump anchore/sbom-action from 0.17.5 to 0.17.7 (#4307) Bumps [anchore/sbom-action](https://github.com/anchore/sbom-action) from 0.17.5 to 0.17.7. - [Release notes](https://github.com/anchore/sbom-action/releases) - [Changelog](https://github.com/anchore/sbom-action/blob/main/RELEASE.md) - [Commits](https://github.com/anchore/sbom-action/compare/v0.17.5...v0.17.7) --- updated-dependencies: - dependency-name: anchore/sbom-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * docs: Update readme with explore traces info (#4263) * docs: Update readme with explore traces info Co-authored-by: Kim Nylander <[email protected]> * chore: remove spanlogger (#4312) * chore: remove spanlogger * Query-Frontend: Add middleware to drop headers (#4298) * header strip ware Signed-off-by: Joe Elliott <[email protected]> * comment Signed-off-by: Joe Elliott <[email protected]> * changelog Signed-off-by: Joe Elliott <[email protected]> * remove header strip wear from metrics summary Signed-off-by: Joe Elliott <[email protected]> --------- Signed-off-by: Joe Elliott <[email protected]> * Increase length of time compactions have to fail (#4315) * increase length of time compactions have to fail Signed-off-by: Joe Elliott <[email protected]> * gen Signed-off-by: Joe Elliott <[email protected]> --------- Signed-off-by: Joe Elliott <[email protected]> * docs: mark serverless as deprecated (#4017) * docs: mark serverless as deprecated * Changelog + readme * docs: Remove duplicated examples (#4295) This removes duplicates examples from the Configure TraceQL metrics page. Signed-off-by: Alex Bikfalvi <[email protected]> * tempo-cli: support dropping multiple traces in a single operation (#4266) * tempo-cli: support dropping multiple traces in a single operation * update final log message --------- Co-authored-by: Suraj Nath <[email protected]> * [DOC] Add clarification for metrics summary and traceQL metrics (#4316) * Add clarification for metrics summary and traceQL metrics * Apply suggestions from code review Co-authored-by: Jennifer Villa <[email protected]> * Update docs/sources/tempo/api_docs/metrics-summary.md --------- Co-authored-by: Jennifer Villa <[email protected]> * TraceQL metrics time range fixes (#4325) * Disconnect job time range filtering from step, so that results in split backend/recent range is accurate * changelog * Fix to assert metrics query range before alignment because alignment may increase it, which is not the responsibility of the caller to account for (#4331) * Add doc about configuring TLS with Helm (#4328) * Add doc about configuring TLS with Helm * Add memberlist and readinessProbe to example * Include server config for listening on TLS * Add note about scraping * Update docs/sources/tempo/configuration/network/tls.md Co-authored-by: Markus Toivonen <[email protected]> * Update docs/sources/tempo/configuration/network/tls.md Co-authored-by: Kim Nylander <[email protected]> * Update docs/sources/tempo/configuration/network/tls.md Co-authored-by: Kim Nylander <[email protected]> * Add memcached config for TLS --------- Co-authored-by: Markus Toivonen <[email protected]> Co-authored-by: Kim Nylander <[email protected]> * [DOC] Add TLS info to Helm chart doc (#4334) * fix deprecation warning by switching to DoBatchWithOptions (#4343) Signed-off-by: Daniel Strobusch <[email protected]> * bump dskit to v0.0.0-20241115082728-f2a7eb3aa0e9 to leverage benefits for context causes for DoBatch calls. (#4341) See https://github.com/grafana/dskit/issues/576 Signed-off-by: Daniel Strobusch <[email protected]> * Bump github.com/minio/minio-go/v7 from 7.0.70 to 7.0.80 (#4282) * Bump github.com/minio/minio-go/v7 from 7.0.70 to 7.0.80 Bumps [github.com/minio/minio-go/v7](https://github.com/minio/minio-go) from 7.0.70 to 7.0.80. - [Release notes](https://github.com/minio/minio-go/releases) - [Commits](https://github.com/minio/minio-go/compare/v7.0.70...v7.0.80) --- updated-dependencies: - dependency-name: github.com/minio/minio-go/v7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> * Update serverless vendor --------- Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Zach Leslie <[email protected]> * update default config values to better align with production workloads (#4340) * update default config values to better align with production workloads * Update CHANGELOG.md and config docs * Ingester memory improvements by adjusting prealloc (#4344) * remove trace ids Signed-off-by: Joe Elliott <[email protected]> * linear buckets Signed-off-by: Joe Elliott <[email protected]> * changelog Signed-off-by: Joe Elliott <[email protected]> * tuney tune Signed-off-by: Joe Elliott <[email protected]> * metric misses and increase pool size Signed-off-by: Joe Elliott <[email protected]> * lint Signed-off-by: Joe Elliott <[email protected]> --------- Signed-off-by: Joe Elliott <[email protected]> * Bump github.com/Azure/azure-sdk-for-go/sdk/azcore from 1.13.0 to 1.16.0 (#4302) * Bump github.com/Azure/azure-sdk-for-go/sdk/azcore from 1.13.0 to 1.16.0 Bumps [github.com/Azure/azure-sdk-for-go/sdk/azcore](https://github.com/Azure/azure-sdk-for-go) from 1.13.0 to 1.16.0. - [Release notes](https://github.com/Azure/azure-sdk-for-go/releases) - [Changelog](https://github.com/Azure/azure-sdk-for-go/blob/main/documentation/release.md) - [Commits](https://github.com/Azure/azure-sdk-for-go/compare/sdk/azcore/v1.13.0...sdk/azcore/v1.16.0) --- updated-dependencies: - dependency-name: github.com/Azure/azure-sdk-for-go/sdk/azcore dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <[email protected]> * Update serverless vendor --------- Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Zach Leslie <[email protected]> * Use Prometheus fast regexp (#4329) * basic integration Signed-off-by: Joe Elliott <[email protected]> * patch tests for new meaning Signed-off-by: Joe Elliott <[email protected]> * patch up more tests Signed-off-by: Joe Elliott <[email protected]> * add basic tests Signed-off-by: Joe Elliott <[email protected]> * changelog + docs Signed-off-by: Joe Elliott <[email protected]> * remove benches Signed-off-by: Joe Elliott <[email protected]> * Cleaned up + tests Signed-off-by: Joe Elliott <[email protected]> * comment Signed-off-by: Joe Elliott <[email protected]> * lint Signed-off-by: Joe Elliott <[email protected]> * Update docs/sources/tempo/traceql/_index.md Co-authored-by: Kim Nylander <[email protected]> * comment Signed-off-by: Joe Elliott <[email protected]> --------- Signed-off-by: Joe Elliott <[email protected]> Co-authored-by: Kim Nylander <[email protected]> * Fix broken link in service-graphs docs (#4351) * Fix minor typo in TraceQL docs (#4356) * Bump default memcached version (#4363) * Exemplar fixes (#4366) * Fix exemplars based on duration to convert to seconds, fix various other issues * changelog * fix: initialize histogram buckets to 0 to avoid them being downsampled (#4368) * initialized histogram buckets to 0 to avoid them being downsampled * Ingester/Generator Live trace cleanup (#4365) * moved trace sizes somewhere shareable Signed-off-by: Joe Elliott <[email protected]> * use tracesizes in ingester Signed-off-by: Joe Elliott <[email protected]> * make tests work Signed-off-by: Joe Elliott <[email protected]> * trace bytes in generator Signed-off-by: Joe Elliott <[email protected]> * remove traceCount Signed-off-by: Joe Elliott <[email protected]> * live trace shenanigans Signed-off-by: Joe Elliott <[email protected]> * changelog Signed-off-by: Joe Elliott <[email protected]> * Update modules/generator/processor/localblocks/livetraces.go Co-authored-by: Mario <[email protected]> * Update modules/ingester/instance.go Co-authored-by: Mario <[email protected]> * Test cleanup. Add sz test, restore commented out and fix e2e Signed-off-by: Joe Elliott <[email protected]> * remove todo comment Signed-off-by: Joe Elliott <[email protected]> --------- Signed-off-by: Joe Elliott <[email protected]> Co-authored-by: Mario <[email protected]> * Bump anchore/sbom-action from 0.17.7 to 0.17.8 (#4371) Bumps [anchore/sbom-action](https://github.com/anchore/sbom-action) from 0.17.7 to 0.17.8. - [Release notes](https://github.com/anchore/sbom-action/releases) - [Changelog](https://github.com/anchore/sbom-action/blob/main/RELEASE.md) - [Commits](https://github.com/anchore/sbom-action/compare/v0.17.7...v0.17.8) --- updated-dependencies: - dependency-name: anchore/sbom-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update for IDs change * Only run blockbuilder if ingest enabled --------- Signed-off-by: Joe Elliott <[email protected]> Signed-off-by: dependabot[bot] <[email protected]> Signed-off-by: Alex Bikfalvi <[email protected]> Signed-off-by: Daniel Strobusch <[email protected]> Co-authored-by: Javier Molina Reyes <[email protected]> Co-authored-by: Zach Leslie <[email protected]> Co-authored-by: Joe Elliott <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ryan Perry <[email protected]> Co-authored-by: Kim Nylander <[email protected]> Co-authored-by: Suraj Nath <[email protected]> Co-authored-by: Alex Bikfalvi <[email protected]> Co-authored-by: Andrey Karpov <[email protected]> Co-authored-by: Jennifer Villa <[email protected]> Co-authored-by: Martin Disibio <[email protected]> Co-authored-by: Markus Toivonen <[email protected]> Co-authored-by: Daniel Strobusch <[email protected]> Co-authored-by: Carles Garcia <[email protected]> * [rhythm] Changes to simplify operations (#4389) * Use mapping for assigning partitions * Use mapping for assigning partitions in the generator too * Add support for SASL auth to kafka clients * Add metrics to ingest (#4395) * [rhythm] Extract block-builder into its own module (#4396) * Extract block-builder into its own module * Update /operations and examples * No ephemeral storage * No rolling strategy either * fmt and compile * Address review comment * [rhythm] Correctly pass start/end time when appending a trace (#4410) * Correctly pass start/end times * Different code, same result * [rhythm] Multiple fixes to block-builder consumption (#4413) * Multiple fixes to cycle consumption * fmt * happy now? * ups * Rhythm: Separate non-flushing local blocks processor to store new queue data for reads (#4411) * wip: separate non-flushing local blocks processor to store new queue data for reads * Make real config for non-flushing local blocks processor, optional, validate wal config and use defaults if needed * Fix defaulting of second WAL config * [rhythm] Make ID generator more robust (#4416) * Make ID generator more robust * Simplify * Update toc3ff5ba61* Fix registering of kafka read client metrics (#4502) * [rhythm] Make ID generator more robust (#4416) (#4507) * Make ID generator more robust * Simplify * Removed references to Loki and Mimir (#4509) Signed-off-by: Joe Elliott <[email protected]> * [Rhythm] Block builder test updates (#4510) * Make blockbuilder tests closer to real kafka and less implementation specific by always enabling support for consumer groups, call commit control func in order * Verify last committed offset in each test * hide test function * lint * lint * [Rhythm] Block-builder consumption loop (#4480) * Alternate block-builder consume * Set timeout on PollFetches, reduce initial poll delay, update 1 test to work using real consumergroup functionality * restore metrics * Re-add original partition lag metric, polled in separate goroutine. Fix consume loop to only consume full-duration cycles for more determinism * merge conflict * Review feedback * Review feedback * Comment * code cleanup, lint * logs * code cleanup * lint * Review feedback * Remove missed lookback_on_no_commit config in e2e tests and regen manifest * Review feedback * Fix rewind to latest commit to init correctly, it didn't work in some clusters (#4532) * [rhythm] merge main at2c44177(#4531) * Fixes * More fixes --------- Signed-off-by: Joe Elliott <[email protected]> Signed-off-by: dependabot[bot] <[email protected]> Signed-off-by: Alex Bikfalvi <[email protected]> Signed-off-by: Daniel Strobusch <[email protected]> Co-authored-by: Javier Molina Reyes <[email protected]> Co-authored-by: Zach Leslie <[email protected]> Co-authored-by: Joe Elliott <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ryan Perry <[email protected]> Co-authored-by: Kim Nylander <[email protected]> Co-authored-by: Suraj Nath <[email protected]> Co-authored-by: Alex Bikfalvi <[email protected]> Co-authored-by: Andrey Karpov <[email protected]> Co-authored-by: Jennifer Villa <[email protected]> Co-authored-by: Martin Disibio <[email protected]> Co-authored-by: Markus Toivonen <[email protected]> Co-authored-by: Daniel Strobusch <[email protected]> Co-authored-by: Carles Garcia <[email protected]>
615 lines
20 KiB
Go
615 lines
20 KiB
Go
package kgo
|
|
|
|
import (
|
|
"math"
|
|
"math/rand"
|
|
"time"
|
|
|
|
"github.com/twmb/franz-go/pkg/kbin"
|
|
)
|
|
|
|
// Partitioner creates topic partitioners to determine which partition messages
|
|
// should be sent to.
|
|
//
|
|
// Note that a record struct is unmodified (minus a potential default topic)
|
|
// from producing through partitioning, so you can set fields in the record
|
|
// struct before producing to aid in partitioning with a custom partitioner.
|
|
type Partitioner interface {
|
|
// forTopic returns a partitioner for an individual topic. It is
|
|
// guaranteed that only one record will use the an individual topic's
|
|
// topicPartitioner at a time, meaning partitioning within a topic does
|
|
// not require locks.
|
|
ForTopic(string) TopicPartitioner
|
|
}
|
|
|
|
// TopicPartitioner partitions records in an individual topic.
|
|
type TopicPartitioner interface {
|
|
// RequiresConsistency returns true if a record must hash to the same
|
|
// partition even if a partition is down.
|
|
// If true, a record may hash to a partition that cannot be written to
|
|
// and will error until the partition comes back.
|
|
RequiresConsistency(*Record) bool
|
|
// Partition determines, among a set of n partitions, which index should
|
|
// be chosen to use for the partition for r.
|
|
Partition(r *Record, n int) int
|
|
}
|
|
|
|
// TopicPartitionerOnNewBatch is an optional extension interface to
|
|
// TopicPartitioner that calls OnNewBatch before any new batch is created. If
|
|
// buffering a record would cause a new batch, OnNewBatch is called.
|
|
//
|
|
// This interface allows for partitioner implementations that effectively pin
|
|
// to a partition until a new batch is created, after which the partitioner can
|
|
// choose which next partition to use.
|
|
type TopicPartitionerOnNewBatch interface {
|
|
// OnNewBatch is called when producing a record if that record would
|
|
// trigger a new batch on its current partition.
|
|
OnNewBatch()
|
|
}
|
|
|
|
// TopicBackupPartitioner is an optional extension interface to
|
|
// TopicPartitioner that can partition by the number of records buffered.
|
|
//
|
|
// If a partitioner implements this interface, the Partition function will
|
|
// never be called.
|
|
type TopicBackupPartitioner interface {
|
|
TopicPartitioner
|
|
|
|
// PartitionByBackup is similar to Partition, but has an additional
|
|
// backupIter. This iterator will return the number of buffered records
|
|
// per partition index. The iterator's Next function can only be called
|
|
// up to n times, calling it any more will panic.
|
|
PartitionByBackup(r *Record, n int, backupIter TopicBackupIter) int
|
|
}
|
|
|
|
// TopicBackupIter is an iterates through partition indices.
|
|
type TopicBackupIter interface {
|
|
// Next returns the next partition index and the total buffered records
|
|
// for the partition. If Rem returns 0, calling this function again
|
|
// will panic.
|
|
Next() (int, int64)
|
|
// Rem returns the number of elements left to iterate through.
|
|
Rem() int
|
|
}
|
|
|
|
////////////
|
|
// SIMPLE // - BasicConsistent, Manual, RoundRobin
|
|
////////////
|
|
|
|
// BasicConsistentPartitioner wraps a single function to provide a Partitioner
|
|
// and TopicPartitioner (that function is essentially a combination of
|
|
// Partitioner.ForTopic and TopicPartitioner.Partition).
|
|
//
|
|
// As a minimal example, if you do not care about the topic and you set the
|
|
// partition before producing:
|
|
//
|
|
// kgo.BasicConsistentPartitioner(func(topic) func(*Record, int) int {
|
|
// return func(r *Record, n int) int {
|
|
// return int(r.Partition)
|
|
// }
|
|
// })
|
|
func BasicConsistentPartitioner(partition func(string) func(r *Record, n int) int) Partitioner {
|
|
return &basicPartitioner{partition}
|
|
}
|
|
|
|
type (
|
|
basicPartitioner struct {
|
|
fn func(string) func(*Record, int) int
|
|
}
|
|
|
|
basicTopicPartitioner struct {
|
|
fn func(*Record, int) int
|
|
}
|
|
)
|
|
|
|
func (b *basicPartitioner) ForTopic(t string) TopicPartitioner {
|
|
return &basicTopicPartitioner{b.fn(t)}
|
|
}
|
|
|
|
func (*basicTopicPartitioner) RequiresConsistency(*Record) bool { return true }
|
|
func (b *basicTopicPartitioner) Partition(r *Record, n int) int { return b.fn(r, n) }
|
|
|
|
// ManualPartitioner is a partitioner that simply returns the Partition field
|
|
// that is already set on any record.
|
|
//
|
|
// Any record with an invalid partition will be immediately failed. This
|
|
// partitioner is simply the partitioner that is demonstrated in the
|
|
// BasicConsistentPartitioner documentation.
|
|
func ManualPartitioner() Partitioner {
|
|
return BasicConsistentPartitioner(func(string) func(*Record, int) int {
|
|
return func(r *Record, _ int) int {
|
|
return int(r.Partition)
|
|
}
|
|
})
|
|
}
|
|
|
|
// RoundRobinPartitioner is a partitioner that round-robin's through all
|
|
// available partitions. This algorithm has lower throughput and causes higher
|
|
// CPU load on brokers, but can be useful if you want to ensure an even
|
|
// distribution of records to partitions.
|
|
func RoundRobinPartitioner() Partitioner {
|
|
return new(roundRobinPartitioner)
|
|
}
|
|
|
|
type (
|
|
roundRobinPartitioner struct{}
|
|
|
|
roundRobinTopicPartitioner struct {
|
|
on int
|
|
}
|
|
)
|
|
|
|
func (*roundRobinPartitioner) ForTopic(string) TopicPartitioner {
|
|
return new(roundRobinTopicPartitioner)
|
|
}
|
|
|
|
func (*roundRobinTopicPartitioner) RequiresConsistency(*Record) bool { return false }
|
|
func (r *roundRobinTopicPartitioner) Partition(_ *Record, n int) int {
|
|
if r.on >= n {
|
|
r.on = 0
|
|
}
|
|
ret := r.on
|
|
r.on++
|
|
return ret
|
|
}
|
|
|
|
//////////////////
|
|
// LEAST BACKUP //
|
|
//////////////////
|
|
|
|
// LeastBackupPartitioner prioritizes partitioning by three factors, in order:
|
|
//
|
|
// 1. pin to the current pick until there is a new batch
|
|
// 2. on new batch, choose the least backed up partition (the partition with
|
|
// the fewest amount of buffered records)
|
|
// 3. if multiple partitions are equally least-backed-up, choose one at random
|
|
//
|
|
// This algorithm prioritizes least-backed-up throughput, which may result in
|
|
// unequal partitioning. It is likely that this algorithm will talk most to the
|
|
// broker that it has the best connection to.
|
|
//
|
|
// This algorithm is resilient to brokers going down: if a few brokers die, it
|
|
// is possible your throughput will be so high that the maximum buffered
|
|
// records will be reached in the now-offline partitions before metadata
|
|
// responds that the broker is offline. With the standard partitioning
|
|
// algorithms, the only recovery is if the partition is remapped or if the
|
|
// broker comes back online. With the least backup partitioner, downed
|
|
// partitions will see slight backup, but then the other partitions that are
|
|
// still accepting writes will get all of the writes and your client will not
|
|
// be blocked.
|
|
//
|
|
// Under ideal scenarios (no broker / connection issues), StickyPartitioner is
|
|
// equivalent to LeastBackupPartitioner. This partitioner is only recommended
|
|
// if you are a producer consistently dealing with flaky connections or
|
|
// problematic brokers and do not mind uneven load on your brokers.
|
|
func LeastBackupPartitioner() Partitioner {
|
|
return new(leastBackupPartitioner)
|
|
}
|
|
|
|
type (
|
|
leastBackupInput struct{ mapping []*topicPartition }
|
|
|
|
leastBackupPartitioner struct{}
|
|
|
|
leastBackupTopicPartitioner struct {
|
|
onPart int
|
|
rng *rand.Rand
|
|
}
|
|
)
|
|
|
|
func (i *leastBackupInput) Next() (int, int64) {
|
|
last := len(i.mapping) - 1
|
|
buffered := i.mapping[last].records.buffered.Load()
|
|
i.mapping = i.mapping[:last]
|
|
return last, buffered
|
|
}
|
|
|
|
func (i *leastBackupInput) Rem() int {
|
|
return len(i.mapping)
|
|
}
|
|
|
|
func (*leastBackupPartitioner) ForTopic(string) TopicPartitioner {
|
|
return &leastBackupTopicPartitioner{
|
|
onPart: -1,
|
|
rng: rand.New(rand.NewSource(time.Now().UnixNano())),
|
|
}
|
|
}
|
|
|
|
func (p *leastBackupTopicPartitioner) OnNewBatch() { p.onPart = -1 }
|
|
func (*leastBackupTopicPartitioner) RequiresConsistency(*Record) bool { return false }
|
|
func (*leastBackupTopicPartitioner) Partition(*Record, int) int { panic("unreachable") }
|
|
|
|
func (p *leastBackupTopicPartitioner) PartitionByBackup(_ *Record, n int, backup TopicBackupIter) int {
|
|
if p.onPart == -1 || p.onPart >= n {
|
|
leastBackup := int64(math.MaxInt64)
|
|
npicked := 0
|
|
for ; n > 0; n-- {
|
|
pick, backup := backup.Next()
|
|
if backup < leastBackup {
|
|
leastBackup = backup
|
|
p.onPart = pick
|
|
npicked = 1
|
|
} else {
|
|
npicked++ // reservoir sampling with k = 1
|
|
if p.rng.Intn(npicked) == 0 {
|
|
p.onPart = pick
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return p.onPart
|
|
}
|
|
|
|
///////////////////
|
|
// UNIFORM BYTES //
|
|
///////////////////
|
|
|
|
// UniformBytesPartitioner is a redux of the StickyPartitioner, proposed in
|
|
// KIP-794 and release with the Java client in Kafka 3.3. This partitioner
|
|
// returns the same partition until 'bytes' is hit. At that point, a
|
|
// re-partitioning happens. If adaptive is false, this chooses a new random
|
|
// partition, otherwise this chooses a broker based on the inverse of the
|
|
// backlog currently buffered for that broker. If keys is true, this uses
|
|
// standard hashing based on record key for records with non-nil keys. hasher
|
|
// is optional; if nil, the default hasher murmur2 (Kafka's default).
|
|
//
|
|
// The point of this hasher is to create larger batches while producing the
|
|
// same amount to all partitions over the long run. Adaptive opts in to a
|
|
// slight imbalance so that this can produce more to brokers that are less
|
|
// loaded.
|
|
//
|
|
// This implementation differs slightly from Kafka's because this does not
|
|
// account for the compressed size of a batch, nor batch overhead. For
|
|
// overhead, in practice, the overhead is relatively constant so it would
|
|
// affect all batches equally. For compression, this client does not compress
|
|
// until after a batch is created and frozen, so it is not possible to track
|
|
// compression. This client also uses the number of records for backup
|
|
// calculation rather than number of bytes, but the heuristic should be
|
|
// similar. Lastly, this client does not have a timeout for partition
|
|
// availability. Realistically, these will be the most backed up partitions so
|
|
// they should be chosen the least.
|
|
//
|
|
// NOTE: This implementation may create sub-optimal batches if lingering is
|
|
// enabled. This client's default is to disable lingering. The patch used to
|
|
// address this in Kafka is KAFKA-14156 (which itself is not perfect in the
|
|
// context of disabling lingering). For more details, read KAFKA-14156.
|
|
func UniformBytesPartitioner(bytes int, adaptive, keys bool, hasher PartitionerHasher) Partitioner {
|
|
if hasher == nil {
|
|
hasher = KafkaHasher(murmur2)
|
|
}
|
|
return &uniformBytesPartitioner{
|
|
bytes,
|
|
adaptive,
|
|
keys,
|
|
hasher,
|
|
}
|
|
}
|
|
|
|
type (
|
|
uniformBytesPartitioner struct {
|
|
bytes int
|
|
adaptive bool
|
|
keys bool
|
|
hasher PartitionerHasher
|
|
}
|
|
|
|
uniformBytesTopicPartitioner struct {
|
|
u uniformBytesPartitioner
|
|
bytes int
|
|
onPart int
|
|
rng *rand.Rand
|
|
|
|
calc []struct {
|
|
f float64
|
|
n int
|
|
}
|
|
}
|
|
)
|
|
|
|
func (u *uniformBytesPartitioner) ForTopic(string) TopicPartitioner {
|
|
return &uniformBytesTopicPartitioner{
|
|
u: *u,
|
|
onPart: -1,
|
|
rng: rand.New(rand.NewSource(time.Now().UnixNano())),
|
|
}
|
|
}
|
|
|
|
func (p *uniformBytesTopicPartitioner) RequiresConsistency(r *Record) bool {
|
|
return p.u.keys && r.Key != nil
|
|
}
|
|
func (*uniformBytesTopicPartitioner) Partition(*Record, int) int { panic("unreachable") }
|
|
|
|
func (p *uniformBytesTopicPartitioner) PartitionByBackup(r *Record, n int, backup TopicBackupIter) int {
|
|
if p.u.keys && r.Key != nil {
|
|
return p.u.hasher(r.Key, n)
|
|
}
|
|
|
|
l := 1 + // attributes, int8 unused
|
|
1 + // ts delta, 1 minimum (likely 2 or 3)
|
|
1 + // offset delta, likely 1
|
|
kbin.VarintLen(int32(len(r.Key))) +
|
|
len(r.Key) +
|
|
kbin.VarintLen(int32(len(r.Value))) +
|
|
len(r.Value) +
|
|
kbin.VarintLen(int32(len(r.Headers))) // varint array len headers
|
|
|
|
for _, h := range r.Headers {
|
|
l += kbin.VarintLen(int32(len(h.Key))) +
|
|
len(h.Key) +
|
|
kbin.VarintLen(int32(len(h.Value))) +
|
|
len(h.Value)
|
|
}
|
|
|
|
p.bytes += l
|
|
if p.bytes >= p.u.bytes {
|
|
p.bytes = l
|
|
p.onPart = -1
|
|
}
|
|
|
|
if p.onPart >= 0 && p.onPart < n {
|
|
return p.onPart
|
|
}
|
|
|
|
if !p.u.adaptive {
|
|
p.onPart = p.rng.Intn(n)
|
|
} else {
|
|
p.calc = p.calc[:0]
|
|
|
|
// For adaptive, the logic is that we pick by broker according
|
|
// to the inverse of the queue size. Presumably this means
|
|
// bytes, but we use records for simplicity.
|
|
//
|
|
// We calculate 1/recs for all brokers and choose the first one
|
|
// in this ordering that takes us negative.
|
|
//
|
|
// e.g., 1/1 + 1/3; pick is 0.2; 0.2*1.3333 = 0.26666; minus 1
|
|
// is negative, meaning our pick is the first. If rng was 0.9,
|
|
// scaled is 1.2, meaning our pick is the second (-1, still
|
|
// positive, second pick takes us negative).
|
|
//
|
|
// To guard floating rounding problems, if we pick nothing,
|
|
// then this means we pick our last.
|
|
var t float64
|
|
for ; n > 0; n-- {
|
|
n, backup := backup.Next()
|
|
backup++ // ensure non-zero
|
|
f := 1 / float64(backup)
|
|
t += f
|
|
p.calc = append(p.calc, struct {
|
|
f float64
|
|
n int
|
|
}{f, n})
|
|
}
|
|
r := p.rng.Float64()
|
|
pick := r * t
|
|
for _, c := range p.calc {
|
|
pick -= c.f
|
|
if pick <= 0 {
|
|
p.onPart = c.n
|
|
break
|
|
}
|
|
}
|
|
if p.onPart == -1 {
|
|
p.onPart = p.calc[len(p.calc)-1].n
|
|
}
|
|
}
|
|
return p.onPart
|
|
}
|
|
|
|
/////////////////////
|
|
// STICKY & COMPAT // - Sticky, Kafka (custom hash), Sarama (custom hash)
|
|
/////////////////////
|
|
|
|
// StickyPartitioner is the same as StickyKeyPartitioner, but with no logic to
|
|
// consistently hash keys. That is, this only partitions according to the
|
|
// sticky partition strategy.
|
|
func StickyPartitioner() Partitioner {
|
|
return new(stickyPartitioner)
|
|
}
|
|
|
|
type (
|
|
stickyPartitioner struct{}
|
|
|
|
stickyTopicPartitioner struct {
|
|
lastPart int
|
|
onPart int
|
|
rng *rand.Rand
|
|
}
|
|
)
|
|
|
|
func (*stickyPartitioner) ForTopic(string) TopicPartitioner {
|
|
p := newStickyTopicPartitioner()
|
|
return &p
|
|
}
|
|
|
|
func newStickyTopicPartitioner() stickyTopicPartitioner {
|
|
return stickyTopicPartitioner{
|
|
lastPart: -1,
|
|
onPart: -1,
|
|
rng: rand.New(rand.NewSource(time.Now().UnixNano())),
|
|
}
|
|
}
|
|
|
|
func (p *stickyTopicPartitioner) OnNewBatch() { p.lastPart, p.onPart = p.onPart, -1 }
|
|
func (*stickyTopicPartitioner) RequiresConsistency(*Record) bool { return false }
|
|
func (p *stickyTopicPartitioner) Partition(_ *Record, n int) int {
|
|
if p.onPart == -1 || p.onPart >= n {
|
|
p.onPart = p.rng.Intn(n)
|
|
if p.onPart == p.lastPart {
|
|
p.onPart = (p.onPart + 1) % n
|
|
}
|
|
}
|
|
return p.onPart
|
|
}
|
|
|
|
// StickyKeyPartitioner mirrors the default Java partitioner from Kafka's 2.4
|
|
// release (see KIP-480 and KAFKA-8601) until their 3.3 release. This was
|
|
// replaced in 3.3 with the uniform sticky partitioner (KIP-794), which is
|
|
// reimplemented in this client as the UniformBytesPartitioner.
|
|
//
|
|
// This is the same "hash the key consistently, if no key, choose random
|
|
// partition" strategy that the Java partitioner has always used, but rather
|
|
// than always choosing a random partition, the partitioner pins a partition to
|
|
// produce to until that partition rolls over to a new batch. Only when rolling
|
|
// to new batches does this partitioner switch partitions.
|
|
//
|
|
// The benefit with this pinning is less CPU utilization on Kafka brokers.
|
|
// Over time, the random distribution is the same, but the brokers are handling
|
|
// on average larger batches.
|
|
//
|
|
// hasher is optional; if nil, this will return a partitioner that partitions
|
|
// exactly how Kafka does. Specifically, the partitioner will use murmur2 to
|
|
// hash keys, will mask out the 32nd bit, and then will mod by the number of
|
|
// potential partitions.
|
|
func StickyKeyPartitioner(hasher PartitionerHasher) Partitioner {
|
|
if hasher == nil {
|
|
hasher = KafkaHasher(murmur2)
|
|
}
|
|
return &keyPartitioner{hasher}
|
|
}
|
|
|
|
// PartitionerHasher returns a partition to use given the input data and number
|
|
// of partitions.
|
|
type PartitionerHasher func([]byte, int) int
|
|
|
|
// KafkaHasher returns a PartitionerHasher using hashFn that mirrors how Kafka
|
|
// partitions after hashing data. In Kafka, after hashing into a uint32, the
|
|
// hash is converted to an int32 and the high bit is stripped. Kafka by default
|
|
// uses murmur2 hashing, and the StickyKeyPartiitoner uses this by default.
|
|
// Using this KafkaHasher function is only necessary if you want to change the
|
|
// underlying hashing algorithm.
|
|
func KafkaHasher(hashFn func([]byte) uint32) PartitionerHasher {
|
|
return func(key []byte, n int) int {
|
|
// https://github.com/apache/kafka/blob/d91a94e/clients/src/main/java/org/apache/kafka/clients/producer/internals/DefaultPartitioner.java#L59
|
|
// https://github.com/apache/kafka/blob/d91a94e/clients/src/main/java/org/apache/kafka/common/utils/Utils.java#L865-L867
|
|
// Masking before or after the int conversion makes no difference.
|
|
return int(hashFn(key)&0x7fffffff) % n
|
|
}
|
|
}
|
|
|
|
// SaramaHasher is a historical misnamed partitioner. This library's original
|
|
// implementation of the SaramaHasher was incorrect, if you want an exact
|
|
// match for the Sarama partitioner, use the [SaramaCompatHasher].
|
|
//
|
|
// This partitioner remains because as it turns out, other ecosystems provide
|
|
// a similar partitioner and this partitioner is useful for compatibility.
|
|
//
|
|
// In particular, using this function with a crc32.ChecksumIEEE hasher makes
|
|
// this partitioner match librdkafka's consistent partitioner, or the
|
|
// zendesk/ruby-kafka partitioner.
|
|
func SaramaHasher(hashFn func([]byte) uint32) PartitionerHasher {
|
|
return func(key []byte, n int) int {
|
|
p := int(hashFn(key)) % n
|
|
if p < 0 {
|
|
p = -p
|
|
}
|
|
return p
|
|
}
|
|
}
|
|
|
|
// SaramaCompatHasher returns a PartitionerHasher using hashFn that mirrors how
|
|
// Sarama partitions after hashing data.
|
|
//
|
|
// Sarama has two differences from Kafka when partitioning:
|
|
//
|
|
// 1) In Kafka, when converting the uint32 hash to an int32, Kafka masks the
|
|
// high bit. In Sarama, if the high bit is 1 (i.e., the number as an int32 is
|
|
// negative), Sarama negates the number.
|
|
//
|
|
// 2) Kafka by default uses the murmur2 hashing algorithm. Sarama by default
|
|
// uses fnv-1a.
|
|
//
|
|
// Sarama added a NewReferenceHashPartitioner function that attempted to align
|
|
// with Kafka, but the reference partitioner only fixed the first difference,
|
|
// not the second. Further customization options were added later that made it
|
|
// possible to exactly match Kafka when hashing.
|
|
//
|
|
// In short, to *exactly* match the Sarama defaults, use the following:
|
|
//
|
|
// kgo.StickyKeyPartitioner(kgo.SaramaCompatHasher(fnv32a))
|
|
//
|
|
// Where fnv32a is a function returning a new 32 bit fnv-1a hasher.
|
|
//
|
|
// func fnv32a(b []byte) uint32 {
|
|
// h := fnv.New32a()
|
|
// h.Reset()
|
|
// h.Write(b)
|
|
// return h.Sum32()
|
|
// }
|
|
func SaramaCompatHasher(hashFn func([]byte) uint32) PartitionerHasher {
|
|
return func(key []byte, n int) int {
|
|
p := int32(hashFn(key)) % int32(n)
|
|
if p < 0 {
|
|
p = -p
|
|
}
|
|
return int(p)
|
|
}
|
|
}
|
|
|
|
type (
|
|
keyPartitioner struct {
|
|
hasher PartitionerHasher
|
|
}
|
|
|
|
stickyKeyTopicPartitioner struct {
|
|
hasher PartitionerHasher
|
|
stickyTopicPartitioner
|
|
}
|
|
)
|
|
|
|
func (k *keyPartitioner) ForTopic(string) TopicPartitioner {
|
|
return &stickyKeyTopicPartitioner{k.hasher, newStickyTopicPartitioner()}
|
|
}
|
|
|
|
func (*stickyKeyTopicPartitioner) RequiresConsistency(r *Record) bool { return r.Key != nil }
|
|
func (p *stickyKeyTopicPartitioner) Partition(r *Record, n int) int {
|
|
if r.Key != nil {
|
|
return p.hasher(r.Key, n)
|
|
}
|
|
return p.stickyTopicPartitioner.Partition(r, n)
|
|
}
|
|
|
|
/////////////
|
|
// MURMUR2 //
|
|
/////////////
|
|
|
|
// Straight from the C++ code and from the Java code duplicating it.
|
|
// https://github.com/apache/kafka/blob/d91a94e/clients/src/main/java/org/apache/kafka/common/utils/Utils.java#L383-L421
|
|
// https://github.com/aappleby/smhasher/blob/61a0530f/src/MurmurHash2.cpp#L37-L86
|
|
//
|
|
// The Java code uses ints but with unsigned shifts; we do not need to.
|
|
func murmur2(b []byte) uint32 {
|
|
const (
|
|
seed uint32 = 0x9747b28c
|
|
m uint32 = 0x5bd1e995
|
|
r = 24
|
|
)
|
|
h := seed ^ uint32(len(b))
|
|
for len(b) >= 4 {
|
|
k := uint32(b[3])<<24 + uint32(b[2])<<16 + uint32(b[1])<<8 + uint32(b[0])
|
|
b = b[4:]
|
|
k *= m
|
|
k ^= k >> r
|
|
k *= m
|
|
|
|
h *= m
|
|
h ^= k
|
|
}
|
|
switch len(b) {
|
|
case 3:
|
|
h ^= uint32(b[2]) << 16
|
|
fallthrough
|
|
case 2:
|
|
h ^= uint32(b[1]) << 8
|
|
fallthrough
|
|
case 1:
|
|
h ^= uint32(b[0])
|
|
h *= m
|
|
}
|
|
|
|
h ^= h >> 13
|
|
h *= m
|
|
h ^= h >> 15
|
|
return h
|
|
}
|