Skip to content

Commit f9b1bc8

Browse files
authored
[Cosmos] Add optimistic concurrency samples (etag/ifmatch) (#33812)
1 parent d678ef6 commit f9b1bc8

File tree

2 files changed

+86
-30
lines changed

2 files changed

+86
-30
lines changed

sdk/cosmos/azure-cosmos/samples/document_management.py

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,35 @@ def replace_item(container, doc_id):
119119
print('Replaced Item\'s Id is {0}, new subtotal={1}'.format(response['id'], response['subtotal']))
120120

121121

122+
def replace_item_using_etags(container, doc_id):
123+
print('\n1.7 Replace an Item using Etags and IfMatch\n')
124+
# The use of etags and if-match/if-none-match options allows users to run conditional replace operations
125+
# based on the etag value passed. When using if-match, the request will only succeed if the item's latest etag
126+
# matches the passed in value. For more on optimistic concurrency control, see the link below:
127+
# https://learn.microsoft.com/azure/cosmos-db/nosql/database-transactions-optimistic-concurrency
128+
129+
read_item = container.read_item(item=doc_id, partition_key=doc_id)
130+
item_etag = read_item["_etag"]
131+
read_item['subtotal'] = read_item['subtotal'] + 1
132+
response = container.replace_item(
133+
read_item,
134+
read_item,
135+
if_match=item_etag)
136+
137+
print('Replaced Item\'s Id is {0}, new subtotal={1}'.format(response['id'], response['subtotal']))
138+
139+
read_item = container.read_item(item=doc_id, partition_key=doc_id)
140+
read_item['subtotal'] = read_item['subtotal'] + 1
141+
response = container.replace_item(
142+
read_item,
143+
read_item,
144+
if_none_match="some-etag")
145+
146+
print('Replaced Item\'s Id is {0}, new subtotal={1}'.format(response['id'], response['subtotal']))
147+
148+
122149
def upsert_item(container, doc_id):
123-
print('\n1.7 Upserting an item\n')
150+
print('\n1.8 Upserting an item\n')
124151

125152
read_item = container.read_item(item=doc_id, partition_key=doc_id)
126153
read_item['subtotal'] = read_item['subtotal'] + 1
@@ -130,7 +157,7 @@ def upsert_item(container, doc_id):
130157

131158

132159
def conditional_patch_item(container, doc_id):
133-
print('\n1.8 Patching Item by Id based on filter\n')
160+
print('\n1.9 Patching Item by Id based on filter\n')
134161
operations = [
135162
{"op": "add", "path": "/favorite_color", "value": "red"},
136163
{"op": "remove", "path": "/ttl"},
@@ -151,7 +178,7 @@ def conditional_patch_item(container, doc_id):
151178

152179

153180
def patch_item(container, doc_id):
154-
print('\n1.9 Patching Item by Id\n')
181+
print('\n1.10 Patching Item by Id\n')
155182

156183
operations = [
157184
{"op": "add", "path": "/favorite_color", "value": "red"},
@@ -172,7 +199,7 @@ def patch_item(container, doc_id):
172199

173200

174201
def execute_item_batch(database):
175-
print('\n1.10 Executing Batch Item operations\n')
202+
print('\n1.11 Executing Batch Item operations\n')
176203
container = database.create_container_if_not_exists(id="batch_container",
177204
partition_key=PartitionKey(path='/account_number'))
178205
# We create three items to use for the sample.
@@ -227,15 +254,15 @@ def execute_item_batch(database):
227254

228255

229256
def delete_item(container, doc_id):
230-
print('\n1.11 Deleting Item by Id\n')
257+
print('\n1.12 Deleting Item by Id\n')
231258

232259
response = container.delete_item(item=doc_id, partition_key=doc_id)
233260

234261
print('Deleted item\'s Id is {0}'.format(doc_id))
235262

236263

237264
def delete_all_items_by_partition_key(db, partitionkey):
238-
print('\n1.12 Deleting all Items by Partition Key\n')
265+
print('\n1.13 Deleting all Items by Partition Key\n')
239266

240267
# A container with a partition key that is different from id is needed
241268
container = db.create_container_if_not_exists(id="Partition Key Delete Container",
@@ -278,7 +305,7 @@ def delete_all_items_by_partition_key(db, partitionkey):
278305

279306
def create_mh_items(container):
280307
print('Creating Items')
281-
print('\n1.13 Create Item with Multi Hash Partition Key\n')
308+
print('\n2.1 Create Item with Multi Hash Partition Key\n')
282309

283310
# Create a SalesOrder object. This object has nested properties and various types including numbers, DateTimes and strings.
284311
# This can be saved as JSON as is without converting into rows/columns.
@@ -292,7 +319,7 @@ def create_mh_items(container):
292319

293320

294321
def read_mh_item(container, doc_id, pk):
295-
print('\n1.14 Reading Item by Multi Hash Partition Key\n')
322+
print('\n2.2 Reading Item by Multi Hash Partition Key\n')
296323

297324
# Note that Reads require a partition key to be specified.
298325
response = container.read_item(item=doc_id, partition_key=pk)
@@ -303,7 +330,7 @@ def read_mh_item(container, doc_id, pk):
303330

304331

305332
def query_mh_items(container, pk):
306-
print('\n1.15 Querying for an Item by Multi Hash Partition Key\n')
333+
print('\n2.3 Querying for an Item by Multi Hash Partition Key\n')
307334

308335
# enable_cross_partition_query should be set to True as the container is partitioned
309336
items = list(container.query_items(
@@ -320,7 +347,7 @@ def query_mh_items(container, pk):
320347

321348

322349
def replace_mh_item(container, doc_id, pk):
323-
print('\n1.16 Replace an Item with Multi Hash Partition Key\n')
350+
print('\n2.4 Replace an Item with Multi Hash Partition Key\n')
324351

325352
read_item = container.read_item(item=doc_id, partition_key=pk)
326353
read_item['subtotal'] = read_item['subtotal'] + 1
@@ -331,7 +358,7 @@ def replace_mh_item(container, doc_id, pk):
331358

332359

333360
def upsert_mh_item(container, doc_id, pk):
334-
print('\n1.17 Upserting an item with Multi Hash Partition Key\n')
361+
print('\n2.5 Upserting an item with Multi Hash Partition Key\n')
335362

336363
read_item = container.read_item(item=doc_id, partition_key=pk)
337364
read_item['subtotal'] = read_item['subtotal'] + 1
@@ -342,7 +369,7 @@ def upsert_mh_item(container, doc_id, pk):
342369

343370

344371
def patch_mh_item(container, doc_id, pk):
345-
print('\n1.18 Patching Item by Multi Hash Partition Key\n')
372+
print('\n2.6 Patching Item by Multi Hash Partition Key\n')
346373

347374
operations = [
348375
{"op": "add", "path": "/favorite_color", "value": "red"},
@@ -363,14 +390,14 @@ def patch_mh_item(container, doc_id, pk):
363390

364391

365392
def delete_mh_item(container, doc_id, pk):
366-
print('\n1.19 Deleting Item by Multi Hash Partition Key\n')
393+
print('\n2.7 Deleting Item by Multi Hash Partition Key\n')
367394

368395
response = container.delete_item(item=doc_id, partition_key=pk)
369396
print('Deleted item\'s Account Number is {0} Purchase Order Number is {1}'.format(pk[0], pk[1]))
370397

371398

372399
def delete_all_items_by_partition_key_mh(db, partitionkey):
373-
print('\n1.20 Deleting all Items by Partition Key Multi Hash\n')
400+
print('\n2.8 Deleting all Items by Partition Key Multi Hash\n')
374401

375402
# A container with a partition key that is different from id is needed
376403
container = db.create_container_if_not_exists(id="Partition Key Delete Container Multi Hash",
@@ -413,7 +440,7 @@ def delete_all_items_by_partition_key_mh(db, partitionkey):
413440

414441

415442
def query_items_with_continuation_token_size_limit(container, doc_id):
416-
print('\n1.21 Query Items With Continuation Token Size Limit.\n')
443+
print('\n2.9 Query Items With Continuation Token Size Limit.\n')
417444

418445
size_limit_in_kb = 8
419446
sales_order = get_sales_order(doc_id)
@@ -495,6 +522,7 @@ def run_sample():
495522
query_items(container, 'SalesOrder1')
496523
query_items_with_continuation_token(container)
497524
replace_item(container, 'SalesOrder1')
525+
replace_item_using_etags(container, 'SalesOrder1')
498526
upsert_item(container, 'SalesOrder1')
499527
conditional_patch_item(container, 'SalesOrder1')
500528
patch_item(container, 'SalesOrder1')

sdk/cosmos/azure-cosmos/samples/document_management_async.py

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,35 @@ async def replace_item(container, doc_id):
136136
print('Replaced Item\'s Id is {0}, new subtotal={1}'.format(response['id'], response['subtotal']))
137137

138138

139+
async def replace_item_using_etags(container, doc_id):
140+
print('\n1.7 Replace an Item using Etags and IfMatch\n')
141+
# The use of etags and if-match/if-none-match options allows users to run conditional replace operations
142+
# based on the etag value passed. When using if-match, the request will only succeed if the item's latest etag
143+
# matches the passed in value. For more on optimistic concurrency control, see the link below:
144+
# https://learn.microsoft.com/azure/cosmos-db/nosql/database-transactions-optimistic-concurrency
145+
146+
read_item = await container.read_item(item=doc_id, partition_key=doc_id)
147+
item_etag = read_item["_etag"]
148+
read_item['subtotal'] = read_item['subtotal'] + 1
149+
response = await container.replace_item(
150+
read_item,
151+
read_item,
152+
if_match=item_etag)
153+
154+
print('Replaced Item\'s Id is {0}, new subtotal={1}'.format(response['id'], response['subtotal']))
155+
156+
read_item = await container.read_item(item=doc_id, partition_key=doc_id)
157+
read_item['subtotal'] = read_item['subtotal'] + 1
158+
response = await container.replace_item(
159+
read_item,
160+
read_item,
161+
if_none_match="some-etag")
162+
163+
print('Replaced Item\'s Id is {0}, new subtotal={1}'.format(response['id'], response['subtotal']))
164+
165+
139166
async def upsert_item(container, doc_id):
140-
print('\n1.7 Upserting an item\n')
167+
print('\n1.8 Upserting an item\n')
141168

142169
read_item = await container.read_item(item=doc_id, partition_key=doc_id)
143170
read_item['subtotal'] = read_item['subtotal'] + 1
@@ -147,7 +174,7 @@ async def upsert_item(container, doc_id):
147174

148175

149176
async def conditional_patch_item(container, doc_id):
150-
print('\n1.8 Patching Item by Id based on filter\n')
177+
print('\n1.9 Patching Item by Id based on filter\n')
151178
operations = [
152179
{"op": "add", "path": "/favorite_color", "value": "red"},
153180
{"op": "remove", "path": "/ttl"},
@@ -168,7 +195,7 @@ async def conditional_patch_item(container, doc_id):
168195

169196

170197
async def patch_item(container, doc_id):
171-
print('\n1.9 Patching Item by Id\n')
198+
print('\n1.10 Patching Item by Id\n')
172199

173200
operations = [
174201
{"op": "add", "path": "/favorite_color", "value": "red"},
@@ -191,7 +218,7 @@ async def patch_item(container, doc_id):
191218

192219

193220
async def execute_item_batch(database):
194-
print('\n1.10 Executing Batch Item operations\n')
221+
print('\n1.11 Executing Batch Item operations\n')
195222
container = await database.create_container_if_not_exists(id="batch_container",
196223
partition_key=PartitionKey(path='/account_number'))
197224
# We create three items to use for the sample.
@@ -246,15 +273,15 @@ async def execute_item_batch(database):
246273

247274

248275
async def delete_item(container, doc_id):
249-
print('\n1.11 Deleting Item by Id\n')
276+
print('\n1.12 Deleting Item by Id\n')
250277

251278
await container.delete_item(item=doc_id, partition_key=doc_id)
252279

253280
print('Deleted item\'s Id is {0}'.format(doc_id))
254281

255282

256283
async def delete_all_items_by_partition_key(db, partitionkey):
257-
print('\n1.12 Deleting all Items by Partition Key\n')
284+
print('\n1.13 Deleting all Items by Partition Key\n')
258285

259286
# A container with a partition key that is different from id is needed
260287
container = await db.create_container_if_not_exists(id="Partition Key Delete Container",
@@ -297,7 +324,7 @@ async def delete_all_items_by_partition_key(db, partitionkey):
297324

298325
async def create_mh_items(container):
299326
print('Creating Items')
300-
print('\n1.13 Create Item with Multi Hash Partition Key\n')
327+
print('\n2.1 Create Item with Multi Hash Partition Key\n')
301328

302329
# Create a SalesOrder object. This object has nested properties and various types including numbers, DateTimes and strings.
303330
# This can be saved as JSON as is without converting into rows/columns.
@@ -311,7 +338,7 @@ async def create_mh_items(container):
311338

312339

313340
async def read_mh_item(container, doc_id, pk):
314-
print('\n1.14 Reading Item by Multi Hash Partition Key\n')
341+
print('\n2.2 Reading Item by Multi Hash Partition Key\n')
315342

316343
# Note that Reads require a partition key to be specified.
317344
response = await container.read_item(item=doc_id, partition_key=pk)
@@ -322,7 +349,7 @@ async def read_mh_item(container, doc_id, pk):
322349

323350

324351
async def query_mh_items(container, pk):
325-
print('\n1.15 Querying for an Item by Multi Hash Partition Key\n')
352+
print('\n2.3 Querying for an Item by Multi Hash Partition Key\n')
326353

327354
query_items_response = container.query_items(
328355
query="SELECT * FROM r WHERE r.account_number=@account_number and r.purchase_order_number=@purchase_order_number",
@@ -339,7 +366,7 @@ async def query_mh_items(container, pk):
339366

340367

341368
async def replace_mh_item(container, doc_id, pk):
342-
print('\n1.16 Replace an Item with Multi Hash Partition Key\n')
369+
print('\n2.4 Replace an Item with Multi Hash Partition Key\n')
343370

344371
read_item = await container.read_item(item=doc_id, partition_key=pk)
345372
read_item['subtotal'] = read_item['subtotal'] + 1
@@ -350,7 +377,7 @@ async def replace_mh_item(container, doc_id, pk):
350377

351378

352379
async def upsert_mh_item(container, doc_id, pk):
353-
print('\n1.17 Upserting an item with Multi Hash Partition Key\n')
380+
print('\n2.5 Upserting an item with Multi Hash Partition Key\n')
354381

355382
read_item = await container.read_item(item=doc_id, partition_key=pk)
356383
read_item['subtotal'] = read_item['subtotal'] + 1
@@ -361,7 +388,7 @@ async def upsert_mh_item(container, doc_id, pk):
361388

362389

363390
async def patch_mh_item(container, doc_id, pk):
364-
print('\n1.18 Patching Item by Multi Hash Partition Key\n')
391+
print('\n2.6 Patching Item by Multi Hash Partition Key\n')
365392

366393
operations = [
367394
{"op": "add", "path": "/favorite_color", "value": "red"},
@@ -382,14 +409,14 @@ async def patch_mh_item(container, doc_id, pk):
382409

383410

384411
async def delete_mh_item(container, doc_id, pk):
385-
print('\n1.19 Deleting Item by Multi Hash Partition Key\n')
412+
print('\n2.7 Deleting Item by Multi Hash Partition Key\n')
386413

387414
response = await container.delete_item(item=doc_id, partition_key=pk)
388415
print('Deleted item\'s Account Number is {0} Purchase Order Number is {1}'.format(pk[0], pk[1]))
389416

390417

391418
async def delete_all_items_by_partition_key_mh(db, partitionkey):
392-
print('\n1.20 Deleting all Items by Partition Key Multi Hash\n')
419+
print('\n2.8 Deleting all Items by Partition Key Multi Hash\n')
393420

394421
# A container with a partition key that is different from id is needed
395422
container = await db.create_container_if_not_exists(id="Partition Key Delete Container Multi Hash",
@@ -432,7 +459,7 @@ async def delete_all_items_by_partition_key_mh(db, partitionkey):
432459

433460

434461
async def query_items_with_continuation_token_size_limit(container, doc_id):
435-
print('\n1.21 Query Items With Continuation Token Size Limit.\n')
462+
print('\n2.9 Query Items With Continuation Token Size Limit.\n')
436463

437464
size_limit_in_kb = 8
438465
sales_order = get_sales_order(doc_id)
@@ -516,6 +543,7 @@ async def run_sample():
516543
await query_items(container, 'SalesOrder1')
517544
await query_items_with_continuation_token(container)
518545
await replace_item(container, 'SalesOrder1')
546+
await replace_item_using_etags(container, 'SalesOrder1')
519547
await upsert_item(container, 'SalesOrder1')
520548
await conditional_patch_item(container, 'SalesOrder1')
521549
await patch_item(container, 'SalesOrder1')

0 commit comments

Comments
 (0)