Skip to content

Commit 5562909

Browse files
committed
produce: adds option to use default partitioner (#25).
- currently only the hashCode partitioner is available - by default no partitioner is used. this means that either the partition is specified or it defaults to 0. - if the stdin line cannot be deserialized into a json object, its string value is used as the value and the key will be nil/null.
1 parent fe3926b commit 5562909

File tree

2 files changed

+107
-19
lines changed

2 files changed

+107
-19
lines changed

produce.go

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,26 @@ import (
1616
)
1717

1818
type produceConfig struct {
19-
topic string
20-
brokers []string
21-
batch int
22-
timeout time.Duration
23-
verbose bool
24-
args struct {
25-
topic string
26-
brokers string
27-
batch int
28-
timeout time.Duration
29-
verbose bool
19+
topic string
20+
brokers []string
21+
batch int
22+
timeout time.Duration
23+
verbose bool
24+
partitioner string
25+
args struct {
26+
topic string
27+
brokers string
28+
batch int
29+
timeout time.Duration
30+
verbose bool
31+
partitioner string
3032
}
3133
}
3234

3335
type message struct {
3436
Key *string `json:"key"`
3537
Value *string `json:"value"`
36-
Partition int32 `json:"partition"`
38+
Partition *int32 `json:"partition"`
3739
}
3840

3941
func produceFlags() *flag.FlagSet {
@@ -68,6 +70,12 @@ func produceFlags() *flag.FlagSet {
6870
false,
6971
"Verbose output",
7072
)
73+
flags.StringVar(
74+
&config.produce.args.partitioner,
75+
"partitioner",
76+
"",
77+
"Optional partitioner to use. Available: hashCode",
78+
)
7179

7280
flags.Usage = func() {
7381
fmt.Fprintln(os.Stderr, "Usage of produce:")
@@ -244,7 +252,7 @@ func produceCommand() command {
244252
var wg sync.WaitGroup
245253
wg.Add(4)
246254
go readInput(&wg, closer, stdin, lines)
247-
go deserializeLines(&wg, lines, messages)
255+
go deserializeLines(&wg, lines, messages, int32(len(leaders)))
248256
go batchRecords(&wg, messages, batchedMessages)
249257
go produce(&wg, leaders, batchedMessages)
250258

@@ -253,7 +261,7 @@ func produceCommand() command {
253261
}
254262
}
255263

256-
func deserializeLines(wg *sync.WaitGroup, in chan string, out chan message) {
264+
func deserializeLines(wg *sync.WaitGroup, in chan string, out chan message, partitionCount int32) {
257265
defer func() {
258266
close(out)
259267
wg.Done()
@@ -268,10 +276,23 @@ func deserializeLines(wg *sync.WaitGroup, in chan string, out chan message) {
268276
var msg message
269277
if err := json.Unmarshal([]byte(l), &msg); err != nil {
270278
if config.produce.verbose {
271-
fmt.Printf("Failed to unmarshal input, falling back to defaults. err=%v\n", err)
279+
fmt.Printf("Failed to unmarshal input [%v], falling back to defaults. err=%v\n", l, err)
280+
}
281+
var v *string = &l
282+
if len(l) == 0 {
283+
v = nil
272284
}
273-
msg = message{Key: &l, Value: &l, Partition: 0}
285+
msg = message{Key: nil, Value: v}
274286
}
287+
288+
var p int32 = 0
289+
if msg.Key != nil && config.produce.partitioner == "hashCode" {
290+
p = hashCodePartition(*msg.Key, partitionCount)
291+
}
292+
if msg.Partition == nil {
293+
msg.Partition = &p
294+
}
295+
275296
out <- msg
276297
}
277298
}
@@ -327,9 +348,9 @@ func (m message) asSaramaMessage() *sarama.Message {
327348
func produceBatch(leaders map[int32]*sarama.Broker, batch []message) error {
328349
requests := map[*sarama.Broker]*sarama.ProduceRequest{}
329350
for _, msg := range batch {
330-
broker, ok := leaders[msg.Partition]
351+
broker, ok := leaders[*msg.Partition]
331352
if !ok {
332-
err := fmt.Errorf("Non-configured partition %v", msg.Partition)
353+
err := fmt.Errorf("Non-configured partition %v", *msg.Partition)
333354
fmt.Fprintf(os.Stderr, "%v.\n", err)
334355
return err
335356
}
@@ -339,7 +360,7 @@ func produceBatch(leaders map[int32]*sarama.Broker, batch []message) error {
339360
requests[broker] = req
340361
}
341362

342-
req.AddMessage(config.produce.topic, msg.Partition, msg.asSaramaMessage())
363+
req.AddMessage(config.produce.topic, *msg.Partition, msg.asSaramaMessage())
343364
}
344365

345366
for broker, req := range requests {

produce_test.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ package main
33
import (
44
"os"
55
"reflect"
6+
"sync"
67
"testing"
8+
"time"
79
)
810

911
func TestHashCode(t *testing.T) {
@@ -184,3 +186,68 @@ func TestProduceParseArgs(t *testing.T) {
184186
return
185187
}
186188
}
189+
190+
func newMessage(key, value string, partition int32) message {
191+
var k *string
192+
if key != "" {
193+
k = &key
194+
}
195+
196+
var v *string
197+
if value != "" {
198+
v = &value
199+
}
200+
201+
return message{
202+
Key: k,
203+
Value: v,
204+
Partition: &partition,
205+
}
206+
}
207+
208+
func TestDeserializeLines(t *testing.T) {
209+
config.produce.partitioner = "hashCode"
210+
data := []struct {
211+
in string
212+
partitionCount int32
213+
expected message
214+
}{
215+
{
216+
in: "",
217+
partitionCount: 1,
218+
expected: newMessage("", "", 0),
219+
},
220+
{
221+
in: `{"key":"hans","value":"123"}`,
222+
partitionCount: 4,
223+
expected: newMessage("hans", "123", hashCodePartition("hans", 4)),
224+
},
225+
{
226+
in: `{"key":"hans","value":"123","partition":1}`,
227+
partitionCount: 3,
228+
expected: newMessage("hans", "123", 1),
229+
},
230+
{
231+
in: `so lange schon`,
232+
partitionCount: 3,
233+
expected: newMessage("", "so lange schon", 0),
234+
},
235+
}
236+
237+
for _, d := range data {
238+
var wg sync.WaitGroup
239+
in := make(chan string, 1)
240+
out := make(chan message)
241+
go deserializeLines(&wg, in, out, d.partitionCount)
242+
in <- d.in
243+
244+
select {
245+
case <-time.After(50 * time.Millisecond):
246+
t.Errorf("did not receive output in time")
247+
case actual := <-out:
248+
if !reflect.DeepEqual(d.expected, actual) {
249+
t.Errorf("\nexpected %#v\nactual %#v", d.expected, actual)
250+
}
251+
}
252+
}
253+
}

0 commit comments

Comments
 (0)