Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions gcloud/datastore/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -440,12 +440,17 @@ def batch(self):
"""
return Batch(self)

def transaction(self):
def transaction(self, serializable=False):
"""Proxy to :class:`gcloud.datastore.transaction.Transaction`.

Passes our ``dataset_id``.

:type serializable: boolean
:param serializable: if true, perform this transaction at
``serializable`` isolation level; otherwise,
perform it at ``snapshot`` level.
"""
return Transaction(self)
return Transaction(self, serializable=serializable)

def query(self, **kwargs):
"""Proxy to :class:`gcloud.datastore.query.Query`.
Expand Down
18 changes: 16 additions & 2 deletions gcloud/datastore/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -790,7 +790,7 @@ def test_batch(self):
self.assertEqual(batch.args, (client,))
self.assertEqual(batch.kwargs, {})

def test_transaction(self):
def test_transaction_defaults(self):
from gcloud.datastore import client as MUT
from gcloud._testing import _Monkey

Expand All @@ -802,7 +802,21 @@ def test_transaction(self):

self.assertTrue(isinstance(xact, _Dummy))
self.assertEqual(xact.args, (client,))
self.assertEqual(xact.kwargs, {})
self.assertEqual(xact.kwargs, {'serializable': False})

def test_transaction_explicit(self):
from gcloud.datastore import client as MUT
from gcloud._testing import _Monkey

creds = object()
client = self._makeOne(credentials=creds)

with _Monkey(MUT, Transaction=_Dummy):
xact = client.transaction(serializable=True)

self.assertTrue(isinstance(xact, _Dummy))
self.assertEqual(xact.args, (client,))
self.assertEqual(xact.kwargs, {'serializable': True})

def test_query_w_client(self):
KIND = 'KIND'
Expand Down
52 changes: 41 additions & 11 deletions gcloud/datastore/test_transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ def _getTargetClass(self):
from gcloud.datastore.transaction import Transaction
return Transaction

def _makeOne(self, client):
return self._getTargetClass()(client)
def _makeOne(self, client, **kw):
return self._getTargetClass()(client, **kw)

def test_ctor(self):
def test_ctor_defaults(self):
from gcloud.datastore._datastore_v1_pb2 import Mutation

_DATASET = 'DATASET'
Expand All @@ -37,6 +37,22 @@ def test_ctor(self):
self.assertEqual(xact._status, self._getTargetClass()._INITIAL)
self.assertTrue(isinstance(xact.mutation, Mutation))
self.assertEqual(len(xact._auto_id_entities), 0)
self.assertFalse(xact.serializable)

def test_ctor_explicit(self):
from gcloud.datastore._datastore_v1_pb2 import Mutation

_DATASET = 'DATASET'
connection = _Connection()
client = _Client(_DATASET, connection)
xact = self._makeOne(client, serializable=True)
self.assertEqual(xact.dataset_id, _DATASET)
self.assertEqual(xact.connection, connection)
self.assertEqual(xact.id, None)
self.assertEqual(xact._status, self._getTargetClass()._INITIAL)
self.assertTrue(isinstance(xact.mutation, Mutation))
self.assertEqual(len(xact._auto_id_entities), 0)
self.assertTrue(xact.serializable)

def test_current(self):
from gcloud.datastore.test_client import _NoCommitBatch
Expand Down Expand Up @@ -64,14 +80,25 @@ def test_current(self):
self.assertTrue(xact1.current() is None)
self.assertTrue(xact2.current() is None)

def test_begin(self):
def test_begin_wo_serializable(self):
_DATASET = 'DATASET'
connection = _Connection(234)
client = _Client(_DATASET, connection)
xact = self._makeOne(client)
xact.begin()
self.assertEqual(xact.id, 234)
self.assertEqual(connection._begun, _DATASET)
self.assertEqual(connection._begun[0], _DATASET)
self.assertFalse(connection._begun[1])

def test_begin_w_serializable(self):
_DATASET = 'DATASET'
connection = _Connection(234)
client = _Client(_DATASET, connection)
xact = self._makeOne(client, serializable=True)
xact.begin()
self.assertEqual(xact.id, 234)
self.assertEqual(connection._begun[0], _DATASET)
self.assertTrue(connection._begun[1])

