Skip to content

Commit a0569f1

Browse files
dokaiaholmberg
authored andcommitted
Expose conflicting data in case of LWTException
Adds a custom attribute, `existing`, to the LWTException instance which allows the application layer to inspect the values read from Cassandra which conflicted with the attempted conditional write. The documentation already referred to the `existing` attribute but it was not implemented.
1 parent 9759a32 commit a0569f1

5 files changed

Lines changed: 59 additions & 12 deletions

File tree

cassandra/cqlengine/query.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,17 @@ class IfNotExistsWithCounterColumn(CQLEngineException):
4040

4141

4242
class LWTException(CQLEngineException):
43-
pass
43+
"""Lightweight transaction exception.
44+
45+
This exception will be raised when a write using an `IF` clause could not be
46+
applied due to existing data violating the condition. The existing data is
47+
available through the ``existing`` attribute.
48+
49+
:param existing: The current state of the data which prevented the write.
50+
"""
51+
def __init__(self, existing):
52+
super(LWTException, self).__init__(self)
53+
self.existing = existing
4454

4555

4656
class DoesNotExist(QueryException):
@@ -58,7 +68,7 @@ def check_applied(result):
5868
applied to database.
5969
"""
6070
if result and '[applied]' in result[0] and not result[0]['[applied]']:
61-
raise LWTException('')
71+
raise LWTException(result[0])
6272

6373

6474
class AbstractQueryableColumn(UnicodeMixin):

docs/api/cassandra/cqlengine/models.rst

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ Model
4848
See the `list of supported table properties for more information
4949
<http://www.datastax.com/documentation/cql/3.1/cql/cql_reference/tabProp.html>`_.
5050

51-
5251
.. attribute:: __options__
5352

5453
For example:
@@ -89,7 +88,7 @@ Model
8988
object is determined by its primary key(s). And please note using this flag
9089
would incur performance cost.
9190

92-
if the insertion didn't applied, a LWTException exception would be raised.
91+
If the insertion isn't applied, a :class:`~cassandra.cqlengine.query.LWTException` is raised.
9392

9493
.. code-block:: python
9594
@@ -111,15 +110,16 @@ Model
111110
Simply specify the column(s) and the expected value(s). As with if_not_exists,
112111
this incurs a performance cost.
113112

114-
If the insertion isn't applied, a LWTException is raised
113+
If the insertion isn't applied, a :class:`~cassandra.cqlengine.query.LWTException` is raised.
115114

116115
.. code-block:: python
117116
118117
t = TestTransactionModel(text='some text', count=5)
119118
try:
120119
t.iff(count=5).update('other text')
121120
except LWTException as e:
122-
# handle failure
121+
# handle failure case
122+
print e.existing # existing object
123123
124124
.. automethod:: get
125125

docs/api/cassandra/cqlengine/query.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,4 @@ The methods here are used to filter, order, and constrain results.
4040

4141
.. autoclass:: MultipleObjectsReturned
4242

43+
.. autoclass:: LWTException

tests/integration/cqlengine/test_ifnotexists.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,17 @@ def test_insert_if_not_exists_success(self):
8181
id = uuid4()
8282

8383
TestIfNotExistsModel.create(id=id, count=8, text='123456789')
84-
with self.assertRaises(LWTException):
84+
85+
with self.assertRaises(LWTException) as assertion:
8586
TestIfNotExistsModel.if_not_exists().create(id=id, count=9, text='111111111111')
8687

88+
self.assertEquals(assertion.exception.existing, {
89+
'count': 8,
90+
'id': id,
91+
'text': '123456789',
92+
'[applied]': False,
93+
})
94+
8795
q = TestIfNotExistsModel.objects(id=id)
8896
self.assertEqual(len(q), 1)
8997

@@ -117,9 +125,16 @@ def test_batch_insert_if_not_exists_success(self):
117125

118126
b = BatchQuery()
119127
TestIfNotExistsModel.batch(b).if_not_exists().create(id=id, count=9, text='111111111111')
120-
with self.assertRaises(LWTException):
128+
with self.assertRaises(LWTException) as assertion:
121129
b.execute()
122130

131+
self.assertEquals(assertion.exception.existing, {
132+
'count': 8,
133+
'id': id,
134+
'text': '123456789',
135+
'[applied]': False,
136+
})
137+
123138
q = TestIfNotExistsModel.objects(id=id)
124139
self.assertEqual(len(q), 1)
125140

tests/integration/cqlengine/test_transaction.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,9 @@
2929
from tests.integration.cqlengine.base import BaseCassEngTestCase
3030
from tests.integration import CASSANDRA_VERSION
3131

32+
3233
class TestTransactionModel(Model):
33-
id = columns.UUID(primary_key=True, default=lambda:uuid4())
34+
id = columns.UUID(primary_key=True, default=uuid4)
3435
count = columns.Integer()
3536
text = columns.Text(required=False)
3637

@@ -71,7 +72,14 @@ def test_update_failure(self):
7172
t = TestTransactionModel.create(text='blah blah')
7273
t.text = 'new blah'
7374
t = t.iff(text='something wrong')
74-
self.assertRaises(LWTException, t.save)
75+
76+
with self.assertRaises(LWTException) as assertion:
77+
t.save()
78+
79+
self.assertEqual(assertion.exception.existing, {
80+
'text': 'blah blah',
81+
'[applied]': False,
82+
})
7583

7684
def test_blind_update(self):
7785
t = TestTransactionModel.create(text='blah blah')
@@ -89,7 +97,13 @@ def test_blind_update_fail(self):
8997
t.text = 'something else'
9098
uid = t.id
9199
qs = TestTransactionModel.objects(id=uid).iff(text='Not dis!')
92-
self.assertRaises(LWTException, qs.update, text='this will never work')
100+
with self.assertRaises(LWTException) as assertion:
101+
qs.update(text='this will never work')
102+
103+
self.assertEqual(assertion.exception.existing, {
104+
'text': 'blah blah',
105+
'[applied]': False,
106+
})
93107

94108
def test_transaction_clause(self):
95109
tc = TransactionClause('some_value', 23)
@@ -109,7 +123,14 @@ def test_batch_update_transaction(self):
109123

110124
b = BatchQuery()
111125
updated.batch(b).iff(count=6).update(text='and another thing')
112-
self.assertRaises(LWTException, b.execute)
126+
with self.assertRaises(LWTException) as assertion:
127+
b.execute()
128+
129+
self.assertEqual(assertion.exception.existing, {
130+
'id': id,
131+
'count': 5,
132+
'[applied]': False,
133+
})
113134

114135
updated = TestTransactionModel.objects(id=id).first()
115136
self.assertEqual(updated.text, 'something else')

0 commit comments

Comments
 (0)