async def test_sender__do_add_partitions_to_txn_ok(self): sender = await self._setup_sender() tps = [ TopicPartition("topic", 0), TopicPartition("topic", 1), TopicPartition("topic2", 1) ] add_handler = AddPartitionsToTxnHandler(sender, tps) tm = sender._txn_manager tm.partition_added = mock.Mock() # Handle response cls = AddPartitionsToTxnResponse[0] resp = cls( throttle_time_ms=300, errors=[ ("topic", [ (0, NoError.errno), (1, NoError.errno) ]), ("topic2", [ (1, NoError.errno) ]) ] ) backoff = add_handler.handle_response(resp) self.assertIsNone(backoff) self.assertEqual(tm.partition_added.call_count, 3) tm.partition_added.assert_has_calls([ mock.call(tp) for tp in tps ])
async def test_sender__do_add_partitions_to_txn_create(self): sender = await self._setup_sender() tps = [ TopicPartition("topic", 0), TopicPartition("topic", 1), TopicPartition("topic2", 1) ] add_handler = AddPartitionsToTxnHandler(sender, tps) req = add_handler.create_request() self.assertEqual(req.API_KEY, AddPartitionsToTxnRequest[0].API_KEY) self.assertEqual(req.API_VERSION, 0) self.assertEqual(req.transactional_id, "test_tid") self.assertEqual(req.producer_id, 120) self.assertEqual(req.producer_epoch, 22) self.assertEqual(list(sorted(req.topics)), list(sorted([("topic", [0, 1]), ("topic2", [1])])))
async def test_sender__do_add_partitions_to_txn_create(self): sender = await self._setup_sender() tps = [ TopicPartition("topic", 0), TopicPartition("topic", 1), TopicPartition("topic2", 1) ] add_handler = AddPartitionsToTxnHandler(sender, tps) req = add_handler.create_request() self.assertEqual(req.API_KEY, AddPartitionsToTxnRequest[0].API_KEY) self.assertEqual(req.API_VERSION, 0) self.assertEqual(req.transactional_id, "test_tid") self.assertEqual(req.producer_id, 120) self.assertEqual(req.producer_epoch, 22) self.assertEqual( list(sorted(req.topics)), list(sorted([("topic", [0, 1]), ("topic2", [1])])))
async def test_sender__do_add_partitions_to_txn_not_ok(self): sender = await self._setup_sender() tps = [ TopicPartition("topic", 0), TopicPartition("topic", 1), TopicPartition("topic2", 1) ] add_handler = AddPartitionsToTxnHandler(sender, tps) tm = sender._txn_manager tm.begin_transaction() tm.partition_added = mock.Mock() def create_response(error_type): cls = AddPartitionsToTxnResponse[0] resp = cls( throttle_time_ms=300, errors=[ ("topic", [ (0, error_type.errno), (1, error_type.errno) ]), ("topic2", [ (1, error_type.errno) ]) ] ) return resp # Handle coordination errors for error_cls in [CoordinatorNotAvailableError, NotCoordinatorError]: with mock.patch.object(sender, "_coordinator_dead") as mocked: resp = create_response(error_cls) backoff = add_handler.handle_response(resp) self.assertEqual(backoff, 0.1) tm.partition_added.assert_not_called() mocked.assert_called_with(CoordinationType.TRANSACTION) # Special case for ConcurrentTransactions resp = create_response(ConcurrentTransactions) backoff = add_handler.handle_response(resp) self.assertEqual(backoff, 0.02) # BACKOFF_OVERRIDE tm.partition_added.assert_not_called() # Special case for ConcurrentTransactions if this is not the first call tm._txn_partitions.add(tps[0]) resp = create_response(ConcurrentTransactions) backoff = add_handler.handle_response(resp) self.assertEqual(backoff, 0.1) # Default backoff tm.partition_added.assert_not_called() # Not coordination retriable errors for error_cls in [ CoordinatorLoadInProgressError, UnknownTopicOrPartitionError]: resp = create_response(error_cls) backoff = add_handler.handle_response(resp) self.assertEqual(backoff, 0.1) tm.partition_added.assert_not_called() # ProducerFenced case resp = create_response(InvalidProducerEpoch) with self.assertRaises(ProducerFenced): add_handler.handle_response(resp) tm.partition_added.assert_not_called() for error_type in [InvalidProducerIdMapping, InvalidTxnState]: resp = create_response(error_type) with self.assertRaises(error_type): add_handler.handle_response(resp) tm.partition_added.assert_not_called() # Handle unknown error resp = create_response(UnknownError) with self.assertRaises(UnknownError): add_handler.handle_response(resp) tm.partition_added.assert_not_called() # Handle TransactionalIdAuthorizationFailed resp = create_response(TransactionalIdAuthorizationFailed) with self.assertRaises(TransactionalIdAuthorizationFailed) as cm: add_handler.handle_response(resp) tm.partition_added.assert_not_called() self.assertEqual(cm.exception.args[0], "test_tid") # TopicAuthorizationFailedError case resp = create_response(OperationNotAttempted) backoff = add_handler.handle_response(resp) self.assertIsNone(backoff) tm.partition_added.assert_not_called() # TopicAuthorizationFailedError case self.assertNotEqual(tm.state, TransactionState.ABORTABLE_ERROR) resp = create_response(TopicAuthorizationFailedError) backoff = add_handler.handle_response(resp) self.assertIsNone(backoff) tm.partition_added.assert_not_called() self.assertEqual(tm.state, TransactionState.ABORTABLE_ERROR)
async def test_sender__do_add_partitions_to_txn_not_ok(self): sender = await self._setup_sender() tps = [ TopicPartition("topic", 0), TopicPartition("topic", 1), TopicPartition("topic2", 1) ] add_handler = AddPartitionsToTxnHandler(sender, tps) tm = sender._txn_manager tm.begin_transaction() tm.partition_added = mock.Mock() def create_response(error_type): cls = AddPartitionsToTxnResponse[0] resp = cls(throttle_time_ms=300, errors=[("topic", [(0, error_type.errno), (1, error_type.errno)]), ("topic2", [(1, error_type.errno)])]) return resp # Handle coordination errors for error_cls in [CoordinatorNotAvailableError, NotCoordinatorError]: with mock.patch.object(sender, "_coordinator_dead") as mocked: resp = create_response(error_cls) backoff = add_handler.handle_response(resp) self.assertEqual(backoff, 0.1) tm.partition_added.assert_not_called() mocked.assert_called_with(CoordinationType.TRANSACTION) # Special case for ConcurrentTransactions resp = create_response(ConcurrentTransactions) backoff = add_handler.handle_response(resp) self.assertEqual(backoff, 0.02) # BACKOFF_OVERRIDE tm.partition_added.assert_not_called() # Special case for ConcurrentTransactions if this is not the first call tm._txn_partitions.add(tps[0]) resp = create_response(ConcurrentTransactions) backoff = add_handler.handle_response(resp) self.assertEqual(backoff, 0.1) # Default backoff tm.partition_added.assert_not_called() # Not coordination retriable errors for error_cls in [ CoordinatorLoadInProgressError, UnknownTopicOrPartitionError ]: resp = create_response(error_cls) backoff = add_handler.handle_response(resp) self.assertEqual(backoff, 0.1) tm.partition_added.assert_not_called() # ProducerFenced case resp = create_response(InvalidProducerEpoch) with self.assertRaises(ProducerFenced): add_handler.handle_response(resp) tm.partition_added.assert_not_called() for error_type in [InvalidProducerIdMapping, InvalidTxnState]: resp = create_response(error_type) with self.assertRaises(error_type): add_handler.handle_response(resp) tm.partition_added.assert_not_called() # Handle unknown error resp = create_response(UnknownError) with self.assertRaises(UnknownError): add_handler.handle_response(resp) tm.partition_added.assert_not_called() # Handle TransactionalIdAuthorizationFailed resp = create_response(TransactionalIdAuthorizationFailed) with self.assertRaises(TransactionalIdAuthorizationFailed) as cm: add_handler.handle_response(resp) tm.partition_added.assert_not_called() self.assertEqual(cm.exception.args[0], "test_tid") # TopicAuthorizationFailedError case resp = create_response(OperationNotAttempted) backoff = add_handler.handle_response(resp) self.assertIsNone(backoff) tm.partition_added.assert_not_called() # TopicAuthorizationFailedError case self.assertNotEqual(tm.state, TransactionState.ABORTABLE_ERROR) resp = create_response(TopicAuthorizationFailedError) backoff = add_handler.handle_response(resp) self.assertIsNone(backoff) tm.partition_added.assert_not_called() self.assertEqual(tm.state, TransactionState.ABORTABLE_ERROR)