def test_begin_tombstoned(self):
_DATASET = 'DATASET'
Expand All @@ -80,7 +107,8 @@ def test_begin_tombstoned(self):
xact = self._makeOne(client)
xact.begin()
self.assertEqual(xact.id, 234)
self.assertEqual(connection._begun, _DATASET)
self.assertEqual(connection._begun[0], _DATASET)
self.assertFalse(connection._begun[1])

xact.rollback()
self.assertEqual(xact.id, None)
Expand Down Expand Up @@ -134,7 +162,8 @@ def test_context_manager_no_raise(self):
xact._mutation = mutation = object()
with xact:
self.assertEqual(xact.id, 234)
self.assertEqual(connection._begun, _DATASET)
self.assertEqual(connection._begun[0], _DATASET)
self.assertFalse(connection._begun[1])
self.assertEqual(connection._committed, (_DATASET, mutation, 234))
self.assertEqual(xact.id, None)

Expand All @@ -146,12 +175,13 @@ class Foo(Exception):
_DATASET = 'DATASET'
connection = _Connection(234)
client = _Client(_DATASET, connection)
xact = self._makeOne(client)
xact = self._makeOne(client, serializable=True)
xact._mutation = object()
try:
with xact:
self.assertEqual(xact.id, 234)
self.assertEqual(connection._begun, _DATASET)
self.assertEqual(connection._begun[0], _DATASET)
self.assertTrue(connection._begun[1])
raise Foo()
except Foo:
self.assertEqual(xact.id, None)
Expand Down Expand Up @@ -179,8 +209,8 @@ def __init__(self, xact_id=123):
self._xact_id = xact_id
self._commit_result = _CommitResult()

def begin_transaction(self, dataset_id):
self._begun = dataset_id
def begin_transaction(self, dataset_id, serializable):
self._begun = (dataset_id, serializable)
return self._xact_id

def rollback(self, dataset_id, transaction_id):
Expand Down
24 changes: 21 additions & 3 deletions gcloud/datastore/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,12 @@ class Transaction(Batch):
... transaction.commit()

:type client: :class:`gcloud.datastore.client.Client`
:param client: The client used to connect to datastore.
:param client: the client used to connect to datastore.

:type serializable: boolean
:param serializable: if true, perform this transaction at
``serializable`` isolation level; otherwise, perform
it at ``snapshot`` level.
"""

_INITIAL = 0
Expand All @@ -100,10 +105,11 @@ class Transaction(Batch):
_FINISHED = 3
"""Enum value for _FINISHED status of transaction."""

def __init__(self, client):
def __init__(self, client, serializable=False):
super(Transaction, self).__init__(client)
self._id = None
self._status = self._INITIAL
self._serializable = serializable

@property
def id(self):
Expand All @@ -114,6 +120,17 @@ def id(self):
"""
return self._id

@property
def serializable(self):
"""Should this transaction be run at ``serializable`` isolation

:rtype: boolean
:returns: if true, perform this transaction at
``serializable`` isolation level; otherwise, perform
it at ``snapshot`` level.
"""
return self._serializable

def current(self):
"""Return the topmost transaction.

Expand All @@ -138,7 +155,8 @@ def begin(self):
if self._status != self._INITIAL:
raise ValueError('Transaction already started previously.')
self._status = self._IN_PROGRESS
self._id = self.connection.begin_transaction(self.dataset_id)
self._id = self.connection.begin_transaction(
self.dataset_id, serializable=self.serializable)

def rollback(self):
"""Rolls back the current transaction.
Expand Down
4 changes: 2 additions & 2 deletions system_tests/datastore.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,8 @@ def test_post_with_id(self):
def test_post_with_generated_id(self):
self._generic_test_post()

def test_save_multiple(self):
with CLIENT.transaction() as xact:
def test_save_multiple_serializable(self):
with CLIENT.transaction(serializable=True) as xact:
entity1 = self._get_post()
xact.put(entity1)
# Register entity to be deleted.
Expand Down