omnibase_infra 0.3.1__py3-none-any.whl → 0.4.0__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 +1 -1
- omnibase_infra/enums/__init__.py +3 -0
- omnibase_infra/enums/enum_consumer_group_purpose.py +9 -0
- omnibase_infra/enums/enum_postgres_error_code.py +188 -0
- omnibase_infra/errors/__init__.py +4 -0
- omnibase_infra/errors/error_infra.py +60 -0
- omnibase_infra/handlers/__init__.py +3 -0
- omnibase_infra/handlers/handler_slack_webhook.py +426 -0
- omnibase_infra/handlers/models/__init__.py +14 -0
- omnibase_infra/handlers/models/enum_alert_severity.py +36 -0
- omnibase_infra/handlers/models/model_slack_alert.py +24 -0
- omnibase_infra/handlers/models/model_slack_alert_payload.py +77 -0
- omnibase_infra/handlers/models/model_slack_alert_result.py +73 -0
- omnibase_infra/handlers/registration_storage/handler_registration_storage_postgres.py +29 -20
- omnibase_infra/mixins/__init__.py +14 -0
- omnibase_infra/mixins/mixin_node_introspection.py +42 -20
- omnibase_infra/mixins/mixin_postgres_error_response.py +314 -0
- omnibase_infra/mixins/mixin_postgres_op_executor.py +298 -0
- omnibase_infra/models/__init__.py +3 -0
- omnibase_infra/models/discovery/model_dependency_spec.py +1 -0
- omnibase_infra/models/discovery/model_discovered_capabilities.py +1 -1
- omnibase_infra/models/discovery/model_introspection_config.py +28 -1
- omnibase_infra/models/discovery/model_introspection_performance_metrics.py +1 -0
- omnibase_infra/models/discovery/model_introspection_task_config.py +1 -0
- omnibase_infra/{nodes/effects/models → models}/model_backend_result.py +22 -6
- omnibase_infra/models/projection/__init__.py +11 -0
- omnibase_infra/models/projection/model_contract_projection.py +170 -0
- omnibase_infra/models/projection/model_topic_projection.py +148 -0
- omnibase_infra/models/runtime/__init__.py +4 -0
- omnibase_infra/models/runtime/model_resolved_dependencies.py +116 -0
- omnibase_infra/nodes/contract_registry_reducer/__init__.py +5 -0
- omnibase_infra/nodes/contract_registry_reducer/contract.yaml +6 -5
- omnibase_infra/nodes/contract_registry_reducer/contract_registration_event_router.py +689 -0
- omnibase_infra/nodes/contract_registry_reducer/reducer.py +9 -26
- omnibase_infra/nodes/effects/__init__.py +1 -1
- omnibase_infra/nodes/effects/models/__init__.py +6 -4
- omnibase_infra/nodes/effects/models/model_registry_response.py +1 -1
- omnibase_infra/nodes/effects/protocol_consul_client.py +1 -1
- omnibase_infra/nodes/effects/protocol_postgres_adapter.py +1 -1
- omnibase_infra/nodes/effects/registry_effect.py +1 -1
- omnibase_infra/nodes/node_contract_persistence_effect/__init__.py +101 -0
- omnibase_infra/nodes/node_contract_persistence_effect/contract.yaml +490 -0
- omnibase_infra/nodes/node_contract_persistence_effect/handlers/__init__.py +74 -0
- omnibase_infra/nodes/node_contract_persistence_effect/handlers/handler_postgres_cleanup_topics.py +217 -0
- omnibase_infra/nodes/node_contract_persistence_effect/handlers/handler_postgres_contract_upsert.py +242 -0
- omnibase_infra/nodes/node_contract_persistence_effect/handlers/handler_postgres_deactivate.py +194 -0
- omnibase_infra/nodes/node_contract_persistence_effect/handlers/handler_postgres_heartbeat.py +243 -0
- omnibase_infra/nodes/node_contract_persistence_effect/handlers/handler_postgres_mark_stale.py +208 -0
- omnibase_infra/nodes/node_contract_persistence_effect/handlers/handler_postgres_topic_update.py +298 -0
- omnibase_infra/nodes/node_contract_persistence_effect/models/__init__.py +15 -0
- omnibase_infra/nodes/node_contract_persistence_effect/models/model_persistence_result.py +52 -0
- omnibase_infra/nodes/node_contract_persistence_effect/node.py +131 -0
- omnibase_infra/nodes/node_contract_persistence_effect/registry/__init__.py +27 -0
- omnibase_infra/nodes/node_contract_persistence_effect/registry/registry_infra_contract_persistence_effect.py +251 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_postgres_intent_payload.py +8 -12
- omnibase_infra/nodes/node_registry_effect/models/__init__.py +2 -2
- omnibase_infra/nodes/node_slack_alerter_effect/__init__.py +33 -0
- omnibase_infra/nodes/node_slack_alerter_effect/contract.yaml +291 -0
- omnibase_infra/nodes/node_slack_alerter_effect/node.py +106 -0
- omnibase_infra/projectors/__init__.py +6 -0
- omnibase_infra/projectors/projection_reader_contract.py +1301 -0
- omnibase_infra/runtime/__init__.py +12 -0
- omnibase_infra/runtime/baseline_subscriptions.py +13 -6
- omnibase_infra/runtime/contract_dependency_resolver.py +455 -0
- omnibase_infra/runtime/contract_registration_event_router.py +500 -0
- omnibase_infra/runtime/db/__init__.py +4 -0
- omnibase_infra/runtime/db/models/__init__.py +15 -10
- omnibase_infra/runtime/db/models/model_db_operation.py +40 -0
- omnibase_infra/runtime/db/models/model_db_param.py +24 -0
- omnibase_infra/runtime/db/models/model_db_repository_contract.py +40 -0
- omnibase_infra/runtime/db/models/model_db_return.py +26 -0
- omnibase_infra/runtime/db/models/model_db_safety_policy.py +32 -0
- omnibase_infra/runtime/emit_daemon/event_registry.py +34 -22
- omnibase_infra/runtime/event_bus_subcontract_wiring.py +63 -23
- omnibase_infra/runtime/intent_execution_router.py +430 -0
- omnibase_infra/runtime/models/__init__.py +6 -0
- omnibase_infra/runtime/models/model_contract_registry_config.py +41 -0
- omnibase_infra/runtime/models/model_intent_execution_summary.py +79 -0
- omnibase_infra/runtime/models/model_runtime_config.py +8 -0
- omnibase_infra/runtime/protocols/__init__.py +16 -0
- omnibase_infra/runtime/protocols/protocol_intent_executor.py +107 -0
- omnibase_infra/runtime/publisher_topic_scoped.py +16 -11
- omnibase_infra/runtime/registry_policy.py +29 -15
- omnibase_infra/runtime/request_response_wiring.py +793 -0
- omnibase_infra/runtime/service_kernel.py +295 -8
- omnibase_infra/runtime/service_runtime_host_process.py +149 -5
- omnibase_infra/runtime/util_version.py +5 -1
- omnibase_infra/schemas/schema_latency_baseline.sql +135 -0
- omnibase_infra/services/contract_publisher/config.py +4 -4
- omnibase_infra/services/contract_publisher/service.py +8 -5
- omnibase_infra/services/observability/injection_effectiveness/__init__.py +67 -0
- omnibase_infra/services/observability/injection_effectiveness/config.py +295 -0
- omnibase_infra/services/observability/injection_effectiveness/consumer.py +1461 -0
- omnibase_infra/services/observability/injection_effectiveness/models/__init__.py +32 -0
- omnibase_infra/services/observability/injection_effectiveness/models/model_agent_match.py +79 -0
- omnibase_infra/services/observability/injection_effectiveness/models/model_context_utilization.py +118 -0
- omnibase_infra/services/observability/injection_effectiveness/models/model_latency_breakdown.py +107 -0
- omnibase_infra/services/observability/injection_effectiveness/models/model_pattern_utilization.py +46 -0
- omnibase_infra/services/observability/injection_effectiveness/writer_postgres.py +596 -0
- omnibase_infra/services/registry_api/models/__init__.py +25 -0
- omnibase_infra/services/registry_api/models/model_contract_ref.py +44 -0
- omnibase_infra/services/registry_api/models/model_contract_view.py +81 -0
- omnibase_infra/services/registry_api/models/model_response_contracts.py +50 -0
- omnibase_infra/services/registry_api/models/model_response_topics.py +50 -0
- omnibase_infra/services/registry_api/models/model_topic_summary.py +57 -0
- omnibase_infra/services/registry_api/models/model_topic_view.py +63 -0
- omnibase_infra/services/registry_api/routes.py +205 -6
- omnibase_infra/services/registry_api/service.py +528 -1
- omnibase_infra/utils/__init__.py +7 -0
- omnibase_infra/utils/util_db_error_context.py +292 -0
- omnibase_infra/validation/infra_validators.py +3 -1
- omnibase_infra/validation/validation_exemptions.yaml +65 -0
- {omnibase_infra-0.3.1.dist-info → omnibase_infra-0.4.0.dist-info}/METADATA +3 -3
- {omnibase_infra-0.3.1.dist-info → omnibase_infra-0.4.0.dist-info}/RECORD +117 -58
- {omnibase_infra-0.3.1.dist-info → omnibase_infra-0.4.0.dist-info}/WHEEL +0 -0
- {omnibase_infra-0.3.1.dist-info → omnibase_infra-0.4.0.dist-info}/entry_points.txt +0 -0
- {omnibase_infra-0.3.1.dist-info → omnibase_infra-0.4.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""Shared execution core for PostgreSQL operation handlers.
|
|
4
|
+
|
|
5
|
+
This mixin centralizes the mechanical aspects of PostgreSQL handler execution:
|
|
6
|
+
- Timing via time.perf_counter()
|
|
7
|
+
- Error classification and sanitization
|
|
8
|
+
- ModelBackendResult construction
|
|
9
|
+
- Structured logging with correlation IDs
|
|
10
|
+
|
|
11
|
+
By extracting this boilerplate into a reusable mixin, handlers are reduced from
|
|
12
|
+
~200 lines to ~30 lines, eliminating drift risk where error handling patterns
|
|
13
|
+
could diverge across handlers.
|
|
14
|
+
|
|
15
|
+
Architecture:
|
|
16
|
+
Handlers inherit from MixinPostgresOpExecutor and call _execute_postgres_op()
|
|
17
|
+
with their operation-specific logic wrapped in a callable. The mixin handles
|
|
18
|
+
all timing, error classification, sanitization, and result construction.
|
|
19
|
+
|
|
20
|
+
Error Classification:
|
|
21
|
+
- TimeoutError, InfraTimeoutError → POSTGRES_TIMEOUT_ERROR (retriable)
|
|
22
|
+
- InfraAuthenticationError → POSTGRES_AUTH_ERROR (non-retriable)
|
|
23
|
+
- InfraConnectionError → POSTGRES_CONNECTION_ERROR (retriable)
|
|
24
|
+
- RepositoryExecutionError → op_error_code (handler-specified)
|
|
25
|
+
- Exception → POSTGRES_UNKNOWN_ERROR (non-retriable)
|
|
26
|
+
|
|
27
|
+
Usage:
|
|
28
|
+
```python
|
|
29
|
+
class HandlerPostgresHeartbeat(MixinPostgresOpExecutor):
|
|
30
|
+
async def handle(self, payload, correlation_id) -> ModelBackendResult:
|
|
31
|
+
return await self._execute_postgres_op(
|
|
32
|
+
op_error_code=EnumPostgresErrorCode.HEARTBEAT_ERROR,
|
|
33
|
+
correlation_id=correlation_id,
|
|
34
|
+
log_context={"contract_id": payload.contract_id},
|
|
35
|
+
fn=lambda: self._do_heartbeat(payload),
|
|
36
|
+
)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Related:
|
|
40
|
+
- EnumPostgresErrorCode: Error code enumeration with retriability metadata
|
|
41
|
+
- MixinAsyncCircuitBreaker: Circuit breaker mixin (not integrated here)
|
|
42
|
+
- OMN-1857: Extraction ticket for this mixin
|
|
43
|
+
|
|
44
|
+
Note on Circuit Breaker:
|
|
45
|
+
Per OMN-1857 design decision 1A, the executor should manage circuit breaker
|
|
46
|
+
internally. However, this initial implementation focuses on the core execution
|
|
47
|
+
mechanics. Circuit breaker integration will be added as a follow-up once the
|
|
48
|
+
basic pattern is validated.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
from __future__ import annotations
|
|
52
|
+
|
|
53
|
+
import logging
|
|
54
|
+
import time
|
|
55
|
+
from collections.abc import Awaitable, Callable
|
|
56
|
+
from typing import TYPE_CHECKING, TypeVar
|
|
57
|
+
|
|
58
|
+
from omnibase_infra.enums import EnumPostgresErrorCode
|
|
59
|
+
from omnibase_infra.errors import (
|
|
60
|
+
InfraAuthenticationError,
|
|
61
|
+
InfraConnectionError,
|
|
62
|
+
InfraTimeoutError,
|
|
63
|
+
RepositoryExecutionError,
|
|
64
|
+
)
|
|
65
|
+
from omnibase_infra.models.model_backend_result import ModelBackendResult
|
|
66
|
+
from omnibase_infra.utils import sanitize_backend_error, sanitize_error_message
|
|
67
|
+
|
|
68
|
+
if TYPE_CHECKING:
|
|
69
|
+
from uuid import UUID
|
|
70
|
+
|
|
71
|
+
logger = logging.getLogger(__name__)
|
|
72
|
+
|
|
73
|
+
T = TypeVar("T")
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class MixinPostgresOpExecutor:
|
|
77
|
+
"""Shared execution core for PostgreSQL operation handlers.
|
|
78
|
+
|
|
79
|
+
Centralizes timing, error handling, sanitization, and result construction
|
|
80
|
+
for PostgreSQL operations. Handlers inherit this mixin and delegate to
|
|
81
|
+
_execute_postgres_op() for consistent mechanical behavior.
|
|
82
|
+
|
|
83
|
+
This mixin does NOT manage circuit breaker state - that responsibility
|
|
84
|
+
remains with the handler or a separate MixinAsyncCircuitBreaker composition.
|
|
85
|
+
|
|
86
|
+
Example:
|
|
87
|
+
```python
|
|
88
|
+
class HandlerPostgresUpsert(MixinPostgresOpExecutor):
|
|
89
|
+
def __init__(self, pool: asyncpg.Pool) -> None:
|
|
90
|
+
self._pool = pool
|
|
91
|
+
|
|
92
|
+
async def handle(
|
|
93
|
+
self, payload: ModelPayloadUpsertContract, correlation_id: UUID
|
|
94
|
+
) -> ModelBackendResult:
|
|
95
|
+
return await self._execute_postgres_op(
|
|
96
|
+
op_error_code=EnumPostgresErrorCode.UPSERT_ERROR,
|
|
97
|
+
correlation_id=correlation_id,
|
|
98
|
+
log_context={
|
|
99
|
+
"contract_id": payload.contract_id,
|
|
100
|
+
"node_name": payload.node_name,
|
|
101
|
+
},
|
|
102
|
+
fn=lambda: self._execute_upsert(payload),
|
|
103
|
+
)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
See Also:
|
|
107
|
+
- EnumPostgresErrorCode: Error codes with is_retriable property
|
|
108
|
+
- sanitize_error_message: Error sanitization utility
|
|
109
|
+
- ModelBackendResult: Structured result model
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
async def _execute_postgres_op(
|
|
113
|
+
self,
|
|
114
|
+
*,
|
|
115
|
+
op_error_code: EnumPostgresErrorCode,
|
|
116
|
+
correlation_id: UUID,
|
|
117
|
+
log_context: dict[str, object],
|
|
118
|
+
fn: Callable[[], Awaitable[T]],
|
|
119
|
+
) -> ModelBackendResult:
|
|
120
|
+
"""Execute a PostgreSQL operation with timing, error handling, and sanitization.
|
|
121
|
+
|
|
122
|
+
This method wraps the actual database operation (fn) with:
|
|
123
|
+
1. Timing measurement via time.perf_counter()
|
|
124
|
+
2. Exception classification into appropriate error codes
|
|
125
|
+
3. Error message sanitization to prevent credential exposure
|
|
126
|
+
4. Structured logging with correlation ID and context
|
|
127
|
+
5. ModelBackendResult construction
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
op_error_code: Operation-specific error code for non-infrastructure
|
|
131
|
+
failures (e.g., UPSERT_ERROR, HEARTBEAT_ERROR). Used when the
|
|
132
|
+
operation fails due to business logic or query issues rather
|
|
133
|
+
than connection/auth problems.
|
|
134
|
+
correlation_id: Request correlation ID for distributed tracing.
|
|
135
|
+
log_context: Additional fields for structured logging (e.g.,
|
|
136
|
+
contract_id, node_name). Included in all log messages.
|
|
137
|
+
fn: Async callable that performs the actual database operation.
|
|
138
|
+
Should return any value on success. The return value is not
|
|
139
|
+
used - only success/failure matters.
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
ModelBackendResult with:
|
|
143
|
+
- success: True if fn() completed without exception
|
|
144
|
+
- error: Sanitized error message (empty string on success)
|
|
145
|
+
- error_code: Appropriate EnumPostgresErrorCode
|
|
146
|
+
- duration_ms: Operation duration in milliseconds
|
|
147
|
+
- backend_id: "postgres"
|
|
148
|
+
- correlation_id: Passed through for tracing
|
|
149
|
+
|
|
150
|
+
Error Classification:
|
|
151
|
+
| Exception Type | Error Code | Retriable |
|
|
152
|
+
|-----------------------------|-------------------------|-----------|
|
|
153
|
+
| TimeoutError | TIMEOUT_ERROR | Yes |
|
|
154
|
+
| InfraTimeoutError | TIMEOUT_ERROR | Yes |
|
|
155
|
+
| InfraAuthenticationError | AUTH_ERROR | No |
|
|
156
|
+
| InfraConnectionError | CONNECTION_ERROR | Yes |
|
|
157
|
+
| RepositoryExecutionError | op_error_code | No |
|
|
158
|
+
| Exception | UNKNOWN_ERROR | No |
|
|
159
|
+
|
|
160
|
+
Note:
|
|
161
|
+
This method never raises exceptions. All errors are captured,
|
|
162
|
+
sanitized, logged, and returned in the result model.
|
|
163
|
+
"""
|
|
164
|
+
start_time = time.perf_counter()
|
|
165
|
+
log_extra = {
|
|
166
|
+
"correlation_id": str(correlation_id),
|
|
167
|
+
**log_context,
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
try:
|
|
171
|
+
# Execute the operation
|
|
172
|
+
await fn()
|
|
173
|
+
|
|
174
|
+
duration_ms = (time.perf_counter() - start_time) * 1000
|
|
175
|
+
|
|
176
|
+
logger.debug(
|
|
177
|
+
"PostgreSQL operation completed successfully",
|
|
178
|
+
extra={**log_extra, "duration_ms": duration_ms},
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
return ModelBackendResult(
|
|
182
|
+
success=True,
|
|
183
|
+
duration_ms=duration_ms,
|
|
184
|
+
backend_id="postgres",
|
|
185
|
+
correlation_id=correlation_id,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
except (TimeoutError, InfraTimeoutError) as e:
|
|
189
|
+
# Timeout - retriable error
|
|
190
|
+
duration_ms = (time.perf_counter() - start_time) * 1000
|
|
191
|
+
sanitized_error = sanitize_error_message(e)
|
|
192
|
+
logger.warning(
|
|
193
|
+
"PostgreSQL operation timed out",
|
|
194
|
+
extra={
|
|
195
|
+
**log_extra,
|
|
196
|
+
"duration_ms": duration_ms,
|
|
197
|
+
"error": sanitized_error,
|
|
198
|
+
},
|
|
199
|
+
)
|
|
200
|
+
return ModelBackendResult(
|
|
201
|
+
success=False,
|
|
202
|
+
error=sanitized_error,
|
|
203
|
+
error_code=EnumPostgresErrorCode.TIMEOUT_ERROR,
|
|
204
|
+
duration_ms=duration_ms,
|
|
205
|
+
backend_id="postgres",
|
|
206
|
+
correlation_id=correlation_id,
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
except InfraAuthenticationError as e:
|
|
210
|
+
# Authentication failure - non-retriable
|
|
211
|
+
duration_ms = (time.perf_counter() - start_time) * 1000
|
|
212
|
+
sanitized_error = sanitize_error_message(e)
|
|
213
|
+
logger.exception(
|
|
214
|
+
"PostgreSQL authentication failed",
|
|
215
|
+
extra={
|
|
216
|
+
**log_extra,
|
|
217
|
+
"duration_ms": duration_ms,
|
|
218
|
+
"error": sanitized_error,
|
|
219
|
+
},
|
|
220
|
+
)
|
|
221
|
+
return ModelBackendResult(
|
|
222
|
+
success=False,
|
|
223
|
+
error=sanitized_error,
|
|
224
|
+
error_code=EnumPostgresErrorCode.AUTH_ERROR,
|
|
225
|
+
duration_ms=duration_ms,
|
|
226
|
+
backend_id="postgres",
|
|
227
|
+
correlation_id=correlation_id,
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
except InfraConnectionError as e:
|
|
231
|
+
# Connection failure - retriable
|
|
232
|
+
duration_ms = (time.perf_counter() - start_time) * 1000
|
|
233
|
+
sanitized_error = sanitize_error_message(e)
|
|
234
|
+
logger.warning(
|
|
235
|
+
"PostgreSQL connection failed",
|
|
236
|
+
extra={
|
|
237
|
+
**log_extra,
|
|
238
|
+
"duration_ms": duration_ms,
|
|
239
|
+
"error": sanitized_error,
|
|
240
|
+
},
|
|
241
|
+
)
|
|
242
|
+
return ModelBackendResult(
|
|
243
|
+
success=False,
|
|
244
|
+
error=sanitized_error,
|
|
245
|
+
error_code=EnumPostgresErrorCode.CONNECTION_ERROR,
|
|
246
|
+
duration_ms=duration_ms,
|
|
247
|
+
backend_id="postgres",
|
|
248
|
+
correlation_id=correlation_id,
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
except RepositoryExecutionError as e:
|
|
252
|
+
# Query/operation failure - use handler-provided error code
|
|
253
|
+
duration_ms = (time.perf_counter() - start_time) * 1000
|
|
254
|
+
sanitized_error = sanitize_error_message(e)
|
|
255
|
+
logger.warning(
|
|
256
|
+
"PostgreSQL operation failed",
|
|
257
|
+
extra={
|
|
258
|
+
**log_extra,
|
|
259
|
+
"duration_ms": duration_ms,
|
|
260
|
+
"error": sanitized_error,
|
|
261
|
+
"error_code": op_error_code.value,
|
|
262
|
+
},
|
|
263
|
+
)
|
|
264
|
+
return ModelBackendResult(
|
|
265
|
+
success=False,
|
|
266
|
+
error=sanitized_error,
|
|
267
|
+
error_code=op_error_code,
|
|
268
|
+
duration_ms=duration_ms,
|
|
269
|
+
backend_id="postgres",
|
|
270
|
+
correlation_id=correlation_id,
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
except (
|
|
274
|
+
Exception
|
|
275
|
+
) as e: # ONEX: catch-all for driver errors, encoding errors, pool errors
|
|
276
|
+
# Unknown error - non-retriable, requires investigation
|
|
277
|
+
duration_ms = (time.perf_counter() - start_time) * 1000
|
|
278
|
+
sanitized_error = sanitize_backend_error("postgres", e)
|
|
279
|
+
logger.exception(
|
|
280
|
+
"PostgreSQL operation failed with unexpected error",
|
|
281
|
+
extra={
|
|
282
|
+
**log_extra,
|
|
283
|
+
"duration_ms": duration_ms,
|
|
284
|
+
"error_type": type(e).__name__,
|
|
285
|
+
"error": sanitized_error,
|
|
286
|
+
},
|
|
287
|
+
)
|
|
288
|
+
return ModelBackendResult(
|
|
289
|
+
success=False,
|
|
290
|
+
error=sanitized_error,
|
|
291
|
+
error_code=EnumPostgresErrorCode.UNKNOWN_ERROR,
|
|
292
|
+
duration_ms=duration_ms,
|
|
293
|
+
backend_id="postgres",
|
|
294
|
+
correlation_id=correlation_id,
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
__all__ = ["MixinPostgresOpExecutor"]
|
|
@@ -30,6 +30,7 @@ from omnibase_infra.models.event_bus import (
|
|
|
30
30
|
from omnibase_infra.models.handlers import ModelHandlerIdentifier
|
|
31
31
|
from omnibase_infra.models.health import ModelHealthCheckResult
|
|
32
32
|
from omnibase_infra.models.logging import ModelLogContext
|
|
33
|
+
from omnibase_infra.models.model_backend_result import ModelBackendResult
|
|
33
34
|
from omnibase_infra.models.model_node_identity import ModelNodeIdentity
|
|
34
35
|
from omnibase_infra.models.model_retry_error_classification import (
|
|
35
36
|
ModelRetryErrorClassification,
|
|
@@ -93,6 +94,8 @@ __all__: list[str] = [
|
|
|
93
94
|
"ModelConsumerRetryConfig",
|
|
94
95
|
"ModelIdempotencyConfig",
|
|
95
96
|
"ModelOffsetPolicyConfig",
|
|
97
|
+
# Backend result models
|
|
98
|
+
"ModelBackendResult",
|
|
96
99
|
# Resilience models
|
|
97
100
|
"ModelCircuitBreakerConfig",
|
|
98
101
|
# Validation models
|
|
@@ -27,7 +27,7 @@ class ModelDiscoveredCapabilities(BaseModel):
|
|
|
27
27
|
... )
|
|
28
28
|
"""
|
|
29
29
|
|
|
30
|
-
model_config = ConfigDict(extra="forbid", frozen=True)
|
|
30
|
+
model_config = ConfigDict(extra="forbid", frozen=True, from_attributes=True)
|
|
31
31
|
|
|
32
32
|
operations: tuple[str, ...] = Field(
|
|
33
33
|
default=(),
|
|
@@ -108,6 +108,7 @@ class ModelIntrospectionConfig(BaseModel):
|
|
|
108
108
|
config = ModelIntrospectionConfig(
|
|
109
109
|
node_id=node_id,
|
|
110
110
|
node_type=EnumNodeKind.EFFECT, # Use enum directly (preferred)
|
|
111
|
+
node_name="my_effect_node",
|
|
111
112
|
event_bus=event_bus,
|
|
112
113
|
version="1.2.0",
|
|
113
114
|
)
|
|
@@ -119,6 +120,7 @@ class ModelIntrospectionConfig(BaseModel):
|
|
|
119
120
|
config = ModelIntrospectionConfig(
|
|
120
121
|
node_id=node_id or uuid4(),
|
|
121
122
|
node_type=EnumNodeKind.EFFECT, # Use enum directly (preferred)
|
|
123
|
+
node_name="my_custom_effect_node",
|
|
122
124
|
event_bus=event_bus,
|
|
123
125
|
operation_keywords=frozenset({"fetch", "upload", "download"}),
|
|
124
126
|
)
|
|
@@ -141,6 +143,27 @@ class ModelIntrospectionConfig(BaseModel):
|
|
|
141
143
|
"Accepts EnumNodeKind directly (preferred) or string (deprecated, will be coerced).",
|
|
142
144
|
)
|
|
143
145
|
|
|
146
|
+
node_name: str = Field( # pattern-ok: canonical identifier, not a foreign key reference
|
|
147
|
+
...,
|
|
148
|
+
min_length=1,
|
|
149
|
+
description="Node name for consumer group identification (e.g., 'claude_hook_effect'). "
|
|
150
|
+
"Cannot be empty.",
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
env: str = Field(
|
|
154
|
+
default="dev",
|
|
155
|
+
min_length=1,
|
|
156
|
+
description="Environment identifier (e.g., 'dev', 'staging', 'prod'). "
|
|
157
|
+
"Used for node identity in event bus subscriptions. Cannot be empty.",
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
service: str = Field(
|
|
161
|
+
default="onex",
|
|
162
|
+
min_length=1,
|
|
163
|
+
description="Service name (e.g., 'omniintelligence', 'omnibridge'). "
|
|
164
|
+
"Used for node identity in event bus subscriptions. Cannot be empty.",
|
|
165
|
+
)
|
|
166
|
+
|
|
144
167
|
# Event bus for publishing introspection events.
|
|
145
168
|
# Uses _EventBusType which provides:
|
|
146
169
|
# - ProtocolEventBus | None during static analysis (TYPE_CHECKING)
|
|
@@ -155,7 +178,8 @@ class ModelIntrospectionConfig(BaseModel):
|
|
|
155
178
|
|
|
156
179
|
version: str = Field(
|
|
157
180
|
default="1.0.0",
|
|
158
|
-
|
|
181
|
+
min_length=1,
|
|
182
|
+
description="Node version string. Cannot be empty.",
|
|
159
183
|
)
|
|
160
184
|
|
|
161
185
|
cache_ttl: float = Field(
|
|
@@ -284,6 +308,7 @@ class ModelIntrospectionConfig(BaseModel):
|
|
|
284
308
|
model_config = ConfigDict(
|
|
285
309
|
frozen=True,
|
|
286
310
|
extra="forbid",
|
|
311
|
+
from_attributes=True, # ORM/pytest-xdist compatibility
|
|
287
312
|
arbitrary_types_allowed=True, # Allow arbitrary types for event_bus
|
|
288
313
|
json_schema_extra={
|
|
289
314
|
"examples": [
|
|
@@ -291,6 +316,7 @@ class ModelIntrospectionConfig(BaseModel):
|
|
|
291
316
|
{
|
|
292
317
|
"node_id": "550e8400-e29b-41d4-a716-446655440000",
|
|
293
318
|
"node_type": "EFFECT",
|
|
319
|
+
"node_name": "example_effect_node",
|
|
294
320
|
"event_bus": None,
|
|
295
321
|
"version": "1.0.0",
|
|
296
322
|
"cache_ttl": 300.0,
|
|
@@ -305,6 +331,7 @@ class ModelIntrospectionConfig(BaseModel):
|
|
|
305
331
|
{
|
|
306
332
|
"node_id": "550e8400-e29b-41d4-a716-446655440001",
|
|
307
333
|
"node_type": "COMPUTE",
|
|
334
|
+
"node_name": "example_compute_node",
|
|
308
335
|
"event_bus": None,
|
|
309
336
|
"version": "2.1.0",
|
|
310
337
|
"cache_ttl": 120.0,
|
|
@@ -61,7 +61,9 @@ class ModelBackendResult(BaseModel):
|
|
|
61
61
|
Attributes:
|
|
62
62
|
success: Whether the backend operation completed successfully.
|
|
63
63
|
error: Sanitized error message if success is False.
|
|
64
|
-
error_code:
|
|
64
|
+
error_code: Error code for programmatic handling. PostgreSQL handlers
|
|
65
|
+
use EnumPostgresErrorCode enum values which serialize to strings
|
|
66
|
+
(e.g., "POSTGRES_CONNECTION_ERROR"). Other backends use string codes.
|
|
65
67
|
duration_ms: Time taken for the operation in milliseconds.
|
|
66
68
|
backend_id: Optional identifier for the backend instance.
|
|
67
69
|
|
|
@@ -96,18 +98,30 @@ class ModelBackendResult(BaseModel):
|
|
|
96
98
|
>>> result.success
|
|
97
99
|
True
|
|
98
100
|
|
|
99
|
-
Example (failure case):
|
|
101
|
+
Example (failure case with PostgreSQL enum):
|
|
102
|
+
>>> from omnibase_infra.enums import EnumPostgresErrorCode
|
|
100
103
|
>>> result = ModelBackendResult(
|
|
101
104
|
... success=False,
|
|
102
105
|
... error="Connection refused to database host",
|
|
103
|
-
... error_code=
|
|
106
|
+
... error_code=EnumPostgresErrorCode.CONNECTION_ERROR,
|
|
104
107
|
... duration_ms=5000.0,
|
|
105
108
|
... backend_id="postgres",
|
|
106
109
|
... )
|
|
107
110
|
>>> result.success
|
|
108
111
|
False
|
|
109
|
-
>>> result.
|
|
110
|
-
'
|
|
112
|
+
>>> result.error_code # Enum serializes to string
|
|
113
|
+
'POSTGRES_CONNECTION_ERROR'
|
|
114
|
+
|
|
115
|
+
Example (failure case with Consul string code):
|
|
116
|
+
>>> result = ModelBackendResult(
|
|
117
|
+
... success=False,
|
|
118
|
+
... error="Service registration failed",
|
|
119
|
+
... error_code="CONSUL_CONNECTION_ERROR",
|
|
120
|
+
... duration_ms=1500.0,
|
|
121
|
+
... backend_id="consul",
|
|
122
|
+
... )
|
|
123
|
+
>>> result.error_code
|
|
124
|
+
'CONSUL_CONNECTION_ERROR'
|
|
111
125
|
"""
|
|
112
126
|
|
|
113
127
|
model_config = ConfigDict(frozen=True, extra="forbid", from_attributes=True)
|
|
@@ -122,7 +136,9 @@ class ModelBackendResult(BaseModel):
|
|
|
122
136
|
)
|
|
123
137
|
error_code: str | None = Field(
|
|
124
138
|
default=None,
|
|
125
|
-
description="Error code for programmatic handling
|
|
139
|
+
description="Error code for programmatic handling. PostgreSQL handlers use "
|
|
140
|
+
"EnumPostgresErrorCode enum values (which serialize to strings like "
|
|
141
|
+
"'POSTGRES_CONNECTION_ERROR'). Other backends use string codes directly.",
|
|
126
142
|
)
|
|
127
143
|
duration_ms: float = Field(
|
|
128
144
|
default=0.0,
|
|
@@ -8,12 +8,15 @@ by orchestrators to query current entity state.
|
|
|
8
8
|
|
|
9
9
|
Exports:
|
|
10
10
|
ModelCapabilityFields: Container for capability fields in projection persistence
|
|
11
|
+
ModelContractProjection: Contract projection for Registry API queries
|
|
11
12
|
ModelRegistrationProjection: Registration projection for orchestrator state queries
|
|
12
13
|
ModelRegistrationSnapshot: Compacted snapshot for read optimization
|
|
13
14
|
ModelSequenceInfo: Sequence information for projection ordering and idempotency
|
|
14
15
|
ModelSnapshotTopicConfig: Kafka topic configuration for snapshot publishing
|
|
16
|
+
ModelTopicProjection: Topic projection for Registry API queries
|
|
15
17
|
|
|
16
18
|
Related Tickets:
|
|
19
|
+
- OMN-1845: Create ProjectionReaderContract for contract/topic queries
|
|
17
20
|
- OMN-1134: Registry Projection Extensions for Capabilities
|
|
18
21
|
- OMN-947 (F2): Snapshot Publishing
|
|
19
22
|
- OMN-944 (F1): Implement Registration Projection Schema
|
|
@@ -23,6 +26,9 @@ Related Tickets:
|
|
|
23
26
|
from omnibase_infra.models.projection.model_capability_fields import (
|
|
24
27
|
ModelCapabilityFields,
|
|
25
28
|
)
|
|
29
|
+
from omnibase_infra.models.projection.model_contract_projection import (
|
|
30
|
+
ModelContractProjection,
|
|
31
|
+
)
|
|
26
32
|
from omnibase_infra.models.projection.model_registration_projection import (
|
|
27
33
|
ModelRegistrationProjection,
|
|
28
34
|
)
|
|
@@ -33,11 +39,16 @@ from omnibase_infra.models.projection.model_sequence_info import ModelSequenceIn
|
|
|
33
39
|
from omnibase_infra.models.projection.model_snapshot_topic_config import (
|
|
34
40
|
ModelSnapshotTopicConfig,
|
|
35
41
|
)
|
|
42
|
+
from omnibase_infra.models.projection.model_topic_projection import (
|
|
43
|
+
ModelTopicProjection,
|
|
44
|
+
)
|
|
36
45
|
|
|
37
46
|
__all__ = [
|
|
38
47
|
"ModelCapabilityFields",
|
|
48
|
+
"ModelContractProjection",
|
|
39
49
|
"ModelRegistrationProjection",
|
|
40
50
|
"ModelRegistrationSnapshot",
|
|
41
51
|
"ModelSequenceInfo",
|
|
42
52
|
"ModelSnapshotTopicConfig",
|
|
53
|
+
"ModelTopicProjection",
|
|
43
54
|
]
|