0
# ShareLeaseClient - Lease Management
1
2
The ShareLeaseClient provides lease management operations for files and shares, enabling exclusive access control through lease acquisition, renewal, release, and breaking operations.
3
4
## Import and Initialization
5
6
```python { .api }
7
from azure.storage.fileshare import ShareLeaseClient, ShareClient, ShareFileClient
8
from typing import Union, Any, Optional
9
```
10
11
## Constructor
12
13
```python { .api }
14
class ShareLeaseClient:
15
def __init__(
16
self,
17
client: Union[ShareClient, ShareFileClient],
18
lease_id: Optional[str] = None
19
) -> None:
20
"""
21
Create a ShareLeaseClient for managing leases.
22
23
Parameters:
24
client: ShareClient or ShareFileClient to manage leases for
25
lease_id: Optional existing lease ID (UUID format)
26
"""
27
```
28
29
## Lease Operations
30
31
```python { .api }
32
def acquire(
33
self,
34
**kwargs: Any
35
) -> None:
36
"""
37
Requests a new lease for the file or share.
38
39
Parameters:
40
lease_duration: Duration of the lease in seconds (-1 for infinite, default: -1)
41
timeout: Request timeout in seconds
42
43
Note:
44
- For files: lease_duration can be 15-60 seconds or -1 for infinite
45
- For shares: lease_duration is always infinite (-1)
46
- After acquisition, the lease_id property will contain the lease ID
47
"""
48
49
def renew(
50
self,
51
**kwargs: Any
52
) -> None:
53
"""
54
Renews the lease for the file or share.
55
56
Parameters:
57
timeout: Request timeout in seconds
58
59
Note:
60
- Lease must be in 'leased' state to be renewed
61
- Extends the lease for the same duration as originally acquired
62
"""
63
64
def release(
65
self,
66
**kwargs: Any
67
) -> None:
68
"""
69
Releases the lease for the file or share.
70
71
Parameters:
72
timeout: Request timeout in seconds
73
74
Note:
75
- Makes the file/share available for other operations immediately
76
- Lease cannot be renewed after release
77
"""
78
79
def change(
80
self,
81
proposed_lease_id: str,
82
**kwargs: Any
83
) -> None:
84
"""
85
Changes the lease ID of an active lease.
86
87
Parameters:
88
proposed_lease_id: New lease ID (UUID format)
89
timeout: Request timeout in seconds
90
91
Note:
92
- Lease must be in 'leased' state to change ID
93
- Updates the lease_id property to the new ID
94
"""
95
96
def break_lease(
97
self,
98
**kwargs: Any
99
) -> int:
100
"""
101
Breaks the lease, if the file or share has an active lease.
102
103
Parameters:
104
lease_break_period: Time to wait before lease is broken (0-60 seconds)
105
timeout: Request timeout in seconds
106
107
Returns:
108
int: Time remaining until lease is broken (seconds)
109
110
Note:
111
- If lease_break_period is not specified, lease breaks immediately
112
- Returns 0 if lease is broken immediately
113
"""
114
```
115
116
## Properties
117
118
```python { .api }
119
@property
120
def id(self) -> str:
121
"""
122
The lease ID for the lease.
123
124
Returns:
125
str: UUID string representing the lease ID
126
"""
127
128
@property
129
def etag(self) -> Optional[str]:
130
"""
131
The ETag of the lease.
132
133
Returns:
134
Optional[str]: ETag from the last lease operation
135
"""
136
137
@property
138
def last_modified(self) -> Optional[datetime]:
139
"""
140
The last modified timestamp of the lease.
141
142
Returns:
143
Optional[datetime]: Last modified time from the last lease operation
144
"""
145
```
146
147
## Context Manager Support
148
149
```python { .api }
150
def __enter__(self) -> ShareLeaseClient:
151
"""
152
Enter context manager and acquire lease.
153
154
Returns:
155
ShareLeaseClient: Self for use in with statement
156
"""
157
158
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
159
"""
160
Exit context manager and release lease.
161
162
Parameters:
163
exc_type: Exception type if exception occurred
164
exc_val: Exception value if exception occurred
165
exc_tb: Exception traceback if exception occurred
166
"""
167
```
168
169
## Usage Examples
170
171
### Basic Lease Operations
172
173
```python { .api }
174
from azure.storage.fileshare import ShareFileClient, ShareLeaseClient
175
from azure.core.credentials import AzureNamedKeyCredential
176
import uuid
177
178
# Initialize file client
179
credential = AzureNamedKeyCredential("myaccount", "mykey")
180
file_client = ShareFileClient(
181
account_url="https://myaccount.file.core.windows.net",
182
share_name="documents",
183
file_path="important_file.txt",
184
credential=credential
185
)
186
187
# Acquire lease with specific ID
188
lease_id = str(uuid.uuid4())
189
lease_client = ShareLeaseClient(file_client, lease_id=lease_id)
190
lease_client.acquire(lease_duration=60) # 60-second lease
191
print(f"Lease acquired: {lease_client.id}")
192
193
# Perform operations with lease
194
try:
195
# File operations now require lease_id
196
file_client.set_file_metadata(
197
{"locked": "true", "locked_by": "process_123"},
198
lease_id=lease_client.id
199
)
200
201
# Upload new content with lease protection
202
file_client.upload_file(
203
data=b"Updated content under lease",
204
overwrite=True,
205
lease_id=lease_client.id
206
)
207
208
finally:
209
# Always release the lease
210
lease_client.release()
211
print("Lease released")
212
```
213
214
### Lease Management with Context Manager
215
216
```python { .api }
217
# Automatic lease management using context manager
218
with ShareLeaseClient(file_client) as lease:
219
print(f"Lease automatically acquired: {lease.id}")
220
221
# Perform protected operations
222
file_client.upload_file(
223
data=b"Content updated with automatic lease management",
224
overwrite=True,
225
lease_id=lease.id
226
)
227
228
properties = file_client.get_file_properties()
229
print(f"File size: {properties.size} bytes")
230
231
# Lease is automatically released when exiting context
232
print("Lease automatically released")
233
```
234
235
### Share-Level Lease Management
236
237
```python { .api }
238
from azure.storage.fileshare import ShareClient
239
240
# Initialize share client
241
share_client = ShareClient(
242
account_url="https://myaccount.file.core.windows.net",
243
share_name="protected-share",
244
credential=credential
245
)
246
247
# Acquire infinite lease on share
248
share_lease = ShareLeaseClient(share_client)
249
share_lease.acquire() # Share leases are always infinite
250
print(f"Share lease acquired: {share_lease.id}")
251
252
try:
253
# Share operations require lease_id
254
share_client.set_share_metadata(
255
{"maintenance_mode": "true", "locked_by": "admin"},
256
lease_id=share_lease.id
257
)
258
259
# Create directory with share lease
260
directory_client = share_client.create_directory(
261
"maintenance",
262
lease_id=share_lease.id
263
)
264
265
finally:
266
share_lease.release()
267
print("Share lease released")
268
```
269
270
### Lease Renewal and Extension
271
272
```python { .api }
273
import time
274
275
# Acquire short-term lease
276
lease_client = ShareLeaseClient(file_client)
277
lease_client.acquire(lease_duration=30) # 30-second lease
278
print(f"Initial lease acquired: {lease_client.id}")
279
280
# Simulate long-running operation with lease renewal
281
for i in range(5):
282
# Perform some work
283
print(f"Processing step {i+1}...")
284
time.sleep(10)
285
286
# Renew lease before it expires
287
try:
288
lease_client.renew()
289
print("Lease renewed successfully")
290
except Exception as e:
291
print(f"Failed to renew lease: {e}")
292
break
293
294
lease_client.release()
295
```
296
297
### Lease ID Management and Change
298
299
```python { .api }
300
import uuid
301
302
# Start with specific lease ID
303
original_lease_id = str(uuid.uuid4())
304
lease_client = ShareLeaseClient(file_client, lease_id=original_lease_id)
305
lease_client.acquire(lease_duration=-1) # Infinite lease
306
print(f"Original lease ID: {lease_client.id}")
307
308
# Change lease ID (useful for transferring lease ownership)
309
new_lease_id = str(uuid.uuid4())
310
lease_client.change(proposed_lease_id=new_lease_id)
311
print(f"Changed lease ID to: {lease_client.id}")
312
313
# Verify the change worked
314
assert lease_client.id == new_lease_id
315
print("Lease ID change confirmed")
316
317
# Operations now use new lease ID
318
file_client.set_file_metadata(
319
{"lease_changed": "true"},
320
lease_id=lease_client.id
321
)
322
323
lease_client.release()
324
```
325
326
### Lease Breaking and Recovery
327
328
```python { .api }
329
# Scenario: Another process needs to break an existing lease
330
lease_client = ShareLeaseClient(file_client)
331
lease_client.acquire(lease_duration=-1) # Infinite lease
332
print(f"Lease acquired: {lease_client.id}")
333
334
# Simulate another process that needs to break the lease
335
breaker_lease = ShareLeaseClient(file_client)
336
337
# Break the lease with 30-second break period
338
remaining_time = breaker_lease.break_lease(lease_break_period=30)
339
print(f"Lease break initiated, {remaining_time} seconds remaining")
340
341
# Original lease operations will now fail
342
try:
343
file_client.upload_file(
344
data=b"This will fail",
345
lease_id=lease_client.id
346
)
347
except Exception as e:
348
print(f"Operation failed as expected: {e}")
349
350
# Wait for lease to be broken or break immediately
351
if remaining_time > 0:
352
print("Waiting for lease to break naturally...")
353
time.sleep(remaining_time + 1)
354
else:
355
print("Lease broken immediately")
356
357
# Now operations work without lease
358
file_client.upload_file(data=b"This works after lease break", overwrite=True)
359
print("Operation successful after lease break")
360
```
361
362
### Error Handling and Lease States
363
364
```python { .api }
365
from azure.core.exceptions import HttpResponseError
366
367
def safe_lease_operations(file_client):
368
"""Demonstrate safe lease handling with error recovery."""
369
lease_client = ShareLeaseClient(file_client)
370
371
try:
372
# Attempt to acquire lease
373
lease_client.acquire(lease_duration=60)
374
print(f"Lease acquired successfully: {lease_client.id}")
375
376
# Perform operations
377
return perform_lease_operations(file_client, lease_client)
378
379
except HttpResponseError as e:
380
if e.error_code == "LeaseAlreadyPresent":
381
print("File is already leased by another client")
382
# Try to break existing lease
383
existing_lease = ShareLeaseClient(file_client)
384
existing_lease.break_lease(lease_break_period=0) # Break immediately
385
print("Broke existing lease, retrying...")
386
387
# Retry acquisition
388
lease_client.acquire(lease_duration=60)
389
return perform_lease_operations(file_client, lease_client)
390
else:
391
print(f"Lease operation failed: {e.message}")
392
raise
393
394
except Exception as e:
395
print(f"Unexpected error: {e}")
396
raise
397
398
finally:
399
# Ensure lease is released
400
try:
401
lease_client.release()
402
print("Lease released in cleanup")
403
except:
404
pass # Ignore release errors in cleanup
405
406
def perform_lease_operations(file_client, lease_client):
407
"""Perform file operations under lease protection."""
408
try:
409
# Update file metadata
410
file_client.set_file_metadata(
411
{"processing": "true", "start_time": time.time()},
412
lease_id=lease_client.id
413
)
414
415
# Simulate processing
416
print("Performing protected operations...")
417
time.sleep(2)
418
419
# Update content
420
file_client.upload_file(
421
data=b"Processed content",
422
overwrite=True,
423
lease_id=lease_client.id
424
)
425
426
# Final metadata update
427
file_client.set_file_metadata(
428
{"processing": "complete", "end_time": time.time()},
429
lease_id=lease_client.id
430
)
431
432
return True
433
434
except HttpResponseError as e:
435
if e.error_code in ["LeaseIdMissing", "LeaseIdMismatch", "LeaseNotPresent"]:
436
print("Lease was lost during operation")
437
return False
438
raise
439
440
# Use the safe lease operations
441
success = safe_lease_operations(file_client)
442
print(f"Operation completed successfully: {success}")
443
```
444
445
### Concurrent Access Control
446
447
```python { .api }
448
import threading
449
import time
450
451
def worker_with_lease(worker_id, file_client, work_duration=5):
452
"""Worker function that tries to acquire lease and perform work."""
453
print(f"Worker {worker_id}: Starting")
454
455
lease_client = ShareLeaseClient(file_client)
456
457
try:
458
# Try to acquire lease
459
lease_client.acquire(lease_duration=work_duration + 5) # Extra buffer
460
print(f"Worker {worker_id}: Lease acquired {lease_client.id}")
461
462
# Perform exclusive work
463
for i in range(work_duration):
464
print(f"Worker {worker_id}: Working... step {i+1}")
465
466
# Update file to show work progress
467
file_client.set_file_metadata(
468
{
469
"worker": str(worker_id),
470
"step": str(i+1),
471
"timestamp": str(time.time())
472
},
473
lease_id=lease_client.id
474
)
475
476
time.sleep(1)
477
478
print(f"Worker {worker_id}: Work completed")
479
480
except HttpResponseError as e:
481
if e.error_code == "LeaseAlreadyPresent":
482
print(f"Worker {worker_id}: File is locked by another worker")
483
else:
484
print(f"Worker {worker_id}: Error - {e.message}")
485
486
except Exception as e:
487
print(f"Worker {worker_id}: Unexpected error - {e}")
488
489
finally:
490
try:
491
lease_client.release()
492
print(f"Worker {worker_id}: Lease released")
493
except:
494
pass
495
496
# Start multiple workers (only one will get the lease)
497
threads = []
498
for i in range(3):
499
thread = threading.Thread(target=worker_with_lease, args=(i, file_client))
500
threads.append(thread)
501
thread.start()
502
503
# Wait for all workers to complete
504
for thread in threads:
505
thread.join()
506
507
print("All workers finished")
508
```
509
510
### Lease Monitoring and Diagnostics
511
512
```python { .api }
513
def monitor_lease_status(file_client, lease_client):
514
"""Monitor and report lease status."""
515
properties = file_client.get_file_properties()
516
lease_props = properties.lease
517
518
print(f"Lease Status Report:")
519
print(f" Status: {lease_props.status}") # locked/unlocked
520
print(f" State: {lease_props.state}") # available/leased/expired/breaking/broken
521
print(f" Duration: {lease_props.duration}") # infinite/fixed
522
523
if lease_props.state == "leased":
524
print(f" Current lease ID: {lease_client.id}")
525
print(f" Lease ETag: {lease_client.etag}")
526
print(f" Last modified: {lease_client.last_modified}")
527
528
return lease_props.state
529
530
# Demonstrate lease monitoring
531
lease_client = ShareLeaseClient(file_client)
532
533
# Before lease
534
print("Before acquiring lease:")
535
monitor_lease_status(file_client, lease_client)
536
537
# Acquire lease
538
lease_client.acquire(lease_duration=60)
539
print("\nAfter acquiring lease:")
540
state = monitor_lease_status(file_client, lease_client)
541
542
# Renew lease
543
if state == "leased":
544
lease_client.renew()
545
print("\nAfter renewing lease:")
546
monitor_lease_status(file_client, lease_client)
547
548
# Release lease
549
lease_client.release()
550
print("\nAfter releasing lease:")
551
monitor_lease_status(file_client, lease_client)
552
```
553
554
The ShareLeaseClient provides robust lease management capabilities for controlling exclusive access to files and shares, ensuring data consistency and preventing concurrent modification conflicts in multi-client scenarios.