omnibase_infra 0.2.6__py3-none-any.whl
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.
- omnibase_infra/__init__.py +101 -0
- omnibase_infra/adapters/adapter_onex_tool_execution.py +451 -0
- omnibase_infra/capabilities/__init__.py +15 -0
- omnibase_infra/capabilities/capability_inference_rules.py +211 -0
- omnibase_infra/capabilities/contract_capability_extractor.py +221 -0
- omnibase_infra/capabilities/intent_type_extractor.py +160 -0
- omnibase_infra/cli/__init__.py +1 -0
- omnibase_infra/cli/commands.py +216 -0
- omnibase_infra/clients/__init__.py +0 -0
- omnibase_infra/configs/widget_mapping.yaml +176 -0
- omnibase_infra/constants_topic_patterns.py +26 -0
- omnibase_infra/contracts/handlers/filesystem/handler_contract.yaml +264 -0
- omnibase_infra/contracts/handlers/mcp/handler_contract.yaml +141 -0
- omnibase_infra/decorators/__init__.py +29 -0
- omnibase_infra/decorators/allow_any.py +109 -0
- omnibase_infra/dlq/__init__.py +90 -0
- omnibase_infra/dlq/constants_dlq.py +57 -0
- omnibase_infra/dlq/models/__init__.py +26 -0
- omnibase_infra/dlq/models/enum_replay_status.py +37 -0
- omnibase_infra/dlq/models/model_dlq_replay_record.py +135 -0
- omnibase_infra/dlq/models/model_dlq_tracking_config.py +184 -0
- omnibase_infra/dlq/service_dlq_tracking.py +611 -0
- omnibase_infra/enums/__init__.py +132 -0
- omnibase_infra/enums/enum_any_type_violation.py +104 -0
- omnibase_infra/enums/enum_backend_type.py +27 -0
- omnibase_infra/enums/enum_capture_outcome.py +42 -0
- omnibase_infra/enums/enum_capture_state.py +88 -0
- omnibase_infra/enums/enum_chain_violation_type.py +119 -0
- omnibase_infra/enums/enum_circuit_state.py +51 -0
- omnibase_infra/enums/enum_confirmation_event_type.py +27 -0
- omnibase_infra/enums/enum_consumer_group_purpose.py +92 -0
- omnibase_infra/enums/enum_contract_type.py +84 -0
- omnibase_infra/enums/enum_dedupe_strategy.py +46 -0
- omnibase_infra/enums/enum_dispatch_status.py +191 -0
- omnibase_infra/enums/enum_environment.py +46 -0
- omnibase_infra/enums/enum_execution_shape_violation.py +103 -0
- omnibase_infra/enums/enum_handler_error_type.py +111 -0
- omnibase_infra/enums/enum_handler_loader_error.py +178 -0
- omnibase_infra/enums/enum_handler_source_mode.py +86 -0
- omnibase_infra/enums/enum_handler_source_type.py +87 -0
- omnibase_infra/enums/enum_handler_type.py +77 -0
- omnibase_infra/enums/enum_handler_type_category.py +61 -0
- omnibase_infra/enums/enum_infra_transport_type.py +73 -0
- omnibase_infra/enums/enum_introspection_reason.py +154 -0
- omnibase_infra/enums/enum_kafka_acks.py +99 -0
- omnibase_infra/enums/enum_message_category.py +213 -0
- omnibase_infra/enums/enum_node_archetype.py +74 -0
- omnibase_infra/enums/enum_node_output_type.py +185 -0
- omnibase_infra/enums/enum_non_retryable_error_category.py +224 -0
- omnibase_infra/enums/enum_policy_type.py +32 -0
- omnibase_infra/enums/enum_registration_state.py +261 -0
- omnibase_infra/enums/enum_registration_status.py +33 -0
- omnibase_infra/enums/enum_registry_response_status.py +28 -0
- omnibase_infra/enums/enum_response_status.py +26 -0
- omnibase_infra/enums/enum_retry_error_category.py +98 -0
- omnibase_infra/enums/enum_security_rule_id.py +103 -0
- omnibase_infra/enums/enum_selection_strategy.py +91 -0
- omnibase_infra/enums/enum_topic_standard.py +42 -0
- omnibase_infra/enums/enum_validation_severity.py +78 -0
- omnibase_infra/errors/__init__.py +160 -0
- omnibase_infra/errors/error_architecture_violation.py +152 -0
- omnibase_infra/errors/error_binding_resolution.py +128 -0
- omnibase_infra/errors/error_chain_propagation.py +188 -0
- omnibase_infra/errors/error_compute_registry.py +95 -0
- omnibase_infra/errors/error_consul.py +132 -0
- omnibase_infra/errors/error_container_wiring.py +243 -0
- omnibase_infra/errors/error_event_bus_registry.py +105 -0
- omnibase_infra/errors/error_infra.py +610 -0
- omnibase_infra/errors/error_message_type_registry.py +101 -0
- omnibase_infra/errors/error_policy_registry.py +115 -0
- omnibase_infra/errors/error_vault.py +123 -0
- omnibase_infra/event_bus/__init__.py +72 -0
- omnibase_infra/event_bus/configs/kafka_event_bus_config.yaml +84 -0
- omnibase_infra/event_bus/event_bus_inmemory.py +797 -0
- omnibase_infra/event_bus/event_bus_kafka.py +1716 -0
- omnibase_infra/event_bus/mixin_kafka_broadcast.py +180 -0
- omnibase_infra/event_bus/mixin_kafka_dlq.py +771 -0
- omnibase_infra/event_bus/models/__init__.py +29 -0
- omnibase_infra/event_bus/models/config/__init__.py +20 -0
- omnibase_infra/event_bus/models/config/model_kafka_event_bus_config.py +693 -0
- omnibase_infra/event_bus/models/model_dlq_event.py +206 -0
- omnibase_infra/event_bus/models/model_dlq_metrics.py +304 -0
- omnibase_infra/event_bus/models/model_event_headers.py +115 -0
- omnibase_infra/event_bus/models/model_event_message.py +60 -0
- omnibase_infra/event_bus/testing/__init__.py +26 -0
- omnibase_infra/event_bus/testing/adapter_protocol_event_publisher_inmemory.py +418 -0
- omnibase_infra/event_bus/testing/model_publisher_metrics.py +64 -0
- omnibase_infra/event_bus/topic_constants.py +376 -0
- omnibase_infra/handlers/__init__.py +82 -0
- omnibase_infra/handlers/filesystem/__init__.py +48 -0
- omnibase_infra/handlers/filesystem/enum_file_system_operation.py +35 -0
- omnibase_infra/handlers/filesystem/model_file_system_request.py +298 -0
- omnibase_infra/handlers/filesystem/model_file_system_result.py +166 -0
- omnibase_infra/handlers/handler_consul.py +795 -0
- omnibase_infra/handlers/handler_db.py +1046 -0
- omnibase_infra/handlers/handler_filesystem.py +1478 -0
- omnibase_infra/handlers/handler_graph.py +2015 -0
- omnibase_infra/handlers/handler_http.py +926 -0
- omnibase_infra/handlers/handler_intent.py +387 -0
- omnibase_infra/handlers/handler_manifest_persistence.contract.yaml +184 -0
- omnibase_infra/handlers/handler_manifest_persistence.py +1539 -0
- omnibase_infra/handlers/handler_mcp.py +1430 -0
- omnibase_infra/handlers/handler_qdrant.py +1076 -0
- omnibase_infra/handlers/handler_vault.py +428 -0
- omnibase_infra/handlers/mcp/__init__.py +19 -0
- omnibase_infra/handlers/mcp/adapter_onex_to_mcp.py +446 -0
- omnibase_infra/handlers/mcp/protocols.py +178 -0
- omnibase_infra/handlers/mcp/transport_streamable_http.py +352 -0
- omnibase_infra/handlers/mixins/__init__.py +47 -0
- omnibase_infra/handlers/mixins/mixin_consul_initialization.py +349 -0
- omnibase_infra/handlers/mixins/mixin_consul_kv.py +338 -0
- omnibase_infra/handlers/mixins/mixin_consul_service.py +542 -0
- omnibase_infra/handlers/mixins/mixin_consul_topic_index.py +585 -0
- omnibase_infra/handlers/mixins/mixin_vault_initialization.py +338 -0
- omnibase_infra/handlers/mixins/mixin_vault_retry.py +412 -0
- omnibase_infra/handlers/mixins/mixin_vault_secrets.py +450 -0
- omnibase_infra/handlers/mixins/mixin_vault_token.py +365 -0
- omnibase_infra/handlers/models/__init__.py +286 -0
- omnibase_infra/handlers/models/consul/__init__.py +81 -0
- omnibase_infra/handlers/models/consul/enum_consul_operation_type.py +57 -0
- omnibase_infra/handlers/models/consul/model_consul_deregister_payload.py +51 -0
- omnibase_infra/handlers/models/consul/model_consul_handler_config.py +153 -0
- omnibase_infra/handlers/models/consul/model_consul_handler_payload.py +89 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_get_found_payload.py +55 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_get_not_found_payload.py +49 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_get_recurse_payload.py +50 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_item.py +33 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_put_payload.py +41 -0
- omnibase_infra/handlers/models/consul/model_consul_register_payload.py +53 -0
- omnibase_infra/handlers/models/consul/model_consul_retry_config.py +66 -0
- omnibase_infra/handlers/models/consul/model_payload_consul.py +66 -0
- omnibase_infra/handlers/models/consul/registry_payload_consul.py +214 -0
- omnibase_infra/handlers/models/graph/__init__.py +35 -0
- omnibase_infra/handlers/models/graph/enum_graph_operation_type.py +20 -0
- omnibase_infra/handlers/models/graph/model_graph_execute_payload.py +38 -0
- omnibase_infra/handlers/models/graph/model_graph_handler_config.py +54 -0
- omnibase_infra/handlers/models/graph/model_graph_handler_payload.py +44 -0
- omnibase_infra/handlers/models/graph/model_graph_query_payload.py +40 -0
- omnibase_infra/handlers/models/graph/model_graph_record.py +22 -0
- omnibase_infra/handlers/models/http/__init__.py +50 -0
- omnibase_infra/handlers/models/http/enum_http_operation_type.py +29 -0
- omnibase_infra/handlers/models/http/model_http_body_content.py +45 -0
- omnibase_infra/handlers/models/http/model_http_get_payload.py +88 -0
- omnibase_infra/handlers/models/http/model_http_handler_payload.py +90 -0
- omnibase_infra/handlers/models/http/model_http_post_payload.py +88 -0
- omnibase_infra/handlers/models/http/model_payload_http.py +66 -0
- omnibase_infra/handlers/models/http/registry_payload_http.py +212 -0
- omnibase_infra/handlers/models/mcp/__init__.py +23 -0
- omnibase_infra/handlers/models/mcp/enum_mcp_operation_type.py +24 -0
- omnibase_infra/handlers/models/mcp/model_mcp_handler_config.py +40 -0
- omnibase_infra/handlers/models/mcp/model_mcp_tool_call.py +32 -0
- omnibase_infra/handlers/models/mcp/model_mcp_tool_result.py +45 -0
- omnibase_infra/handlers/models/model_consul_handler_response.py +96 -0
- omnibase_infra/handlers/models/model_db_describe_response.py +83 -0
- omnibase_infra/handlers/models/model_db_query_payload.py +95 -0
- omnibase_infra/handlers/models/model_db_query_response.py +60 -0
- omnibase_infra/handlers/models/model_filesystem_config.py +98 -0
- omnibase_infra/handlers/models/model_filesystem_delete_payload.py +54 -0
- omnibase_infra/handlers/models/model_filesystem_delete_result.py +77 -0
- omnibase_infra/handlers/models/model_filesystem_directory_entry.py +75 -0
- omnibase_infra/handlers/models/model_filesystem_ensure_directory_payload.py +54 -0
- omnibase_infra/handlers/models/model_filesystem_ensure_directory_result.py +60 -0
- omnibase_infra/handlers/models/model_filesystem_list_directory_payload.py +60 -0
- omnibase_infra/handlers/models/model_filesystem_list_directory_result.py +68 -0
- omnibase_infra/handlers/models/model_filesystem_read_payload.py +62 -0
- omnibase_infra/handlers/models/model_filesystem_read_result.py +61 -0
- omnibase_infra/handlers/models/model_filesystem_write_payload.py +70 -0
- omnibase_infra/handlers/models/model_filesystem_write_result.py +55 -0
- omnibase_infra/handlers/models/model_graph_handler_response.py +98 -0
- omnibase_infra/handlers/models/model_handler_response.py +103 -0
- omnibase_infra/handlers/models/model_http_handler_response.py +101 -0
- omnibase_infra/handlers/models/model_manifest_metadata.py +75 -0
- omnibase_infra/handlers/models/model_manifest_persistence_config.py +62 -0
- omnibase_infra/handlers/models/model_manifest_query_payload.py +90 -0
- omnibase_infra/handlers/models/model_manifest_query_result.py +97 -0
- omnibase_infra/handlers/models/model_manifest_retrieve_payload.py +44 -0
- omnibase_infra/handlers/models/model_manifest_retrieve_result.py +98 -0
- omnibase_infra/handlers/models/model_manifest_store_payload.py +47 -0
- omnibase_infra/handlers/models/model_manifest_store_result.py +67 -0
- omnibase_infra/handlers/models/model_operation_context.py +187 -0
- omnibase_infra/handlers/models/model_qdrant_handler_response.py +98 -0
- omnibase_infra/handlers/models/model_retry_state.py +162 -0
- omnibase_infra/handlers/models/model_vault_handler_response.py +98 -0
- omnibase_infra/handlers/models/qdrant/__init__.py +44 -0
- omnibase_infra/handlers/models/qdrant/enum_qdrant_operation_type.py +26 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_collection_payload.py +42 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_delete_payload.py +36 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_handler_config.py +42 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_handler_payload.py +54 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_search_payload.py +42 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_search_result.py +30 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_upsert_payload.py +36 -0
- omnibase_infra/handlers/models/vault/__init__.py +69 -0
- omnibase_infra/handlers/models/vault/enum_vault_operation_type.py +35 -0
- omnibase_infra/handlers/models/vault/model_payload_vault.py +66 -0
- omnibase_infra/handlers/models/vault/model_vault_delete_payload.py +57 -0
- omnibase_infra/handlers/models/vault/model_vault_handler_config.py +148 -0
- omnibase_infra/handlers/models/vault/model_vault_handler_payload.py +101 -0
- omnibase_infra/handlers/models/vault/model_vault_list_payload.py +58 -0
- omnibase_infra/handlers/models/vault/model_vault_renew_token_payload.py +67 -0
- omnibase_infra/handlers/models/vault/model_vault_retry_config.py +66 -0
- omnibase_infra/handlers/models/vault/model_vault_secret_payload.py +106 -0
- omnibase_infra/handlers/models/vault/model_vault_write_payload.py +66 -0
- omnibase_infra/handlers/models/vault/registry_payload_vault.py +213 -0
- omnibase_infra/handlers/registration_storage/__init__.py +43 -0
- omnibase_infra/handlers/registration_storage/handler_registration_storage_mock.py +392 -0
- omnibase_infra/handlers/registration_storage/handler_registration_storage_postgres.py +922 -0
- omnibase_infra/handlers/registration_storage/models/__init__.py +23 -0
- omnibase_infra/handlers/registration_storage/models/model_delete_registration_request.py +58 -0
- omnibase_infra/handlers/registration_storage/models/model_update_registration_request.py +73 -0
- omnibase_infra/handlers/registration_storage/protocol_registration_persistence.py +191 -0
- omnibase_infra/handlers/service_discovery/__init__.py +43 -0
- omnibase_infra/handlers/service_discovery/handler_service_discovery_consul.py +1051 -0
- omnibase_infra/handlers/service_discovery/handler_service_discovery_mock.py +258 -0
- omnibase_infra/handlers/service_discovery/models/__init__.py +22 -0
- omnibase_infra/handlers/service_discovery/models/model_discovery_result.py +64 -0
- omnibase_infra/handlers/service_discovery/models/model_registration_result.py +138 -0
- omnibase_infra/handlers/service_discovery/models/model_service_info.py +109 -0
- omnibase_infra/handlers/service_discovery/protocol_discovery_operations.py +170 -0
- omnibase_infra/idempotency/__init__.py +94 -0
- omnibase_infra/idempotency/models/__init__.py +43 -0
- omnibase_infra/idempotency/models/model_idempotency_check_result.py +85 -0
- omnibase_infra/idempotency/models/model_idempotency_guard_config.py +130 -0
- omnibase_infra/idempotency/models/model_idempotency_record.py +86 -0
- omnibase_infra/idempotency/models/model_idempotency_store_health_check_result.py +81 -0
- omnibase_infra/idempotency/models/model_idempotency_store_metrics.py +140 -0
- omnibase_infra/idempotency/models/model_postgres_idempotency_store_config.py +299 -0
- omnibase_infra/idempotency/protocol_idempotency_store.py +184 -0
- omnibase_infra/idempotency/store_inmemory.py +265 -0
- omnibase_infra/idempotency/store_postgres.py +923 -0
- omnibase_infra/infrastructure/__init__.py +0 -0
- omnibase_infra/migrations/001_create_event_ledger.sql +166 -0
- omnibase_infra/migrations/001_drop_event_ledger.sql +18 -0
- omnibase_infra/mixins/__init__.py +71 -0
- omnibase_infra/mixins/mixin_async_circuit_breaker.py +656 -0
- omnibase_infra/mixins/mixin_dict_like_accessors.py +146 -0
- omnibase_infra/mixins/mixin_envelope_extraction.py +119 -0
- omnibase_infra/mixins/mixin_node_introspection.py +2670 -0
- omnibase_infra/mixins/mixin_retry_execution.py +386 -0
- omnibase_infra/mixins/protocol_circuit_breaker_aware.py +133 -0
- omnibase_infra/models/__init__.py +144 -0
- omnibase_infra/models/bindings/__init__.py +59 -0
- omnibase_infra/models/bindings/constants.py +144 -0
- omnibase_infra/models/bindings/model_binding_resolution_result.py +103 -0
- omnibase_infra/models/bindings/model_operation_binding.py +44 -0
- omnibase_infra/models/bindings/model_operation_bindings_subcontract.py +152 -0
- omnibase_infra/models/bindings/model_parsed_binding.py +52 -0
- omnibase_infra/models/corpus/__init__.py +17 -0
- omnibase_infra/models/corpus/model_capture_config.py +133 -0
- omnibase_infra/models/corpus/model_capture_result.py +86 -0
- omnibase_infra/models/discovery/__init__.py +42 -0
- omnibase_infra/models/discovery/model_dependency_spec.py +319 -0
- omnibase_infra/models/discovery/model_discovered_capabilities.py +50 -0
- omnibase_infra/models/discovery/model_introspection_config.py +330 -0
- omnibase_infra/models/discovery/model_introspection_performance_metrics.py +169 -0
- omnibase_infra/models/discovery/model_introspection_task_config.py +116 -0
- omnibase_infra/models/dispatch/__init__.py +155 -0
- omnibase_infra/models/dispatch/model_debug_trace_snapshot.py +114 -0
- omnibase_infra/models/dispatch/model_dispatch_context.py +439 -0
- omnibase_infra/models/dispatch/model_dispatch_error.py +336 -0
- omnibase_infra/models/dispatch/model_dispatch_log_context.py +400 -0
- omnibase_infra/models/dispatch/model_dispatch_metadata.py +228 -0
- omnibase_infra/models/dispatch/model_dispatch_metrics.py +496 -0
- omnibase_infra/models/dispatch/model_dispatch_outcome.py +317 -0
- omnibase_infra/models/dispatch/model_dispatch_outputs.py +231 -0
- omnibase_infra/models/dispatch/model_dispatch_result.py +436 -0
- omnibase_infra/models/dispatch/model_dispatch_route.py +279 -0
- omnibase_infra/models/dispatch/model_dispatcher_metrics.py +275 -0
- omnibase_infra/models/dispatch/model_dispatcher_registration.py +352 -0
- omnibase_infra/models/dispatch/model_materialized_dispatch.py +141 -0
- omnibase_infra/models/dispatch/model_parsed_topic.py +135 -0
- omnibase_infra/models/dispatch/model_topic_parser.py +725 -0
- omnibase_infra/models/dispatch/model_tracing_context.py +285 -0
- omnibase_infra/models/errors/__init__.py +45 -0
- omnibase_infra/models/errors/model_handler_validation_error.py +594 -0
- omnibase_infra/models/errors/model_infra_error_context.py +99 -0
- omnibase_infra/models/errors/model_message_type_registry_error_context.py +71 -0
- omnibase_infra/models/errors/model_timeout_error_context.py +110 -0
- omnibase_infra/models/handlers/__init__.py +80 -0
- omnibase_infra/models/handlers/model_bootstrap_handler_descriptor.py +162 -0
- omnibase_infra/models/handlers/model_contract_discovery_result.py +82 -0
- omnibase_infra/models/handlers/model_handler_descriptor.py +200 -0
- omnibase_infra/models/handlers/model_handler_identifier.py +215 -0
- omnibase_infra/models/handlers/model_handler_source_config.py +220 -0
- omnibase_infra/models/health/__init__.py +9 -0
- omnibase_infra/models/health/model_health_check_result.py +40 -0
- omnibase_infra/models/lifecycle/__init__.py +39 -0
- omnibase_infra/models/logging/__init__.py +51 -0
- omnibase_infra/models/logging/model_log_context.py +756 -0
- omnibase_infra/models/mcp/__init__.py +15 -0
- omnibase_infra/models/mcp/model_mcp_contract_config.py +80 -0
- omnibase_infra/models/mcp/model_mcp_server_config.py +67 -0
- omnibase_infra/models/mcp/model_mcp_tool_definition.py +73 -0
- omnibase_infra/models/mcp/model_mcp_tool_parameter.py +35 -0
- omnibase_infra/models/model_node_identity.py +126 -0
- omnibase_infra/models/model_retry_error_classification.py +78 -0
- omnibase_infra/models/projection/__init__.py +43 -0
- omnibase_infra/models/projection/model_capability_fields.py +112 -0
- omnibase_infra/models/projection/model_registration_projection.py +434 -0
- omnibase_infra/models/projection/model_registration_snapshot.py +322 -0
- omnibase_infra/models/projection/model_sequence_info.py +182 -0
- omnibase_infra/models/projection/model_snapshot_topic_config.py +591 -0
- omnibase_infra/models/projectors/__init__.py +41 -0
- omnibase_infra/models/projectors/model_projector_column.py +289 -0
- omnibase_infra/models/projectors/model_projector_discovery_result.py +65 -0
- omnibase_infra/models/projectors/model_projector_index.py +270 -0
- omnibase_infra/models/projectors/model_projector_schema.py +415 -0
- omnibase_infra/models/projectors/model_projector_validation_error.py +63 -0
- omnibase_infra/models/projectors/util_sql_identifiers.py +115 -0
- omnibase_infra/models/registration/__init__.py +68 -0
- omnibase_infra/models/registration/commands/__init__.py +15 -0
- omnibase_infra/models/registration/commands/model_node_registration_acked.py +108 -0
- omnibase_infra/models/registration/events/__init__.py +56 -0
- omnibase_infra/models/registration/events/model_node_became_active.py +103 -0
- omnibase_infra/models/registration/events/model_node_liveness_expired.py +103 -0
- omnibase_infra/models/registration/events/model_node_registration_accepted.py +98 -0
- omnibase_infra/models/registration/events/model_node_registration_ack_received.py +98 -0
- omnibase_infra/models/registration/events/model_node_registration_ack_timed_out.py +112 -0
- omnibase_infra/models/registration/events/model_node_registration_initiated.py +107 -0
- omnibase_infra/models/registration/events/model_node_registration_rejected.py +104 -0
- omnibase_infra/models/registration/model_event_bus_topic_entry.py +59 -0
- omnibase_infra/models/registration/model_introspection_metrics.py +253 -0
- omnibase_infra/models/registration/model_node_capabilities.py +190 -0
- omnibase_infra/models/registration/model_node_event_bus_config.py +99 -0
- omnibase_infra/models/registration/model_node_heartbeat_event.py +126 -0
- omnibase_infra/models/registration/model_node_introspection_event.py +195 -0
- omnibase_infra/models/registration/model_node_metadata.py +79 -0
- omnibase_infra/models/registration/model_node_registration.py +162 -0
- omnibase_infra/models/registration/model_node_registration_record.py +162 -0
- omnibase_infra/models/registry/__init__.py +29 -0
- omnibase_infra/models/registry/model_domain_constraint.py +202 -0
- omnibase_infra/models/registry/model_message_type_entry.py +271 -0
- omnibase_infra/models/resilience/__init__.py +9 -0
- omnibase_infra/models/resilience/model_circuit_breaker_config.py +227 -0
- omnibase_infra/models/routing/__init__.py +25 -0
- omnibase_infra/models/routing/model_routing_entry.py +52 -0
- omnibase_infra/models/routing/model_routing_subcontract.py +70 -0
- omnibase_infra/models/runtime/__init__.py +49 -0
- omnibase_infra/models/runtime/model_contract_security_config.py +41 -0
- omnibase_infra/models/runtime/model_discovery_error.py +81 -0
- omnibase_infra/models/runtime/model_discovery_result.py +162 -0
- omnibase_infra/models/runtime/model_discovery_warning.py +74 -0
- omnibase_infra/models/runtime/model_failed_plugin_load.py +63 -0
- omnibase_infra/models/runtime/model_handler_contract.py +296 -0
- omnibase_infra/models/runtime/model_loaded_handler.py +129 -0
- omnibase_infra/models/runtime/model_plugin_load_context.py +93 -0
- omnibase_infra/models/runtime/model_plugin_load_summary.py +124 -0
- omnibase_infra/models/security/__init__.py +50 -0
- omnibase_infra/models/security/classification_levels.py +99 -0
- omnibase_infra/models/security/model_environment_policy.py +145 -0
- omnibase_infra/models/security/model_handler_security_policy.py +107 -0
- omnibase_infra/models/security/model_security_error.py +81 -0
- omnibase_infra/models/security/model_security_validation_result.py +328 -0
- omnibase_infra/models/security/model_security_warning.py +67 -0
- omnibase_infra/models/snapshot/__init__.py +27 -0
- omnibase_infra/models/snapshot/model_field_change.py +65 -0
- omnibase_infra/models/snapshot/model_snapshot.py +270 -0
- omnibase_infra/models/snapshot/model_snapshot_diff.py +203 -0
- omnibase_infra/models/snapshot/model_subject_ref.py +81 -0
- omnibase_infra/models/types/__init__.py +71 -0
- omnibase_infra/models/validation/__init__.py +89 -0
- omnibase_infra/models/validation/model_any_type_validation_result.py +118 -0
- omnibase_infra/models/validation/model_any_type_violation.py +141 -0
- omnibase_infra/models/validation/model_category_match_result.py +345 -0
- omnibase_infra/models/validation/model_chain_violation.py +166 -0
- omnibase_infra/models/validation/model_coverage_metrics.py +316 -0
- omnibase_infra/models/validation/model_execution_shape_rule.py +159 -0
- omnibase_infra/models/validation/model_execution_shape_validation.py +208 -0
- omnibase_infra/models/validation/model_execution_shape_validation_result.py +294 -0
- omnibase_infra/models/validation/model_execution_shape_violation.py +122 -0
- omnibase_infra/models/validation/model_localhandler_validation_result.py +139 -0
- omnibase_infra/models/validation/model_localhandler_violation.py +100 -0
- omnibase_infra/models/validation/model_output_validation_params.py +74 -0
- omnibase_infra/models/validation/model_validate_and_raise_params.py +84 -0
- omnibase_infra/models/validation/model_validation_error_params.py +84 -0
- omnibase_infra/models/validation/model_validation_outcome.py +287 -0
- omnibase_infra/nodes/__init__.py +57 -0
- omnibase_infra/nodes/architecture_validator/__init__.py +79 -0
- omnibase_infra/nodes/architecture_validator/contract.yaml +252 -0
- omnibase_infra/nodes/architecture_validator/contract_architecture_validator.yaml +203 -0
- omnibase_infra/nodes/architecture_validator/mixins/__init__.py +16 -0
- omnibase_infra/nodes/architecture_validator/mixins/mixin_file_path_rule.py +92 -0
- omnibase_infra/nodes/architecture_validator/models/__init__.py +36 -0
- omnibase_infra/nodes/architecture_validator/models/model_architecture_validation_request.py +56 -0
- omnibase_infra/nodes/architecture_validator/models/model_architecture_validation_result.py +311 -0
- omnibase_infra/nodes/architecture_validator/models/model_architecture_violation.py +163 -0
- omnibase_infra/nodes/architecture_validator/models/model_rule_check_result.py +265 -0
- omnibase_infra/nodes/architecture_validator/models/model_validation_request.py +105 -0
- omnibase_infra/nodes/architecture_validator/models/model_validation_result.py +314 -0
- omnibase_infra/nodes/architecture_validator/node.py +262 -0
- omnibase_infra/nodes/architecture_validator/node_architecture_validator.py +383 -0
- omnibase_infra/nodes/architecture_validator/protocols/__init__.py +9 -0
- omnibase_infra/nodes/architecture_validator/protocols/protocol_architecture_rule.py +225 -0
- omnibase_infra/nodes/architecture_validator/registry/__init__.py +28 -0
- omnibase_infra/nodes/architecture_validator/registry/registry_infra_architecture_validator.py +106 -0
- omnibase_infra/nodes/architecture_validator/validators/__init__.py +104 -0
- omnibase_infra/nodes/architecture_validator/validators/validator_no_direct_dispatch.py +422 -0
- omnibase_infra/nodes/architecture_validator/validators/validator_no_handler_publishing.py +481 -0
- omnibase_infra/nodes/architecture_validator/validators/validator_no_orchestrator_fsm.py +491 -0
- omnibase_infra/nodes/contract_registry_reducer/__init__.py +29 -0
- omnibase_infra/nodes/contract_registry_reducer/contract.yaml +255 -0
- omnibase_infra/nodes/contract_registry_reducer/models/__init__.py +38 -0
- omnibase_infra/nodes/contract_registry_reducer/models/model_contract_registry_state.py +266 -0
- omnibase_infra/nodes/contract_registry_reducer/models/model_payload_cleanup_topic_references.py +55 -0
- omnibase_infra/nodes/contract_registry_reducer/models/model_payload_deactivate_contract.py +58 -0
- omnibase_infra/nodes/contract_registry_reducer/models/model_payload_mark_stale.py +49 -0
- omnibase_infra/nodes/contract_registry_reducer/models/model_payload_update_heartbeat.py +71 -0
- omnibase_infra/nodes/contract_registry_reducer/models/model_payload_update_topic.py +66 -0
- omnibase_infra/nodes/contract_registry_reducer/models/model_payload_upsert_contract.py +92 -0
- omnibase_infra/nodes/contract_registry_reducer/node.py +121 -0
- omnibase_infra/nodes/contract_registry_reducer/reducer.py +784 -0
- omnibase_infra/nodes/contract_registry_reducer/registry/__init__.py +9 -0
- omnibase_infra/nodes/contract_registry_reducer/registry/registry_infra_contract_registry_reducer.py +101 -0
- omnibase_infra/nodes/effects/README.md +358 -0
- omnibase_infra/nodes/effects/__init__.py +26 -0
- omnibase_infra/nodes/effects/contract.yaml +167 -0
- omnibase_infra/nodes/effects/models/__init__.py +32 -0
- omnibase_infra/nodes/effects/models/model_backend_result.py +190 -0
- omnibase_infra/nodes/effects/models/model_effect_idempotency_config.py +92 -0
- omnibase_infra/nodes/effects/models/model_registry_request.py +132 -0
- omnibase_infra/nodes/effects/models/model_registry_response.py +263 -0
- omnibase_infra/nodes/effects/protocol_consul_client.py +89 -0
- omnibase_infra/nodes/effects/protocol_effect_idempotency_store.py +143 -0
- omnibase_infra/nodes/effects/protocol_postgres_adapter.py +96 -0
- omnibase_infra/nodes/effects/registry_effect.py +525 -0
- omnibase_infra/nodes/effects/store_effect_idempotency_inmemory.py +425 -0
- omnibase_infra/nodes/handlers/consul/contract.yaml +85 -0
- omnibase_infra/nodes/handlers/db/contract.yaml +72 -0
- omnibase_infra/nodes/handlers/graph/contract.yaml +127 -0
- omnibase_infra/nodes/handlers/http/contract.yaml +74 -0
- omnibase_infra/nodes/handlers/intent/contract.yaml +66 -0
- omnibase_infra/nodes/handlers/mcp/contract.yaml +69 -0
- omnibase_infra/nodes/handlers/vault/contract.yaml +91 -0
- omnibase_infra/nodes/node_intent_storage_effect/__init__.py +50 -0
- omnibase_infra/nodes/node_intent_storage_effect/contract.yaml +194 -0
- omnibase_infra/nodes/node_intent_storage_effect/models/__init__.py +24 -0
- omnibase_infra/nodes/node_intent_storage_effect/models/model_intent_storage_input.py +141 -0
- omnibase_infra/nodes/node_intent_storage_effect/models/model_intent_storage_output.py +130 -0
- omnibase_infra/nodes/node_intent_storage_effect/node.py +94 -0
- omnibase_infra/nodes/node_intent_storage_effect/registry/__init__.py +35 -0
- omnibase_infra/nodes/node_intent_storage_effect/registry/registry_infra_intent_storage.py +294 -0
- omnibase_infra/nodes/node_ledger_projection_compute/__init__.py +50 -0
- omnibase_infra/nodes/node_ledger_projection_compute/contract.yaml +104 -0
- omnibase_infra/nodes/node_ledger_projection_compute/node.py +284 -0
- omnibase_infra/nodes/node_ledger_projection_compute/registry/__init__.py +29 -0
- omnibase_infra/nodes/node_ledger_projection_compute/registry/registry_infra_ledger_projection.py +118 -0
- omnibase_infra/nodes/node_ledger_write_effect/__init__.py +82 -0
- omnibase_infra/nodes/node_ledger_write_effect/contract.yaml +200 -0
- omnibase_infra/nodes/node_ledger_write_effect/handlers/__init__.py +22 -0
- omnibase_infra/nodes/node_ledger_write_effect/handlers/handler_ledger_append.py +372 -0
- omnibase_infra/nodes/node_ledger_write_effect/handlers/handler_ledger_query.py +597 -0
- omnibase_infra/nodes/node_ledger_write_effect/models/__init__.py +31 -0
- omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_append_result.py +54 -0
- omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_entry.py +92 -0
- omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_query.py +53 -0
- omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_query_result.py +41 -0
- omnibase_infra/nodes/node_ledger_write_effect/node.py +89 -0
- omnibase_infra/nodes/node_ledger_write_effect/protocols/__init__.py +13 -0
- omnibase_infra/nodes/node_ledger_write_effect/protocols/protocol_ledger_persistence.py +127 -0
- omnibase_infra/nodes/node_ledger_write_effect/registry/__init__.py +9 -0
- omnibase_infra/nodes/node_ledger_write_effect/registry/registry_infra_ledger_write.py +121 -0
- omnibase_infra/nodes/node_registration_orchestrator/README.md +542 -0
- omnibase_infra/nodes/node_registration_orchestrator/__init__.py +120 -0
- omnibase_infra/nodes/node_registration_orchestrator/contract.yaml +482 -0
- omnibase_infra/nodes/node_registration_orchestrator/dispatchers/__init__.py +53 -0
- omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_introspected.py +376 -0
- omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_registration_acked.py +376 -0
- omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_runtime_tick.py +373 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/__init__.py +62 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_heartbeat.py +376 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_introspected.py +694 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_registration_acked.py +458 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_runtime_tick.py +364 -0
- omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py +544 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/__init__.py +75 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_consul_intent_payload.py +194 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_consul_registration_intent.py +67 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_intent_execution_result.py +50 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_node_liveness_expired.py +107 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_config.py +67 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_input.py +41 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_output.py +166 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_postgres_intent_payload.py +235 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_postgres_upsert_intent.py +68 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_reducer_execution_result.py +384 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_reducer_state.py +60 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_registration_intent.py +177 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_registry_intent.py +247 -0
- omnibase_infra/nodes/node_registration_orchestrator/node.py +195 -0
- omnibase_infra/nodes/node_registration_orchestrator/plugin.py +909 -0
- omnibase_infra/nodes/node_registration_orchestrator/protocols.py +439 -0
- omnibase_infra/nodes/node_registration_orchestrator/registry/__init__.py +41 -0
- omnibase_infra/nodes/node_registration_orchestrator/registry/registry_infra_node_registration_orchestrator.py +528 -0
- omnibase_infra/nodes/node_registration_orchestrator/timeout_coordinator.py +393 -0
- omnibase_infra/nodes/node_registration_orchestrator/wiring.py +743 -0
- omnibase_infra/nodes/node_registration_reducer/__init__.py +15 -0
- omnibase_infra/nodes/node_registration_reducer/contract.yaml +301 -0
- omnibase_infra/nodes/node_registration_reducer/models/__init__.py +38 -0
- omnibase_infra/nodes/node_registration_reducer/models/model_validation_result.py +113 -0
- omnibase_infra/nodes/node_registration_reducer/node.py +139 -0
- omnibase_infra/nodes/node_registration_reducer/registry/__init__.py +9 -0
- omnibase_infra/nodes/node_registration_reducer/registry/registry_infra_node_registration_reducer.py +79 -0
- omnibase_infra/nodes/node_registration_storage_effect/__init__.py +41 -0
- omnibase_infra/nodes/node_registration_storage_effect/contract.yaml +220 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/__init__.py +44 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_delete_result.py +132 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_registration_record.py +199 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_registration_update.py +155 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_health_check_details.py +123 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_health_check_result.py +117 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_query.py +100 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_result.py +136 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_upsert_result.py +127 -0
- omnibase_infra/nodes/node_registration_storage_effect/node.py +112 -0
- omnibase_infra/nodes/node_registration_storage_effect/protocols/__init__.py +22 -0
- omnibase_infra/nodes/node_registration_storage_effect/protocols/protocol_registration_persistence.py +333 -0
- omnibase_infra/nodes/node_registration_storage_effect/registry/__init__.py +23 -0
- omnibase_infra/nodes/node_registration_storage_effect/registry/registry_infra_registration_storage.py +215 -0
- omnibase_infra/nodes/node_registry_effect/__init__.py +85 -0
- omnibase_infra/nodes/node_registry_effect/contract.yaml +677 -0
- omnibase_infra/nodes/node_registry_effect/handlers/__init__.py +70 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_consul_deregister.py +211 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_consul_register.py +212 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_partial_retry.py +417 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_postgres_deactivate.py +215 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_postgres_upsert.py +208 -0
- omnibase_infra/nodes/node_registry_effect/models/__init__.py +43 -0
- omnibase_infra/nodes/node_registry_effect/models/model_partial_retry_request.py +92 -0
- omnibase_infra/nodes/node_registry_effect/node.py +165 -0
- omnibase_infra/nodes/node_registry_effect/registry/__init__.py +27 -0
- omnibase_infra/nodes/node_registry_effect/registry/registry_infra_registry_effect.py +196 -0
- omnibase_infra/nodes/node_service_discovery_effect/__init__.py +111 -0
- omnibase_infra/nodes/node_service_discovery_effect/contract.yaml +246 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/__init__.py +67 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/enum_health_status.py +72 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/enum_service_discovery_operation.py +58 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_discovery_query.py +99 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_discovery_result.py +98 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_health_check_config.py +121 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_query_metadata.py +63 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_registration_result.py +130 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_service_discovery_health_check_details.py +111 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_service_discovery_health_check_result.py +119 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_service_info.py +106 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_service_registration.py +121 -0
- omnibase_infra/nodes/node_service_discovery_effect/node.py +111 -0
- omnibase_infra/nodes/node_service_discovery_effect/protocols/__init__.py +14 -0
- omnibase_infra/nodes/node_service_discovery_effect/protocols/protocol_discovery_operations.py +279 -0
- omnibase_infra/nodes/node_service_discovery_effect/registry/__init__.py +13 -0
- omnibase_infra/nodes/node_service_discovery_effect/registry/registry_infra_service_discovery.py +222 -0
- omnibase_infra/nodes/reducers/__init__.py +30 -0
- omnibase_infra/nodes/reducers/models/__init__.py +37 -0
- omnibase_infra/nodes/reducers/models/model_payload_consul_register.py +87 -0
- omnibase_infra/nodes/reducers/models/model_payload_ledger_append.py +133 -0
- omnibase_infra/nodes/reducers/models/model_payload_postgres_upsert_registration.py +60 -0
- omnibase_infra/nodes/reducers/models/model_registration_confirmation.py +166 -0
- omnibase_infra/nodes/reducers/models/model_registration_state.py +433 -0
- omnibase_infra/nodes/reducers/registration_reducer.py +1138 -0
- omnibase_infra/observability/__init__.py +143 -0
- omnibase_infra/observability/constants_metrics.py +91 -0
- omnibase_infra/observability/factory_observability_sink.py +525 -0
- omnibase_infra/observability/handlers/__init__.py +118 -0
- omnibase_infra/observability/handlers/handler_logging_structured.py +967 -0
- omnibase_infra/observability/handlers/handler_metrics_prometheus.py +1120 -0
- omnibase_infra/observability/handlers/model_logging_handler_config.py +71 -0
- omnibase_infra/observability/handlers/model_logging_handler_response.py +77 -0
- omnibase_infra/observability/handlers/model_metrics_handler_config.py +172 -0
- omnibase_infra/observability/handlers/model_metrics_handler_payload.py +135 -0
- omnibase_infra/observability/handlers/model_metrics_handler_response.py +101 -0
- omnibase_infra/observability/hooks/__init__.py +74 -0
- omnibase_infra/observability/hooks/hook_observability.py +1223 -0
- omnibase_infra/observability/models/__init__.py +30 -0
- omnibase_infra/observability/models/enum_required_log_context_key.py +77 -0
- omnibase_infra/observability/models/model_buffered_log_entry.py +117 -0
- omnibase_infra/observability/models/model_logging_sink_config.py +73 -0
- omnibase_infra/observability/models/model_metrics_sink_config.py +156 -0
- omnibase_infra/observability/sinks/__init__.py +69 -0
- omnibase_infra/observability/sinks/sink_logging_structured.py +809 -0
- omnibase_infra/observability/sinks/sink_metrics_prometheus.py +710 -0
- omnibase_infra/plugins/__init__.py +27 -0
- omnibase_infra/plugins/examples/__init__.py +28 -0
- omnibase_infra/plugins/examples/plugin_json_normalizer.py +271 -0
- omnibase_infra/plugins/examples/plugin_json_normalizer_error_handling.py +210 -0
- omnibase_infra/plugins/models/__init__.py +21 -0
- omnibase_infra/plugins/models/model_plugin_context.py +76 -0
- omnibase_infra/plugins/models/model_plugin_input_data.py +58 -0
- omnibase_infra/plugins/models/model_plugin_output_data.py +62 -0
- omnibase_infra/plugins/plugin_compute_base.py +449 -0
- omnibase_infra/projectors/__init__.py +30 -0
- omnibase_infra/projectors/contracts/__init__.py +63 -0
- omnibase_infra/projectors/contracts/registration_projector.yaml +370 -0
- omnibase_infra/projectors/projection_reader_registration.py +1559 -0
- omnibase_infra/projectors/snapshot_publisher_registration.py +1329 -0
- omnibase_infra/protocols/__init__.py +104 -0
- omnibase_infra/protocols/protocol_capability_projection.py +253 -0
- omnibase_infra/protocols/protocol_capability_query.py +251 -0
- omnibase_infra/protocols/protocol_container_aware.py +200 -0
- omnibase_infra/protocols/protocol_dispatch_engine.py +152 -0
- omnibase_infra/protocols/protocol_event_bus_like.py +127 -0
- omnibase_infra/protocols/protocol_event_projector.py +96 -0
- omnibase_infra/protocols/protocol_idempotency_store.py +142 -0
- omnibase_infra/protocols/protocol_message_dispatcher.py +247 -0
- omnibase_infra/protocols/protocol_message_type_registry.py +306 -0
- omnibase_infra/protocols/protocol_plugin_compute.py +368 -0
- omnibase_infra/protocols/protocol_projector_schema_validator.py +82 -0
- omnibase_infra/protocols/protocol_registry_metrics.py +215 -0
- omnibase_infra/protocols/protocol_snapshot_publisher.py +396 -0
- omnibase_infra/protocols/protocol_snapshot_store.py +567 -0
- omnibase_infra/runtime/__init__.py +445 -0
- omnibase_infra/runtime/binding_config_resolver.py +2771 -0
- omnibase_infra/runtime/binding_resolver.py +753 -0
- omnibase_infra/runtime/chain_aware_dispatch.py +467 -0
- omnibase_infra/runtime/constants_notification.py +75 -0
- omnibase_infra/runtime/constants_security.py +70 -0
- omnibase_infra/runtime/contract_handler_discovery.py +587 -0
- omnibase_infra/runtime/contract_loaders/__init__.py +51 -0
- omnibase_infra/runtime/contract_loaders/handler_routing_loader.py +464 -0
- omnibase_infra/runtime/contract_loaders/operation_bindings_loader.py +789 -0
- omnibase_infra/runtime/dispatch_context_enforcer.py +427 -0
- omnibase_infra/runtime/emit_daemon/__init__.py +97 -0
- omnibase_infra/runtime/emit_daemon/cli.py +844 -0
- omnibase_infra/runtime/emit_daemon/client.py +811 -0
- omnibase_infra/runtime/emit_daemon/config.py +535 -0
- omnibase_infra/runtime/emit_daemon/daemon.py +812 -0
- omnibase_infra/runtime/emit_daemon/event_registry.py +477 -0
- omnibase_infra/runtime/emit_daemon/model_daemon_request.py +139 -0
- omnibase_infra/runtime/emit_daemon/model_daemon_response.py +191 -0
- omnibase_infra/runtime/emit_daemon/queue.py +618 -0
- omnibase_infra/runtime/enums/__init__.py +18 -0
- omnibase_infra/runtime/enums/enum_config_ref_scheme.py +33 -0
- omnibase_infra/runtime/enums/enum_scheduler_status.py +170 -0
- omnibase_infra/runtime/envelope_validator.py +179 -0
- omnibase_infra/runtime/event_bus_subcontract_wiring.py +466 -0
- omnibase_infra/runtime/handler_bootstrap_source.py +507 -0
- omnibase_infra/runtime/handler_contract_config_loader.py +603 -0
- omnibase_infra/runtime/handler_contract_source.py +750 -0
- omnibase_infra/runtime/handler_identity.py +81 -0
- omnibase_infra/runtime/handler_plugin_loader.py +2046 -0
- omnibase_infra/runtime/handler_registry.py +329 -0
- omnibase_infra/runtime/handler_source_resolver.py +367 -0
- omnibase_infra/runtime/invocation_security_enforcer.py +427 -0
- omnibase_infra/runtime/kafka_contract_source.py +984 -0
- omnibase_infra/runtime/kernel.py +40 -0
- omnibase_infra/runtime/mixin_policy_validation.py +522 -0
- omnibase_infra/runtime/mixin_semver_cache.py +402 -0
- omnibase_infra/runtime/mixins/__init__.py +24 -0
- omnibase_infra/runtime/mixins/mixin_projector_notification_publishing.py +566 -0
- omnibase_infra/runtime/mixins/mixin_projector_sql_operations.py +778 -0
- omnibase_infra/runtime/models/__init__.py +229 -0
- omnibase_infra/runtime/models/model_batch_lifecycle_result.py +217 -0
- omnibase_infra/runtime/models/model_binding_config.py +168 -0
- omnibase_infra/runtime/models/model_binding_config_cache_stats.py +135 -0
- omnibase_infra/runtime/models/model_binding_config_resolver_config.py +329 -0
- omnibase_infra/runtime/models/model_cached_secret.py +138 -0
- omnibase_infra/runtime/models/model_compute_key.py +138 -0
- omnibase_infra/runtime/models/model_compute_registration.py +97 -0
- omnibase_infra/runtime/models/model_config_cache_entry.py +61 -0
- omnibase_infra/runtime/models/model_config_ref.py +331 -0
- omnibase_infra/runtime/models/model_config_ref_parse_result.py +125 -0
- omnibase_infra/runtime/models/model_contract_load_result.py +224 -0
- omnibase_infra/runtime/models/model_domain_plugin_config.py +92 -0
- omnibase_infra/runtime/models/model_domain_plugin_result.py +270 -0
- omnibase_infra/runtime/models/model_duplicate_response.py +54 -0
- omnibase_infra/runtime/models/model_enabled_protocols_config.py +61 -0
- omnibase_infra/runtime/models/model_event_bus_config.py +54 -0
- omnibase_infra/runtime/models/model_failed_component.py +55 -0
- omnibase_infra/runtime/models/model_health_check_response.py +168 -0
- omnibase_infra/runtime/models/model_health_check_result.py +229 -0
- omnibase_infra/runtime/models/model_lifecycle_result.py +245 -0
- omnibase_infra/runtime/models/model_logging_config.py +42 -0
- omnibase_infra/runtime/models/model_optional_correlation_id.py +167 -0
- omnibase_infra/runtime/models/model_optional_string.py +94 -0
- omnibase_infra/runtime/models/model_optional_uuid.py +110 -0
- omnibase_infra/runtime/models/model_policy_context.py +100 -0
- omnibase_infra/runtime/models/model_policy_key.py +138 -0
- omnibase_infra/runtime/models/model_policy_registration.py +139 -0
- omnibase_infra/runtime/models/model_policy_result.py +103 -0
- omnibase_infra/runtime/models/model_policy_type_filter.py +157 -0
- omnibase_infra/runtime/models/model_projector_notification_config.py +171 -0
- omnibase_infra/runtime/models/model_projector_plugin_loader_config.py +47 -0
- omnibase_infra/runtime/models/model_protocol_registration_config.py +65 -0
- omnibase_infra/runtime/models/model_retry_policy.py +105 -0
- omnibase_infra/runtime/models/model_runtime_config.py +150 -0
- omnibase_infra/runtime/models/model_runtime_contract_config.py +268 -0
- omnibase_infra/runtime/models/model_runtime_scheduler_config.py +625 -0
- omnibase_infra/runtime/models/model_runtime_scheduler_metrics.py +233 -0
- omnibase_infra/runtime/models/model_runtime_tick.py +193 -0
- omnibase_infra/runtime/models/model_secret_cache_stats.py +82 -0
- omnibase_infra/runtime/models/model_secret_mapping.py +63 -0
- omnibase_infra/runtime/models/model_secret_resolver_config.py +107 -0
- omnibase_infra/runtime/models/model_secret_resolver_metrics.py +111 -0
- omnibase_infra/runtime/models/model_secret_source_info.py +72 -0
- omnibase_infra/runtime/models/model_secret_source_spec.py +66 -0
- omnibase_infra/runtime/models/model_security_config.py +109 -0
- omnibase_infra/runtime/models/model_shutdown_batch_result.py +75 -0
- omnibase_infra/runtime/models/model_shutdown_config.py +94 -0
- omnibase_infra/runtime/models/model_transition_notification_outbox_config.py +112 -0
- omnibase_infra/runtime/models/model_transition_notification_outbox_metrics.py +140 -0
- omnibase_infra/runtime/models/model_transition_notification_publisher_metrics.py +357 -0
- omnibase_infra/runtime/projector_plugin_loader.py +1462 -0
- omnibase_infra/runtime/projector_schema_manager.py +565 -0
- omnibase_infra/runtime/projector_shell.py +1330 -0
- omnibase_infra/runtime/protocol_contract_descriptor.py +92 -0
- omnibase_infra/runtime/protocol_contract_source.py +92 -0
- omnibase_infra/runtime/protocol_domain_plugin.py +474 -0
- omnibase_infra/runtime/protocol_handler_discovery.py +221 -0
- omnibase_infra/runtime/protocol_handler_plugin_loader.py +327 -0
- omnibase_infra/runtime/protocol_lifecycle_executor.py +435 -0
- omnibase_infra/runtime/protocol_policy.py +366 -0
- omnibase_infra/runtime/protocols/__init__.py +37 -0
- omnibase_infra/runtime/protocols/protocol_runtime_scheduler.py +468 -0
- omnibase_infra/runtime/publisher_topic_scoped.py +294 -0
- omnibase_infra/runtime/registry/__init__.py +93 -0
- omnibase_infra/runtime/registry/mixin_message_type_query.py +326 -0
- omnibase_infra/runtime/registry/mixin_message_type_registration.py +354 -0
- omnibase_infra/runtime/registry/registry_event_bus_binding.py +268 -0
- omnibase_infra/runtime/registry/registry_message_type.py +542 -0
- omnibase_infra/runtime/registry/registry_protocol_binding.py +445 -0
- omnibase_infra/runtime/registry_compute.py +1143 -0
- omnibase_infra/runtime/registry_contract_source.py +693 -0
- omnibase_infra/runtime/registry_dispatcher.py +678 -0
- omnibase_infra/runtime/registry_policy.py +1185 -0
- omnibase_infra/runtime/runtime_contract_config_loader.py +406 -0
- omnibase_infra/runtime/runtime_scheduler.py +1070 -0
- omnibase_infra/runtime/secret_resolver.py +2112 -0
- omnibase_infra/runtime/security_metadata_validator.py +776 -0
- omnibase_infra/runtime/service_kernel.py +1651 -0
- omnibase_infra/runtime/service_message_dispatch_engine.py +2350 -0
- omnibase_infra/runtime/service_runtime_host_process.py +3493 -0
- omnibase_infra/runtime/transition_notification_outbox.py +1190 -0
- omnibase_infra/runtime/transition_notification_publisher.py +765 -0
- omnibase_infra/runtime/util_container_wiring.py +1124 -0
- omnibase_infra/runtime/util_validation.py +314 -0
- omnibase_infra/runtime/util_version.py +98 -0
- omnibase_infra/runtime/util_wiring.py +723 -0
- omnibase_infra/schemas/schema_registration_projection.sql +320 -0
- omnibase_infra/schemas/schema_transition_notification_outbox.sql +245 -0
- omnibase_infra/services/__init__.py +89 -0
- omnibase_infra/services/corpus_capture.py +684 -0
- omnibase_infra/services/mcp/__init__.py +31 -0
- omnibase_infra/services/mcp/mcp_server_lifecycle.py +449 -0
- omnibase_infra/services/mcp/service_mcp_tool_discovery.py +411 -0
- omnibase_infra/services/mcp/service_mcp_tool_registry.py +329 -0
- omnibase_infra/services/mcp/service_mcp_tool_sync.py +565 -0
- omnibase_infra/services/registry_api/__init__.py +40 -0
- omnibase_infra/services/registry_api/main.py +261 -0
- omnibase_infra/services/registry_api/models/__init__.py +66 -0
- omnibase_infra/services/registry_api/models/model_capability_widget_mapping.py +38 -0
- omnibase_infra/services/registry_api/models/model_pagination_info.py +48 -0
- omnibase_infra/services/registry_api/models/model_registry_discovery_response.py +73 -0
- omnibase_infra/services/registry_api/models/model_registry_health_response.py +49 -0
- omnibase_infra/services/registry_api/models/model_registry_instance_view.py +88 -0
- omnibase_infra/services/registry_api/models/model_registry_node_view.py +88 -0
- omnibase_infra/services/registry_api/models/model_registry_summary.py +60 -0
- omnibase_infra/services/registry_api/models/model_response_list_instances.py +43 -0
- omnibase_infra/services/registry_api/models/model_response_list_nodes.py +51 -0
- omnibase_infra/services/registry_api/models/model_warning.py +49 -0
- omnibase_infra/services/registry_api/models/model_widget_defaults.py +28 -0
- omnibase_infra/services/registry_api/models/model_widget_mapping.py +51 -0
- omnibase_infra/services/registry_api/routes.py +371 -0
- omnibase_infra/services/registry_api/service.py +837 -0
- omnibase_infra/services/service_capability_query.py +945 -0
- omnibase_infra/services/service_health.py +898 -0
- omnibase_infra/services/service_node_selector.py +530 -0
- omnibase_infra/services/service_timeout_emitter.py +699 -0
- omnibase_infra/services/service_timeout_scanner.py +394 -0
- omnibase_infra/services/session/__init__.py +56 -0
- omnibase_infra/services/session/config_consumer.py +137 -0
- omnibase_infra/services/session/config_store.py +139 -0
- omnibase_infra/services/session/consumer.py +1007 -0
- omnibase_infra/services/session/protocol_session_aggregator.py +117 -0
- omnibase_infra/services/session/store.py +997 -0
- omnibase_infra/services/snapshot/__init__.py +31 -0
- omnibase_infra/services/snapshot/service_snapshot.py +647 -0
- omnibase_infra/services/snapshot/store_inmemory.py +637 -0
- omnibase_infra/services/snapshot/store_postgres.py +1279 -0
- omnibase_infra/shared/__init__.py +8 -0
- omnibase_infra/testing/__init__.py +10 -0
- omnibase_infra/testing/utils.py +23 -0
- omnibase_infra/topics/__init__.py +45 -0
- omnibase_infra/topics/platform_topic_suffixes.py +140 -0
- omnibase_infra/topics/util_topic_composition.py +95 -0
- omnibase_infra/types/__init__.py +48 -0
- omnibase_infra/types/type_cache_info.py +49 -0
- omnibase_infra/types/type_dsn.py +173 -0
- omnibase_infra/types/type_infra_aliases.py +60 -0
- omnibase_infra/types/typed_dict/__init__.py +29 -0
- omnibase_infra/types/typed_dict/typed_dict_envelope_build_params.py +115 -0
- omnibase_infra/types/typed_dict/typed_dict_introspection_cache.py +128 -0
- omnibase_infra/types/typed_dict/typed_dict_performance_metrics_cache.py +140 -0
- omnibase_infra/types/typed_dict_capabilities.py +64 -0
- omnibase_infra/utils/__init__.py +117 -0
- omnibase_infra/utils/correlation.py +208 -0
- omnibase_infra/utils/util_atomic_file.py +261 -0
- omnibase_infra/utils/util_consumer_group.py +232 -0
- omnibase_infra/utils/util_datetime.py +372 -0
- omnibase_infra/utils/util_db_transaction.py +239 -0
- omnibase_infra/utils/util_dsn_validation.py +333 -0
- omnibase_infra/utils/util_env_parsing.py +264 -0
- omnibase_infra/utils/util_error_sanitization.py +457 -0
- omnibase_infra/utils/util_pydantic_validators.py +477 -0
- omnibase_infra/utils/util_retry_optimistic.py +281 -0
- omnibase_infra/utils/util_semver.py +233 -0
- omnibase_infra/validation/__init__.py +307 -0
- omnibase_infra/validation/contracts/security.validation.yaml +114 -0
- omnibase_infra/validation/enums/__init__.py +11 -0
- omnibase_infra/validation/enums/enum_contract_violation_severity.py +13 -0
- omnibase_infra/validation/infra_validators.py +1514 -0
- omnibase_infra/validation/linter_contract.py +907 -0
- omnibase_infra/validation/mixin_any_type_classification.py +120 -0
- omnibase_infra/validation/mixin_any_type_exemption.py +580 -0
- omnibase_infra/validation/mixin_any_type_reporting.py +106 -0
- omnibase_infra/validation/mixin_execution_shape_violation_checks.py +596 -0
- omnibase_infra/validation/mixin_node_archetype_detection.py +254 -0
- omnibase_infra/validation/models/__init__.py +15 -0
- omnibase_infra/validation/models/model_contract_lint_result.py +101 -0
- omnibase_infra/validation/models/model_contract_violation.py +41 -0
- omnibase_infra/validation/service_validation_aggregator.py +395 -0
- omnibase_infra/validation/validation_exemptions.yaml +2033 -0
- omnibase_infra/validation/validator_any_type.py +715 -0
- omnibase_infra/validation/validator_chain_propagation.py +839 -0
- omnibase_infra/validation/validator_execution_shape.py +465 -0
- omnibase_infra/validation/validator_localhandler.py +261 -0
- omnibase_infra/validation/validator_registration_security.py +410 -0
- omnibase_infra/validation/validator_routing_coverage.py +1020 -0
- omnibase_infra/validation/validator_runtime_shape.py +915 -0
- omnibase_infra/validation/validator_security.py +513 -0
- omnibase_infra/validation/validator_topic_category.py +1152 -0
- omnibase_infra-0.2.6.dist-info/METADATA +197 -0
- omnibase_infra-0.2.6.dist-info/RECORD +833 -0
- omnibase_infra-0.2.6.dist-info/WHEEL +4 -0
- omnibase_infra-0.2.6.dist-info/entry_points.txt +5 -0
- omnibase_infra-0.2.6.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,1559 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""Registration Projection Reader Implementation.
|
|
4
|
+
|
|
5
|
+
Implements projection reads for the registration domain to support
|
|
6
|
+
orchestrator state queries. Orchestrators read current state using
|
|
7
|
+
projections only - never scanning Kafka topics.
|
|
8
|
+
|
|
9
|
+
Concurrency Safety:
|
|
10
|
+
This implementation is coroutine-safe for concurrent async read operations.
|
|
11
|
+
Uses asyncpg connection pool for connection management, and asyncio.Lock
|
|
12
|
+
(via MixinAsyncCircuitBreaker) for circuit breaker state protection.
|
|
13
|
+
|
|
14
|
+
Note: This is not thread-safe. For multi-threaded access, additional
|
|
15
|
+
synchronization would be required.
|
|
16
|
+
|
|
17
|
+
State Filtering on Capability Queries:
|
|
18
|
+
All capability query methods (get_by_capability_tag, get_by_intent_type,
|
|
19
|
+
get_by_protocol, get_by_contract_type, get_by_capability_tags_all,
|
|
20
|
+
get_by_capability_tags_any) support an optional `state` parameter for
|
|
21
|
+
filtering results by registration state.
|
|
22
|
+
|
|
23
|
+
This is particularly useful for service discovery patterns where you
|
|
24
|
+
typically want to find only ACTIVE nodes that provide specific capabilities:
|
|
25
|
+
|
|
26
|
+
Example - Find active Kafka consumers:
|
|
27
|
+
>>> active_consumers = await reader.get_by_capability_tag(
|
|
28
|
+
... "kafka.consumer",
|
|
29
|
+
... state=EnumRegistrationState.ACTIVE,
|
|
30
|
+
... )
|
|
31
|
+
|
|
32
|
+
Example - Find active nodes implementing a protocol:
|
|
33
|
+
>>> active_publishers = await reader.get_by_protocol(
|
|
34
|
+
... "ProtocolEventPublisher",
|
|
35
|
+
... state=EnumRegistrationState.ACTIVE,
|
|
36
|
+
... )
|
|
37
|
+
|
|
38
|
+
Example - Find all effect nodes (including inactive):
|
|
39
|
+
>>> all_effects = await reader.get_by_contract_type("effect")
|
|
40
|
+
|
|
41
|
+
When state is None (default), all registrations matching the capability
|
|
42
|
+
criteria are returned regardless of their current FSM state. When state
|
|
43
|
+
is provided, an additional WHERE clause filters on current_state.
|
|
44
|
+
|
|
45
|
+
Valid state values (EnumRegistrationState):
|
|
46
|
+
- PENDING_REGISTRATION: Initial state after registration initiated
|
|
47
|
+
- ACCEPTED: Registration accepted, awaiting acknowledgment
|
|
48
|
+
- AWAITING_ACK: Waiting for node to acknowledge registration
|
|
49
|
+
- ACK_RECEIVED: Node acknowledged registration
|
|
50
|
+
- ACTIVE: Fully registered and operational (most common filter for discovery)
|
|
51
|
+
- ACK_TIMED_OUT: Ack deadline passed (retriable)
|
|
52
|
+
- REJECTED: Registration rejected (terminal)
|
|
53
|
+
- LIVENESS_EXPIRED: Liveness check failed (terminal)
|
|
54
|
+
|
|
55
|
+
Related Tickets:
|
|
56
|
+
- OMN-944 (F1): Implement Registration Projection Schema
|
|
57
|
+
- OMN-940 (F0): Define Projector Execution Model
|
|
58
|
+
- OMN-930 (C0): Projection Reader Protocol
|
|
59
|
+
- OMN-932 (C2): Durable Timeout Handling
|
|
60
|
+
- OMN-1134: Registry Projection Extensions for Capabilities
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
from __future__ import annotations
|
|
64
|
+
|
|
65
|
+
import json
|
|
66
|
+
import logging
|
|
67
|
+
from datetime import datetime
|
|
68
|
+
from uuid import UUID, uuid4
|
|
69
|
+
|
|
70
|
+
import asyncpg
|
|
71
|
+
|
|
72
|
+
from omnibase_core.models.primitives.model_semver import ModelSemVer
|
|
73
|
+
from omnibase_infra.enums import EnumInfraTransportType, EnumRegistrationState
|
|
74
|
+
from omnibase_infra.errors import (
|
|
75
|
+
InfraConnectionError,
|
|
76
|
+
InfraTimeoutError,
|
|
77
|
+
ModelInfraErrorContext,
|
|
78
|
+
ModelTimeoutErrorContext,
|
|
79
|
+
ProtocolConfigurationError,
|
|
80
|
+
RuntimeHostError,
|
|
81
|
+
)
|
|
82
|
+
from omnibase_infra.mixins import MixinAsyncCircuitBreaker
|
|
83
|
+
from omnibase_infra.models.projection import ModelRegistrationProjection
|
|
84
|
+
from omnibase_infra.models.projection.model_registration_projection import ContractType
|
|
85
|
+
from omnibase_infra.models.registration.model_node_capabilities import (
|
|
86
|
+
ModelNodeCapabilities,
|
|
87
|
+
)
|
|
88
|
+
from omnibase_infra.models.resilience import ModelCircuitBreakerConfig
|
|
89
|
+
|
|
90
|
+
logger = logging.getLogger(__name__)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class ProjectionReaderRegistration(MixinAsyncCircuitBreaker):
|
|
94
|
+
"""Registration projection reader implementation using asyncpg.
|
|
95
|
+
|
|
96
|
+
Provides read access to registration projections for orchestrators.
|
|
97
|
+
Supports entity lookups, state queries, and deadline scans for
|
|
98
|
+
timeout handling.
|
|
99
|
+
|
|
100
|
+
Circuit Breaker:
|
|
101
|
+
Uses MixinAsyncCircuitBreaker for resilience. Opens after 5 consecutive
|
|
102
|
+
failures and resets after 60 seconds.
|
|
103
|
+
|
|
104
|
+
Security:
|
|
105
|
+
All queries use parameterized statements for SQL injection protection.
|
|
106
|
+
|
|
107
|
+
Error Handling Pattern:
|
|
108
|
+
All public methods follow a consistent error handling structure:
|
|
109
|
+
|
|
110
|
+
1. Create fresh ModelInfraErrorContext per operation (intentionally NOT
|
|
111
|
+
reused to ensure each operation has isolated context with its own
|
|
112
|
+
correlation ID for distributed tracing).
|
|
113
|
+
|
|
114
|
+
2. Check circuit breaker before database operation.
|
|
115
|
+
|
|
116
|
+
3. Map exceptions consistently:
|
|
117
|
+
- asyncpg.PostgresConnectionError -> InfraConnectionError
|
|
118
|
+
- asyncpg.QueryCanceledError -> InfraTimeoutError
|
|
119
|
+
- Generic Exception -> RuntimeHostError
|
|
120
|
+
|
|
121
|
+
4. Record circuit breaker failures for all exception types.
|
|
122
|
+
|
|
123
|
+
This pattern ensures predictable error behavior and enables consistent
|
|
124
|
+
error handling by callers across all reader methods.
|
|
125
|
+
|
|
126
|
+
JSONB Handling:
|
|
127
|
+
The capabilities field is stored as JSONB in PostgreSQL. While asyncpg
|
|
128
|
+
typically returns JSONB as Python dicts, some connection configurations
|
|
129
|
+
may return strings. The _row_to_projection method handles both cases
|
|
130
|
+
using json.loads() for string fallback.
|
|
131
|
+
|
|
132
|
+
Example:
|
|
133
|
+
>>> pool = await asyncpg.create_pool(dsn)
|
|
134
|
+
>>> reader = ProjectionReaderRegistration(pool)
|
|
135
|
+
>>> proj = await reader.get_entity_state(node_id, "registration")
|
|
136
|
+
>>> if proj and proj.current_state.is_active():
|
|
137
|
+
... print("Node is active")
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
def __init__(self, pool: asyncpg.Pool) -> None:
|
|
141
|
+
"""Initialize reader with connection pool.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
pool: asyncpg connection pool for database access.
|
|
145
|
+
Pool should be created by the caller (e.g., from HandlerDb).
|
|
146
|
+
"""
|
|
147
|
+
self._pool = pool
|
|
148
|
+
config = ModelCircuitBreakerConfig.from_env(
|
|
149
|
+
service_name="projection_reader.registration",
|
|
150
|
+
transport_type=EnumInfraTransportType.DATABASE,
|
|
151
|
+
)
|
|
152
|
+
self._init_circuit_breaker_from_config(config)
|
|
153
|
+
|
|
154
|
+
def _row_to_projection(self, row: asyncpg.Record) -> ModelRegistrationProjection:
|
|
155
|
+
"""Convert database row to projection model.
|
|
156
|
+
|
|
157
|
+
Args:
|
|
158
|
+
row: asyncpg Record from query result
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
ModelRegistrationProjection instance
|
|
162
|
+
|
|
163
|
+
Note:
|
|
164
|
+
JSON parse errors in capabilities are handled gracefully by returning
|
|
165
|
+
an empty ModelNodeCapabilities with a warning log. This ensures
|
|
166
|
+
projections remain readable even if capabilities data is malformed.
|
|
167
|
+
"""
|
|
168
|
+
# Parse capabilities from JSONB.
|
|
169
|
+
# asyncpg typically returns JSONB as Python dicts, but connection
|
|
170
|
+
# configuration (e.g., custom type codecs) may return strings.
|
|
171
|
+
# Handle both cases for robustness.
|
|
172
|
+
capabilities_data = row["capabilities"]
|
|
173
|
+
if isinstance(capabilities_data, str):
|
|
174
|
+
try:
|
|
175
|
+
capabilities_data = json.loads(capabilities_data)
|
|
176
|
+
except json.JSONDecodeError as e:
|
|
177
|
+
logger.warning(
|
|
178
|
+
"Failed to parse capabilities JSON for entity %s: %s. "
|
|
179
|
+
"Using empty capabilities.",
|
|
180
|
+
row["entity_id"],
|
|
181
|
+
str(e),
|
|
182
|
+
)
|
|
183
|
+
capabilities_data = {}
|
|
184
|
+
capabilities = ModelNodeCapabilities.model_validate(capabilities_data)
|
|
185
|
+
|
|
186
|
+
# Parse node_version from string if needed.
|
|
187
|
+
# Database stores version as string, model expects ModelSemVer.
|
|
188
|
+
# See util_semver.py "Database Persistence" section for pattern docs.
|
|
189
|
+
node_version_data = row["node_version"]
|
|
190
|
+
if isinstance(node_version_data, str):
|
|
191
|
+
node_version_data = ModelSemVer.parse(node_version_data)
|
|
192
|
+
|
|
193
|
+
return ModelRegistrationProjection(
|
|
194
|
+
entity_id=row["entity_id"],
|
|
195
|
+
domain=row["domain"],
|
|
196
|
+
current_state=EnumRegistrationState(row["current_state"]),
|
|
197
|
+
node_type=row["node_type"],
|
|
198
|
+
node_version=node_version_data,
|
|
199
|
+
capabilities=capabilities,
|
|
200
|
+
# Capability fields (OMN-1134)
|
|
201
|
+
contract_type=row.get("contract_type"),
|
|
202
|
+
intent_types=row.get("intent_types") or [],
|
|
203
|
+
protocols=row.get("protocols") or [],
|
|
204
|
+
capability_tags=row.get("capability_tags") or [],
|
|
205
|
+
contract_version=row.get("contract_version"),
|
|
206
|
+
# Timeout fields
|
|
207
|
+
ack_deadline=row["ack_deadline"],
|
|
208
|
+
liveness_deadline=row["liveness_deadline"],
|
|
209
|
+
last_heartbeat_at=row["last_heartbeat_at"],
|
|
210
|
+
ack_timeout_emitted_at=row["ack_timeout_emitted_at"],
|
|
211
|
+
liveness_timeout_emitted_at=row["liveness_timeout_emitted_at"],
|
|
212
|
+
last_applied_event_id=row["last_applied_event_id"],
|
|
213
|
+
last_applied_offset=row["last_applied_offset"],
|
|
214
|
+
last_applied_sequence=row["last_applied_sequence"],
|
|
215
|
+
last_applied_partition=row["last_applied_partition"],
|
|
216
|
+
registered_at=row["registered_at"],
|
|
217
|
+
updated_at=row["updated_at"],
|
|
218
|
+
correlation_id=row["correlation_id"],
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
async def get_entity_state(
|
|
222
|
+
self,
|
|
223
|
+
entity_id: UUID,
|
|
224
|
+
domain: str = "registration",
|
|
225
|
+
correlation_id: UUID | None = None,
|
|
226
|
+
) -> ModelRegistrationProjection | None:
|
|
227
|
+
"""Get current projection for entity.
|
|
228
|
+
|
|
229
|
+
Point lookup for a single entity's current state.
|
|
230
|
+
Primary method for orchestrators to check entity state.
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
entity_id: Node UUID
|
|
234
|
+
domain: Domain namespace (default: "registration")
|
|
235
|
+
correlation_id: Optional correlation ID for tracing
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
Projection if exists, None otherwise
|
|
239
|
+
|
|
240
|
+
Raises:
|
|
241
|
+
InfraConnectionError: If database connection fails
|
|
242
|
+
InfraTimeoutError: If query times out
|
|
243
|
+
RuntimeHostError: For other database errors
|
|
244
|
+
|
|
245
|
+
Example:
|
|
246
|
+
>>> proj = await reader.get_entity_state(node_id)
|
|
247
|
+
>>> if proj and proj.current_state.is_active():
|
|
248
|
+
... route_work_to_node(node_id)
|
|
249
|
+
"""
|
|
250
|
+
corr_id = correlation_id or uuid4()
|
|
251
|
+
ctx = ModelInfraErrorContext(
|
|
252
|
+
transport_type=EnumInfraTransportType.DATABASE,
|
|
253
|
+
operation="get_entity_state",
|
|
254
|
+
target_name="projection_reader.registration",
|
|
255
|
+
correlation_id=corr_id,
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
# Check circuit breaker
|
|
259
|
+
async with self._circuit_breaker_lock:
|
|
260
|
+
await self._check_circuit_breaker("get_entity_state", corr_id)
|
|
261
|
+
|
|
262
|
+
query_sql = """
|
|
263
|
+
SELECT * FROM registration_projections
|
|
264
|
+
WHERE entity_id = $1 AND domain = $2
|
|
265
|
+
"""
|
|
266
|
+
|
|
267
|
+
try:
|
|
268
|
+
async with self._pool.acquire() as conn:
|
|
269
|
+
row = await conn.fetchrow(query_sql, entity_id, domain)
|
|
270
|
+
|
|
271
|
+
async with self._circuit_breaker_lock:
|
|
272
|
+
await self._reset_circuit_breaker()
|
|
273
|
+
|
|
274
|
+
if row is None:
|
|
275
|
+
return None
|
|
276
|
+
|
|
277
|
+
return self._row_to_projection(row)
|
|
278
|
+
|
|
279
|
+
except asyncpg.PostgresConnectionError as e:
|
|
280
|
+
async with self._circuit_breaker_lock:
|
|
281
|
+
await self._record_circuit_failure("get_entity_state", corr_id)
|
|
282
|
+
raise InfraConnectionError(
|
|
283
|
+
"Failed to connect to database for entity state lookup",
|
|
284
|
+
context=ctx,
|
|
285
|
+
) from e
|
|
286
|
+
|
|
287
|
+
except asyncpg.QueryCanceledError as e:
|
|
288
|
+
async with self._circuit_breaker_lock:
|
|
289
|
+
await self._record_circuit_failure("get_entity_state", corr_id)
|
|
290
|
+
raise InfraTimeoutError(
|
|
291
|
+
"Entity state lookup timed out",
|
|
292
|
+
context=ModelTimeoutErrorContext(
|
|
293
|
+
transport_type=ctx.transport_type,
|
|
294
|
+
operation=ctx.operation,
|
|
295
|
+
target_name=ctx.target_name,
|
|
296
|
+
correlation_id=ctx.correlation_id,
|
|
297
|
+
# timeout_seconds omitted - value not available in this context (defaults to None)
|
|
298
|
+
),
|
|
299
|
+
) from e
|
|
300
|
+
|
|
301
|
+
except Exception as e:
|
|
302
|
+
async with self._circuit_breaker_lock:
|
|
303
|
+
await self._record_circuit_failure("get_entity_state", corr_id)
|
|
304
|
+
raise RuntimeHostError(
|
|
305
|
+
f"Failed to get entity state: {type(e).__name__}",
|
|
306
|
+
context=ctx,
|
|
307
|
+
) from e
|
|
308
|
+
|
|
309
|
+
async def get_registration_status(
|
|
310
|
+
self,
|
|
311
|
+
entity_id: UUID,
|
|
312
|
+
domain: str = "registration",
|
|
313
|
+
correlation_id: UUID | None = None,
|
|
314
|
+
) -> EnumRegistrationState | None:
|
|
315
|
+
"""Get current registration state (convenience method).
|
|
316
|
+
|
|
317
|
+
Lightweight method that returns only the FSM state without
|
|
318
|
+
the full projection. Useful for quick state checks.
|
|
319
|
+
|
|
320
|
+
Args:
|
|
321
|
+
entity_id: Node UUID
|
|
322
|
+
domain: Domain namespace (default: "registration")
|
|
323
|
+
correlation_id: Optional correlation ID for tracing
|
|
324
|
+
|
|
325
|
+
Returns:
|
|
326
|
+
Current FSM state if exists, None otherwise
|
|
327
|
+
|
|
328
|
+
Example:
|
|
329
|
+
>>> state = await reader.get_registration_status(node_id)
|
|
330
|
+
>>> if state == EnumRegistrationState.ACTIVE:
|
|
331
|
+
... print("Node is active")
|
|
332
|
+
"""
|
|
333
|
+
corr_id = correlation_id or uuid4()
|
|
334
|
+
ctx = ModelInfraErrorContext(
|
|
335
|
+
transport_type=EnumInfraTransportType.DATABASE,
|
|
336
|
+
operation="get_registration_status",
|
|
337
|
+
target_name="projection_reader.registration",
|
|
338
|
+
correlation_id=corr_id,
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
# Check circuit breaker
|
|
342
|
+
async with self._circuit_breaker_lock:
|
|
343
|
+
await self._check_circuit_breaker("get_registration_status", corr_id)
|
|
344
|
+
|
|
345
|
+
query_sql = """
|
|
346
|
+
SELECT current_state FROM registration_projections
|
|
347
|
+
WHERE entity_id = $1 AND domain = $2
|
|
348
|
+
"""
|
|
349
|
+
|
|
350
|
+
try:
|
|
351
|
+
async with self._pool.acquire() as conn:
|
|
352
|
+
row = await conn.fetchrow(query_sql, entity_id, domain)
|
|
353
|
+
|
|
354
|
+
async with self._circuit_breaker_lock:
|
|
355
|
+
await self._reset_circuit_breaker()
|
|
356
|
+
|
|
357
|
+
if row is None:
|
|
358
|
+
return None
|
|
359
|
+
|
|
360
|
+
return EnumRegistrationState(row["current_state"])
|
|
361
|
+
|
|
362
|
+
except asyncpg.PostgresConnectionError as e:
|
|
363
|
+
async with self._circuit_breaker_lock:
|
|
364
|
+
await self._record_circuit_failure("get_registration_status", corr_id)
|
|
365
|
+
raise InfraConnectionError(
|
|
366
|
+
"Failed to connect to database for status lookup",
|
|
367
|
+
context=ctx,
|
|
368
|
+
) from e
|
|
369
|
+
|
|
370
|
+
except asyncpg.QueryCanceledError as e:
|
|
371
|
+
async with self._circuit_breaker_lock:
|
|
372
|
+
await self._record_circuit_failure("get_registration_status", corr_id)
|
|
373
|
+
raise InfraTimeoutError(
|
|
374
|
+
"Registration status lookup timed out",
|
|
375
|
+
context=ModelTimeoutErrorContext(
|
|
376
|
+
transport_type=ctx.transport_type,
|
|
377
|
+
operation=ctx.operation,
|
|
378
|
+
target_name=ctx.target_name,
|
|
379
|
+
correlation_id=ctx.correlation_id,
|
|
380
|
+
# timeout_seconds omitted - value not available in this context (defaults to None)
|
|
381
|
+
),
|
|
382
|
+
) from e
|
|
383
|
+
|
|
384
|
+
except Exception as e:
|
|
385
|
+
async with self._circuit_breaker_lock:
|
|
386
|
+
await self._record_circuit_failure("get_registration_status", corr_id)
|
|
387
|
+
raise RuntimeHostError(
|
|
388
|
+
f"Failed to get registration status: {type(e).__name__}",
|
|
389
|
+
context=ctx,
|
|
390
|
+
) from e
|
|
391
|
+
|
|
392
|
+
async def get_by_state(
|
|
393
|
+
self,
|
|
394
|
+
state: EnumRegistrationState,
|
|
395
|
+
domain: str = "registration",
|
|
396
|
+
limit: int = 100,
|
|
397
|
+
correlation_id: UUID | None = None,
|
|
398
|
+
) -> list[ModelRegistrationProjection]:
|
|
399
|
+
"""Query projections by state.
|
|
400
|
+
|
|
401
|
+
Find all projections with a specific FSM state.
|
|
402
|
+
|
|
403
|
+
Args:
|
|
404
|
+
state: FSM state to filter by
|
|
405
|
+
domain: Domain namespace (default: "registration")
|
|
406
|
+
limit: Maximum results to return (default: 100)
|
|
407
|
+
correlation_id: Optional correlation ID for tracing
|
|
408
|
+
|
|
409
|
+
Returns:
|
|
410
|
+
List of matching projections
|
|
411
|
+
|
|
412
|
+
Example:
|
|
413
|
+
>>> active = await reader.get_by_state(EnumRegistrationState.ACTIVE)
|
|
414
|
+
>>> for proj in active:
|
|
415
|
+
... print(f"Active node: {proj.entity_id}")
|
|
416
|
+
"""
|
|
417
|
+
corr_id = correlation_id or uuid4()
|
|
418
|
+
ctx = ModelInfraErrorContext(
|
|
419
|
+
transport_type=EnumInfraTransportType.DATABASE,
|
|
420
|
+
operation="get_by_state",
|
|
421
|
+
target_name="projection_reader.registration",
|
|
422
|
+
correlation_id=corr_id,
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
# Check circuit breaker
|
|
426
|
+
async with self._circuit_breaker_lock:
|
|
427
|
+
await self._check_circuit_breaker("get_by_state", corr_id)
|
|
428
|
+
|
|
429
|
+
query_sql = """
|
|
430
|
+
SELECT * FROM registration_projections
|
|
431
|
+
WHERE domain = $1 AND current_state = $2
|
|
432
|
+
ORDER BY updated_at DESC
|
|
433
|
+
LIMIT $3
|
|
434
|
+
"""
|
|
435
|
+
|
|
436
|
+
try:
|
|
437
|
+
async with self._pool.acquire() as conn:
|
|
438
|
+
rows = await conn.fetch(query_sql, domain, state.value, limit)
|
|
439
|
+
|
|
440
|
+
async with self._circuit_breaker_lock:
|
|
441
|
+
await self._reset_circuit_breaker()
|
|
442
|
+
|
|
443
|
+
return [self._row_to_projection(row) for row in rows]
|
|
444
|
+
|
|
445
|
+
except asyncpg.PostgresConnectionError as e:
|
|
446
|
+
async with self._circuit_breaker_lock:
|
|
447
|
+
await self._record_circuit_failure("get_by_state", corr_id)
|
|
448
|
+
raise InfraConnectionError(
|
|
449
|
+
"Failed to connect to database for state query",
|
|
450
|
+
context=ctx,
|
|
451
|
+
) from e
|
|
452
|
+
|
|
453
|
+
except asyncpg.QueryCanceledError as e:
|
|
454
|
+
async with self._circuit_breaker_lock:
|
|
455
|
+
await self._record_circuit_failure("get_by_state", corr_id)
|
|
456
|
+
raise InfraTimeoutError(
|
|
457
|
+
"State query timed out",
|
|
458
|
+
context=ModelTimeoutErrorContext(
|
|
459
|
+
transport_type=ctx.transport_type,
|
|
460
|
+
operation=ctx.operation,
|
|
461
|
+
target_name=ctx.target_name,
|
|
462
|
+
correlation_id=ctx.correlation_id,
|
|
463
|
+
# timeout_seconds omitted - value not available in this context (defaults to None)
|
|
464
|
+
),
|
|
465
|
+
) from e
|
|
466
|
+
|
|
467
|
+
except Exception as e:
|
|
468
|
+
async with self._circuit_breaker_lock:
|
|
469
|
+
await self._record_circuit_failure("get_by_state", corr_id)
|
|
470
|
+
raise RuntimeHostError(
|
|
471
|
+
f"Failed to query by state: {type(e).__name__}",
|
|
472
|
+
context=ctx,
|
|
473
|
+
) from e
|
|
474
|
+
|
|
475
|
+
async def get_overdue_ack_registrations(
|
|
476
|
+
self,
|
|
477
|
+
now: datetime,
|
|
478
|
+
domain: str = "registration",
|
|
479
|
+
limit: int = 100,
|
|
480
|
+
correlation_id: UUID | None = None,
|
|
481
|
+
) -> list[ModelRegistrationProjection]:
|
|
482
|
+
"""Get registrations with overdue ack deadlines (not yet emitted).
|
|
483
|
+
|
|
484
|
+
Per C2: Returns entities where:
|
|
485
|
+
- ack_deadline < now
|
|
486
|
+
- ack_timeout_emitted_at IS NULL
|
|
487
|
+
- current_state requires ack (ACCEPTED or AWAITING_ACK)
|
|
488
|
+
|
|
489
|
+
Used by orchestrators during RuntimeTick processing to find
|
|
490
|
+
registrations that need ack timeout events emitted.
|
|
491
|
+
|
|
492
|
+
Args:
|
|
493
|
+
now: Current time (injected by runtime)
|
|
494
|
+
domain: Domain namespace (default: "registration")
|
|
495
|
+
limit: Maximum results to return (default: 100)
|
|
496
|
+
correlation_id: Optional correlation ID for tracing
|
|
497
|
+
|
|
498
|
+
Returns:
|
|
499
|
+
List of registrations needing ack timeout events
|
|
500
|
+
|
|
501
|
+
Example:
|
|
502
|
+
>>> overdue = await reader.get_overdue_ack_registrations(datetime.now(UTC))
|
|
503
|
+
>>> for proj in overdue:
|
|
504
|
+
... emit_ack_timeout_event(proj.entity_id)
|
|
505
|
+
"""
|
|
506
|
+
corr_id = correlation_id or uuid4()
|
|
507
|
+
ctx = ModelInfraErrorContext(
|
|
508
|
+
transport_type=EnumInfraTransportType.DATABASE,
|
|
509
|
+
operation="get_overdue_ack_registrations",
|
|
510
|
+
target_name="projection_reader.registration",
|
|
511
|
+
correlation_id=corr_id,
|
|
512
|
+
)
|
|
513
|
+
|
|
514
|
+
# Check circuit breaker
|
|
515
|
+
async with self._circuit_breaker_lock:
|
|
516
|
+
await self._check_circuit_breaker("get_overdue_ack_registrations", corr_id)
|
|
517
|
+
|
|
518
|
+
# States that require ack
|
|
519
|
+
ack_states = [
|
|
520
|
+
EnumRegistrationState.ACCEPTED.value,
|
|
521
|
+
EnumRegistrationState.AWAITING_ACK.value,
|
|
522
|
+
]
|
|
523
|
+
|
|
524
|
+
query_sql = """
|
|
525
|
+
SELECT * FROM registration_projections
|
|
526
|
+
WHERE domain = $1
|
|
527
|
+
AND ack_deadline < $2
|
|
528
|
+
AND ack_timeout_emitted_at IS NULL
|
|
529
|
+
AND current_state = ANY($3)
|
|
530
|
+
ORDER BY ack_deadline ASC
|
|
531
|
+
LIMIT $4
|
|
532
|
+
"""
|
|
533
|
+
|
|
534
|
+
try:
|
|
535
|
+
async with self._pool.acquire() as conn:
|
|
536
|
+
rows = await conn.fetch(query_sql, domain, now, ack_states, limit)
|
|
537
|
+
|
|
538
|
+
async with self._circuit_breaker_lock:
|
|
539
|
+
await self._reset_circuit_breaker()
|
|
540
|
+
|
|
541
|
+
return [self._row_to_projection(row) for row in rows]
|
|
542
|
+
|
|
543
|
+
except asyncpg.PostgresConnectionError as e:
|
|
544
|
+
async with self._circuit_breaker_lock:
|
|
545
|
+
await self._record_circuit_failure(
|
|
546
|
+
"get_overdue_ack_registrations", corr_id
|
|
547
|
+
)
|
|
548
|
+
raise InfraConnectionError(
|
|
549
|
+
"Failed to connect to database for overdue ack query",
|
|
550
|
+
context=ctx,
|
|
551
|
+
) from e
|
|
552
|
+
|
|
553
|
+
except asyncpg.QueryCanceledError as e:
|
|
554
|
+
async with self._circuit_breaker_lock:
|
|
555
|
+
await self._record_circuit_failure(
|
|
556
|
+
"get_overdue_ack_registrations", corr_id
|
|
557
|
+
)
|
|
558
|
+
raise InfraTimeoutError(
|
|
559
|
+
"Overdue ack registrations query timed out",
|
|
560
|
+
context=ModelTimeoutErrorContext(
|
|
561
|
+
transport_type=ctx.transport_type,
|
|
562
|
+
operation=ctx.operation,
|
|
563
|
+
target_name=ctx.target_name,
|
|
564
|
+
correlation_id=ctx.correlation_id,
|
|
565
|
+
# timeout_seconds omitted - value not available in this context (defaults to None)
|
|
566
|
+
),
|
|
567
|
+
) from e
|
|
568
|
+
|
|
569
|
+
except Exception as e:
|
|
570
|
+
async with self._circuit_breaker_lock:
|
|
571
|
+
await self._record_circuit_failure(
|
|
572
|
+
"get_overdue_ack_registrations", corr_id
|
|
573
|
+
)
|
|
574
|
+
raise RuntimeHostError(
|
|
575
|
+
f"Failed to query overdue ack registrations: {type(e).__name__}",
|
|
576
|
+
context=ctx,
|
|
577
|
+
) from e
|
|
578
|
+
|
|
579
|
+
async def get_overdue_liveness_registrations(
|
|
580
|
+
self,
|
|
581
|
+
now: datetime,
|
|
582
|
+
domain: str = "registration",
|
|
583
|
+
limit: int = 100,
|
|
584
|
+
correlation_id: UUID | None = None,
|
|
585
|
+
) -> list[ModelRegistrationProjection]:
|
|
586
|
+
"""Get registrations with overdue liveness deadlines (not yet emitted).
|
|
587
|
+
|
|
588
|
+
Per C2: Returns entities where:
|
|
589
|
+
- liveness_deadline < now
|
|
590
|
+
- liveness_timeout_emitted_at IS NULL
|
|
591
|
+
- current_state = ACTIVE
|
|
592
|
+
|
|
593
|
+
Used by orchestrators during RuntimeTick processing to find
|
|
594
|
+
active registrations that have missed their liveness deadline.
|
|
595
|
+
|
|
596
|
+
Args:
|
|
597
|
+
now: Current time (injected by runtime)
|
|
598
|
+
domain: Domain namespace (default: "registration")
|
|
599
|
+
limit: Maximum results to return (default: 100)
|
|
600
|
+
correlation_id: Optional correlation ID for tracing
|
|
601
|
+
|
|
602
|
+
Returns:
|
|
603
|
+
List of registrations needing liveness timeout events
|
|
604
|
+
|
|
605
|
+
Example:
|
|
606
|
+
>>> overdue = await reader.get_overdue_liveness_registrations(datetime.now(UTC))
|
|
607
|
+
>>> for proj in overdue:
|
|
608
|
+
... emit_liveness_expired_event(proj.entity_id)
|
|
609
|
+
"""
|
|
610
|
+
corr_id = correlation_id or uuid4()
|
|
611
|
+
ctx = ModelInfraErrorContext(
|
|
612
|
+
transport_type=EnumInfraTransportType.DATABASE,
|
|
613
|
+
operation="get_overdue_liveness_registrations",
|
|
614
|
+
target_name="projection_reader.registration",
|
|
615
|
+
correlation_id=corr_id,
|
|
616
|
+
)
|
|
617
|
+
|
|
618
|
+
# Check circuit breaker
|
|
619
|
+
async with self._circuit_breaker_lock:
|
|
620
|
+
await self._check_circuit_breaker(
|
|
621
|
+
"get_overdue_liveness_registrations", corr_id
|
|
622
|
+
)
|
|
623
|
+
|
|
624
|
+
query_sql = """
|
|
625
|
+
SELECT * FROM registration_projections
|
|
626
|
+
WHERE domain = $1
|
|
627
|
+
AND liveness_deadline < $2
|
|
628
|
+
AND liveness_timeout_emitted_at IS NULL
|
|
629
|
+
AND current_state = $3
|
|
630
|
+
ORDER BY liveness_deadline ASC
|
|
631
|
+
LIMIT $4
|
|
632
|
+
"""
|
|
633
|
+
|
|
634
|
+
try:
|
|
635
|
+
async with self._pool.acquire() as conn:
|
|
636
|
+
rows = await conn.fetch(
|
|
637
|
+
query_sql,
|
|
638
|
+
domain,
|
|
639
|
+
now,
|
|
640
|
+
EnumRegistrationState.ACTIVE.value,
|
|
641
|
+
limit,
|
|
642
|
+
)
|
|
643
|
+
|
|
644
|
+
async with self._circuit_breaker_lock:
|
|
645
|
+
await self._reset_circuit_breaker()
|
|
646
|
+
|
|
647
|
+
return [self._row_to_projection(row) for row in rows]
|
|
648
|
+
|
|
649
|
+
except asyncpg.PostgresConnectionError as e:
|
|
650
|
+
async with self._circuit_breaker_lock:
|
|
651
|
+
await self._record_circuit_failure(
|
|
652
|
+
"get_overdue_liveness_registrations", corr_id
|
|
653
|
+
)
|
|
654
|
+
raise InfraConnectionError(
|
|
655
|
+
"Failed to connect to database for overdue liveness query",
|
|
656
|
+
context=ctx,
|
|
657
|
+
) from e
|
|
658
|
+
|
|
659
|
+
except asyncpg.QueryCanceledError as e:
|
|
660
|
+
async with self._circuit_breaker_lock:
|
|
661
|
+
await self._record_circuit_failure(
|
|
662
|
+
"get_overdue_liveness_registrations", corr_id
|
|
663
|
+
)
|
|
664
|
+
raise InfraTimeoutError(
|
|
665
|
+
"Overdue liveness registrations query timed out",
|
|
666
|
+
context=ModelTimeoutErrorContext(
|
|
667
|
+
transport_type=ctx.transport_type,
|
|
668
|
+
operation=ctx.operation,
|
|
669
|
+
target_name=ctx.target_name,
|
|
670
|
+
correlation_id=ctx.correlation_id,
|
|
671
|
+
# timeout_seconds omitted - value not available in this context (defaults to None)
|
|
672
|
+
),
|
|
673
|
+
) from e
|
|
674
|
+
|
|
675
|
+
except Exception as e:
|
|
676
|
+
async with self._circuit_breaker_lock:
|
|
677
|
+
await self._record_circuit_failure(
|
|
678
|
+
"get_overdue_liveness_registrations", corr_id
|
|
679
|
+
)
|
|
680
|
+
raise RuntimeHostError(
|
|
681
|
+
f"Failed to query overdue liveness registrations: {type(e).__name__}",
|
|
682
|
+
context=ctx,
|
|
683
|
+
) from e
|
|
684
|
+
|
|
685
|
+
async def count_by_state(
|
|
686
|
+
self,
|
|
687
|
+
domain: str = "registration",
|
|
688
|
+
correlation_id: UUID | None = None,
|
|
689
|
+
) -> dict[EnumRegistrationState, int]:
|
|
690
|
+
"""Count projections by state.
|
|
691
|
+
|
|
692
|
+
Aggregates projection counts for each FSM state.
|
|
693
|
+
Useful for monitoring and dashboard metrics.
|
|
694
|
+
|
|
695
|
+
Args:
|
|
696
|
+
domain: Domain namespace (default: "registration")
|
|
697
|
+
correlation_id: Optional correlation ID for tracing
|
|
698
|
+
|
|
699
|
+
Returns:
|
|
700
|
+
Dict mapping state to count
|
|
701
|
+
|
|
702
|
+
Example:
|
|
703
|
+
>>> counts = await reader.count_by_state()
|
|
704
|
+
>>> print(f"Active nodes: {counts.get(EnumRegistrationState.ACTIVE, 0)}")
|
|
705
|
+
"""
|
|
706
|
+
corr_id = correlation_id or uuid4()
|
|
707
|
+
ctx = ModelInfraErrorContext(
|
|
708
|
+
transport_type=EnumInfraTransportType.DATABASE,
|
|
709
|
+
operation="count_by_state",
|
|
710
|
+
target_name="projection_reader.registration",
|
|
711
|
+
correlation_id=corr_id,
|
|
712
|
+
)
|
|
713
|
+
|
|
714
|
+
# Check circuit breaker
|
|
715
|
+
async with self._circuit_breaker_lock:
|
|
716
|
+
await self._check_circuit_breaker("count_by_state", corr_id)
|
|
717
|
+
|
|
718
|
+
query_sql = """
|
|
719
|
+
SELECT current_state, COUNT(*) as count
|
|
720
|
+
FROM registration_projections
|
|
721
|
+
WHERE domain = $1
|
|
722
|
+
GROUP BY current_state
|
|
723
|
+
"""
|
|
724
|
+
|
|
725
|
+
try:
|
|
726
|
+
async with self._pool.acquire() as conn:
|
|
727
|
+
rows = await conn.fetch(query_sql, domain)
|
|
728
|
+
|
|
729
|
+
async with self._circuit_breaker_lock:
|
|
730
|
+
await self._reset_circuit_breaker()
|
|
731
|
+
|
|
732
|
+
result: dict[EnumRegistrationState, int] = {}
|
|
733
|
+
for row in rows:
|
|
734
|
+
state = EnumRegistrationState(row["current_state"])
|
|
735
|
+
result[state] = row["count"]
|
|
736
|
+
|
|
737
|
+
return result
|
|
738
|
+
|
|
739
|
+
except asyncpg.PostgresConnectionError as e:
|
|
740
|
+
async with self._circuit_breaker_lock:
|
|
741
|
+
await self._record_circuit_failure("count_by_state", corr_id)
|
|
742
|
+
raise InfraConnectionError(
|
|
743
|
+
"Failed to connect to database for state count",
|
|
744
|
+
context=ctx,
|
|
745
|
+
) from e
|
|
746
|
+
|
|
747
|
+
except asyncpg.QueryCanceledError as e:
|
|
748
|
+
async with self._circuit_breaker_lock:
|
|
749
|
+
await self._record_circuit_failure("count_by_state", corr_id)
|
|
750
|
+
raise InfraTimeoutError(
|
|
751
|
+
"State count query timed out",
|
|
752
|
+
context=ModelTimeoutErrorContext(
|
|
753
|
+
transport_type=ctx.transport_type,
|
|
754
|
+
operation=ctx.operation,
|
|
755
|
+
target_name=ctx.target_name,
|
|
756
|
+
correlation_id=ctx.correlation_id,
|
|
757
|
+
# timeout_seconds omitted - value not available in this context (defaults to None)
|
|
758
|
+
),
|
|
759
|
+
) from e
|
|
760
|
+
|
|
761
|
+
except Exception as e:
|
|
762
|
+
async with self._circuit_breaker_lock:
|
|
763
|
+
await self._record_circuit_failure("count_by_state", corr_id)
|
|
764
|
+
raise RuntimeHostError(
|
|
765
|
+
f"Failed to count by state: {type(e).__name__}",
|
|
766
|
+
context=ctx,
|
|
767
|
+
) from e
|
|
768
|
+
|
|
769
|
+
# ============================================================
|
|
770
|
+
# Capability Query Methods (OMN-1134)
|
|
771
|
+
# ============================================================
|
|
772
|
+
|
|
773
|
+
async def get_by_capability_tag(
|
|
774
|
+
self,
|
|
775
|
+
tag: str,
|
|
776
|
+
state: EnumRegistrationState | None = None,
|
|
777
|
+
domain: str = "registration",
|
|
778
|
+
limit: int = 100,
|
|
779
|
+
correlation_id: UUID | None = None,
|
|
780
|
+
) -> list[ModelRegistrationProjection]:
|
|
781
|
+
"""Find all registrations with the specified capability tag.
|
|
782
|
+
|
|
783
|
+
Uses GIN index on capability_tags column for efficient lookup.
|
|
784
|
+
|
|
785
|
+
Args:
|
|
786
|
+
tag: The capability tag to search for (e.g., "postgres.storage")
|
|
787
|
+
state: Optional state filter (e.g., EnumRegistrationState.ACTIVE)
|
|
788
|
+
domain: Domain namespace (default: "registration")
|
|
789
|
+
limit: Maximum results to return (default: 100)
|
|
790
|
+
correlation_id: Optional correlation ID for tracing
|
|
791
|
+
|
|
792
|
+
Returns:
|
|
793
|
+
List of matching registration projections
|
|
794
|
+
|
|
795
|
+
Raises:
|
|
796
|
+
InfraConnectionError: If database connection fails
|
|
797
|
+
InfraTimeoutError: If query times out
|
|
798
|
+
RuntimeHostError: For other database errors
|
|
799
|
+
|
|
800
|
+
Example:
|
|
801
|
+
>>> adapters = await reader.get_by_capability_tag("kafka.consumer")
|
|
802
|
+
>>> for adapter in adapters:
|
|
803
|
+
... print(f"{adapter.entity_id}: {adapter.node_type}")
|
|
804
|
+
>>> # Filter by state
|
|
805
|
+
>>> active = await reader.get_by_capability_tag(
|
|
806
|
+
... "postgres.storage",
|
|
807
|
+
... state=EnumRegistrationState.ACTIVE,
|
|
808
|
+
... )
|
|
809
|
+
"""
|
|
810
|
+
corr_id = correlation_id or uuid4()
|
|
811
|
+
ctx = ModelInfraErrorContext(
|
|
812
|
+
transport_type=EnumInfraTransportType.DATABASE,
|
|
813
|
+
operation="get_by_capability_tag",
|
|
814
|
+
target_name="projection_reader.registration",
|
|
815
|
+
correlation_id=corr_id,
|
|
816
|
+
)
|
|
817
|
+
|
|
818
|
+
async with self._circuit_breaker_lock:
|
|
819
|
+
await self._check_circuit_breaker("get_by_capability_tag", corr_id)
|
|
820
|
+
|
|
821
|
+
# Build query with optional state filter
|
|
822
|
+
if state is not None:
|
|
823
|
+
query_sql = """
|
|
824
|
+
SELECT * FROM registration_projections
|
|
825
|
+
WHERE domain = $1
|
|
826
|
+
AND capability_tags @> $2::text[]
|
|
827
|
+
AND current_state = $3
|
|
828
|
+
ORDER BY updated_at DESC
|
|
829
|
+
LIMIT $4
|
|
830
|
+
"""
|
|
831
|
+
params = [domain, [tag], state.value, limit]
|
|
832
|
+
else:
|
|
833
|
+
query_sql = """
|
|
834
|
+
SELECT * FROM registration_projections
|
|
835
|
+
WHERE domain = $1
|
|
836
|
+
AND capability_tags @> $2::text[]
|
|
837
|
+
ORDER BY updated_at DESC
|
|
838
|
+
LIMIT $3
|
|
839
|
+
"""
|
|
840
|
+
params = [domain, [tag], limit]
|
|
841
|
+
|
|
842
|
+
try:
|
|
843
|
+
async with self._pool.acquire() as conn:
|
|
844
|
+
rows = await conn.fetch(query_sql, *params)
|
|
845
|
+
|
|
846
|
+
async with self._circuit_breaker_lock:
|
|
847
|
+
await self._reset_circuit_breaker()
|
|
848
|
+
|
|
849
|
+
return [self._row_to_projection(row) for row in rows]
|
|
850
|
+
|
|
851
|
+
except asyncpg.PostgresConnectionError as e:
|
|
852
|
+
async with self._circuit_breaker_lock:
|
|
853
|
+
await self._record_circuit_failure("get_by_capability_tag", corr_id)
|
|
854
|
+
raise InfraConnectionError(
|
|
855
|
+
"Failed to connect to database for capability tag query",
|
|
856
|
+
context=ctx,
|
|
857
|
+
) from e
|
|
858
|
+
|
|
859
|
+
except asyncpg.QueryCanceledError as e:
|
|
860
|
+
async with self._circuit_breaker_lock:
|
|
861
|
+
await self._record_circuit_failure("get_by_capability_tag", corr_id)
|
|
862
|
+
raise InfraTimeoutError(
|
|
863
|
+
"Capability tag query timed out",
|
|
864
|
+
context=ModelTimeoutErrorContext(
|
|
865
|
+
transport_type=ctx.transport_type,
|
|
866
|
+
operation=ctx.operation,
|
|
867
|
+
target_name=ctx.target_name,
|
|
868
|
+
correlation_id=ctx.correlation_id,
|
|
869
|
+
# timeout_seconds omitted - value not available in this context (defaults to None)
|
|
870
|
+
),
|
|
871
|
+
) from e
|
|
872
|
+
|
|
873
|
+
except Exception as e:
|
|
874
|
+
async with self._circuit_breaker_lock:
|
|
875
|
+
await self._record_circuit_failure("get_by_capability_tag", corr_id)
|
|
876
|
+
raise RuntimeHostError(
|
|
877
|
+
f"Failed to query by capability tag: {type(e).__name__}",
|
|
878
|
+
context=ctx,
|
|
879
|
+
) from e
|
|
880
|
+
|
|
881
|
+
async def get_by_intent_type(
|
|
882
|
+
self,
|
|
883
|
+
intent_type: str,
|
|
884
|
+
state: EnumRegistrationState | None = None,
|
|
885
|
+
domain: str = "registration",
|
|
886
|
+
limit: int = 100,
|
|
887
|
+
correlation_id: UUID | None = None,
|
|
888
|
+
) -> list[ModelRegistrationProjection]:
|
|
889
|
+
"""Find all registrations that handle the specified intent type.
|
|
890
|
+
|
|
891
|
+
Uses GIN index on intent_types column for efficient lookup.
|
|
892
|
+
|
|
893
|
+
Args:
|
|
894
|
+
intent_type: The intent type to search for (e.g., "postgres.upsert")
|
|
895
|
+
state: Optional state filter (e.g., EnumRegistrationState.ACTIVE)
|
|
896
|
+
domain: Domain namespace (default: "registration")
|
|
897
|
+
limit: Maximum results to return (default: 100)
|
|
898
|
+
correlation_id: Optional correlation ID for tracing
|
|
899
|
+
|
|
900
|
+
Returns:
|
|
901
|
+
List of matching registration projections
|
|
902
|
+
|
|
903
|
+
Raises:
|
|
904
|
+
InfraConnectionError: If database connection fails
|
|
905
|
+
InfraTimeoutError: If query times out
|
|
906
|
+
RuntimeHostError: For other database errors
|
|
907
|
+
|
|
908
|
+
Example:
|
|
909
|
+
>>> handlers = await reader.get_by_intent_type("postgres.query")
|
|
910
|
+
>>> for handler in handlers:
|
|
911
|
+
... print(f"Can handle postgres.query: {handler.entity_id}")
|
|
912
|
+
"""
|
|
913
|
+
corr_id = correlation_id or uuid4()
|
|
914
|
+
ctx = ModelInfraErrorContext(
|
|
915
|
+
transport_type=EnumInfraTransportType.DATABASE,
|
|
916
|
+
operation="get_by_intent_type",
|
|
917
|
+
target_name="projection_reader.registration",
|
|
918
|
+
correlation_id=corr_id,
|
|
919
|
+
)
|
|
920
|
+
|
|
921
|
+
async with self._circuit_breaker_lock:
|
|
922
|
+
await self._check_circuit_breaker("get_by_intent_type", corr_id)
|
|
923
|
+
|
|
924
|
+
if state is not None:
|
|
925
|
+
query_sql = """
|
|
926
|
+
SELECT * FROM registration_projections
|
|
927
|
+
WHERE domain = $1
|
|
928
|
+
AND intent_types @> $2::text[]
|
|
929
|
+
AND current_state = $3
|
|
930
|
+
ORDER BY updated_at DESC
|
|
931
|
+
LIMIT $4
|
|
932
|
+
"""
|
|
933
|
+
params = [domain, [intent_type], state.value, limit]
|
|
934
|
+
else:
|
|
935
|
+
query_sql = """
|
|
936
|
+
SELECT * FROM registration_projections
|
|
937
|
+
WHERE domain = $1
|
|
938
|
+
AND intent_types @> $2::text[]
|
|
939
|
+
ORDER BY updated_at DESC
|
|
940
|
+
LIMIT $3
|
|
941
|
+
"""
|
|
942
|
+
params = [domain, [intent_type], limit]
|
|
943
|
+
|
|
944
|
+
try:
|
|
945
|
+
async with self._pool.acquire() as conn:
|
|
946
|
+
rows = await conn.fetch(query_sql, *params)
|
|
947
|
+
|
|
948
|
+
async with self._circuit_breaker_lock:
|
|
949
|
+
await self._reset_circuit_breaker()
|
|
950
|
+
|
|
951
|
+
return [self._row_to_projection(row) for row in rows]
|
|
952
|
+
|
|
953
|
+
except asyncpg.PostgresConnectionError as e:
|
|
954
|
+
async with self._circuit_breaker_lock:
|
|
955
|
+
await self._record_circuit_failure("get_by_intent_type", corr_id)
|
|
956
|
+
raise InfraConnectionError(
|
|
957
|
+
"Failed to connect to database for intent type query",
|
|
958
|
+
context=ctx,
|
|
959
|
+
) from e
|
|
960
|
+
|
|
961
|
+
except asyncpg.QueryCanceledError as e:
|
|
962
|
+
async with self._circuit_breaker_lock:
|
|
963
|
+
await self._record_circuit_failure("get_by_intent_type", corr_id)
|
|
964
|
+
raise InfraTimeoutError(
|
|
965
|
+
"Intent type query timed out",
|
|
966
|
+
context=ModelTimeoutErrorContext(
|
|
967
|
+
transport_type=ctx.transport_type,
|
|
968
|
+
operation=ctx.operation,
|
|
969
|
+
target_name=ctx.target_name,
|
|
970
|
+
correlation_id=ctx.correlation_id,
|
|
971
|
+
# timeout_seconds omitted - value not available in this context (defaults to None)
|
|
972
|
+
),
|
|
973
|
+
) from e
|
|
974
|
+
|
|
975
|
+
except Exception as e:
|
|
976
|
+
async with self._circuit_breaker_lock:
|
|
977
|
+
await self._record_circuit_failure("get_by_intent_type", corr_id)
|
|
978
|
+
raise RuntimeHostError(
|
|
979
|
+
f"Failed to query by intent type: {type(e).__name__}",
|
|
980
|
+
context=ctx,
|
|
981
|
+
) from e
|
|
982
|
+
|
|
983
|
+
async def get_by_intent_types(
|
|
984
|
+
self,
|
|
985
|
+
intent_types: list[str],
|
|
986
|
+
state: EnumRegistrationState | None = None,
|
|
987
|
+
domain: str = "registration",
|
|
988
|
+
limit: int = 100,
|
|
989
|
+
correlation_id: UUID | None = None,
|
|
990
|
+
) -> list[ModelRegistrationProjection]:
|
|
991
|
+
"""Find all registrations that handle ANY of the specified intent types.
|
|
992
|
+
|
|
993
|
+
Bulk query method that retrieves nodes matching any intent type in a single
|
|
994
|
+
database call using the && (array overlap) operator. This is more efficient
|
|
995
|
+
than calling get_by_intent_type repeatedly for each intent type.
|
|
996
|
+
|
|
997
|
+
Performance Note:
|
|
998
|
+
This method reduces N database queries to 1 query when resolving
|
|
999
|
+
dependencies with multiple intent types. For N intent types, the
|
|
1000
|
+
previous approach required N separate database calls; this method
|
|
1001
|
+
uses a single query with SQL array overlap.
|
|
1002
|
+
|
|
1003
|
+
Args:
|
|
1004
|
+
intent_types: List of intent types to search for (e.g.,
|
|
1005
|
+
["postgres.upsert", "postgres.query", "postgres.delete"]).
|
|
1006
|
+
At least one must be present in the node's intent_types.
|
|
1007
|
+
state: Optional state filter (e.g., EnumRegistrationState.ACTIVE)
|
|
1008
|
+
domain: Domain namespace (default: "registration")
|
|
1009
|
+
limit: Maximum results to return (default: 100)
|
|
1010
|
+
correlation_id: Optional correlation ID for tracing
|
|
1011
|
+
|
|
1012
|
+
Returns:
|
|
1013
|
+
List of matching registration projections (deduplicated by entity_id)
|
|
1014
|
+
|
|
1015
|
+
Raises:
|
|
1016
|
+
ProtocolConfigurationError: If intent_types list is empty
|
|
1017
|
+
InfraConnectionError: If database connection fails
|
|
1018
|
+
InfraTimeoutError: If query times out
|
|
1019
|
+
RuntimeHostError: For other database errors
|
|
1020
|
+
|
|
1021
|
+
Example:
|
|
1022
|
+
>>> # Find nodes that handle any postgres intent
|
|
1023
|
+
>>> handlers = await reader.get_by_intent_types(
|
|
1024
|
+
... ["postgres.query", "postgres.upsert", "postgres.delete"],
|
|
1025
|
+
... state=EnumRegistrationState.ACTIVE,
|
|
1026
|
+
... )
|
|
1027
|
+
>>> for handler in handlers:
|
|
1028
|
+
... print(f"Can handle postgres intents: {handler.entity_id}")
|
|
1029
|
+
"""
|
|
1030
|
+
if not intent_types:
|
|
1031
|
+
raise ProtocolConfigurationError(
|
|
1032
|
+
"intent_types list cannot be empty for get_by_intent_types - "
|
|
1033
|
+
"use get_by_state() to query all registrations",
|
|
1034
|
+
context=ModelInfraErrorContext(
|
|
1035
|
+
transport_type=EnumInfraTransportType.DATABASE,
|
|
1036
|
+
operation="get_by_intent_types",
|
|
1037
|
+
target_name="projection_reader.registration",
|
|
1038
|
+
correlation_id=correlation_id or uuid4(),
|
|
1039
|
+
),
|
|
1040
|
+
)
|
|
1041
|
+
|
|
1042
|
+
corr_id = correlation_id or uuid4()
|
|
1043
|
+
ctx = ModelInfraErrorContext(
|
|
1044
|
+
transport_type=EnumInfraTransportType.DATABASE,
|
|
1045
|
+
operation="get_by_intent_types",
|
|
1046
|
+
target_name="projection_reader.registration",
|
|
1047
|
+
correlation_id=corr_id,
|
|
1048
|
+
)
|
|
1049
|
+
|
|
1050
|
+
async with self._circuit_breaker_lock:
|
|
1051
|
+
await self._check_circuit_breaker("get_by_intent_types", corr_id)
|
|
1052
|
+
|
|
1053
|
+
# Use && (array overlap) operator to find nodes matching ANY intent type
|
|
1054
|
+
if state is not None:
|
|
1055
|
+
query_sql = """
|
|
1056
|
+
SELECT * FROM registration_projections
|
|
1057
|
+
WHERE domain = $1
|
|
1058
|
+
AND intent_types && $2::text[]
|
|
1059
|
+
AND current_state = $3
|
|
1060
|
+
ORDER BY updated_at DESC
|
|
1061
|
+
LIMIT $4
|
|
1062
|
+
"""
|
|
1063
|
+
params = [domain, intent_types, state.value, limit]
|
|
1064
|
+
else:
|
|
1065
|
+
query_sql = """
|
|
1066
|
+
SELECT * FROM registration_projections
|
|
1067
|
+
WHERE domain = $1
|
|
1068
|
+
AND intent_types && $2::text[]
|
|
1069
|
+
ORDER BY updated_at DESC
|
|
1070
|
+
LIMIT $3
|
|
1071
|
+
"""
|
|
1072
|
+
params = [domain, intent_types, limit]
|
|
1073
|
+
|
|
1074
|
+
try:
|
|
1075
|
+
async with self._pool.acquire() as conn:
|
|
1076
|
+
rows = await conn.fetch(query_sql, *params)
|
|
1077
|
+
|
|
1078
|
+
async with self._circuit_breaker_lock:
|
|
1079
|
+
await self._reset_circuit_breaker()
|
|
1080
|
+
|
|
1081
|
+
return [self._row_to_projection(row) for row in rows]
|
|
1082
|
+
|
|
1083
|
+
except asyncpg.PostgresConnectionError as e:
|
|
1084
|
+
async with self._circuit_breaker_lock:
|
|
1085
|
+
await self._record_circuit_failure("get_by_intent_types", corr_id)
|
|
1086
|
+
raise InfraConnectionError(
|
|
1087
|
+
"Failed to connect to database for intent types query",
|
|
1088
|
+
context=ctx,
|
|
1089
|
+
) from e
|
|
1090
|
+
|
|
1091
|
+
except asyncpg.QueryCanceledError as e:
|
|
1092
|
+
async with self._circuit_breaker_lock:
|
|
1093
|
+
await self._record_circuit_failure("get_by_intent_types", corr_id)
|
|
1094
|
+
raise InfraTimeoutError(
|
|
1095
|
+
"Intent types query timed out",
|
|
1096
|
+
context=ModelTimeoutErrorContext(
|
|
1097
|
+
transport_type=ctx.transport_type,
|
|
1098
|
+
operation=ctx.operation,
|
|
1099
|
+
target_name=ctx.target_name,
|
|
1100
|
+
correlation_id=ctx.correlation_id,
|
|
1101
|
+
# timeout_seconds omitted - value not available in this context (defaults to None)
|
|
1102
|
+
),
|
|
1103
|
+
) from e
|
|
1104
|
+
|
|
1105
|
+
except Exception as e:
|
|
1106
|
+
async with self._circuit_breaker_lock:
|
|
1107
|
+
await self._record_circuit_failure("get_by_intent_types", corr_id)
|
|
1108
|
+
raise RuntimeHostError(
|
|
1109
|
+
f"Failed to query by intent types: {type(e).__name__}",
|
|
1110
|
+
context=ctx,
|
|
1111
|
+
) from e
|
|
1112
|
+
|
|
1113
|
+
async def get_by_protocol(
|
|
1114
|
+
self,
|
|
1115
|
+
protocol_name: str,
|
|
1116
|
+
state: EnumRegistrationState | None = None,
|
|
1117
|
+
domain: str = "registration",
|
|
1118
|
+
limit: int = 100,
|
|
1119
|
+
correlation_id: UUID | None = None,
|
|
1120
|
+
) -> list[ModelRegistrationProjection]:
|
|
1121
|
+
"""Find all registrations implementing the specified protocol.
|
|
1122
|
+
|
|
1123
|
+
Uses GIN index on protocols column for efficient lookup.
|
|
1124
|
+
|
|
1125
|
+
Args:
|
|
1126
|
+
protocol_name: The protocol name (e.g., "ProtocolDatabaseAdapter")
|
|
1127
|
+
state: Optional state filter (e.g., EnumRegistrationState.ACTIVE)
|
|
1128
|
+
domain: Domain namespace (default: "registration")
|
|
1129
|
+
limit: Maximum results to return (default: 100)
|
|
1130
|
+
correlation_id: Optional correlation ID for tracing
|
|
1131
|
+
|
|
1132
|
+
Returns:
|
|
1133
|
+
List of matching registration projections
|
|
1134
|
+
|
|
1135
|
+
Raises:
|
|
1136
|
+
InfraConnectionError: If database connection fails
|
|
1137
|
+
InfraTimeoutError: If query times out
|
|
1138
|
+
RuntimeHostError: For other database errors
|
|
1139
|
+
|
|
1140
|
+
Example:
|
|
1141
|
+
>>> adapters = await reader.get_by_protocol("ProtocolEventPublisher")
|
|
1142
|
+
>>> print(f"Found {len(adapters)} event publishers")
|
|
1143
|
+
"""
|
|
1144
|
+
corr_id = correlation_id or uuid4()
|
|
1145
|
+
ctx = ModelInfraErrorContext(
|
|
1146
|
+
transport_type=EnumInfraTransportType.DATABASE,
|
|
1147
|
+
operation="get_by_protocol",
|
|
1148
|
+
target_name="projection_reader.registration",
|
|
1149
|
+
correlation_id=corr_id,
|
|
1150
|
+
)
|
|
1151
|
+
|
|
1152
|
+
async with self._circuit_breaker_lock:
|
|
1153
|
+
await self._check_circuit_breaker("get_by_protocol", corr_id)
|
|
1154
|
+
|
|
1155
|
+
if state is not None:
|
|
1156
|
+
query_sql = """
|
|
1157
|
+
SELECT * FROM registration_projections
|
|
1158
|
+
WHERE domain = $1
|
|
1159
|
+
AND protocols @> $2::text[]
|
|
1160
|
+
AND current_state = $3
|
|
1161
|
+
ORDER BY updated_at DESC
|
|
1162
|
+
LIMIT $4
|
|
1163
|
+
"""
|
|
1164
|
+
params = [domain, [protocol_name], state.value, limit]
|
|
1165
|
+
else:
|
|
1166
|
+
query_sql = """
|
|
1167
|
+
SELECT * FROM registration_projections
|
|
1168
|
+
WHERE domain = $1
|
|
1169
|
+
AND protocols @> $2::text[]
|
|
1170
|
+
ORDER BY updated_at DESC
|
|
1171
|
+
LIMIT $3
|
|
1172
|
+
"""
|
|
1173
|
+
params = [domain, [protocol_name], limit]
|
|
1174
|
+
|
|
1175
|
+
try:
|
|
1176
|
+
async with self._pool.acquire() as conn:
|
|
1177
|
+
rows = await conn.fetch(query_sql, *params)
|
|
1178
|
+
|
|
1179
|
+
async with self._circuit_breaker_lock:
|
|
1180
|
+
await self._reset_circuit_breaker()
|
|
1181
|
+
|
|
1182
|
+
return [self._row_to_projection(row) for row in rows]
|
|
1183
|
+
|
|
1184
|
+
except asyncpg.PostgresConnectionError as e:
|
|
1185
|
+
async with self._circuit_breaker_lock:
|
|
1186
|
+
await self._record_circuit_failure("get_by_protocol", corr_id)
|
|
1187
|
+
raise InfraConnectionError(
|
|
1188
|
+
"Failed to connect to database for protocol query",
|
|
1189
|
+
context=ctx,
|
|
1190
|
+
) from e
|
|
1191
|
+
|
|
1192
|
+
except asyncpg.QueryCanceledError as e:
|
|
1193
|
+
async with self._circuit_breaker_lock:
|
|
1194
|
+
await self._record_circuit_failure("get_by_protocol", corr_id)
|
|
1195
|
+
raise InfraTimeoutError(
|
|
1196
|
+
"Protocol query timed out",
|
|
1197
|
+
context=ModelTimeoutErrorContext(
|
|
1198
|
+
transport_type=ctx.transport_type,
|
|
1199
|
+
operation=ctx.operation,
|
|
1200
|
+
target_name=ctx.target_name,
|
|
1201
|
+
correlation_id=ctx.correlation_id,
|
|
1202
|
+
# timeout_seconds omitted - value not available in this context (defaults to None)
|
|
1203
|
+
),
|
|
1204
|
+
) from e
|
|
1205
|
+
|
|
1206
|
+
except Exception as e:
|
|
1207
|
+
async with self._circuit_breaker_lock:
|
|
1208
|
+
await self._record_circuit_failure("get_by_protocol", corr_id)
|
|
1209
|
+
raise RuntimeHostError(
|
|
1210
|
+
f"Failed to query by protocol: {type(e).__name__}",
|
|
1211
|
+
context=ctx,
|
|
1212
|
+
) from e
|
|
1213
|
+
|
|
1214
|
+
async def get_by_contract_type(
|
|
1215
|
+
self,
|
|
1216
|
+
contract_type: ContractType,
|
|
1217
|
+
state: EnumRegistrationState | None = None,
|
|
1218
|
+
domain: str = "registration",
|
|
1219
|
+
limit: int = 100,
|
|
1220
|
+
correlation_id: UUID | None = None,
|
|
1221
|
+
) -> list[ModelRegistrationProjection]:
|
|
1222
|
+
"""Find all registrations of the specified contract type.
|
|
1223
|
+
|
|
1224
|
+
Uses B-tree index on contract_type column for efficient lookup.
|
|
1225
|
+
|
|
1226
|
+
Args:
|
|
1227
|
+
contract_type: The contract type. Must be one of: "effect", "compute",
|
|
1228
|
+
"reducer", or "orchestrator"
|
|
1229
|
+
state: Optional state filter (e.g., EnumRegistrationState.ACTIVE)
|
|
1230
|
+
domain: Domain namespace (default: "registration")
|
|
1231
|
+
limit: Maximum results to return (default: 100)
|
|
1232
|
+
correlation_id: Optional correlation ID for tracing
|
|
1233
|
+
|
|
1234
|
+
Returns:
|
|
1235
|
+
List of matching registration projections
|
|
1236
|
+
|
|
1237
|
+
Raises:
|
|
1238
|
+
InfraConnectionError: If database connection fails
|
|
1239
|
+
InfraTimeoutError: If query times out
|
|
1240
|
+
RuntimeHostError: For other database errors
|
|
1241
|
+
|
|
1242
|
+
Example:
|
|
1243
|
+
>>> effects = await reader.get_by_contract_type("effect")
|
|
1244
|
+
>>> print(f"Found {len(effects)} effect nodes")
|
|
1245
|
+
"""
|
|
1246
|
+
corr_id = correlation_id or uuid4()
|
|
1247
|
+
ctx = ModelInfraErrorContext(
|
|
1248
|
+
transport_type=EnumInfraTransportType.DATABASE,
|
|
1249
|
+
operation="get_by_contract_type",
|
|
1250
|
+
target_name="projection_reader.registration",
|
|
1251
|
+
correlation_id=corr_id,
|
|
1252
|
+
)
|
|
1253
|
+
|
|
1254
|
+
async with self._circuit_breaker_lock:
|
|
1255
|
+
await self._check_circuit_breaker("get_by_contract_type", corr_id)
|
|
1256
|
+
|
|
1257
|
+
if state is not None:
|
|
1258
|
+
query_sql = """
|
|
1259
|
+
SELECT * FROM registration_projections
|
|
1260
|
+
WHERE domain = $1
|
|
1261
|
+
AND contract_type = $2
|
|
1262
|
+
AND current_state = $3
|
|
1263
|
+
ORDER BY updated_at DESC
|
|
1264
|
+
LIMIT $4
|
|
1265
|
+
"""
|
|
1266
|
+
params = [domain, contract_type, state.value, limit]
|
|
1267
|
+
else:
|
|
1268
|
+
query_sql = """
|
|
1269
|
+
SELECT * FROM registration_projections
|
|
1270
|
+
WHERE domain = $1
|
|
1271
|
+
AND contract_type = $2
|
|
1272
|
+
ORDER BY updated_at DESC
|
|
1273
|
+
LIMIT $3
|
|
1274
|
+
"""
|
|
1275
|
+
params = [domain, contract_type, limit]
|
|
1276
|
+
|
|
1277
|
+
try:
|
|
1278
|
+
async with self._pool.acquire() as conn:
|
|
1279
|
+
rows = await conn.fetch(query_sql, *params)
|
|
1280
|
+
|
|
1281
|
+
async with self._circuit_breaker_lock:
|
|
1282
|
+
await self._reset_circuit_breaker()
|
|
1283
|
+
|
|
1284
|
+
return [self._row_to_projection(row) for row in rows]
|
|
1285
|
+
|
|
1286
|
+
except asyncpg.PostgresConnectionError as e:
|
|
1287
|
+
async with self._circuit_breaker_lock:
|
|
1288
|
+
await self._record_circuit_failure("get_by_contract_type", corr_id)
|
|
1289
|
+
raise InfraConnectionError(
|
|
1290
|
+
"Failed to connect to database for contract type query",
|
|
1291
|
+
context=ctx,
|
|
1292
|
+
) from e
|
|
1293
|
+
|
|
1294
|
+
except asyncpg.QueryCanceledError as e:
|
|
1295
|
+
async with self._circuit_breaker_lock:
|
|
1296
|
+
await self._record_circuit_failure("get_by_contract_type", corr_id)
|
|
1297
|
+
raise InfraTimeoutError(
|
|
1298
|
+
"Contract type query timed out",
|
|
1299
|
+
context=ModelTimeoutErrorContext(
|
|
1300
|
+
transport_type=ctx.transport_type,
|
|
1301
|
+
operation=ctx.operation,
|
|
1302
|
+
target_name=ctx.target_name,
|
|
1303
|
+
correlation_id=ctx.correlation_id,
|
|
1304
|
+
# timeout_seconds omitted - value not available in this context (defaults to None)
|
|
1305
|
+
),
|
|
1306
|
+
) from e
|
|
1307
|
+
|
|
1308
|
+
except Exception as e:
|
|
1309
|
+
async with self._circuit_breaker_lock:
|
|
1310
|
+
await self._record_circuit_failure("get_by_contract_type", corr_id)
|
|
1311
|
+
raise RuntimeHostError(
|
|
1312
|
+
f"Failed to query by contract type: {type(e).__name__}",
|
|
1313
|
+
context=ctx,
|
|
1314
|
+
) from e
|
|
1315
|
+
|
|
1316
|
+
async def get_by_capability_tags_all(
|
|
1317
|
+
self,
|
|
1318
|
+
tags: list[str],
|
|
1319
|
+
state: EnumRegistrationState | None = None,
|
|
1320
|
+
domain: str = "registration",
|
|
1321
|
+
limit: int = 100,
|
|
1322
|
+
correlation_id: UUID | None = None,
|
|
1323
|
+
) -> list[ModelRegistrationProjection]:
|
|
1324
|
+
"""Find registrations with ALL specified capability tags.
|
|
1325
|
+
|
|
1326
|
+
Uses GIN index with @> (contains all) operator.
|
|
1327
|
+
|
|
1328
|
+
Args:
|
|
1329
|
+
tags: List of capability tags that must all be present
|
|
1330
|
+
state: Optional state filter (e.g., EnumRegistrationState.ACTIVE)
|
|
1331
|
+
domain: Domain namespace (default: "registration")
|
|
1332
|
+
limit: Maximum results to return (default: 100)
|
|
1333
|
+
correlation_id: Optional correlation ID for tracing
|
|
1334
|
+
|
|
1335
|
+
Returns:
|
|
1336
|
+
List of matching registration projections
|
|
1337
|
+
|
|
1338
|
+
Raises:
|
|
1339
|
+
ProtocolConfigurationError: If tags list is empty
|
|
1340
|
+
InfraConnectionError: If database connection fails
|
|
1341
|
+
InfraTimeoutError: If query times out
|
|
1342
|
+
RuntimeHostError: For other database errors
|
|
1343
|
+
|
|
1344
|
+
Example:
|
|
1345
|
+
>>> adapters = await reader.get_by_capability_tags_all(
|
|
1346
|
+
... ["postgres.storage", "transactions"]
|
|
1347
|
+
... )
|
|
1348
|
+
"""
|
|
1349
|
+
if not tags:
|
|
1350
|
+
raise ProtocolConfigurationError(
|
|
1351
|
+
"tags list cannot be empty for get_by_capability_tags_all - "
|
|
1352
|
+
"use get_by_state() to query all registrations",
|
|
1353
|
+
context=ModelInfraErrorContext(
|
|
1354
|
+
transport_type=EnumInfraTransportType.DATABASE,
|
|
1355
|
+
operation="get_by_capability_tags_all",
|
|
1356
|
+
target_name="projection_reader.registration",
|
|
1357
|
+
correlation_id=correlation_id or uuid4(),
|
|
1358
|
+
),
|
|
1359
|
+
)
|
|
1360
|
+
|
|
1361
|
+
corr_id = correlation_id or uuid4()
|
|
1362
|
+
ctx = ModelInfraErrorContext(
|
|
1363
|
+
transport_type=EnumInfraTransportType.DATABASE,
|
|
1364
|
+
operation="get_by_capability_tags_all",
|
|
1365
|
+
target_name="projection_reader.registration",
|
|
1366
|
+
correlation_id=corr_id,
|
|
1367
|
+
)
|
|
1368
|
+
|
|
1369
|
+
async with self._circuit_breaker_lock:
|
|
1370
|
+
await self._check_circuit_breaker("get_by_capability_tags_all", corr_id)
|
|
1371
|
+
|
|
1372
|
+
if state is not None:
|
|
1373
|
+
query_sql = """
|
|
1374
|
+
SELECT * FROM registration_projections
|
|
1375
|
+
WHERE domain = $1
|
|
1376
|
+
AND capability_tags @> $2::text[]
|
|
1377
|
+
AND current_state = $3
|
|
1378
|
+
ORDER BY updated_at DESC
|
|
1379
|
+
LIMIT $4
|
|
1380
|
+
"""
|
|
1381
|
+
params = [domain, tags, state.value, limit]
|
|
1382
|
+
else:
|
|
1383
|
+
query_sql = """
|
|
1384
|
+
SELECT * FROM registration_projections
|
|
1385
|
+
WHERE domain = $1
|
|
1386
|
+
AND capability_tags @> $2::text[]
|
|
1387
|
+
ORDER BY updated_at DESC
|
|
1388
|
+
LIMIT $3
|
|
1389
|
+
"""
|
|
1390
|
+
params = [domain, tags, limit]
|
|
1391
|
+
|
|
1392
|
+
try:
|
|
1393
|
+
async with self._pool.acquire() as conn:
|
|
1394
|
+
rows = await conn.fetch(query_sql, *params)
|
|
1395
|
+
|
|
1396
|
+
async with self._circuit_breaker_lock:
|
|
1397
|
+
await self._reset_circuit_breaker()
|
|
1398
|
+
|
|
1399
|
+
return [self._row_to_projection(row) for row in rows]
|
|
1400
|
+
|
|
1401
|
+
except asyncpg.PostgresConnectionError as e:
|
|
1402
|
+
async with self._circuit_breaker_lock:
|
|
1403
|
+
await self._record_circuit_failure(
|
|
1404
|
+
"get_by_capability_tags_all", corr_id
|
|
1405
|
+
)
|
|
1406
|
+
raise InfraConnectionError(
|
|
1407
|
+
"Failed to connect to database for capability tags query",
|
|
1408
|
+
context=ctx,
|
|
1409
|
+
) from e
|
|
1410
|
+
|
|
1411
|
+
except asyncpg.QueryCanceledError as e:
|
|
1412
|
+
async with self._circuit_breaker_lock:
|
|
1413
|
+
await self._record_circuit_failure(
|
|
1414
|
+
"get_by_capability_tags_all", corr_id
|
|
1415
|
+
)
|
|
1416
|
+
raise InfraTimeoutError(
|
|
1417
|
+
"Capability tags query timed out",
|
|
1418
|
+
context=ModelTimeoutErrorContext(
|
|
1419
|
+
transport_type=ctx.transport_type,
|
|
1420
|
+
operation=ctx.operation,
|
|
1421
|
+
target_name=ctx.target_name,
|
|
1422
|
+
correlation_id=ctx.correlation_id,
|
|
1423
|
+
# timeout_seconds omitted - value not available in this context (defaults to None)
|
|
1424
|
+
),
|
|
1425
|
+
) from e
|
|
1426
|
+
|
|
1427
|
+
except Exception as e:
|
|
1428
|
+
async with self._circuit_breaker_lock:
|
|
1429
|
+
await self._record_circuit_failure(
|
|
1430
|
+
"get_by_capability_tags_all", corr_id
|
|
1431
|
+
)
|
|
1432
|
+
raise RuntimeHostError(
|
|
1433
|
+
f"Failed to query by capability tags: {type(e).__name__}",
|
|
1434
|
+
context=ctx,
|
|
1435
|
+
) from e
|
|
1436
|
+
|
|
1437
|
+
async def get_by_capability_tags_any(
|
|
1438
|
+
self,
|
|
1439
|
+
tags: list[str],
|
|
1440
|
+
state: EnumRegistrationState | None = None,
|
|
1441
|
+
domain: str = "registration",
|
|
1442
|
+
limit: int = 100,
|
|
1443
|
+
correlation_id: UUID | None = None,
|
|
1444
|
+
) -> list[ModelRegistrationProjection]:
|
|
1445
|
+
"""Find registrations with ANY of the specified capability tags.
|
|
1446
|
+
|
|
1447
|
+
Uses GIN index with && (overlaps) operator.
|
|
1448
|
+
|
|
1449
|
+
Args:
|
|
1450
|
+
tags: List of capability tags, at least one must be present
|
|
1451
|
+
state: Optional state filter (e.g., EnumRegistrationState.ACTIVE)
|
|
1452
|
+
domain: Domain namespace (default: "registration")
|
|
1453
|
+
limit: Maximum results to return (default: 100)
|
|
1454
|
+
correlation_id: Optional correlation ID for tracing
|
|
1455
|
+
|
|
1456
|
+
Returns:
|
|
1457
|
+
List of matching registration projections
|
|
1458
|
+
|
|
1459
|
+
Raises:
|
|
1460
|
+
ProtocolConfigurationError: If tags list is empty
|
|
1461
|
+
InfraConnectionError: If database connection fails
|
|
1462
|
+
InfraTimeoutError: If query times out
|
|
1463
|
+
RuntimeHostError: For other database errors
|
|
1464
|
+
|
|
1465
|
+
Example:
|
|
1466
|
+
>>> adapters = await reader.get_by_capability_tags_any(
|
|
1467
|
+
... ["postgres.storage", "mysql.storage", "sqlite.storage"]
|
|
1468
|
+
... )
|
|
1469
|
+
"""
|
|
1470
|
+
if not tags:
|
|
1471
|
+
raise ProtocolConfigurationError(
|
|
1472
|
+
"tags list cannot be empty for get_by_capability_tags_any - "
|
|
1473
|
+
"use get_by_state() to query all registrations",
|
|
1474
|
+
context=ModelInfraErrorContext(
|
|
1475
|
+
transport_type=EnumInfraTransportType.DATABASE,
|
|
1476
|
+
operation="get_by_capability_tags_any",
|
|
1477
|
+
target_name="projection_reader.registration",
|
|
1478
|
+
correlation_id=correlation_id or uuid4(),
|
|
1479
|
+
),
|
|
1480
|
+
)
|
|
1481
|
+
|
|
1482
|
+
corr_id = correlation_id or uuid4()
|
|
1483
|
+
ctx = ModelInfraErrorContext(
|
|
1484
|
+
transport_type=EnumInfraTransportType.DATABASE,
|
|
1485
|
+
operation="get_by_capability_tags_any",
|
|
1486
|
+
target_name="projection_reader.registration",
|
|
1487
|
+
correlation_id=corr_id,
|
|
1488
|
+
)
|
|
1489
|
+
|
|
1490
|
+
async with self._circuit_breaker_lock:
|
|
1491
|
+
await self._check_circuit_breaker("get_by_capability_tags_any", corr_id)
|
|
1492
|
+
|
|
1493
|
+
if state is not None:
|
|
1494
|
+
query_sql = """
|
|
1495
|
+
SELECT * FROM registration_projections
|
|
1496
|
+
WHERE domain = $1
|
|
1497
|
+
AND capability_tags && $2::text[]
|
|
1498
|
+
AND current_state = $3
|
|
1499
|
+
ORDER BY updated_at DESC
|
|
1500
|
+
LIMIT $4
|
|
1501
|
+
"""
|
|
1502
|
+
params = [domain, tags, state.value, limit]
|
|
1503
|
+
else:
|
|
1504
|
+
query_sql = """
|
|
1505
|
+
SELECT * FROM registration_projections
|
|
1506
|
+
WHERE domain = $1
|
|
1507
|
+
AND capability_tags && $2::text[]
|
|
1508
|
+
ORDER BY updated_at DESC
|
|
1509
|
+
LIMIT $3
|
|
1510
|
+
"""
|
|
1511
|
+
params = [domain, tags, limit]
|
|
1512
|
+
|
|
1513
|
+
try:
|
|
1514
|
+
async with self._pool.acquire() as conn:
|
|
1515
|
+
rows = await conn.fetch(query_sql, *params)
|
|
1516
|
+
|
|
1517
|
+
async with self._circuit_breaker_lock:
|
|
1518
|
+
await self._reset_circuit_breaker()
|
|
1519
|
+
|
|
1520
|
+
return [self._row_to_projection(row) for row in rows]
|
|
1521
|
+
|
|
1522
|
+
except asyncpg.PostgresConnectionError as e:
|
|
1523
|
+
async with self._circuit_breaker_lock:
|
|
1524
|
+
await self._record_circuit_failure(
|
|
1525
|
+
"get_by_capability_tags_any", corr_id
|
|
1526
|
+
)
|
|
1527
|
+
raise InfraConnectionError(
|
|
1528
|
+
"Failed to connect to database for capability tags query",
|
|
1529
|
+
context=ctx,
|
|
1530
|
+
) from e
|
|
1531
|
+
|
|
1532
|
+
except asyncpg.QueryCanceledError as e:
|
|
1533
|
+
async with self._circuit_breaker_lock:
|
|
1534
|
+
await self._record_circuit_failure(
|
|
1535
|
+
"get_by_capability_tags_any", corr_id
|
|
1536
|
+
)
|
|
1537
|
+
raise InfraTimeoutError(
|
|
1538
|
+
"Capability tags query timed out",
|
|
1539
|
+
context=ModelTimeoutErrorContext(
|
|
1540
|
+
transport_type=ctx.transport_type,
|
|
1541
|
+
operation=ctx.operation,
|
|
1542
|
+
target_name=ctx.target_name,
|
|
1543
|
+
correlation_id=ctx.correlation_id,
|
|
1544
|
+
# timeout_seconds omitted - value not available in this context (defaults to None)
|
|
1545
|
+
),
|
|
1546
|
+
) from e
|
|
1547
|
+
|
|
1548
|
+
except Exception as e:
|
|
1549
|
+
async with self._circuit_breaker_lock:
|
|
1550
|
+
await self._record_circuit_failure(
|
|
1551
|
+
"get_by_capability_tags_any", corr_id
|
|
1552
|
+
)
|
|
1553
|
+
raise RuntimeHostError(
|
|
1554
|
+
f"Failed to query by capability tags: {type(e).__name__}",
|
|
1555
|
+
context=ctx,
|
|
1556
|
+
) from e
|
|
1557
|
+
|
|
1558
|
+
|
|
1559
|
+
__all__: list[str] = ["ProjectionReaderRegistration"]
|