21
21
22
22
from confluent_kafka import AdminClient , NewTopic , NewPartitions , ConfigResource , ConfigEntry , KafkaException
23
23
import sys
24
+ import threading
25
+ import logging
26
+
27
+ logging .basicConfig ()
24
28
25
29
26
30
def example_create_topics (a , topics ):
@@ -114,7 +118,9 @@ def example_describe_configs(a, args):
114
118
115
119
116
120
def example_alter_configs (a , args ):
117
- """ alter configs """
121
+ """ Alter configs atomically, replacing non-specified
122
+ configuration properties with their default values.
123
+ """
118
124
119
125
resources = []
120
126
for restype , resname , configs in zip (args [0 ::3 ], args [1 ::3 ], args [2 ::3 ]):
@@ -134,6 +140,101 @@ def example_alter_configs(a, args):
134
140
raise
135
141
136
142
143
+ def example_delta_alter_configs (a , args ):
144
+ """
145
+ Alter only supplied configs (pre incremental/KIP-248)
146
+
147
+ The pre incremental/KIP-248 AlterConfigs Kafka API requires all
148
+ configuration to be passed, any left out configuration properties will
149
+ revert to their default settings.
150
+
151
+ This example shows how to just modify the supplied configuration entries
152
+ by first reading the configuration from the broker, updating the supplied
153
+ configuration with the broker configuration (without overwriting), and
154
+ then writing it all back.
155
+
156
+ The async nature of futures is also show-cased, which makes this example
157
+ a bit more complex than it needs to be in the synchronous case.
158
+ """
159
+
160
+ # Convert supplied config to resources.
161
+ # We can reuse the same resources both for describe_configs and
162
+ # alter_configs.
163
+ resources = []
164
+ for restype , resname , configs in zip (args [0 ::3 ], args [1 ::3 ], args [2 ::3 ]):
165
+ resource = ConfigResource (restype , resname )
166
+ resources .append (resource )
167
+ for k , v in [conf .split ('=' ) for conf in configs .split (',' )]:
168
+ resource .set_config (k , v )
169
+
170
+ # Set up a locked counter and an Event (for signaling) to track when the
171
+ # second level of futures are done. This is a bit of contrived example
172
+ # due to no other asynchronous mechanism being used, so we'll need
173
+ # to wait on something to signal completion.
174
+
175
+ class WaitZero (object ):
176
+ def __init__ (self , waitcnt ):
177
+ self .cnt = waitcnt
178
+ self .lock = threading .Lock ()
179
+ self .event = threading .Event ()
180
+
181
+ def decr (self ):
182
+ """ Decrement cnt by 1"""
183
+ with self .lock :
184
+ assert self .cnt > 0
185
+ self .cnt -= 1
186
+ self .event .set ()
187
+
188
+ def wait (self ):
189
+ """ Wait until cnt reaches 0 """
190
+ self .lock .acquire ()
191
+ while self .cnt > 0 :
192
+ self .lock .release ()
193
+ self .event .wait ()
194
+ self .event .clear ()
195
+ self .lock .acquire ()
196
+ self .lock .release ()
197
+
198
+ def __len__ (self ):
199
+ with self .lock :
200
+ return self .cnt
201
+
202
+ wait_zero = WaitZero (len (resources ))
203
+
204
+ # Read existing configuration from cluster
205
+ fs = a .describe_configs (resources )
206
+
207
+ def delta_alter_configs_done (fut , resource ):
208
+ e = fut .exception ()
209
+ if e is not None :
210
+ print ("Config update for {} failed: {}" .format (resource , e ))
211
+ else :
212
+ print ("Config for {} updated" .format (resource ))
213
+ wait_zero .decr ()
214
+
215
+ def delta_alter_configs (resource , remote_config ):
216
+ print ("Updating {} supplied config entries {} with {} config entries read from cluster" .format (
217
+ len (resource ), resource , len (remote_config )))
218
+ # Only set configuration that is not default
219
+ for k , entry in [(k , v ) for k , v in remote_config .items () if not v .is_default ]:
220
+ resource .set_config (k , entry .value , overwrite = False )
221
+
222
+ fs = a .alter_configs ([resource ])
223
+ fs [resource ].add_done_callback (lambda fut : delta_alter_configs_done (fut , resource ))
224
+
225
+ # For each resource's future set up a completion callback
226
+ # that in turn calls alter_configs() on that single resource.
227
+ # This is ineffective since the resources can usually go in
228
+ # one single alter_configs() call, but we're also show-casing
229
+ # the futures here.
230
+ for res , f in fs .items ():
231
+ f .add_done_callback (lambda fut , resource = res : delta_alter_configs (resource , fut .result ()))
232
+
233
+ # Wait for done callbacks to be triggered and operations to complete.
234
+ print ("Waiting for {} resource updates to finish" .format (len (wait_zero )))
235
+ wait_zero .wait ()
236
+
237
+
137
238
def example_list (a , args ):
138
239
""" list topics and cluster metadata """
139
240
@@ -186,6 +287,8 @@ def example_list(a, args):
186
287
sys .stderr .write (' describe_configs <resource_type1> <resource_name1> <resource2> <resource_name2> ..\n ' )
187
288
sys .stderr .write (' alter_configs <resource_type1> <resource_name1> ' +
188
289
'<config=val,config2=val2> <resource_type2> <resource_name2> <config..> ..\n ' )
290
+ sys .stderr .write (' delta_alter_configs <resource_type1> <resource_name1> ' +
291
+ '<config=val,config2=val2> <resource_type2> <resource_name2> <config..> ..\n ' )
189
292
sys .stderr .write (' list [<all|topics|brokers>]\n ' )
190
293
sys .exit (1 )
191
294
@@ -201,6 +304,7 @@ def example_list(a, args):
201
304
'create_partitions' : example_create_partitions ,
202
305
'describe_configs' : example_describe_configs ,
203
306
'alter_configs' : example_alter_configs ,
307
+ 'delta_alter_configs' : example_delta_alter_configs ,
204
308
'list' : example_list }
205
309
206
310
if operation not in opsmap :
0 commit comments