buz 2.13.1rc7__tar.gz → 2.13.1rc9__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {buz-2.13.1rc7 → buz-2.13.1rc9}/PKG-INFO +1 -1
- {buz-2.13.1rc7 → buz-2.13.1rc9}/pyproject.toml +2 -1
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/infrastructure/buz_kafka/base_buz_aiokafka_async_consumer.py +10 -22
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/infrastructure/buz_kafka/buz_kafka_event_bus.py +17 -12
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/infrastructure/buz_kafka/kafka_event_sync_subscriber_executor.py +0 -5
- buz-2.13.1rc9/src/buz/kafka/domain/exceptions/not_all_partition_assigned_exception.py +8 -0
- buz-2.13.1rc9/src/buz/kafka/domain/exceptions/topic_not_found_exception.py +6 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/domain/services/kafka_admin_client.py +14 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/infrastructure/aiokafka/aiokafka_consumer.py +1 -1
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/infrastructure/cdc/cdc_message.py +3 -1
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/infrastructure/deserializers/implementations/cdc/cdc_record_bytes_to_event_deserializer.py +9 -4
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/infrastructure/kafka_python/kafka_python_admin_client.py +98 -5
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/infrastructure/kafka_python/kafka_python_admin_test_client.py +11 -2
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/infrastructure/serializers/implementations/cdc_record_bytes_to_event_serializer.py +6 -1
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/queue/in_memory/in_memory_multiqueue_repository.py +0 -20
- {buz-2.13.1rc7 → buz-2.13.1rc9}/LICENSE +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/README.md +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/command/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/command/asynchronous/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/command/asynchronous/base_command_handler.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/command/asynchronous/command_bus.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/command/asynchronous/command_handler.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/command/asynchronous/middleware/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/command/asynchronous/middleware/base_handle_middleware.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/command/asynchronous/middleware/handle_middleware.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/command/asynchronous/middleware/handle_middleware_chain_resolver.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/command/asynchronous/self_process/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/command/asynchronous/self_process/self_process_command_bus.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/command/command.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/command/more_than_one_command_handler_related_exception.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/command/synchronous/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/command/synchronous/base_command_handler.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/command/synchronous/command_bus.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/command/synchronous/command_handler.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/command/synchronous/middleware/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/command/synchronous/middleware/base_handle_middleware.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/command/synchronous/middleware/handle_middleware.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/command/synchronous/middleware/handle_middleware_chain_resolver.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/command/synchronous/self_process/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/command/synchronous/self_process/self_process_command_bus.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/command/synchronous/synced_async/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/command/synchronous/synced_async/synced_async_command_bus.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/async_consumer.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/async_subscriber.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/async_worker.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/base_async_subscriber.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/base_subscriber.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/consumer.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/dead_letter_queue/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/dead_letter_queue/dlq_criteria.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/dead_letter_queue/dlq_record.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/dead_letter_queue/dlq_repository.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/event.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/event_bus.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/exceptions/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/exceptions/event_not_published_exception.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/exceptions/event_restore_exception.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/exceptions/subscribers_not_found_exception.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/exceptions/term_signal_interruption_exception.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/exceptions/worker_execution_exception.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/infrastructure/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/infrastructure/buz_kafka/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/infrastructure/buz_kafka/buz_aiokafka_async_consumer.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/infrastructure/buz_kafka/buz_aiokafka_multi_threaded_consumer.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/infrastructure/buz_kafka/consume_strategy/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/infrastructure/buz_kafka/consume_strategy/consume_strategy.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/infrastructure/buz_kafka/consume_strategy/kafka_on_fail_strategy.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/infrastructure/buz_kafka/consume_strategy/topic_and_subscription_group_per_subscriber_kafka_consumer_strategy.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/infrastructure/buz_kafka/exceptions/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/infrastructure/buz_kafka/exceptions/kafka_event_bus_config_not_valid_exception.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/infrastructure/buz_kafka/kafka_event_async_subscriber_executor.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/infrastructure/buz_kafka/kafka_event_subscriber_executor.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/infrastructure/buz_kafka/publish_strategy/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/infrastructure/buz_kafka/publish_strategy/publish_strategy.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/infrastructure/buz_kafka/publish_strategy/topic_per_event_kafka_publish_strategy.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/infrastructure/kombu/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/infrastructure/kombu/allowed_kombu_serializer.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/infrastructure/kombu/consume_strategy/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/infrastructure/kombu/consume_strategy/consume_strategy.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/infrastructure/kombu/consume_strategy/queue_per_subscriber_consume_strategy.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/infrastructure/kombu/kombu_consumer.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/infrastructure/kombu/kombu_event_bus.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/infrastructure/kombu/publish_strategy/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/infrastructure/kombu/publish_strategy/fanout_exchange_per_event_publish_strategy.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/infrastructure/kombu/publish_strategy/publish_strategy.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/infrastructure/kombu/retry_strategy/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/infrastructure/kombu/retry_strategy/publish_retry_policy.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/infrastructure/kombu/retry_strategy/simple_publish_retry_policy.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/infrastructure/models/consuming_task.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/infrastructure/queue/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/meta_base_subscriber.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/meta_subscriber.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/middleware/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/middleware/async_consume_middleware.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/middleware/async_consume_middleware_chain_resolver.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/middleware/base_consume_middleware.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/middleware/base_publish_middleware.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/middleware/consume_middleware.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/middleware/consume_middleware_chain_resolver.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/middleware/exceptions/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/middleware/exceptions/event_already_in_progress_exception.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/middleware/publish_middleware.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/middleware/publish_middleware_chain_resolver.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/strategies/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/strategies/execution_strategy/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/strategies/execution_strategy/async_execution_strategy.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/strategies/execution_strategy/async_self_process_execution_strategy.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/strategies/execution_strategy/cyclic_iterator_execution_strategy.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/strategies/execution_strategy/execution_strategy.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/strategies/execution_strategy/self_process_execution_strategy.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/strategies/retry/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/strategies/retry/consume_retrier.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/strategies/retry/consumed_event_retry.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/strategies/retry/consumed_event_retry_repository.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/strategies/retry/max_retries_consume_retrier.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/strategies/retry/max_retries_negative_exception.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/strategies/retry/reject_callback.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/subscriber.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/sync/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/sync/sync_event_bus.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/transactional_outbox/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/transactional_outbox/event_to_outbox_record_translator.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/transactional_outbox/fqn_to_event_mapper.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/transactional_outbox/outbox_criteria/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/transactional_outbox/outbox_criteria/deliverable_records_outbox_criteria_factory.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/transactional_outbox/outbox_criteria/outbox_criteria.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/transactional_outbox/outbox_criteria/outbox_criteria_factory.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/transactional_outbox/outbox_criteria/outbox_sorting_criteria.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/transactional_outbox/outbox_record.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/transactional_outbox/outbox_record_finder/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/transactional_outbox/outbox_record_finder/outbox_record_stream_finder.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/transactional_outbox/outbox_record_finder/polling_outbox_record_stream_finder.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/transactional_outbox/outbox_record_to_event_translator.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/transactional_outbox/outbox_record_validation/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/transactional_outbox/outbox_record_validation/abstract_outbox_record_validator.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/transactional_outbox/outbox_record_validation/outbox_record_size_not_allowed_exception.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/transactional_outbox/outbox_record_validation/outbox_record_validation_exception.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/transactional_outbox/outbox_record_validation/outbox_record_validator.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/transactional_outbox/outbox_record_validation/size_outbox_record_validator.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/transactional_outbox/outbox_repository.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/transactional_outbox/transactional_outbox_event_bus.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/transactional_outbox/transactional_outbox_worker.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/worker.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/handler.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/domain/exceptions/not_valid_kafka_message_exception.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/domain/exceptions/topic_already_created_exception.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/domain/models/auto_create_topic_configuration.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/domain/models/consumer_initial_offset_position.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/domain/models/create_kafka_topic.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/domain/models/kafka_connection_config.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/domain/models/kafka_connection_credentials.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/domain/models/kafka_connection_plain_text_credentials.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/domain/models/kafka_connection_sasl_credentials.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/domain/models/kafka_consumer_record.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/domain/models/kafka_poll_record.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/domain/models/kafka_supported_sasl_mechanisms.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/domain/models/kafka_supported_security_protocols.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/domain/services/kafka_admin_test_client.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/domain/services/kafka_producer.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/infrastructure/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/infrastructure/aiokafka/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/infrastructure/aiokafka/rebalance/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/infrastructure/aiokafka/rebalance/kafka_callback_rebalancer.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/infrastructure/aiokafka/translators/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/infrastructure/aiokafka/translators/consumer_initial_offset_position_translator.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/infrastructure/deserializers/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/infrastructure/deserializers/byte_deserializer.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/infrastructure/deserializers/bytes_to_message_deserializer.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/infrastructure/deserializers/implementations/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/infrastructure/deserializers/implementations/cdc/not_valid_cdc_message_exception.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/infrastructure/deserializers/implementations/json_byte_deserializer.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/infrastructure/deserializers/implementations/json_bytes_to_message_deserializer.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/infrastructure/kafka_python/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/infrastructure/kafka_python/exception/consumer_interrupted_exception.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/infrastructure/kafka_python/factories/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/infrastructure/kafka_python/factories/kafka_python_producer_factory.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/infrastructure/kafka_python/kafka_python_producer.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/infrastructure/kafka_python/translators/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/infrastructure/kafka_python/translators/consumer_initial_offset_position_translator.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/infrastructure/serializers/byte_serializer.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/infrastructure/serializers/implementations/json_byte_serializer.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/kafka/infrastructure/serializers/kafka_header_serializer.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/locator/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/locator/handler_fqn_not_found_exception.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/locator/locator.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/locator/message_fqn_not_found_exception.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/locator/pypendency/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/locator/pypendency/container_locator.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/locator/pypendency/container_locator_resolution_configuration.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/locator/pypendency/handler_not_found_exception.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/locator/pypendency/handler_not_registered_exception.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/locator/sync/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/locator/sync/handler_already_registered_exception.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/locator/sync/handler_not_registered_exception.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/locator/sync/instance_locator.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/message.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/middleware/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/middleware/middleware.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/middleware/middleware_chain_builder.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/py.typed +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/query/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/query/asynchronous/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/query/asynchronous/base_query_handler.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/query/asynchronous/middleware/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/query/asynchronous/middleware/base_handle_middleware.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/query/asynchronous/middleware/handle_middleware.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/query/asynchronous/middleware/handle_middleware_chain_resolver.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/query/asynchronous/query_bus.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/query/asynchronous/query_handler.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/query/asynchronous/self_process/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/query/asynchronous/self_process/self_process_query_bus.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/query/more_than_one_query_handler_related_exception.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/query/query.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/query/query_response.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/query/synchronous/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/query/synchronous/base_query_handler.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/query/synchronous/middleware/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/query/synchronous/middleware/base_handle_middleware.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/query/synchronous/middleware/handle_middleware.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/query/synchronous/middleware/handle_middleware_chain_resolver.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/query/synchronous/query_bus.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/query/synchronous/query_handler.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/query/synchronous/self_process/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/query/synchronous/self_process/self_process_query_bus.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/query/synchronous/synced_async/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/query/synchronous/synced_async/synced_async_query_bus.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/queue/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/queue/in_memory/in_memory_queue_repository.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/queue/multiqueue_repository.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/queue/queue_repository.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/serializer/message_to_bytes_serializer.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/serializer/message_to_json_bytes_serializer.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/wrapper/__init__.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/wrapper/async_to_sync.py +0 -0
- {buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/wrapper/event_loop.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "buz"
|
|
3
|
-
version = "2.13.
|
|
3
|
+
version = "2.13.1rc9"
|
|
4
4
|
description = "Buz is a set of light, simple and extensible implementations of event, command and query buses."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = ["Luis Pintado Lozano <luis.pintado.lozano@gmail.com>", "Gerardo Parra <gprauxiliar@gmail.com>"]
|
|
@@ -37,6 +37,7 @@ black = "^23.3"
|
|
|
37
37
|
mypy = "^1.2"
|
|
38
38
|
flake8 = "^5.0.4"
|
|
39
39
|
tenacity = "^8.5.0"
|
|
40
|
+
freezegun = "^1.5.1"
|
|
40
41
|
types-cachetools = "^5.5.0.20240820"
|
|
41
42
|
|
|
42
43
|
[tool.poetry.extras]
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
from abc import abstractmethod
|
|
2
1
|
import traceback
|
|
2
|
+
from abc import abstractmethod
|
|
3
3
|
from asyncio import Lock, Task, create_task, gather, Semaphore, Event as AsyncIOEvent, sleep
|
|
4
4
|
from datetime import timedelta, datetime
|
|
5
5
|
from itertools import cycle
|
|
@@ -28,7 +28,6 @@ from buz.kafka.infrastructure.aiokafka.aiokafka_consumer import AIOKafkaConsumer
|
|
|
28
28
|
from buz.queue.in_memory.in_memory_multiqueue_repository import InMemoryMultiqueueRepository
|
|
29
29
|
from buz.queue.multiqueue_repository import MultiqueueRepository
|
|
30
30
|
|
|
31
|
-
|
|
32
31
|
T = TypeVar("T", bound=Event)
|
|
33
32
|
|
|
34
33
|
|
|
@@ -87,17 +86,14 @@ class BaseBuzAIOKafkaAsyncConsumer(AsyncConsumer):
|
|
|
87
86
|
async def run(self) -> None:
|
|
88
87
|
start_time = datetime.now()
|
|
89
88
|
await self.__generate_kafka_consumers()
|
|
89
|
+
self.__initial_coroutines_created_elapsed_time = datetime.now() - start_time
|
|
90
90
|
|
|
91
91
|
if len(self.__executor_per_consumer_mapper) == 0:
|
|
92
92
|
self._logger.error("There are no valid subscribers to execute, finalizing consumer")
|
|
93
93
|
return
|
|
94
94
|
|
|
95
|
-
self.__initial_coroutines_created_elapsed_time = datetime.now() - start_time
|
|
96
|
-
|
|
97
95
|
start_consumption_time = datetime.now()
|
|
98
|
-
|
|
99
96
|
worker_errors = await self.__run_worker()
|
|
100
|
-
|
|
101
97
|
self.__events_processed_elapsed_time = datetime.now() - start_consumption_time
|
|
102
98
|
|
|
103
99
|
await self.__handle_graceful_stop(worker_errors)
|
|
@@ -112,9 +108,9 @@ class BaseBuzAIOKafkaAsyncConsumer(AsyncConsumer):
|
|
|
112
108
|
if self.__exceptions_are_thrown(worker_errors):
|
|
113
109
|
consume_events_exception, polling_task_exception = worker_errors
|
|
114
110
|
if consume_events_exception:
|
|
115
|
-
self._logger.
|
|
111
|
+
self._logger.exception(consume_events_exception)
|
|
116
112
|
if polling_task_exception:
|
|
117
|
-
self._logger.
|
|
113
|
+
self._logger.exception(polling_task_exception)
|
|
118
114
|
|
|
119
115
|
raise WorkerExecutionException("The worker was closed by an unexpected exception")
|
|
120
116
|
|
|
@@ -227,41 +223,32 @@ class BaseBuzAIOKafkaAsyncConsumer(AsyncConsumer):
|
|
|
227
223
|
if len(kafka_poll_records) == 0:
|
|
228
224
|
await sleep(self.__seconds_between_polls_if_there_are_no_new_tasks)
|
|
229
225
|
|
|
230
|
-
return
|
|
231
|
-
|
|
232
226
|
async def __consume_events_task(self) -> None:
|
|
233
227
|
self._logger.info("Initializing consuming task")
|
|
234
|
-
|
|
235
|
-
blocked_tasks_iterator = self.generate_blocked_consuming_tasks_iterator()
|
|
228
|
+
blocked_tasks_iterator = self.__generate_blocked_consuming_tasks_iterator()
|
|
236
229
|
|
|
237
230
|
async for consuming_task in blocked_tasks_iterator:
|
|
238
231
|
consumer = consuming_task.consumer
|
|
239
232
|
kafka_poll_record = consuming_task.kafka_poll_record
|
|
240
|
-
executor = self.__executor_per_consumer_mapper[consuming_task.consumer]
|
|
241
233
|
|
|
234
|
+
executor = self.__executor_per_consumer_mapper[consumer]
|
|
242
235
|
await executor.consume(kafka_poll_record=kafka_poll_record)
|
|
243
|
-
|
|
244
236
|
await consumer.commit_poll_record(kafka_poll_record)
|
|
245
237
|
|
|
246
238
|
self.__events_processed += 1
|
|
247
239
|
|
|
248
240
|
# This iterator return a blocked task, that will be blocked for other process (like rebalancing), until the next task will be requested
|
|
249
|
-
async def
|
|
241
|
+
async def __generate_blocked_consuming_tasks_iterator(self) -> AsyncIterator[ConsumingTask]:
|
|
250
242
|
consumer_queues_cyclic_iterator = cycle(self.__queue_per_consumer_mapper.items())
|
|
251
243
|
last_consumer, _ = next(consumer_queues_cyclic_iterator)
|
|
252
244
|
|
|
253
245
|
while not self.__should_stop.is_set():
|
|
254
|
-
|
|
255
|
-
[queue.is_totally_empty() for queue in self.__queue_per_consumer_mapper.values()]
|
|
256
|
-
)
|
|
257
|
-
|
|
258
|
-
if all_queues_are_empty:
|
|
246
|
+
if await self.__all_queues_are_empty():
|
|
259
247
|
await sleep(self.__seconds_between_executions_if_there_are_no_tasks_in_the_queue)
|
|
260
248
|
continue
|
|
261
249
|
|
|
262
250
|
async with self.__task_execution_mutex:
|
|
263
251
|
consumer: Optional[AIOKafkaConsumer] = None
|
|
264
|
-
kafka_poll_record: Optional[KafkaPollRecord] = None
|
|
265
252
|
|
|
266
253
|
while consumer != last_consumer:
|
|
267
254
|
consumer, queue = next(consumer_queues_cyclic_iterator)
|
|
@@ -272,7 +259,8 @@ class BaseBuzAIOKafkaAsyncConsumer(AsyncConsumer):
|
|
|
272
259
|
last_consumer = consumer
|
|
273
260
|
break
|
|
274
261
|
|
|
275
|
-
|
|
262
|
+
async def __all_queues_are_empty(self) -> bool:
|
|
263
|
+
return all([queue.is_totally_empty() for queue in self.__queue_per_consumer_mapper.values()])
|
|
276
264
|
|
|
277
265
|
async def __on_partition_revoked(self, consumer: AIOKafkaConsumer, topics_partitions: set[TopicPartition]) -> None:
|
|
278
266
|
async with self.__task_execution_mutex:
|
{buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/event/infrastructure/buz_kafka/buz_kafka_event_bus.py
RENAMED
|
@@ -14,6 +14,7 @@ from buz.event.middleware.publish_middleware_chain_resolver import PublishMiddle
|
|
|
14
14
|
from buz.kafka import (
|
|
15
15
|
KafkaPythonProducer,
|
|
16
16
|
)
|
|
17
|
+
from buz.kafka.domain.exceptions.topic_already_created_exception import KafkaTopicsAlreadyCreatedException
|
|
17
18
|
from buz.kafka.domain.models.auto_create_topic_configuration import AutoCreateTopicConfiguration
|
|
18
19
|
from buz.kafka.domain.models.create_kafka_topic import CreateKafkaTopic
|
|
19
20
|
from buz.kafka.domain.services.kafka_admin_client import KafkaAdminClient
|
|
@@ -53,18 +54,22 @@ class BuzKafkaEventBus(EventBus):
|
|
|
53
54
|
topic = self.__publish_strategy.get_topic(event)
|
|
54
55
|
|
|
55
56
|
if self.__auto_create_topic_configuration is not None and self.__is_topic_created(topic) is False:
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
57
|
+
try:
|
|
58
|
+
self.__logger.info(f"Creating missing topic: {topic}..")
|
|
59
|
+
self.__get_kafka_admin_client().create_topics(
|
|
60
|
+
topics=[
|
|
61
|
+
CreateKafkaTopic(
|
|
62
|
+
name=topic,
|
|
63
|
+
partitions=self.__auto_create_topic_configuration.partitions,
|
|
64
|
+
replication_factor=self.__auto_create_topic_configuration.replication_factor,
|
|
65
|
+
configs=self.__auto_create_topic_configuration.configs,
|
|
66
|
+
)
|
|
67
|
+
]
|
|
68
|
+
)
|
|
69
|
+
self.__logger.info(f"Created missing topic: {topic}")
|
|
70
|
+
self.__topics_checked[topic] = True
|
|
71
|
+
except KafkaTopicsAlreadyCreatedException:
|
|
72
|
+
pass
|
|
68
73
|
|
|
69
74
|
headers = self.__get_event_headers(event)
|
|
70
75
|
self.__producer.produce(
|
|
@@ -67,11 +67,6 @@ class KafkaEventSyncSubscriberExecutor(KafkaEventSubscriberExecutor):
|
|
|
67
67
|
self.__logger.error(
|
|
68
68
|
f'The message "{str(kafka_poll_record.value)}" is not valid, it will be consumed but not processed'
|
|
69
69
|
)
|
|
70
|
-
except Exception as exception:
|
|
71
|
-
if self.__on_fail_strategy == KafkaOnFailStrategy.CONSUME_ON_FAIL:
|
|
72
|
-
self.__logger.error(f"Error consuming event: {exception}")
|
|
73
|
-
return
|
|
74
|
-
raise exception
|
|
75
70
|
|
|
76
71
|
def __execution_callback(self, subscriber: Subscriber, message: KafkaConsumerRecord[Event]) -> None:
|
|
77
72
|
self.__consume_middleware_chain_resolver.resolve(
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class NotAllPartitionAssignedException(Exception):
|
|
5
|
+
def __init__(self, topic_name: str) -> None:
|
|
6
|
+
super().__init__(
|
|
7
|
+
f'Not all the partition were assigned for the topic "{topic_name}", please disconnect the rest of subscribers'
|
|
8
|
+
)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from abc import abstractmethod, ABC
|
|
4
|
+
from datetime import datetime
|
|
4
5
|
from typing import Sequence
|
|
5
6
|
|
|
6
7
|
from buz.kafka.domain.models.create_kafka_topic import CreateKafkaTopic
|
|
@@ -37,3 +38,16 @@ class KafkaAdminClient(ABC):
|
|
|
37
38
|
self,
|
|
38
39
|
) -> set[str]:
|
|
39
40
|
pass
|
|
41
|
+
|
|
42
|
+
# This function moves the following offset from the provided date
|
|
43
|
+
# if there are no messages with a date greater than the provided offset
|
|
44
|
+
# the offset will be moved to the end
|
|
45
|
+
@abstractmethod
|
|
46
|
+
def move_offsets_to_datetime(
|
|
47
|
+
self,
|
|
48
|
+
*,
|
|
49
|
+
consumer_group: str,
|
|
50
|
+
topic: str,
|
|
51
|
+
target_datetime: datetime,
|
|
52
|
+
) -> None:
|
|
53
|
+
pass
|
|
@@ -123,7 +123,7 @@ class AIOKafkaConsumer:
|
|
|
123
123
|
try:
|
|
124
124
|
self.__logger.info(f"Creating missing topics: {non_created_topics}...")
|
|
125
125
|
kafka_admin_client.create_topics(topics=topics_to_create)
|
|
126
|
-
self.__logger.info(f"Created missing topics: {non_created_topics}
|
|
126
|
+
self.__logger.info(f"Created missing topics: {non_created_topics}")
|
|
127
127
|
except KafkaTopicsAlreadyCreatedException:
|
|
128
128
|
# there is a possibility to have a race condition between the check and the creation
|
|
129
129
|
# but it does not matters, the important part is that the topic is created
|
|
@@ -3,9 +3,11 @@ from dataclasses import dataclass
|
|
|
3
3
|
|
|
4
4
|
@dataclass(frozen=True)
|
|
5
5
|
class CDCPayload:
|
|
6
|
+
DATE_TIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ"
|
|
7
|
+
|
|
6
8
|
payload: str # json encoded
|
|
7
9
|
event_id: str # uuid
|
|
8
|
-
created_at: str
|
|
10
|
+
created_at: str
|
|
9
11
|
event_fqn: str
|
|
10
12
|
|
|
11
13
|
def validate(self) -> None:
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from datetime import datetime
|
|
3
4
|
from typing import TypeVar, Type, Generic
|
|
4
5
|
|
|
5
6
|
import orjson
|
|
6
7
|
from dacite import from_dict
|
|
7
8
|
|
|
8
|
-
from buz.kafka.infrastructure.deserializers.implementations.cdc.not_valid_cdc_message_exception import (
|
|
9
|
-
NotValidCDCMessageException,
|
|
10
|
-
)
|
|
11
9
|
from buz.event import Event
|
|
12
10
|
from buz.kafka.infrastructure.cdc.cdc_message import CDCMessage, CDCPayload
|
|
13
11
|
from buz.kafka.infrastructure.deserializers.bytes_to_message_deserializer import BytesToMessageDeserializer
|
|
12
|
+
from buz.kafka.infrastructure.deserializers.implementations.cdc.not_valid_cdc_message_exception import (
|
|
13
|
+
NotValidCDCMessageException,
|
|
14
|
+
)
|
|
14
15
|
|
|
15
16
|
T = TypeVar("T", bound=Event)
|
|
16
17
|
|
|
@@ -27,12 +28,16 @@ class CDCRecordBytesToEventDeserializer(BytesToMessageDeserializer[Event], Gener
|
|
|
27
28
|
cdc_message = self.__get_outbox_record_as_dict(decoded_string)
|
|
28
29
|
return self.__event_class.restore(
|
|
29
30
|
id=cdc_message.payload.event_id,
|
|
30
|
-
created_at=cdc_message.payload.created_at,
|
|
31
|
+
created_at=self.__get_created_at_in_event_format(cdc_message.payload.created_at),
|
|
31
32
|
**orjson.loads(cdc_message.payload.payload),
|
|
32
33
|
)
|
|
33
34
|
except Exception as exception:
|
|
34
35
|
raise NotValidCDCMessageException(decoded_string, exception) from exception
|
|
35
36
|
|
|
37
|
+
def __get_created_at_in_event_format(self, cdc_payload_created_at: str) -> str:
|
|
38
|
+
created_at_datetime = datetime.strptime(cdc_payload_created_at, CDCPayload.DATE_TIME_FORMAT)
|
|
39
|
+
return created_at_datetime.strftime(Event.DATE_TIME_FORMAT)
|
|
40
|
+
|
|
36
41
|
def __get_outbox_record_as_dict(self, decoded_string: str) -> CDCMessage:
|
|
37
42
|
decoded_record: dict = orjson.loads(decoded_string)
|
|
38
43
|
|
|
@@ -1,19 +1,30 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from logging import Logger
|
|
3
5
|
import re
|
|
4
|
-
from typing import Any, Callable, Sequence
|
|
6
|
+
from typing import Any, Callable, Optional, Sequence, cast
|
|
5
7
|
|
|
6
8
|
from cachetools import TTLCache
|
|
7
|
-
from kafka import KafkaClient
|
|
9
|
+
from kafka import KafkaClient, KafkaConsumer
|
|
8
10
|
from kafka.admin import KafkaAdminClient as KafkaPythonLibraryAdminClient, NewTopic
|
|
9
11
|
from kafka.errors import TopicAlreadyExistsError
|
|
12
|
+
from kafka.structs import TopicPartition, OffsetAndTimestamp
|
|
10
13
|
|
|
14
|
+
from buz.kafka.domain.exceptions.not_all_partition_assigned_exception import NotAllPartitionAssignedException
|
|
11
15
|
from buz.kafka.domain.exceptions.topic_already_created_exception import KafkaTopicsAlreadyCreatedException
|
|
16
|
+
from buz.kafka.domain.exceptions.topic_not_found_exception import TopicNotFoundException
|
|
17
|
+
from buz.kafka.domain.models.consumer_initial_offset_position import ConsumerInitialOffsetPosition
|
|
12
18
|
from buz.kafka.domain.models.create_kafka_topic import CreateKafkaTopic
|
|
13
19
|
from buz.kafka.domain.models.kafka_connection_config import KafkaConnectionConfig
|
|
14
20
|
from buz.kafka.domain.services.kafka_admin_client import KafkaAdminClient
|
|
15
21
|
|
|
22
|
+
from buz.kafka.infrastructure.kafka_python.translators.consumer_initial_offset_position_translator import (
|
|
23
|
+
KafkaPythonConsumerInitialOffsetPositionTranslator,
|
|
24
|
+
)
|
|
25
|
+
|
|
16
26
|
INTERNAL_KAFKA_TOPICS = {"__consumer_offsets", "_schema"}
|
|
27
|
+
TOPIC_CACHE_KEY = "topics"
|
|
17
28
|
|
|
18
29
|
|
|
19
30
|
class KafkaPythonAdminClient(KafkaAdminClient):
|
|
@@ -22,9 +33,11 @@ class KafkaPythonAdminClient(KafkaAdminClient):
|
|
|
22
33
|
def __init__(
|
|
23
34
|
self,
|
|
24
35
|
*,
|
|
36
|
+
logger: Logger,
|
|
25
37
|
config: KafkaConnectionConfig,
|
|
26
38
|
cache_ttl_seconds: int = 0,
|
|
27
39
|
):
|
|
40
|
+
self._logger = logger
|
|
28
41
|
self._config = config
|
|
29
42
|
self._config_in_library_format = self.__get_kafka_config_in_library_format(config)
|
|
30
43
|
self._kafka_admin = KafkaPythonLibraryAdminClient(**self._config_in_library_format)
|
|
@@ -75,14 +88,13 @@ class KafkaPythonAdminClient(KafkaAdminClient):
|
|
|
75
88
|
self,
|
|
76
89
|
topic: str,
|
|
77
90
|
) -> bool:
|
|
78
|
-
|
|
79
|
-
return topic in topics
|
|
91
|
+
return topic in self.get_topics()
|
|
80
92
|
|
|
81
93
|
def get_topics(
|
|
82
94
|
self,
|
|
83
95
|
) -> set[str]:
|
|
84
96
|
return self.__resolve_cached_property(
|
|
85
|
-
|
|
97
|
+
TOPIC_CACHE_KEY, lambda: set(self._kafka_admin.list_topics()) - INTERNAL_KAFKA_TOPICS
|
|
86
98
|
)
|
|
87
99
|
|
|
88
100
|
def __resolve_cached_property(self, property_key: str, callback: Callable) -> Any:
|
|
@@ -101,6 +113,10 @@ class KafkaPythonAdminClient(KafkaAdminClient):
|
|
|
101
113
|
self._kafka_admin.delete_topics(
|
|
102
114
|
topics=topics,
|
|
103
115
|
)
|
|
116
|
+
self.__remove_cache_property(TOPIC_CACHE_KEY)
|
|
117
|
+
|
|
118
|
+
def __remove_cache_property(self, property_key: str) -> None:
|
|
119
|
+
self.__ttl_cache.pop(property_key, None)
|
|
104
120
|
|
|
105
121
|
def delete_subscription_groups(
|
|
106
122
|
self,
|
|
@@ -119,3 +135,80 @@ class KafkaPythonAdminClient(KafkaAdminClient):
|
|
|
119
135
|
def _wait_for_cluster_update(self) -> None:
|
|
120
136
|
future = self._kafka_client.cluster.request_update()
|
|
121
137
|
self._kafka_client.poll(future=future)
|
|
138
|
+
|
|
139
|
+
def move_offsets_to_datetime(
|
|
140
|
+
self,
|
|
141
|
+
*,
|
|
142
|
+
consumer_group: str,
|
|
143
|
+
topic: str,
|
|
144
|
+
target_datetime: datetime,
|
|
145
|
+
) -> None:
|
|
146
|
+
consumer = KafkaConsumer(
|
|
147
|
+
group_id=consumer_group,
|
|
148
|
+
enable_auto_commit=False,
|
|
149
|
+
auto_offset_reset=KafkaPythonConsumerInitialOffsetPositionTranslator.to_kafka_supported_format(
|
|
150
|
+
ConsumerInitialOffsetPosition.BEGINNING
|
|
151
|
+
),
|
|
152
|
+
**self._config_in_library_format,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
partitions = consumer.partitions_for_topic(topic)
|
|
156
|
+
|
|
157
|
+
if partitions is None:
|
|
158
|
+
raise TopicNotFoundException(topic)
|
|
159
|
+
|
|
160
|
+
topic_partitions = [TopicPartition(topic, p) for p in partitions]
|
|
161
|
+
consumer.subscribe(topics=[topic])
|
|
162
|
+
|
|
163
|
+
self.__force_partition_assignment(consumer)
|
|
164
|
+
|
|
165
|
+
# We need all the partitions in order to update the offsets
|
|
166
|
+
if len(consumer.assignment()) != len(topic_partitions):
|
|
167
|
+
raise NotAllPartitionAssignedException(topic)
|
|
168
|
+
|
|
169
|
+
offsets_for_date = self.__get_first_offset_after_date(
|
|
170
|
+
consumer=consumer,
|
|
171
|
+
topic_partitions=topic_partitions,
|
|
172
|
+
target_datetime=target_datetime,
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
end_offsets = consumer.end_offsets(topic_partitions)
|
|
176
|
+
|
|
177
|
+
if end_offsets is None or len(end_offsets.keys()) != len(topic_partitions):
|
|
178
|
+
raise Exception(f'There was an error extracting the end offsets of the topic "{topic}"')
|
|
179
|
+
|
|
180
|
+
for topic_partition in topic_partitions:
|
|
181
|
+
offset_and_timestamp = offsets_for_date.get(topic_partition)
|
|
182
|
+
if offset_and_timestamp:
|
|
183
|
+
self._logger.info(f'moving "{topic_partition}" to the offset "{offset_and_timestamp.offset}"')
|
|
184
|
+
consumer.seek(topic_partition, offset_and_timestamp.offset)
|
|
185
|
+
else:
|
|
186
|
+
self._logger.info(
|
|
187
|
+
f'moving "{topic_partition}" to the end of the topic because there are no messages later than "{target_datetime}"'
|
|
188
|
+
)
|
|
189
|
+
consumer.seek(topic_partition, end_offsets[topic_partition])
|
|
190
|
+
|
|
191
|
+
consumer.commit()
|
|
192
|
+
consumer.close()
|
|
193
|
+
|
|
194
|
+
def __get_first_offset_after_date(
|
|
195
|
+
self,
|
|
196
|
+
*,
|
|
197
|
+
consumer: KafkaConsumer,
|
|
198
|
+
topic_partitions: Sequence[TopicPartition],
|
|
199
|
+
target_datetime: datetime,
|
|
200
|
+
) -> dict[TopicPartition, Optional[OffsetAndTimestamp]]:
|
|
201
|
+
offset_for_times: dict[TopicPartition, Optional[int]] = {}
|
|
202
|
+
timestamp_ms = int(target_datetime.timestamp() * 1000)
|
|
203
|
+
|
|
204
|
+
for topic_partition in topic_partitions:
|
|
205
|
+
offset_for_times[topic_partition] = timestamp_ms
|
|
206
|
+
|
|
207
|
+
return cast(
|
|
208
|
+
dict[TopicPartition, Optional[OffsetAndTimestamp]],
|
|
209
|
+
consumer.offsets_for_times(offset_for_times),
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
# We are not to commit the new offset, but we need to execute a polling in order to start the partition assignment
|
|
213
|
+
def __force_partition_assignment(self, consumer: KafkaConsumer) -> None:
|
|
214
|
+
consumer.poll(max_records=1, timeout_ms=0)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from logging import Logger
|
|
3
4
|
from typing import Optional
|
|
4
5
|
|
|
5
6
|
from kafka import KafkaConsumer, KafkaProducer
|
|
@@ -22,8 +23,16 @@ CONSUMER_POLL_TIMEOUT_MS = 1000
|
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
class KafkaPythonAdminTestClient(KafkaPythonAdminClient, KafkaAdminTestClient):
|
|
25
|
-
def __init__(
|
|
26
|
-
|
|
26
|
+
def __init__(
|
|
27
|
+
self,
|
|
28
|
+
*,
|
|
29
|
+
logger: Logger,
|
|
30
|
+
config: KafkaConnectionConfig,
|
|
31
|
+
):
|
|
32
|
+
super().__init__(
|
|
33
|
+
config=config,
|
|
34
|
+
logger=logger,
|
|
35
|
+
)
|
|
27
36
|
|
|
28
37
|
def send_message_to_topic(
|
|
29
38
|
self,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from dataclasses import asdict
|
|
4
|
+
from datetime import datetime
|
|
4
5
|
|
|
5
6
|
from buz.event import Event
|
|
6
7
|
from buz.kafka.infrastructure.cdc.cdc_message import CDCMessage, CDCPayload
|
|
@@ -16,13 +17,17 @@ class CDCRecordBytesToEventSerializer(ByteSerializer):
|
|
|
16
17
|
cdc_message: CDCMessage = CDCMessage(
|
|
17
18
|
payload=CDCPayload(
|
|
18
19
|
event_id=data.id,
|
|
19
|
-
created_at=data.created_at,
|
|
20
|
+
created_at=self.__adapt_created_to_cdc_format(data.created_at),
|
|
20
21
|
event_fqn=data.fqn(),
|
|
21
22
|
payload=self.__serialize_payload(data),
|
|
22
23
|
)
|
|
23
24
|
)
|
|
24
25
|
return self.__json_serializer.serialize(asdict(cdc_message))
|
|
25
26
|
|
|
27
|
+
def __adapt_created_to_cdc_format(self, created_at: str) -> str:
|
|
28
|
+
created_at_datetime = datetime.strptime(created_at, Event.DATE_TIME_FORMAT)
|
|
29
|
+
return created_at_datetime.strftime(CDCPayload.DATE_TIME_FORMAT)
|
|
30
|
+
|
|
26
31
|
def __serialize_payload(self, event: Event) -> str:
|
|
27
32
|
# Remove id and created at, because Transactional outbox is not adding them
|
|
28
33
|
payload = asdict(event)
|
|
@@ -1,37 +1,20 @@
|
|
|
1
|
-
from threading import Lock
|
|
2
1
|
from queue import Queue, Empty
|
|
3
2
|
from typing import Optional, TypeVar, cast
|
|
4
3
|
|
|
5
4
|
from buz.queue.multiqueue_repository import MultiqueueRepository
|
|
6
5
|
|
|
7
|
-
|
|
8
6
|
K = TypeVar("K")
|
|
9
7
|
R = TypeVar("R")
|
|
10
8
|
|
|
11
9
|
|
|
12
|
-
def self_mutex(method):
|
|
13
|
-
def call(self, *args, **kwargs):
|
|
14
|
-
lock: Lock = self._get_method_lock() # type: ignore
|
|
15
|
-
with lock:
|
|
16
|
-
return method(self, *args, **kwargs)
|
|
17
|
-
|
|
18
|
-
return call
|
|
19
|
-
|
|
20
|
-
|
|
21
10
|
class InMemoryMultiqueueRepository(MultiqueueRepository[K, R]):
|
|
22
11
|
def __init__(self):
|
|
23
12
|
self.__queues = cast(dict[K, Queue[R]], {})
|
|
24
|
-
self.__mutex = Lock()
|
|
25
13
|
self.__last_key_index = 0
|
|
26
14
|
|
|
27
|
-
def _get_method_lock(self) -> Lock:
|
|
28
|
-
return self.__mutex
|
|
29
|
-
|
|
30
|
-
@self_mutex
|
|
31
15
|
def clear(self, key: K) -> None:
|
|
32
16
|
self.__queues.pop(key, None)
|
|
33
17
|
|
|
34
|
-
@self_mutex
|
|
35
18
|
def push(self, key: K, record: R) -> None:
|
|
36
19
|
if key not in self.__queues:
|
|
37
20
|
self.__add_key(key)
|
|
@@ -41,7 +24,6 @@ class InMemoryMultiqueueRepository(MultiqueueRepository[K, R]):
|
|
|
41
24
|
def __add_key(self, key: K) -> None:
|
|
42
25
|
self.__queues[key] = Queue[R]()
|
|
43
26
|
|
|
44
|
-
@self_mutex
|
|
45
27
|
def pop(self) -> Optional[R]:
|
|
46
28
|
if not self.__queues:
|
|
47
29
|
return None
|
|
@@ -65,10 +47,8 @@ class InMemoryMultiqueueRepository(MultiqueueRepository[K, R]):
|
|
|
65
47
|
|
|
66
48
|
return None
|
|
67
49
|
|
|
68
|
-
@self_mutex
|
|
69
50
|
def get_total_size(self) -> int:
|
|
70
51
|
return sum([queue.qsize() for queue in self.__queues.values()])
|
|
71
52
|
|
|
72
|
-
@self_mutex
|
|
73
53
|
def is_totally_empty(self) -> bool:
|
|
74
54
|
return all([queue.empty() for queue in self.__queues.values()])
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/command/asynchronous/middleware/base_handle_middleware.py
RENAMED
|
File without changes
|
{buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/command/asynchronous/middleware/handle_middleware.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/command/more_than_one_command_handler_related_exception.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/command/synchronous/middleware/base_handle_middleware.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/command/synchronous/self_process/self_process_command_bus.py
RENAMED
|
File without changes
|
|
File without changes
|
{buz-2.13.1rc7 → buz-2.13.1rc9}/src/buz/command/synchronous/synced_async/synced_async_command_bus.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|