-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: race condition issue on batch consumer
- Loading branch information
1 parent
efcead4
commit f9aebc1
Showing
5 changed files
with
200 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package kafka | ||
|
||
import "github.com/segmentio/kafka-go" | ||
|
||
// offsetStash holds the latest committed offsets by topic => partition => offset. | ||
type offsetStash map[string]map[int]int64 | ||
|
||
// UpdateWithNewestCommittedOffsets calculates latest committed, and it | ||
// updates the latest offset value by looking topic => partition => offset | ||
func (o offsetStash) UpdateWithNewestCommittedOffsets(messages []kafka.Message) { | ||
for i := range messages { | ||
offsetsByPartition, ok := o[messages[i].Topic] | ||
if !ok { | ||
offsetsByPartition = map[int]int64{} | ||
o[messages[i].Topic] = offsetsByPartition | ||
} | ||
|
||
// Because of incoming messages is already committed, we need to increase their offsets by 1 | ||
messageOffset := messages[i].Offset + 1 | ||
|
||
if offset, ok := offsetsByPartition[messages[i].Partition]; !ok || messageOffset > offset { | ||
offsetsByPartition[messages[i].Partition] = messageOffset | ||
} | ||
} | ||
} | ||
|
||
// IgnoreAlreadyCommittedMessages When committing messages in consumer groups, the message with the highest offset for | ||
// a given topic/partition determines the value of the committed offset for that partition. For example, if messages at | ||
// offset 1, 2, and 3 of a single partition and if we commit with message offset 3 it will also result in committing the | ||
// messages at offsets 1 and 2 for that partition. It means we can safely ignore the messages which have offsets 1 and 2. | ||
func (o offsetStash) IgnoreAlreadyCommittedMessages(messages []kafka.Message) []kafka.Message { | ||
willBeCommitted := make([]kafka.Message, 0, len(messages)) | ||
for i := range messages { | ||
offsetsByPartition := o[messages[i].Topic] | ||
offset := offsetsByPartition[messages[i].Partition] | ||
|
||
// it means, we already committed this message, so we can safely ignore it | ||
if messages[i].Offset <= offset { | ||
continue | ||
} | ||
|
||
willBeCommitted = append(willBeCommitted, messages[i]) | ||
} | ||
|
||
return willBeCommitted | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package kafka | ||
|
||
import ( | ||
segmentio "github.com/segmentio/kafka-go" | ||
"reflect" | ||
"testing" | ||
) | ||
|
||
func Test_offsetStash_SetWithNewestCommittedOffsets(t *testing.T) { | ||
// Given | ||
offsets := offsetStash{} | ||
messages := []segmentio.Message{ | ||
{Topic: "a", Partition: 0, Offset: 10}, | ||
{Topic: "a", Partition: 0, Offset: 5}, | ||
|
||
{Topic: "a", Partition: 1, Offset: 11}, | ||
{Topic: "a", Partition: 1, Offset: 20}, | ||
|
||
{Topic: "a", Partition: 2, Offset: 12}, | ||
|
||
{Topic: "b", Partition: 0, Offset: 15}, | ||
{Topic: "b", Partition: 0, Offset: 20}, | ||
|
||
{Topic: "c", Partition: 2, Offset: 1}, | ||
{Topic: "c", Partition: 2, Offset: 2}, | ||
{Topic: "c", Partition: 2, Offset: 3}, | ||
} | ||
|
||
// When | ||
offsets.UpdateWithNewestCommittedOffsets(messages) | ||
|
||
// Then | ||
assertFunc := func(actual map[int]int64, expected map[int]int64) { | ||
if !reflect.DeepEqual(actual, expected) { | ||
t.Fatal(actual, "is not equal to", expected) | ||
} | ||
} | ||
|
||
assertFunc(offsets["a"], map[int]int64{0: 11, 1: 21, 2: 13}) | ||
assertFunc(offsets["b"], map[int]int64{0: 21}) | ||
assertFunc(offsets["c"], map[int]int64{2: 4}) | ||
} | ||
|
||
func Test_offsetStash_IgnoreAlreadyCommittedMessages(t *testing.T) { | ||
// Given | ||
offsets := offsetStash{ | ||
"a": map[int]int64{ | ||
0: 10, | ||
}, | ||
"b": map[int]int64{ | ||
0: 5, | ||
1: 6, | ||
}, | ||
"c": map[int]int64{ | ||
10: 15, | ||
}, | ||
} | ||
messages := []segmentio.Message{ | ||
{Topic: "a", Partition: 0, Offset: 8}, // ignore | ||
{Topic: "a", Partition: 0, Offset: 9}, // ignore | ||
{Topic: "a", Partition: 0, Offset: 10}, // ignore | ||
|
||
{Topic: "b", Partition: 0, Offset: 15}, // update | ||
{Topic: "b", Partition: 1, Offset: 5}, // ignore | ||
|
||
{Topic: "c", Partition: 1, Offset: 5}, // update | ||
{Topic: "c", Partition: 10, Offset: 10}, // ignore | ||
} | ||
|
||
// When | ||
actual := offsets.IgnoreAlreadyCommittedMessages(messages) | ||
|
||
// Then | ||
if len(actual) != 2 { | ||
t.Fatal("Actual must be length of 2") | ||
} | ||
|
||
if actual[0].Topic != "b" && actual[0].Partition == 0 && actual[0].Offset == 15 { | ||
t.Fatalf("Actual %v is not equal to expected", actual[0]) | ||
} | ||
|
||
if actual[1].Topic != "c" && actual[1].Partition == 1 && actual[0].Offset == 5 { | ||
t.Fatalf("Actual %v is not equal to expected", actual[1]) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters