Skip to content

Commit a268e5a

Browse files
committed
PYCBC-1657: Add support for Transactions ExtGetMulti
Modification ============ * Update C++ core * Update bindings and transactions logic to add support for transactions ExtGetMulti * Update tests to confirm functionality Results ======= All tests pass Change-Id: Ib2770327c81b0a447d158c997f5307e6e0340fb1 Reviewed-on: https://review.couchbase.org/c/couchbase-python-client/+/227589 Tested-by: Build Bot <build@couchbase.com> Reviewed-by: Dimitris Christodoulou <dimitris.christodoulou@couchbase.com>
1 parent c995cb2 commit a268e5a

13 files changed

Lines changed: 1398 additions & 69 deletions

File tree

acouchbase/tests/transactions_t.py

Lines changed: 283 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,28 @@
2626
DocumentNotFoundException,
2727
DocumentUnretrievableException,
2828
FeatureUnavailableException,
29+
InvalidIndexException,
2930
ParsingFailedException,
3031
TransactionExpired,
3132
TransactionFailed,
3233
TransactionOperationFailed)
3334
from couchbase.n1ql import QueryProfile, QueryScanConsistency
3435
from couchbase.options import (TransactionConfig,
36+
TransactionGetMultiOptions,
37+
TransactionGetMultiReplicasFromPreferredServerGroupOptions,
3538
TransactionGetOptions,
3639
TransactionInsertOptions,
3740
TransactionOptions,
3841
TransactionQueryOptions,
3942
TransactionReplaceOptions)
40-
from couchbase.transactions import TransactionKeyspace, TransactionResult
43+
from couchbase.transactions import (TransactionGetMultiMode,
44+
TransactionGetMultiReplicasFromPreferredServerGroupMode,
45+
TransactionGetMultiReplicasFromPreferredServerGroupResult,
46+
TransactionGetMultiReplicasFromPreferredServerGroupSpec,
47+
TransactionGetMultiResult,
48+
TransactionGetMultiSpec,
49+
TransactionKeyspace,
50+
TransactionResult)
4151
from couchbase.transcoder import RawBinaryTranscoder
4252
from tests.environments import CollectionType
4353
from tests.environments.test_environment import AsyncTestEnvironment
@@ -61,6 +71,16 @@ class TransactionTestSuite:
6171
'test_get_inner_exc_doc_not_found',
6272
'test_get_replica_from_preferred_server_group_unretrievable',
6373
'test_get_replica_from_preferred_server_group_propagate_unretrievable_exc',
74+
'test_get_multi_binary',
75+
'test_get_multi_not_exists',
76+
'test_get_multi_invalid_index',
77+
'test_get_multi_options',
78+
'test_get_multi_simple',
79+
'test_get_multi_replicas_from_preferred_server_group_binary',
80+
'test_get_multi_replicas_from_preferred_server_group_not_exists',
81+
'test_get_multi_replicas_from_preferred_server_group_invalid_index',
82+
'test_get_multi_replicas_from_preferred_server_group_options',
83+
'test_get_multi_replicas_from_preferred_server_group_simple',
6484
'test_insert',
6585
'test_insert_lambda_raises_doc_exists',
6686
'test_insert_inner_exc_doc_exists',
@@ -334,6 +354,268 @@ async def txn_logic(ctx):
334354

335355
assert num_attempts == 1
336356

