Skip to content

Commit 0f5c477

Browse files
committed
Add options for librdkafka partitioning compatibility
1 parent e6a3579 commit 0f5c477

File tree

3 files changed

+62
-14
lines changed

3 files changed

+62
-14
lines changed

builders.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,25 @@ func ProducerBuilderWithConfig(config *sarama.Config) ProducerBuilder {
2626
}
2727
}
2828

29+
// ProducerBuilderWithHashPartitionerOptions creates a Kafka producer using the
30+
// Sarama library. It can be used to configure the partitioner. If both
31+
// sarama.WithCustomHashFunction and goka.WithHasher are used to set the hasher,
32+
// the former will take precedence.
33+
func ProducerBuilderWithHashPartitionerOptions(opts ...sarama.HashPartitionerOption) ProducerBuilder {
34+
return func(brokers []string, clientID string, hasher func() hash.Hash32) (Producer, error) {
35+
config := globalConfig
36+
defaults := []sarama.HashPartitionerOption{
37+
// hasher may be goka.DefaultHasher or it may have been modified by
38+
// goka.WithHasher. It may be overridden by opts.
39+
sarama.WithCustomHashFunction(hasher),
40+
}
41+
opts = append(defaults, opts...)
42+
config.ClientID = clientID
43+
config.Producer.Partitioner = sarama.NewCustomPartitioner(opts...)
44+
return NewProducer(brokers, &config)
45+
}
46+
}
47+
2948
// TopicManagerBuilder creates a TopicManager to check partition counts and
3049
// create tables.
3150
type TopicManagerBuilder func(brokers []string) (TopicManager, error)

options.go

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -401,17 +401,26 @@ func WithRebalanceCallback(cb RebalanceCallback) ProcessorOption {
401401
// view options
402402
///////////////////////////////////////////////////////////////////////////////
403403

404+
type viewPartitionerCompat int
405+
406+
const (
407+
// Interpret the bytes of the key digest as an unsigned integer to match
408+
// librdkafka's partitioning behavior.
409+
librdkafkaCompat viewPartitionerCompat = iota + 1
410+
)
411+
404412
// ViewOption defines a configuration option to be used when creating a view.
405413
type ViewOption func(*voptions, Table, Codec)
406414

407415
type voptions struct {
408-
log logger
409-
clientID string
410-
tableCodec Codec
411-
updateCallback UpdateCallback
412-
hasher func() hash.Hash32
413-
autoreconnect bool
414-
backoffResetTime time.Duration
416+
log logger
417+
clientID string
418+
tableCodec Codec
419+
updateCallback UpdateCallback
420+
hasher func() hash.Hash32
421+
partitionerCompat viewPartitionerCompat
422+
autoreconnect bool
423+
backoffResetTime time.Duration
415424

416425
builders struct {
417426
storage storage.Builder
@@ -476,6 +485,15 @@ func WithViewHasher(hasher func() hash.Hash32) ViewOption {
476485
}
477486
}
478487

488+
// WithViewHashUnsigned instructs the partitioner to interpret the key digest
489+
// as an unsigned integer when partitioning. Combine this option with the
490+
// CRC-32 hash algorithm for compatibility with librdkafka.
491+
func WithViewHashUnsigned() ViewOption {
492+
return func(o *voptions, table Table, codec Codec) {
493+
o.partitionerCompat = librdkafkaCompat
494+
}
495+
}
496+
479497
// WithViewClientID defines the client ID used to identify with Kafka.
480498
func WithViewClientID(clientID string) ViewOption {
481499
return func(o *voptions, table Table, codec Codec) {

view.go

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,11 @@ func (v *View) close() error {
295295
}
296296

297297
func (v *View) hash(key string) (int32, error) {
298+
numPartitions := len(v.partitions)
299+
if numPartitions < 1 {
300+
return 0, errors.New("no partitions found")
301+
}
302+
298303
// create a new hasher every time. Alternative would be to store the hash in
299304
// view and every time reset the hasher (ie, hasher.Reset()). But that would
300305
// also require us to protect the access of the hasher with a mutex.
@@ -304,14 +309,20 @@ func (v *View) hash(key string) (int32, error) {
304309
if err != nil {
305310
return -1, err
306311
}
307-
hash := int32(hasher.Sum32())
308-
if hash < 0 {
309-
hash = -hash
310-
}
311-
if len(v.partitions) == 0 {
312-
return 0, errors.New("no partitions found")
312+
313+
var partition int32
314+
hash := hasher.Sum32()
315+
if v.opts.partitionerCompat == librdkafkaCompat {
316+
partition = int32(hash % uint32(numPartitions))
317+
} else {
318+
partition = int32(hash) % int32(numPartitions)
319+
320+
if partition < 0 {
321+
partition = -partition
322+
}
313323
}
314-
return hash % int32(len(v.partitions)), nil
324+
325+
return partition, nil
315326
}
316327

317328
func (v *View) find(key string) (*PartitionTable, error) {

0 commit comments

Comments
 (0)