1
+ jest . setTimeout ( 30000 ) ;
2
+
3
+ const { waitFor,
4
+ secureRandom,
5
+ createTopic,
6
+ createProducer,
7
+ createConsumer, } = require ( "../testhelpers" ) ;
8
+ const { PartitionAssigners, ErrorCodes } = require ( '../../../lib' ) . KafkaJS ;
9
+
10
+ describe ( 'Consumer > incremental rebalance' , ( ) => {
11
+ let consumer ;
12
+ let groupId , topicName ;
13
+
14
+ const consumerConfig = {
15
+ groupId,
16
+ partitionAssigners : [ PartitionAssigners . cooperativeSticky ] ,
17
+ } ;
18
+
19
+ beforeEach ( async ( ) => {
20
+ topicName = `test-topic1-${ secureRandom ( ) } ` ;
21
+ groupId = `consumer-group-id-${ secureRandom ( ) } `
22
+ consumer = null ;
23
+ await createTopic ( { topic : topicName , partitions : 2 } ) ;
24
+ } ) ;
25
+
26
+ afterEach ( async ( ) => {
27
+ consumer && ( await consumer . disconnect ( ) )
28
+ } ) ;
29
+
30
+ it ( 'returns protocol name' , async ( ) => {
31
+ consumer = createConsumer ( consumerConfig ) ;
32
+ await consumer . connect ( ) ;
33
+ await consumer . subscribe ( { topic : topicName } ) ;
34
+ consumer . run ( { eachMessage : async ( ) => { } } ) ;
35
+
36
+ await waitFor ( ( ) => consumer . assignment ( ) . length > 0 , ( ) => null , 1000 ) ;
37
+
38
+ expect ( consumer . rebalanceProtocol ( ) ) . toEqual ( 'COOPERATIVE' ) ;
39
+ } ) ;
40
+
41
+ it ( 'calls rebalance callback' , async ( ) => {
42
+ let assigns = 0 ;
43
+ let revokes = 0 ;
44
+ const rebalanceCallback = function ( err , assignment ) {
45
+ if ( err . code === ErrorCodes . ERR__ASSIGN_PARTITIONS ) {
46
+ assigns ++ ;
47
+ expect ( assignment . length ) . toBe ( 2 ) ;
48
+ } else if ( err . code === ErrorCodes . ERR__REVOKE_PARTITIONS ) {
49
+ revokes ++ ;
50
+ expect ( assignment . length ) . toBe ( 2 ) ;
51
+ } else {
52
+ // It's either assign or revoke and nothing else.
53
+ jest . fail ( 'Unexpected error code' ) ;
54
+ }
55
+ }
56
+
57
+
58
+ consumer = createConsumer ( consumerConfig , {
59
+ 'rebalance_cb' : rebalanceCallback ,
60
+ } ) ;
61
+ await consumer . connect ( ) ;
62
+ await consumer . subscribe ( { topic : topicName } ) ;
63
+ consumer . run ( { eachMessage : async ( ) => { } } ) ;
64
+
65
+ await waitFor ( ( ) => consumer . assignment ( ) . length > 0 , ( ) => null , 1000 ) ;
66
+ expect ( assigns ) . toBe ( 1 ) ;
67
+ expect ( consumer . assignment ( ) . length ) . toBe ( 2 ) ;
68
+
69
+ await consumer . disconnect ( ) ;
70
+ consumer = null ;
71
+ expect ( revokes ) . toBe ( 1 ) ;
72
+ expect ( assigns ) . toBe ( 1 ) ;
73
+ } ) ;
74
+
75
+ it ( 'allows changing the assignment' , async ( ) => {
76
+ let assigns = 0 ;
77
+ const rebalanceCallback = function ( err , assignment ) {
78
+ if ( err . code === ErrorCodes . ERR__ASSIGN_PARTITIONS ) {
79
+ assigns ++ ;
80
+ expect ( assignment . length ) . toBe ( 2 ) ;
81
+ assignment = [ assignment [ 0 ] ] ;
82
+ return assignment ;
83
+ } else {
84
+ // It's either assign or revoke and nothing else.
85
+ expect ( err . code ) . toBe ( ErrorCodes . ERR__REVOKE_PARTITIONS ) ;
86
+ }
87
+ }
88
+
89
+
90
+ consumer = createConsumer ( consumerConfig , {
91
+ 'rebalance_cb' : rebalanceCallback ,
92
+ } ) ;
93
+ await consumer . connect ( ) ;
94
+ await consumer . subscribe ( { topic : topicName } ) ;
95
+ consumer . run ( { eachMessage : async ( ) => { } } ) ;
96
+
97
+ await waitFor ( ( ) => consumer . assignment ( ) . length > 0 , ( ) => null , 1000 ) ;
98
+ expect ( assigns ) . toBe ( 1 ) ;
99
+ expect ( consumer . assignment ( ) . length ) . toBe ( 1 ) ;
100
+ } ) ;
101
+
102
+ it ( 'is actually incremental' , async ( ) => {
103
+ let expectedAssignmentCount = 0 ;
104
+ const rebalanceCallback = ( err , assignment ) => {
105
+ /* Empty assignments are ignored, they're a rebalance for the synchronization barrier. */
106
+ if ( assignment . length === 0 )
107
+ return ;
108
+ if ( err . code === ErrorCodes . ERR__ASSIGN_PARTITIONS ) {
109
+ expect ( assignment . length ) . toBe ( expectedAssignmentCount ) ;
110
+ } else if ( err . code === ErrorCodes . ERR__REVOKE_PARTITIONS ) {
111
+ expect ( assignment . length ) . toBe ( expectedAssignmentCount ) ;
112
+ } else {
113
+ // It's either assign or revoke and nothing else.
114
+ jest . fail ( 'Unexpected error code' ) ;
115
+ }
116
+ }
117
+
118
+ /* First consumer joins and gets all partitions. */
119
+ expectedAssignmentCount = 2 ;
120
+ consumer = createConsumer ( consumerConfig , {
121
+ 'rebalance_cb' : rebalanceCallback ,
122
+ } ) ;
123
+
124
+ await consumer . connect ( ) ;
125
+ await consumer . subscribe ( { topic : topicName } ) ;
126
+ consumer . run ( { eachMessage : async ( ) => { } } ) ;
127
+
128
+ await waitFor ( ( ) => consumer . assignment ( ) . length > 0 , ( ) => null , 1000 ) ;
129
+ expect ( consumer . assignment ( ) . length ) . toBe ( 2 ) ;
130
+
131
+ /* Second consumer joins and gets one partition. */
132
+ expectedAssignmentCount = 1 ;
133
+ const consumer2 = createConsumer ( consumerConfig , {
134
+ 'rebalance_cb' : rebalanceCallback ,
135
+ } ) ;
136
+
137
+ await consumer2 . connect ( ) ;
138
+ await consumer2 . subscribe ( { topic : topicName } ) ;
139
+ consumer2 . run ( { eachMessage : async ( ) => { } } ) ;
140
+ await waitFor ( ( ) => consumer2 . assignment ( ) . length > 0 , ( ) => null , 1000 ) ;
141
+ expect ( consumer . assignment ( ) . length ) . toBe ( 1 ) ;
142
+ expect ( consumer2 . assignment ( ) . length ) . toBe ( 1 ) ;
143
+
144
+ await consumer2 . disconnect ( ) ;
145
+ } ) ;
146
+
147
+ it ( 'works with promisified handler' , async ( ) => {
148
+ let assigns = 0 ;
149
+ let revokes = 0 ;
150
+
151
+ consumer = createConsumer ( consumerConfig , {
152
+ rebalanceListener : {
153
+ onPartitionsAssigned : async ( assignment ) => {
154
+ assigns ++ ;
155
+ expect ( assignment . length ) . toBe ( 2 ) ;
156
+ } ,
157
+ onPartitionsRevoked : async ( assignment ) => {
158
+ revokes ++ ;
159
+ expect ( assignment . length ) . toBe ( 2 ) ;
160
+ }
161
+ } ,
162
+ } ) ;
163
+ await consumer . connect ( ) ;
164
+ await consumer . subscribe ( { topic : topicName } ) ;
165
+ consumer . run ( { eachMessage : async ( ) => { } } ) ;
166
+
167
+ await waitFor ( ( ) => consumer . assignment ( ) . length > 0 , ( ) => null , 1000 ) ;
168
+ expect ( assigns ) . toBe ( 1 ) ;
169
+ expect ( consumer . assignment ( ) . length ) . toBe ( 2 ) ;
170
+
171
+ await consumer . disconnect ( ) ;
172
+ consumer = null ;
173
+ expect ( revokes ) . toBe ( 1 ) ;
174
+ expect ( assigns ) . toBe ( 1 ) ;
175
+ } ) ;
176
+ } ) ;
0 commit comments