357+
@pytest.mark.asyncio
358+
async def test_get_multi_binary(self, cb_env):
359+
key1 = cb_env.get_new_doc(key_only=True)
360+
tc = RawBinaryTranscoder()
361+
value1 = 'bytes content'.encode('utf-8')
362+
key2, value2 = cb_env.get_new_doc()
363+
364+
await cb_env.collection.insert(key1, value1, transcoder=tc)
365+
await cb_env.collection.insert(key2, value2)
366+
367+
async def txn_logic(ctx):
368+
specs = [
369+
TransactionGetMultiSpec(cb_env.collection, key1, transcoder=tc),
370+
TransactionGetMultiSpec(cb_env.collection, key2),
371+
]
372+
res = await ctx.get_multi(specs)
373+
assert isinstance(res, TransactionGetMultiResult)
374+
assert res.content_as[bytes](0) == value1
375+
assert res.content_as[dict](1) == value2
376+
377+
await cb_env.cluster.transactions.run(txn_logic)
378+
379+
for k in [key1, key2]:
380+
try:
381+
await cb_env.collection.remove(k)
382+
except DocumentNotFoundException:
383+
pass
384+
385+
@pytest.mark.asyncio
386+
async def test_get_multi_not_exists(self, cb_env):
387+
key1, value1 = cb_env.get_new_doc()
388+
key2 = cb_env.get_new_doc(key_only=True)
389+
await cb_env.collection.insert(key1, value1)
390+
391+
async def txn_logic(ctx):
392+
specs = (
393+
TransactionGetMultiSpec(cb_env.collection, key1),
394+
TransactionGetMultiSpec(cb_env.collection, key2)
395+
)
396+
res = await ctx.get_multi(specs)
397+
assert isinstance(res, TransactionGetMultiResult)
398+
assert res.exists(0) is True
399+
assert res.exists(1) is False
400+
401+
with pytest.raises(DocumentNotFoundException):
402+
res.content_as[dict](1)
403+
404+
await cb_env.cluster.transactions.run(txn_logic)
405+
try:
406+
await cb_env.collection.remove(key1)
407+
except DocumentNotFoundException:
408+
pass
409+
410+
@pytest.mark.asyncio
411+
async def test_get_multi_invalid_index(self, cb_env):
412+
key1, value1 = cb_env.get_new_doc()
413+
key2 = cb_env.get_new_doc(key_only=True)
414+
await cb_env.collection.insert(key1, value1)
415+
416+
async def txn_logic(ctx):
417+
specs = [
418+
TransactionGetMultiSpec(cb_env.collection, key1),
419+
TransactionGetMultiSpec(cb_env.collection, key2)
420+
]
421+
res = await ctx.get_multi(specs)
422+
assert isinstance(res, TransactionGetMultiResult)
423+
assert res.exists(0) is True
424+
assert res.exists(1) is False
425+
with pytest.raises(InvalidIndexException):
426+
res.content_as[dict](2)
427+
428+
await cb_env.cluster.transactions.run(txn_logic)
429+
try:
430+
await cb_env.collection.remove(key1)
431+
except DocumentNotFoundException:
432+
pass
433+
434+
@pytest.mark.asyncio
435+
async def test_get_multi_options(self, cb_env):
436+
key1, value1 = cb_env.get_new_doc()
437+
key2, value2 = cb_env.get_new_doc()
438+
await cb_env.collection.insert(key1, value1)
439+
await cb_env.collection.insert(key2, value2)
440+
441+
async def txn_logic(ctx):
442+
specs = (
443+
TransactionGetMultiSpec(cb_env.collection, key1),
444+
TransactionGetMultiSpec(cb_env.collection, key2)
445+
)
446+
opts = TransactionGetMultiOptions(mode=TransactionGetMultiMode.DISABLE_READ_SKEW_DETECTION)
447+
res = await ctx.get_multi(specs, opts)
448+
assert isinstance(res, TransactionGetMultiResult)
449+
assert res.content_as[dict](0) == value1
450+
assert res.content_as[dict](1) == value2
451+
452+
await cb_env.cluster.transactions.run(txn_logic)
453+
454+
for k in [key1, key2]:
455+
try:
456+
await cb_env.collection.remove(k)
457+
except DocumentNotFoundException:
458+
pass
459+
460+
@pytest.mark.asyncio
461+
async def test_get_multi_simple(self, cb_env):
462+
keys_and_docs = []
463+
for _ in range(3):
464+
key, value = cb_env.get_new_doc()
465+
keys_and_docs.append((key, value))
466+
await cb_env.collection.insert(key, value)
467+
468+
async def txn_logic(ctx):
469+
specs = (
470+
TransactionGetMultiSpec(cb_env.collection, keys_and_docs[0][0]),
471+
TransactionGetMultiSpec(cb_env.collection, keys_and_docs[1][0]),
472+
TransactionGetMultiSpec(cb_env.collection, keys_and_docs[2][0])
473+
)
474+
res = await ctx.get_multi(specs)
475+
assert isinstance(res, TransactionGetMultiResult)
476+
for idx in range(len(keys_and_docs)):
477+
assert res.exists(idx) is True
478+
assert res.content_as[dict](idx) == keys_and_docs[idx][1]
479+
480+
await cb_env.cluster.transactions.run(txn_logic)
481+
482+
for kd in keys_and_docs:
483+
try:
484+
await cb_env.collection.remove(kd[0])
485+
except DocumentNotFoundException:
486+
pass
487+
488+
@pytest.mark.asyncio
489+
async def test_get_multi_replicas_from_preferred_server_group_binary(self, cb_env):
490+
key1 = cb_env.get_new_doc(key_only=True)
491+
tc = RawBinaryTranscoder()
492+
value1 = 'bytes content'.encode('utf-8')
493+
key2, value2 = cb_env.get_new_doc()
494+
495+
await cb_env.collection.insert(key1, value1, transcoder=tc)
496+
await cb_env.collection.insert(key2, value2)
497+
498+
async def txn_logic(ctx):
499+
specs = [
500+
TransactionGetMultiReplicasFromPreferredServerGroupSpec(cb_env.collection, key1, transcoder=tc),
501+
TransactionGetMultiReplicasFromPreferredServerGroupSpec(cb_env.collection, key2),
502+
]
503+
res = await ctx.get_multi_replicas_from_preferred_server_group(specs)
504+
assert isinstance(res, TransactionGetMultiReplicasFromPreferredServerGroupResult)
505+
assert res.content_as[bytes](0) == value1
506+
assert res.content_as[dict](1) == value2
507+
508+
await cb_env.cluster.transactions.run(txn_logic)
509+
510+
for k in [key1, key2]:
511+
try:
512+
await cb_env.collection.remove(k)
513+
except DocumentNotFoundException:
514+
pass
515+
516+
@pytest.mark.asyncio
517+
async def test_get_multi_replicas_from_preferred_server_group_not_exists(self, cb_env):
518+
key1, value1 = cb_env.get_new_doc()
519+
key2 = cb_env.get_new_doc(key_only=True)
520+
await cb_env.collection.insert(key1, value1)
521+
522+
async def txn_logic(ctx):
523+
specs = (
524+
TransactionGetMultiReplicasFromPreferredServerGroupSpec(cb_env.collection, key1),
525+
TransactionGetMultiReplicasFromPreferredServerGroupSpec(cb_env.collection, key2)
526+
)
527+
res = await ctx.get_multi_replicas_from_preferred_server_group(specs)
528+
assert isinstance(res, TransactionGetMultiReplicasFromPreferredServerGroupResult)
529+
assert res.exists(0) is True
530+
assert res.exists(1) is False
531+
532+
with pytest.raises(DocumentNotFoundException):
533+
res.content_as[dict](1)
534+
535+
await cb_env.cluster.transactions.run(txn_logic)
536+
try:
537+
await cb_env.collection.remove(key1)
538+
except DocumentNotFoundException:
539+
pass
540+
541+
@pytest.mark.asyncio
542+
async def test_get_multi_replicas_from_preferred_server_group_invalid_index(self, cb_env):
543+
key1, value1 = cb_env.get_new_doc()
544+
key2 = cb_env.get_new_doc(key_only=True)
545+
await cb_env.collection.insert(key1, value1)
546+
547+
async def txn_logic(ctx):
548+
specs = [
549+
TransactionGetMultiReplicasFromPreferredServerGroupSpec(cb_env.collection, key1),
550+
TransactionGetMultiReplicasFromPreferredServerGroupSpec(cb_env.collection, key2)
551+
]
552+
res = await ctx.get_multi_replicas_from_preferred_server_group(specs)
553+
assert isinstance(res, TransactionGetMultiReplicasFromPreferredServerGroupResult)
554+
assert res.exists(0) is True
555+
assert res.exists(1) is False
556+
with pytest.raises(InvalidIndexException):
557+
res.content_as[dict](2)
558+
559+
cb_env.cluster.transactions.run(txn_logic)
560+
try:
561+
await cb_env.collection.remove(key1)
562+
except DocumentNotFoundException:
563+
pass
564+
565+
@pytest.mark.asyncio
566+
async def test_get_multi_replicas_from_preferred_server_group_options(self, cb_env):
567+
key1, value1 = cb_env.get_new_doc()
568+
key2, value2 = cb_env.get_new_doc()
569+
await cb_env.collection.insert(key1, value1)
570+
await cb_env.collection.insert(key2, value2)
571+
572+
async def txn_logic(ctx):
573+
specs = (
574+
TransactionGetMultiReplicasFromPreferredServerGroupSpec(cb_env.collection, key1),
575+
TransactionGetMultiReplicasFromPreferredServerGroupSpec(cb_env.collection, key2)
576+
)
577+
opts = TransactionGetMultiReplicasFromPreferredServerGroupOptions(mode=TransactionGetMultiReplicasFromPreferredServerGroupMode.DISABLE_READ_SKEW_DETECTION) # noqa: E501
578+
res = await ctx.get_multi_replicas_from_preferred_server_group(specs, opts)
579+
assert isinstance(res, TransactionGetMultiReplicasFromPreferredServerGroupResult)
580+
assert res.content_as[dict](0) == value1
581+
assert res.content_as[dict](1) == value2
582+
583+
await cb_env.cluster.transactions.run(txn_logic)
584+
585+
for k in [key1, key2]:
586+
try:
587+
await cb_env.collection.remove(k)
588+
except DocumentNotFoundException:
589+
pass
590+
591+
@pytest.mark.asyncio
592+
async def test_get_multi_replicas_from_preferred_server_group_simple(self, cb_env):
593+
keys_and_docs = []
594+
for _ in range(3):
595+
key, value = cb_env.get_new_doc()
596+
keys_and_docs.append((key, value))
597+
await cb_env.collection.insert(key, value)
598+
599+
async def txn_logic(ctx):
600+
specs = (
601+
TransactionGetMultiReplicasFromPreferredServerGroupSpec(cb_env.collection, keys_and_docs[0][0]),
602+
TransactionGetMultiReplicasFromPreferredServerGroupSpec(cb_env.collection, keys_and_docs[1][0]),
603+
TransactionGetMultiReplicasFromPreferredServerGroupSpec(cb_env.collection, keys_and_docs[2][0])
604+
)
605+
res = await ctx.get_multi_replicas_from_preferred_server_group(specs)
606+
assert isinstance(res, TransactionGetMultiReplicasFromPreferredServerGroupResult)
607+
for idx in range(len(keys_and_docs)):
608+
assert res.exists(idx) is True
609+
assert res.content_as[dict](idx) == keys_and_docs[idx][1]
610+
611+
await cb_env.cluster.transactions.run(txn_logic)
612+
613+
for kd in keys_and_docs:
614+
try:
615+
await cb_env.collection.remove(kd[0])
616+
except DocumentNotFoundException:
617+
pass
618+
337619
@pytest.mark.asyncio
338620
async def test_insert(self, cb_env):
339621
key, value = cb_env.get_new_doc()

0 commit comments

Comments
 (0)