def test__maybe_commit_failure_can_retry(self): from google.api_core import exceptions wrapped = self._make_one(mock.sentinel.callable_) txn_id = b"failed-but-retry" transaction = _make_transaction(txn_id) transaction._id = txn_id # We won't call ``begin()``. wrapped.current_id = txn_id # We won't call ``_pre_commit()``. wrapped.retry_id = txn_id # We won't call ``_pre_commit()``. # Actually force the ``commit`` to fail. exc = exceptions.Aborted("Read-write did a bad.") firestore_api = transaction._client._firestore_api firestore_api.commit.side_effect = exc succeeded = wrapped._maybe_commit(transaction) self.assertFalse(succeeded) self.assertEqual(transaction._id, txn_id) self.assertEqual(wrapped.current_id, txn_id) self.assertEqual(wrapped.retry_id, txn_id) # Verify mocks. firestore_api.begin_transaction.assert_not_called() firestore_api.rollback.assert_not_called() firestore_api.commit.assert_called_once_with( transaction._client._database_string, [], transaction=txn_id, metadata=transaction._client._rpc_metadata, )
def test__transactional__maybe_commit_failure_can_retry(): from google.api_core import exceptions wrapped = _make__transactional(mock.sentinel.callable_) txn_id = b"failed-but-retry" transaction = _make_transaction_pb(txn_id) transaction._id = txn_id # We won't call ``begin()``. wrapped.current_id = txn_id # We won't call ``_pre_commit()``. wrapped.retry_id = txn_id # We won't call ``_pre_commit()``. # Actually force the ``commit`` to fail. exc = exceptions.Aborted("Read-write did a bad.") firestore_api = transaction._client._firestore_api firestore_api.commit.side_effect = exc succeeded = wrapped._maybe_commit(transaction) assert not succeeded assert transaction._id == txn_id assert wrapped.current_id == txn_id assert wrapped.retry_id == txn_id # Verify mocks. firestore_api.begin_transaction.assert_not_called() firestore_api.rollback.assert_not_called() firestore_api.commit.assert_called_once_with( request={ "database": transaction._client._database_string, "writes": [], "transaction": txn_id, }, metadata=transaction._client._rpc_metadata, )
def test__maybe_commit_failure_read_only(self): from google.api_core import exceptions wrapped = self._make_one(mock.sentinel.callable_) txn_id = b"failed" transaction = _make_transaction(txn_id, read_only=True) transaction._id = txn_id # We won't call ``begin()``. wrapped.current_id = txn_id # We won't call ``_pre_commit()``. wrapped.retry_id = txn_id # We won't call ``_pre_commit()``. # Actually force the ``commit`` to fail (use ABORTED, but cannot # retry since read-only). exc = exceptions.Aborted("Read-only did a bad.") firestore_api = transaction._client._firestore_api firestore_api.commit.side_effect = exc with self.assertRaises(exceptions.Aborted) as exc_info: wrapped._maybe_commit(transaction) self.assertIs(exc_info.exception, exc) self.assertEqual(transaction._id, txn_id) self.assertEqual(wrapped.current_id, txn_id) self.assertEqual(wrapped.retry_id, txn_id) # Verify mocks. firestore_api.begin_transaction.assert_not_called() firestore_api.rollback.assert_not_called() firestore_api.commit.assert_called_once_with( transaction._client._database_string, [], transaction=txn_id, metadata=transaction._client._rpc_metadata, )
async def test___call__success_second_attempt(self): from google.api_core import exceptions from google.cloud.firestore_v1.types import common from google.cloud.firestore_v1.types import firestore from google.cloud.firestore_v1.types import write to_wrap = AsyncMock(return_value=mock.sentinel.result, spec=[]) wrapped = self._make_one(to_wrap) txn_id = b"whole-enchilada" transaction = _make_transaction(txn_id) # Actually force the ``commit`` to fail on first / succeed on second. exc = exceptions.Aborted("Contention junction.") firestore_api = transaction._client._firestore_api firestore_api.commit.side_effect = [ exc, firestore.CommitResponse(write_results=[write.WriteResult()]), ] # Call the __call__-able ``wrapped``. result = await wrapped(transaction, "a", b="c") self.assertIs(result, mock.sentinel.result) self.assertIsNone(transaction._id) self.assertEqual(wrapped.current_id, txn_id) self.assertEqual(wrapped.retry_id, txn_id) # Verify mocks. wrapped_call = mock.call(transaction, "a", b="c") self.assertEqual(to_wrap.mock_calls, [wrapped_call, wrapped_call]) firestore_api = transaction._client._firestore_api db_str = transaction._client._database_string options_ = common.TransactionOptions( read_write=common.TransactionOptions.ReadWrite(retry_transaction=txn_id) ) self.assertEqual( firestore_api.begin_transaction.mock_calls, [ mock.call( request={"database": db_str, "options": None}, metadata=transaction._client._rpc_metadata, ), mock.call( request={"database": db_str, "options": options_}, metadata=transaction._client._rpc_metadata, ), ], ) firestore_api.rollback.assert_not_called() commit_call = mock.call( request={"database": db_str, "writes": [], "transaction": txn_id}, metadata=transaction._client._rpc_metadata, ) self.assertEqual(firestore_api.commit.mock_calls, [commit_call, commit_call])
def test___call__success_second_attempt(self): from google.api_core import exceptions from google.cloud.firestore_v1beta1.proto import common_pb2 from google.cloud.firestore_v1beta1.proto import firestore_pb2 from google.cloud.firestore_v1beta1.proto import write_pb2 to_wrap = mock.Mock(return_value=mock.sentinel.result, spec=[]) wrapped = self._make_one(to_wrap) txn_id = b'whole-enchilada' transaction = _make_transaction(txn_id) # Actually force the ``commit`` to fail on first / succeed on second. exc = exceptions.Aborted('Contention junction.') firestore_api = transaction._client._firestore_api firestore_api.commit.side_effect = [ exc, firestore_pb2.CommitResponse(write_results=[ write_pb2.WriteResult(), ], ), ] # Call the __call__-able ``wrapped``. result = wrapped(transaction, 'a', b='c') self.assertIs(result, mock.sentinel.result) self.assertIsNone(transaction._id) self.assertEqual(wrapped.current_id, txn_id) self.assertEqual(wrapped.retry_id, txn_id) # Verify mocks. wrapped_call = mock.call(transaction, 'a', b='c') self.assertEqual(to_wrap.mock_calls, [wrapped_call, wrapped_call]) firestore_api = transaction._client._firestore_api db_str = transaction._client._database_string options_ = common_pb2.TransactionOptions( read_write=common_pb2.TransactionOptions.ReadWrite( retry_transaction=txn_id, ), ) self.assertEqual( firestore_api.begin_transaction.mock_calls, [ mock.call(db_str, options_=None, metadata=transaction._client._rpc_metadata), mock.call(db_str, options_=options_, metadata=transaction._client._rpc_metadata), ], ) firestore_api.rollback.assert_not_called() commit_call = mock.call(db_str, [], transaction=txn_id, metadata=transaction._client._rpc_metadata) self.assertEqual(firestore_api.commit.mock_calls, [commit_call, commit_call])
async def test___call__failure(self): from google.api_core import exceptions from google.cloud.firestore_v1.async_transaction import ( _EXCEED_ATTEMPTS_TEMPLATE, ) to_wrap = AsyncMock(return_value=mock.sentinel.result, spec=[]) wrapped = self._make_one(to_wrap) txn_id = b"only-one-shot" transaction = _make_transaction(txn_id, max_attempts=1) # Actually force the ``commit`` to fail. exc = exceptions.Aborted("Contention just once.") firestore_api = transaction._client._firestore_api firestore_api.commit.side_effect = exc # Call the __call__-able ``wrapped``. with self.assertRaises(ValueError) as exc_info: await wrapped(transaction, "here", there=1.5) err_msg = _EXCEED_ATTEMPTS_TEMPLATE.format(transaction._max_attempts) self.assertEqual(exc_info.exception.args, (err_msg,)) self.assertIsNone(transaction._id) self.assertEqual(wrapped.current_id, txn_id) self.assertEqual(wrapped.retry_id, txn_id) # Verify mocks. to_wrap.assert_called_once_with(transaction, "here", there=1.5) firestore_api.begin_transaction.assert_called_once_with( request={ "database": transaction._client._database_string, "options": None, }, metadata=transaction._client._rpc_metadata, ) firestore_api.rollback.assert_called_once_with( request={ "database": transaction._client._database_string, "transaction": txn_id, }, metadata=transaction._client._rpc_metadata, ) firestore_api.commit.assert_called_once_with( request={ "database": transaction._client._database_string, "writes": [], "transaction": txn_id, }, metadata=transaction._client._rpc_metadata, )
def test_retry_idempotent(identity): # Define our error and OK responses. err409 = exceptions.Aborted(message='derp de derp') err503 = exceptions.ServiceUnavailable(message='whups') errwtf = exceptions.Unknown(message='huh?') ok = showcase_v1alpha3.User(name='users/0', display_name='Guido') server = mock.Mock(side_effect=(err409, err503, errwtf, ok)) # Mock the transport to send back the error responses followed by a # success response. transport = type(identity).get_transport_class() with mock.patch.object( transport, 'get_user', new_callable=mock.PropertyMock(return_value=server)): with mock.patch.object(time, 'sleep'): response = identity.get_user({'name': 'users/0'}) assert response.name == 'users/0' assert response.display_name == 'Guido' assert server.call_count == 4
def test__transactional__maybe_commit_failure_read_only(): from google.api_core import exceptions wrapped = _make__transactional(mock.sentinel.callable_) txn_id = b"failed" transaction = _make_transaction_pb(txn_id, read_only=True) transaction._id = txn_id # We won't call ``begin()``. wrapped.current_id = txn_id # We won't call ``_pre_commit()``. wrapped.retry_id = txn_id # We won't call ``_pre_commit()``. # Actually force the ``commit`` to fail (use ABORTED, but cannot # retry since read-only). exc = exceptions.Aborted("Read-only did a bad.") firestore_api = transaction._client._firestore_api firestore_api.commit.side_effect = exc with pytest.raises(exceptions.Aborted) as exc_info: wrapped._maybe_commit(transaction) assert exc_info.value is exc assert transaction._id == txn_id assert wrapped.current_id == txn_id assert wrapped.retry_id == txn_id # Verify mocks. firestore_api.begin_transaction.assert_not_called() firestore_api.rollback.assert_not_called() firestore_api.commit.assert_called_once_with( request={ "database": transaction._client._database_string, "writes": [], "transaction": txn_id, }, metadata=transaction._client._rpc_metadata, )
def test_aborted(core_retry): error = core_exceptions.Aborted("testing") core_retry.if_transient_error.return_value = False assert _retry.is_transient_error(error) is True core_retry.if_transient_error.assert_called_once_with(error)