@@ -37,6 +37,11 @@ class AzureStorageCheckpointLeaseManager(AbstractCheckpointManager, AbstractLeas
37
37
will be used.
38
38
:param str lease_container_name: The name of the container that will be used to store
39
39
leases. If it does not already exist it will be created. Default value is 'eph-leases'.
40
+ Leases are named via internal partition_ids, locations can be modified via
41
+ storage_blob_prefix and use_consumer_group_as_directory.
42
+ :param str storage_blob_prefix: If populated, prepends a prefix when constructing
43
+ the location that leases are stored within the lease_container. Default None.
44
+ If consumer_group_as_directory is also provided, it is unified as such <prefix><group>/<id>.
40
45
:param int lease_renew_interval: The interval in seconds at which EPH will attempt to
41
46
renew the lease of a particular partition. Default value is 10.
42
47
:param int lease_duration: The duration in seconds of a lease on a partition.
@@ -49,11 +54,16 @@ class AzureStorageCheckpointLeaseManager(AbstractCheckpointManager, AbstractLeas
49
54
:param str connection_string: If specified, this will override all other endpoint parameters.
50
55
See http://azure.microsoft.com/en-us/documentation/articles/storage-configure-connection-string/
51
56
for the connection string format.
57
+ :param bool use_consumer_group_as_directory: If true, includes the consumer group as part of the
58
+ location we use to store leases within the container, as such: <consumer_group>/<partition_id>,
59
+ otherwise leases are simply named by their partition_id. Default False.
60
+ If storage_blob_prefix is provided this prefix will be prepended in either case.
52
61
"""
53
62
54
63
def __init__ (self , storage_account_name = None , storage_account_key = None , lease_container_name = "eph-leases" ,
55
64
storage_blob_prefix = None , lease_renew_interval = 10 , lease_duration = 30 ,
56
- sas_token = None , endpoint_suffix = "core.windows.net" , connection_string = None ):
65
+ sas_token = None , endpoint_suffix = "core.windows.net" , connection_string = None ,
66
+ use_consumer_group_as_directory = False ):
57
67
AbstractCheckpointManager .__init__ (self )
58
68
AbstractLeaseManager .__init__ (self , lease_renew_interval , lease_duration )
59
69
self .storage_account_name = storage_account_name
@@ -63,6 +73,7 @@ def __init__(self, storage_account_name=None, storage_account_key=None, lease_co
63
73
self .connection_string = connection_string
64
74
self .lease_container_name = lease_container_name
65
75
self .storage_blob_prefix = storage_blob_prefix
76
+ self .use_consumer_group_as_directory = use_consumer_group_as_directory
66
77
self .storage_client = None
67
78
self .consumer_group_directory = None
68
79
self .host = None
@@ -97,7 +108,7 @@ def initialize(self, host):
97
108
endpoint_suffix = self .endpoint_suffix ,
98
109
connection_string = self .connection_string ,
99
110
request_session = self .request_session )
100
- self .consumer_group_directory = self .storage_blob_prefix + self . host .eh_config .consumer_group
111
+ self .consumer_group_directory = self .host .eh_config .consumer_group if self . use_consumer_group_as_directory else ""
101
112
102
113
# Checkpoint Managment Methods
103
114
@@ -213,11 +224,13 @@ async def get_lease_async(self, partition_id):
213
224
:rtype: ~azure.eventprocessorhost.lease.Lease
214
225
"""
215
226
try :
227
+ blob_path = self ._get_lease_blob_path (partition_id )
216
228
blob = await self .host .loop .run_in_executor (
217
229
self .executor ,
218
230
functools .partial (
219
231
self .storage_client .get_blob_to_text ,
220
- self .lease_container_name , partition_id ))
232
+ self .lease_container_name ,
233
+ blob_path ))
221
234
lease = AzureBlobLease ()
222
235
lease .with_blob (blob )
223
236
async def state ():
@@ -231,7 +244,7 @@ async def state():
231
244
functools .partial (
232
245
self .storage_client .get_blob_properties ,
233
246
self .lease_container_name ,
234
- partition_id ))
247
+ blob_path ))
235
248
return res .properties .lease .state
236
249
except Exception as err : # pylint: disable=broad-except
237
250
_logger .error ("Failed to get lease state %r %r" , err , partition_id )
@@ -269,20 +282,21 @@ async def create_lease_if_not_exists_async(self, partition_id):
269
282
"""
270
283
return_lease = None
271
284
try :
285
+ blob_path = self ._get_lease_blob_path (partition_id )
272
286
return_lease = AzureBlobLease ()
273
287
return_lease .partition_id = partition_id
274
288
serializable_lease = return_lease .serializable ()
275
289
json_lease = json .dumps (serializable_lease )
276
290
_logger .info ("Creating Lease %r %r %r" ,
277
291
self .lease_container_name ,
278
- partition_id ,
292
+ blob_path ,
279
293
json .dumps ({k :v for k , v in serializable_lease .items () if k != 'event_processor_context' }))
280
294
await self .host .loop .run_in_executor (
281
295
self .executor ,
282
296
functools .partial (
283
297
self .storage_client .create_blob_from_text ,
284
298
self .lease_container_name ,
285
- partition_id ,
299
+ blob_path ,
286
300
json_lease ))
287
301
except Exception : # pylint: disable=broad-except
288
302
try :
@@ -300,12 +314,13 @@ async def delete_lease_async(self, lease):
300
314
:param lease: The stored lease to be deleted.
301
315
:type lease: ~azure.eventprocessorhost.lease.Lease
302
316
"""
317
+ blob_path = self ._get_lease_blob_path (lease .partition_id )
303
318
await self .host .loop .run_in_executor (
304
319
self .executor ,
305
320
functools .partial (
306
321
self .storage_client .delete_blob ,
307
322
self .lease_container_name ,
308
- lease . partition_id ,
323
+ blob_path ,
309
324
lease_id = lease .token ))
310
325
311
326
async def acquire_lease_async (self , lease ):
@@ -323,6 +338,7 @@ async def acquire_lease_async(self, lease):
323
338
new_lease_id = str (uuid .uuid4 ())
324
339
partition_id = lease .partition_id
325
340
try :
341
+ blob_path = self ._get_lease_blob_path (partition_id )
326
342
if asyncio .iscoroutinefunction (lease .state ):
327
343
state = await lease .state ()
328
344
else :
@@ -345,7 +361,7 @@ async def acquire_lease_async(self, lease):
345
361
functools .partial (
346
362
self .storage_client .change_blob_lease ,
347
363
self .lease_container_name ,
348
- partition_id ,
364
+ blob_path ,
349
365
lease .token ,
350
366
new_lease_id ))
351
367
lease .token = new_lease_id
@@ -356,7 +372,7 @@ async def acquire_lease_async(self, lease):
356
372
functools .partial (
357
373
self .storage_client .acquire_blob_lease ,
358
374
self .lease_container_name ,
359
- partition_id ,
375
+ blob_path ,
360
376
self .lease_duration ,
361
377
new_lease_id ))
362
378
lease .owner = self .host .host_name
@@ -381,12 +397,13 @@ async def renew_lease_async(self, lease):
381
397
:rtype: bool
382
398
"""
383
399
try :
400
+ blob_path = self ._get_lease_blob_path (lease .partition_id )
384
401
await self .host .loop .run_in_executor (
385
402
self .executor ,
386
403
functools .partial (
387
404
self .storage_client .renew_blob_lease ,
388
405
self .lease_container_name ,
389
- lease . partition_id ,
406
+ blob_path ,
390
407
lease_id = lease .token ,
391
408
timeout = self .lease_duration ))
392
409
except Exception as err : # pylint: disable=broad-except
@@ -411,6 +428,7 @@ async def release_lease_async(self, lease):
411
428
lease_id = None
412
429
try :
413
430
_logger .info ("Releasing lease %r %r" , self .host .guid , lease .partition_id )
431
+ blob_path = self ._get_lease_blob_path (lease .partition_id )
414
432
lease_id = lease .token
415
433
released_copy = AzureBlobLease ()
416
434
released_copy .with_lease (lease )
@@ -422,15 +440,15 @@ async def release_lease_async(self, lease):
422
440
functools .partial (
423
441
self .storage_client .create_blob_from_text ,
424
442
self .lease_container_name ,
425
- lease . partition_id ,
443
+ blob_path ,
426
444
json .dumps (released_copy .serializable ()),
427
445
lease_id = lease_id ))
428
446
await self .host .loop .run_in_executor (
429
447
self .executor ,
430
448
functools .partial (
431
449
self .storage_client .release_blob_lease ,
432
450
self .lease_container_name ,
433
- lease . partition_id ,
451
+ blob_path ,
434
452
lease_id ))
435
453
except Exception as err : # pylint: disable=broad-except
436
454
_logger .error ("Failed to release lease %r %r %r" ,
@@ -461,12 +479,13 @@ async def update_lease_async(self, lease):
461
479
# First, renew the lease to make sure the update will go through.
462
480
if await self .renew_lease_async (lease ):
463
481
try :
482
+ blob_path = self ._get_lease_blob_path (lease .partition_id )
464
483
await self .host .loop .run_in_executor (
465
484
self .executor ,
466
485
functools .partial (
467
486
self .storage_client .create_blob_from_text ,
468
487
self .lease_container_name ,
469
- lease . partition_id ,
488
+ blob_path ,
470
489
json .dumps (lease .serializable ()),
471
490
lease_id = lease .token ))
472
491
@@ -477,3 +496,18 @@ async def update_lease_async(self, lease):
477
496
else :
478
497
return False
479
498
return True
499
+
500
+ def _get_lease_blob_path (self , partition_id ):
501
+ # Note: In a perfect world, without a prefix provided we'd prepend the
502
+ # consumer group to the partition_id. However this would break
503
+ # backcompat with a historical world in which we just had partition_id
504
+ # within the container, and preclude any way for a user to generate
505
+ # that behavior, so we will fix it in all cases in the Track2 library
506
+ # and simply enable the proper full path here if the optional parameter
507
+ # is present.
508
+ path = partition_id
509
+ if self .consumer_group_directory :
510
+ path = str .format ("{}/{}" , self .consumer_group_directory , partition_id )
511
+ if self .storage_blob_prefix :
512
+ path = "{}{}" .format (self .storage_blob_prefix , path )
513
+ return path
0 commit comments