def test_is_occ_conflict_exception_false(self, mock_client_error): mock_client_error.response = { 'Error': { 'Code': 'NotOccConflictException' } } self.assertFalse(is_occ_conflict_exception(mock_client_error))
def commit_transaction(qldb_session, transaction, statement, parameters): """ Commit the transaction and retry up to a constant number of times. :type qldb_session: :py:class:`pyqldb.session.qldb_session.QldbSession` :param qldb_session: An instance of the QldbSession class. :type transaction: :py:class:`pyqldb.transaction.transaction.Transaction` :param transaction: An instance of the Transaction class. :type statement: str :param statement: The query to execute. :type parameters: list :param parameters: list of parameters in Ion format. """ for i in range(Constants.RETRY_LIMIT): try: transaction.commit() logger.info('Commit successful after {} retries.'.format(i)) break except ClientError as ce: if is_occ_conflict_exception(ce): logger.error('Commit failed due to an OCC conflict. Restart transaction.') transaction = qldb_session.start_transaction() execute_transaction(qldb_session, transaction, statement, parameters)
def execute_transaction(qldb_session, transaction, statement, parameters): """ Execute statement with parameters. If it was unsuccessful, retry with a new transaction. :type qldb_session: :py:class:`pyqldb.session.qldb_session.QldbSession` :param qldb_session: An instance of the QldbSession class. :type transaction: :py:class:`pyqldb.transaction.transaction.Transaction` :param transaction: An open transaction. :type statement: str :param statement: The query to execute. :type parameters: list :param parameters: list of parameters in Ion format. """ for i in range(Constants.RETRY_LIMIT): try: transaction.execute_statement(statement, parameters) logger.info('Execute successful after {} retries.'.format(i)) break except ClientError as ce: if is_occ_conflict_exception(ce): logger.error('Execute on QLDB session failed due to an OCC conflict. Restart transaction.') transaction = qldb_session.start_transaction()
def test_occ_exception_is_thrown(self): # Create driver with zero retry limit to trigger OCC exception. driver = self.integration_test_base.qldb_driver(retry_limit=0) # Insert document. ion_value = loads(dumps({COLUMN_NAME: 0})) def execute_statement_with_parameter_and_return_count( txn, query, parameter): cursor = txn.execute_statement(query, parameter) count = 0 for row in cursor: count += 1 return count query = "INSERT INTO {} ?".format(TABLE_NAME) count = driver.execute_lambda( lambda txn: execute_statement_with_parameter_and_return_count( txn, query, ion_value)) self.assertEqual(1, count) # For testing purposes only. Forcefully causes an OCC conflict to occur. # Do not invoke driver.execute_lambda within the parameter function under normal circumstances. def query_and_update_record(transaction_executor): # Query document. transaction_executor.execute_statement( "SELECT VALUE {} FROM {}".format(COLUMN_NAME, TABLE_NAME)) # The following update document will be committed before query document thus resulting in OCC. driver.execute_lambda( lambda transaction_executor: transaction_executor. execute_statement( "UPDATE {} SET {} = ?".format(TABLE_NAME, COLUMN_NAME), 5)) try: driver.execute_lambda(lambda txn: query_and_update_record(txn)) self.fail("Did not throw OCC exception.") except ClientError as ce: self.assertTrue(is_occ_conflict_exception(ce))
def test_is_occ_conflict_exception_not_client_error(self): self.assertFalse(is_occ_conflict_exception(Exception()))
def test_is_occ_conflict_exception_false(self): clientError = ClientError( {'Error': { 'Code': 'NotOccConflictException' }}, 'SendCommand') self.assertFalse(is_occ_conflict_exception(clientError))