Skip to content

Commit 41bb5a7

Browse files
committed
adminapi example: added an async and delta/incremental alter_configs example
1 parent 3aa92b3 commit 41bb5a7

File tree

1 file changed

+105
-1
lines changed

1 file changed

+105
-1
lines changed

examples/adminapi.py

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121

2222
from confluent_kafka import AdminClient, NewTopic, NewPartitions, ConfigResource, ConfigEntry, KafkaException
2323
import sys
24+
import threading
25+
import logging
26+
27+
logging.basicConfig()
2428

2529

2630
def example_create_topics(a, topics):
@@ -114,7 +118,9 @@ def example_describe_configs(a, args):
114118

115119

116120
def example_alter_configs(a, args):
117-
""" alter configs """
121+
""" Alter configs atomically, replacing non-specified
122+
configuration properties with their default values.
123+
"""
118124

119125
resources = []
120126
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):
134140
raise
135141

136142

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+
137238
def example_list(a, args):
138239
""" list topics and cluster metadata """
139240

@@ -186,6 +287,8 @@ def example_list(a, args):
186287
sys.stderr.write(' describe_configs <resource_type1> <resource_name1> <resource2> <resource_name2> ..\n')
187288
sys.stderr.write(' alter_configs <resource_type1> <resource_name1> ' +
188289
'<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')
189292
sys.stderr.write(' list [<all|topics|brokers>]\n')
190293
sys.exit(1)
191294

@@ -201,6 +304,7 @@ def example_list(a, args):
201304
'create_partitions': example_create_partitions,
202305
'describe_configs': example_describe_configs,
203306
'alter_configs': example_alter_configs,
307+
'delta_alter_configs': example_delta_alter_configs,
204308
'list': example_list}
205309

206310
if operation not in opsmap:

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy