omnibase_infra 0.3.1__py3-none-any.whl → 0.3.2__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/handlers/registration_storage/handler_registration_storage_postgres.py +29 -20
- omnibase_infra/mixins/__init__.py +14 -0
- 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/{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/nodes/contract_registry_reducer/__init__.py +5 -0
- omnibase_infra/nodes/contract_registry_reducer/contract_registration_event_router.py +689 -0
- 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 +114 -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 +220 -0
- omnibase_infra/nodes/node_registry_effect/models/__init__.py +2 -2
- omnibase_infra/projectors/__init__.py +6 -0
- omnibase_infra/projectors/projection_reader_contract.py +1301 -0
- omnibase_infra/runtime/__init__.py +5 -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/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/request_response_wiring.py +785 -0
- omnibase_infra/runtime/service_kernel.py +295 -8
- 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/validation/infra_validators.py +3 -1
- omnibase_infra/validation/validation_exemptions.yaml +54 -0
- {omnibase_infra-0.3.1.dist-info → omnibase_infra-0.3.2.dist-info}/METADATA +3 -3
- {omnibase_infra-0.3.1.dist-info → omnibase_infra-0.3.2.dist-info}/RECORD +72 -34
- {omnibase_infra-0.3.1.dist-info → omnibase_infra-0.3.2.dist-info}/WHEEL +0 -0
- {omnibase_infra-0.3.1.dist-info → omnibase_infra-0.3.2.dist-info}/entry_points.txt +0 -0
- {omnibase_infra-0.3.1.dist-info → omnibase_infra-0.3.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -5,15 +5,17 @@
|
|
|
5
5
|
This module exports models used by Effect layer nodes for external I/O operations.
|
|
6
6
|
|
|
7
7
|
Available Models:
|
|
8
|
-
- ModelBackendResult: Individual backend operation result
|
|
8
|
+
- ModelBackendResult: Individual backend operation result (re-exported)
|
|
9
9
|
- ModelEffectIdempotencyConfig: Configuration for effect idempotency store
|
|
10
10
|
- ModelRegistryRequest: Registry effect input request
|
|
11
11
|
- ModelRegistryResponse: Dual-backend registry operation response
|
|
12
|
+
|
|
13
|
+
Note:
|
|
14
|
+
ModelBackendResult canonical location is omnibase_infra.models.model_backend_result.
|
|
15
|
+
Re-exported here for backward compatibility.
|
|
12
16
|
"""
|
|
13
17
|
|
|
14
|
-
from omnibase_infra.
|
|
15
|
-
ModelBackendResult,
|
|
16
|
-
)
|
|
18
|
+
from omnibase_infra.models.model_backend_result import ModelBackendResult
|
|
17
19
|
from omnibase_infra.nodes.effects.models.model_effect_idempotency_config import (
|
|
18
20
|
ModelEffectIdempotencyConfig,
|
|
19
21
|
)
|
|
@@ -41,7 +41,7 @@ from uuid import UUID
|
|
|
41
41
|
from pydantic import BaseModel, ConfigDict, Field
|
|
42
42
|
|
|
43
43
|
from omnibase_infra.enums import EnumBackendType, EnumRegistryResponseStatus
|
|
44
|
-
from omnibase_infra.
|
|
44
|
+
from omnibase_infra.models.model_backend_result import (
|
|
45
45
|
ModelBackendResult,
|
|
46
46
|
)
|
|
47
47
|
|
|
@@ -24,7 +24,7 @@ from uuid import UUID
|
|
|
24
24
|
|
|
25
25
|
from omnibase_core.enums import EnumNodeKind
|
|
26
26
|
from omnibase_core.models.primitives import ModelSemVer
|
|
27
|
-
from omnibase_infra.
|
|
27
|
+
from omnibase_infra.models import ModelBackendResult
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
@runtime_checkable
|
|
@@ -100,7 +100,7 @@ from omnibase_infra.errors import (
|
|
|
100
100
|
InfraConnectionError,
|
|
101
101
|
InfraTimeoutError,
|
|
102
102
|
)
|
|
103
|
-
from omnibase_infra.
|
|
103
|
+
from omnibase_infra.models.model_backend_result import (
|
|
104
104
|
ModelBackendResult,
|
|
105
105
|
)
|
|
106
106
|
from omnibase_infra.nodes.effects.models.model_effect_idempotency_config import (
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""Node Contract Persistence Effect package - Declarative effect node for contract persistence.
|
|
4
|
+
|
|
5
|
+
This package provides NodeContractPersistenceEffect, a declarative effect node that
|
|
6
|
+
routes intents from ContractRegistryReducer to PostgreSQL handlers for contract
|
|
7
|
+
and topic persistence operations.
|
|
8
|
+
|
|
9
|
+
Architecture (OMN-1845):
|
|
10
|
+
This package follows the ONEX declarative node pattern:
|
|
11
|
+
- node.py: Declarative node shell extending NodeEffect
|
|
12
|
+
- handlers/: PostgreSQL operation handlers
|
|
13
|
+
- registry/: Infrastructure registry for dependency injection
|
|
14
|
+
- contract.yaml: Intent routing and I/O definitions
|
|
15
|
+
|
|
16
|
+
The node is 100% contract-driven with zero custom business logic in node.py.
|
|
17
|
+
All intent routing is defined in contract.yaml and handlers are resolved
|
|
18
|
+
via container dependency injection.
|
|
19
|
+
|
|
20
|
+
Node Type: EFFECT_GENERIC
|
|
21
|
+
Purpose: Execute PostgreSQL I/O operations based on intents from ContractRegistryReducer.
|
|
22
|
+
|
|
23
|
+
Implementation Details:
|
|
24
|
+
- Routes 6 intent types to specialized handlers
|
|
25
|
+
- Circuit breaker protection for PostgreSQL
|
|
26
|
+
- Error sanitization for security
|
|
27
|
+
- Retry policies for transient failures
|
|
28
|
+
|
|
29
|
+
Supported Intent Types:
|
|
30
|
+
- postgres.upsert_contract: Insert/update contract record
|
|
31
|
+
- postgres.update_topic: Update topic routing table
|
|
32
|
+
- postgres.mark_stale: Batch mark stale contracts
|
|
33
|
+
- postgres.update_heartbeat: Update heartbeat timestamp
|
|
34
|
+
- postgres.deactivate_contract: Soft delete contract
|
|
35
|
+
- postgres.cleanup_topic_references: Remove contract from topics
|
|
36
|
+
|
|
37
|
+
Handlers:
|
|
38
|
+
- HandlerPostgresContractUpsert: Contract upsert operations
|
|
39
|
+
- HandlerPostgresTopicUpdate: Topic routing updates
|
|
40
|
+
- HandlerPostgresMarkStale: Batch staleness marking
|
|
41
|
+
- HandlerPostgresHeartbeat: Heartbeat timestamp updates
|
|
42
|
+
- HandlerPostgresDeactivate: Contract deactivation
|
|
43
|
+
- HandlerPostgresCleanupTopics: Topic reference cleanup
|
|
44
|
+
|
|
45
|
+
Usage:
|
|
46
|
+
```python
|
|
47
|
+
from omnibase_core.models.container import ModelONEXContainer
|
|
48
|
+
from omnibase_infra.nodes.node_contract_persistence_effect import (
|
|
49
|
+
NodeContractPersistenceEffect,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# Create via container injection
|
|
53
|
+
container = ModelONEXContainer()
|
|
54
|
+
effect = NodeContractPersistenceEffect(container)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Related:
|
|
58
|
+
- contract.yaml: Intent routing definition
|
|
59
|
+
- node.py: Declarative node implementation
|
|
60
|
+
- handlers/: PostgreSQL operation handlers
|
|
61
|
+
- registry/: Infrastructure registry
|
|
62
|
+
- contract_registry_reducer/: Source of intents
|
|
63
|
+
- OMN-1845: Implementation ticket
|
|
64
|
+
- OMN-1653: ContractRegistryReducer ticket
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
from __future__ import annotations
|
|
68
|
+
|
|
69
|
+
# Export handlers
|
|
70
|
+
from omnibase_infra.nodes.node_contract_persistence_effect.handlers import (
|
|
71
|
+
HandlerPostgresCleanupTopics,
|
|
72
|
+
HandlerPostgresContractUpsert,
|
|
73
|
+
HandlerPostgresDeactivate,
|
|
74
|
+
HandlerPostgresHeartbeat,
|
|
75
|
+
HandlerPostgresMarkStale,
|
|
76
|
+
HandlerPostgresTopicUpdate,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Export the declarative node
|
|
80
|
+
from omnibase_infra.nodes.node_contract_persistence_effect.node import (
|
|
81
|
+
NodeContractPersistenceEffect,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
# Export registry
|
|
85
|
+
from omnibase_infra.nodes.node_contract_persistence_effect.registry import (
|
|
86
|
+
RegistryInfraContractPersistenceEffect,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
__all__: list[str] = [
|
|
90
|
+
# Node
|
|
91
|
+
"NodeContractPersistenceEffect",
|
|
92
|
+
# Registry
|
|
93
|
+
"RegistryInfraContractPersistenceEffect",
|
|
94
|
+
# Handlers
|
|
95
|
+
"HandlerPostgresCleanupTopics",
|
|
96
|
+
"HandlerPostgresContractUpsert",
|
|
97
|
+
"HandlerPostgresDeactivate",
|
|
98
|
+
"HandlerPostgresHeartbeat",
|
|
99
|
+
"HandlerPostgresMarkStale",
|
|
100
|
+
"HandlerPostgresTopicUpdate",
|
|
101
|
+
]
|
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
#
|
|
4
|
+
# ONEX Node Contract
|
|
5
|
+
# Node: NodeContractPersistenceEffect
|
|
6
|
+
#
|
|
7
|
+
# This contract defines the interface for the Contract Persistence Effect node,
|
|
8
|
+
# which handles PostgreSQL persistence for contract registry intents emitted by
|
|
9
|
+
# ContractRegistryReducer (OMN-1653).
|
|
10
|
+
#
|
|
11
|
+
# Related Tickets:
|
|
12
|
+
# - OMN-1845: NodeContractPersistenceEffect implementation
|
|
13
|
+
# - OMN-1653: ContractRegistryReducer (source of intents)
|
|
14
|
+
# Contract identifiers
|
|
15
|
+
name: "node_contract_persistence_effect"
|
|
16
|
+
contract_name: "node_contract_persistence_effect"
|
|
17
|
+
node_name: "node_contract_persistence_effect"
|
|
18
|
+
contract_version:
|
|
19
|
+
major: 1
|
|
20
|
+
minor: 0
|
|
21
|
+
patch: 0
|
|
22
|
+
node_version:
|
|
23
|
+
major: 1
|
|
24
|
+
minor: 0
|
|
25
|
+
patch: 0
|
|
26
|
+
# Node type
|
|
27
|
+
node_type: "EFFECT_GENERIC"
|
|
28
|
+
# Description
|
|
29
|
+
description: >
|
|
30
|
+
Effect node for contract registry persistence. Routes intents from ContractRegistryReducer to PostgreSQL handlers for contract and topic management. Supports upsert, update, deactivate, and cleanup operations with circuit breaker protection and retry policies.
|
|
31
|
+
|
|
32
|
+
# Strongly typed I/O models
|
|
33
|
+
# Note: Input is ModelIntent with typed payloads from ContractRegistryReducer
|
|
34
|
+
input_model:
|
|
35
|
+
name: "ModelIntent"
|
|
36
|
+
module: "omnibase_core.models.reducer.model_intent"
|
|
37
|
+
description: "Intent model from ContractRegistryReducer with typed payload."
|
|
38
|
+
output_model:
|
|
39
|
+
name: "ModelPersistenceResult"
|
|
40
|
+
module: "omnibase_infra.nodes.node_contract_persistence_effect.models"
|
|
41
|
+
description: "Output model containing PostgreSQL operation result."
|
|
42
|
+
# =============================================================================
|
|
43
|
+
# HANDLER ROUTING (Declarative Handler Dispatch)
|
|
44
|
+
# =============================================================================
|
|
45
|
+
# This section defines how intents are routed to PostgreSQL handlers.
|
|
46
|
+
# The routing strategy determines handler selection based on payload.intent_type
|
|
47
|
+
# (per ONEX handler routing standards).
|
|
48
|
+
#
|
|
49
|
+
# Design Rationale:
|
|
50
|
+
# - Single backend (PostgreSQL) with multiple operation types
|
|
51
|
+
# - Each handler is responsible for a specific PostgreSQL operation
|
|
52
|
+
# - Handlers are isolated for clear error handling and retry logic
|
|
53
|
+
#
|
|
54
|
+
# Execution Flow:
|
|
55
|
+
# 1. Receive ModelIntent with typed payload from ContractRegistryReducer
|
|
56
|
+
# 2. Extract payload.intent_type for routing
|
|
57
|
+
# 3. Route to matching handler based on intent_type
|
|
58
|
+
# 4. Execute PostgreSQL operation via handler
|
|
59
|
+
# 5. Return ModelPersistenceResult with operation status
|
|
60
|
+
#
|
|
61
|
+
# ┌─────────────────────────────────────────────────────────────────────────┐
|
|
62
|
+
# │ HANDLER ROUTING ARCHITECTURE │
|
|
63
|
+
# ├─────────────────────────────────────────────────────────────────────────┤
|
|
64
|
+
# │ │
|
|
65
|
+
# │ ┌────────────────────────┐ │
|
|
66
|
+
# │ │ ModelIntent │ │
|
|
67
|
+
# │ │ payload.intent_type │ │
|
|
68
|
+
# │ └───────────┬────────────┘ │
|
|
69
|
+
# │ │ │
|
|
70
|
+
# │ ▼ │
|
|
71
|
+
# │ ┌───────────────────────┐ │
|
|
72
|
+
# │ │ Handler Router │ routing_strategy: "payload_type_match" │
|
|
73
|
+
# │ │ (Contract-Driven) │ Matches payload.intent_type to handlers │
|
|
74
|
+
# │ └───────────┬───────────┘ │
|
|
75
|
+
# │ │ │
|
|
76
|
+
# │ ┌────────┼────────┬────────┬────────┬────────┐ │
|
|
77
|
+
# │ │ │ │ │ │ │ │
|
|
78
|
+
# │ ▼ ▼ ▼ ▼ ▼ ▼ │
|
|
79
|
+
# │ ┌────────┐┌────────┐┌────────┐┌────────┐┌────────┐┌────────┐ │
|
|
80
|
+
# │ │Upsert ││Topic ││Mark ││Heartbeat││Deact. ││Cleanup │ │
|
|
81
|
+
# │ │Contract││Update ││Stale ││Update ││Contract││Topics │ │
|
|
82
|
+
# │ │Handler ││Handler ││Handler ││Handler ││Handler ││Handler │ │
|
|
83
|
+
# │ └────┬───┘└────┬───┘└────┬───┘└────┬───┘└────┬───┘└────┬───┘ │
|
|
84
|
+
# │ │ │ │ │ │ │ │
|
|
85
|
+
# │ └─────────┴─────────┴────┬────┴─────────┴─────────┘ │
|
|
86
|
+
# │ │ │
|
|
87
|
+
# │ ▼ │
|
|
88
|
+
# │ ┌─────────────────────────────────────────────────────────────┐ │
|
|
89
|
+
# │ │ PostgreSQL Database │ │
|
|
90
|
+
# │ │ ┌────────────────┐ ┌────────────────┐ │ │
|
|
91
|
+
# │ │ │ contracts │ │ topics │ │ │
|
|
92
|
+
# │ │ └────────────────┘ └────────────────┘ │ │
|
|
93
|
+
# │ └─────────────────────────────────────────────────────────────┘ │
|
|
94
|
+
# │ │
|
|
95
|
+
# └────────────────────────────────────────────────────────────────────────┘
|
|
96
|
+
#
|
|
97
|
+
# Handler Responsibility:
|
|
98
|
+
# Each handler manages its own retry logic using the retry_policy defined
|
|
99
|
+
# in error_handling. The effect layer dispatches once per handler and
|
|
100
|
+
# delegates retry behavior to the handlers themselves.
|
|
101
|
+
# =============================================================================
|
|
102
|
+
handler_routing:
|
|
103
|
+
routing_strategy: "payload_type_match"
|
|
104
|
+
handlers:
|
|
105
|
+
# Upsert Contract - Insert or update contract record
|
|
106
|
+
# Triggered by contract-registered events from ContractRegistryReducer.
|
|
107
|
+
# Uses upsert semantics (ON CONFLICT DO UPDATE) for idempotency.
|
|
108
|
+
- payload_type: "postgres.upsert_contract"
|
|
109
|
+
handler:
|
|
110
|
+
name: "HandlerPostgresContractUpsert"
|
|
111
|
+
module: "omnibase_infra.nodes.node_contract_persistence_effect.handlers.handler_postgres_contract_upsert"
|
|
112
|
+
backend: "postgres"
|
|
113
|
+
description: "Insert or update contract record in contracts table"
|
|
114
|
+
input_model:
|
|
115
|
+
name: "ModelPayloadUpsertContract"
|
|
116
|
+
module: "omnibase_infra.nodes.contract_registry_reducer.models.model_payload_upsert_contract"
|
|
117
|
+
output_fields:
|
|
118
|
+
- success
|
|
119
|
+
- contract_id
|
|
120
|
+
- operation
|
|
121
|
+
- rows_affected
|
|
122
|
+
# Update Topic - Update topic routing table
|
|
123
|
+
# Triggered by contract-registered events with publish/subscribe declarations.
|
|
124
|
+
# Maintains topic-to-contract mapping for event routing.
|
|
125
|
+
- payload_type: "postgres.update_topic"
|
|
126
|
+
handler:
|
|
127
|
+
name: "HandlerPostgresTopicUpdate"
|
|
128
|
+
module: "omnibase_infra.nodes.node_contract_persistence_effect.handlers.handler_postgres_topic_update"
|
|
129
|
+
backend: "postgres"
|
|
130
|
+
description: "Update topic routing table with producer/consumer info"
|
|
131
|
+
input_model:
|
|
132
|
+
name: "ModelPayloadUpdateTopic"
|
|
133
|
+
module: "omnibase_infra.nodes.contract_registry_reducer.models.model_payload_update_topic"
|
|
134
|
+
output_fields:
|
|
135
|
+
- success
|
|
136
|
+
- topic_suffix
|
|
137
|
+
- direction
|
|
138
|
+
- rows_affected
|
|
139
|
+
# Mark Stale - Batch deactivate contracts that stopped heartbeating
|
|
140
|
+
# Triggered by runtime-tick events to detect unresponsive contracts.
|
|
141
|
+
# Deactivates (is_active=FALSE) contracts not seen within threshold.
|
|
142
|
+
- payload_type: "postgres.mark_stale"
|
|
143
|
+
handler:
|
|
144
|
+
name: "HandlerPostgresMarkStale"
|
|
145
|
+
module: "omnibase_infra.nodes.node_contract_persistence_effect.handlers.handler_postgres_mark_stale"
|
|
146
|
+
backend: "postgres"
|
|
147
|
+
description: "Batch mark contracts as stale based on last_seen_at threshold"
|
|
148
|
+
input_model:
|
|
149
|
+
name: "ModelPayloadMarkStale"
|
|
150
|
+
module: "omnibase_infra.nodes.contract_registry_reducer.models.model_payload_mark_stale"
|
|
151
|
+
output_fields:
|
|
152
|
+
- success
|
|
153
|
+
- contracts_marked
|
|
154
|
+
- stale_cutoff
|
|
155
|
+
# Update Heartbeat - Update last_seen_at timestamp
|
|
156
|
+
# Triggered by node-heartbeat events to track contract liveness.
|
|
157
|
+
# Lightweight update operation for high-frequency heartbeats.
|
|
158
|
+
- payload_type: "postgres.update_heartbeat"
|
|
159
|
+
handler:
|
|
160
|
+
name: "HandlerPostgresHeartbeat"
|
|
161
|
+
module: "omnibase_infra.nodes.node_contract_persistence_effect.handlers.handler_postgres_heartbeat"
|
|
162
|
+
backend: "postgres"
|
|
163
|
+
description: "Update contract heartbeat timestamp"
|
|
164
|
+
input_model:
|
|
165
|
+
name: "ModelPayloadUpdateHeartbeat"
|
|
166
|
+
module: "omnibase_infra.nodes.contract_registry_reducer.models.model_payload_update_heartbeat"
|
|
167
|
+
output_fields:
|
|
168
|
+
- success
|
|
169
|
+
- contract_id
|
|
170
|
+
- last_seen_at
|
|
171
|
+
# Deactivate Contract - Soft delete contract
|
|
172
|
+
# Triggered by contract-deregistered events for graceful shutdown.
|
|
173
|
+
# Marks contract as inactive without deleting the record.
|
|
174
|
+
- payload_type: "postgres.deactivate_contract"
|
|
175
|
+
handler:
|
|
176
|
+
name: "HandlerPostgresDeactivate"
|
|
177
|
+
module: "omnibase_infra.nodes.node_contract_persistence_effect.handlers.handler_postgres_deactivate"
|
|
178
|
+
backend: "postgres"
|
|
179
|
+
description: "Mark contract as inactive (soft delete)"
|
|
180
|
+
input_model:
|
|
181
|
+
name: "ModelPayloadDeactivateContract"
|
|
182
|
+
module: "omnibase_infra.nodes.contract_registry_reducer.models.model_payload_deactivate_contract"
|
|
183
|
+
output_fields:
|
|
184
|
+
- success
|
|
185
|
+
- contract_id
|
|
186
|
+
- deactivated_at
|
|
187
|
+
# Cleanup Topic References - Remove contract from topic arrays
|
|
188
|
+
# Triggered by contract-deregistered events to clean topic mappings.
|
|
189
|
+
# Removes contract_id from topics.contract_ids JSONB arrays.
|
|
190
|
+
- payload_type: "postgres.cleanup_topic_references"
|
|
191
|
+
handler:
|
|
192
|
+
name: "HandlerPostgresCleanupTopics"
|
|
193
|
+
module: "omnibase_infra.nodes.node_contract_persistence_effect.handlers.handler_postgres_cleanup_topics"
|
|
194
|
+
backend: "postgres"
|
|
195
|
+
description: "Remove contract from topic references"
|
|
196
|
+
input_model:
|
|
197
|
+
name: "ModelPayloadCleanupTopicReferences"
|
|
198
|
+
module: "omnibase_infra.nodes.contract_registry_reducer.models.model_payload_cleanup_topic_references"
|
|
199
|
+
output_fields:
|
|
200
|
+
- success
|
|
201
|
+
- contract_id
|
|
202
|
+
- topics_updated
|
|
203
|
+
# Execution configuration
|
|
204
|
+
execution_mode: "sequential"
|
|
205
|
+
partial_failure_handling: false
|
|
206
|
+
aggregation_strategy: "first_match"
|
|
207
|
+
# =============================================================================
|
|
208
|
+
# ERROR HANDLING (Circuit Breaker + Retry + Sanitization)
|
|
209
|
+
# =============================================================================
|
|
210
|
+
# Error handling configuration following ONEX infrastructure patterns.
|
|
211
|
+
# Uses circuit breakers for PostgreSQL protection and retry policies for
|
|
212
|
+
# transient failure recovery.
|
|
213
|
+
#
|
|
214
|
+
# Error Flow:
|
|
215
|
+
# 1. Operation fails -> Check if error is retriable
|
|
216
|
+
# 2. Retry with exponential backoff if retriable
|
|
217
|
+
# 3. After max retries, check circuit breaker state
|
|
218
|
+
# 4. Open circuit if failure threshold exceeded
|
|
219
|
+
# 5. Sanitize error before logging/returning
|
|
220
|
+
# =============================================================================
|
|
221
|
+
error_handling:
|
|
222
|
+
# Circuit breaker configuration
|
|
223
|
+
# Prevents cascading failures by stopping requests to failing PostgreSQL.
|
|
224
|
+
circuit_breaker:
|
|
225
|
+
enabled: true
|
|
226
|
+
failure_threshold: 5
|
|
227
|
+
reset_timeout_ms: 60000
|
|
228
|
+
half_open_max_requests: 3
|
|
229
|
+
per_backend: false
|
|
230
|
+
backends:
|
|
231
|
+
- name: "postgres"
|
|
232
|
+
failure_threshold: 5
|
|
233
|
+
reset_timeout_ms: 60000
|
|
234
|
+
# Retry policy with exponential backoff
|
|
235
|
+
# Handles transient PostgreSQL failures.
|
|
236
|
+
#
|
|
237
|
+
# ==========================================================================
|
|
238
|
+
# IMPORTANT: Effect Layer vs Handler Layer Retry Behavior
|
|
239
|
+
# ==========================================================================
|
|
240
|
+
#
|
|
241
|
+
# Effect Layer (NodeContractPersistenceEffect):
|
|
242
|
+
# - Dispatches to handlers EXACTLY ONCE per intent
|
|
243
|
+
# - Does NOT implement retry loops
|
|
244
|
+
# - Returns immediately after handler execution
|
|
245
|
+
# - Effective retry count at this layer: ZERO (0)
|
|
246
|
+
#
|
|
247
|
+
# Handler Layer (HandlerPostgres*):
|
|
248
|
+
# - OWNS retry logic using this retry_policy configuration
|
|
249
|
+
# - Implements exponential backoff for retriable errors
|
|
250
|
+
# - Returns final result after all retries exhausted
|
|
251
|
+
# - Effective retry count: 0 to max_retries
|
|
252
|
+
#
|
|
253
|
+
# ==========================================================================
|
|
254
|
+
retry_policy:
|
|
255
|
+
max_retries: 3
|
|
256
|
+
initial_delay_ms: 100
|
|
257
|
+
max_delay_ms: 5000
|
|
258
|
+
exponential_base: 2
|
|
259
|
+
retry_on:
|
|
260
|
+
- "InfraConnectionError"
|
|
261
|
+
- "InfraTimeoutError"
|
|
262
|
+
- "InfraUnavailableError"
|
|
263
|
+
- "RepositoryExecutionError"
|
|
264
|
+
# Error sanitization
|
|
265
|
+
# Removes sensitive information from error messages before logging.
|
|
266
|
+
error_sanitization:
|
|
267
|
+
enabled: true
|
|
268
|
+
utility: "omnibase_infra.utils.util_error_sanitization"
|
|
269
|
+
sanitize_patterns:
|
|
270
|
+
- "password"
|
|
271
|
+
- "secret"
|
|
272
|
+
- "token"
|
|
273
|
+
- "api_key"
|
|
274
|
+
- "connection_string"
|
|
275
|
+
# Error type definitions
|
|
276
|
+
error_types:
|
|
277
|
+
- name: "InfraConnectionError"
|
|
278
|
+
description: "Failed to connect to PostgreSQL"
|
|
279
|
+
recoverable: true
|
|
280
|
+
retry_strategy: "exponential_backoff"
|
|
281
|
+
- name: "InfraTimeoutError"
|
|
282
|
+
description: "PostgreSQL operation timed out"
|
|
283
|
+
recoverable: true
|
|
284
|
+
retry_strategy: "exponential_backoff"
|
|
285
|
+
- name: "InfraAuthenticationError"
|
|
286
|
+
description: "Authentication failed with PostgreSQL"
|
|
287
|
+
recoverable: false
|
|
288
|
+
retry_strategy: "none"
|
|
289
|
+
- name: "InfraUnavailableError"
|
|
290
|
+
description: "PostgreSQL is unavailable (circuit open)"
|
|
291
|
+
recoverable: true
|
|
292
|
+
retry_strategy: "exponential_backoff"
|
|
293
|
+
- name: "RepositoryExecutionError"
|
|
294
|
+
description: "PostgreSQL query execution failed"
|
|
295
|
+
recoverable: true
|
|
296
|
+
retry_strategy: "exponential_backoff"
|
|
297
|
+
# Error codes used in result models
|
|
298
|
+
error_codes:
|
|
299
|
+
# Connection errors
|
|
300
|
+
- code: "POSTGRES_CONNECTION_ERROR"
|
|
301
|
+
backend: "postgres"
|
|
302
|
+
operation: "all"
|
|
303
|
+
description: "Connection to PostgreSQL server failed"
|
|
304
|
+
retriable: true
|
|
305
|
+
- code: "POSTGRES_TIMEOUT_ERROR"
|
|
306
|
+
backend: "postgres"
|
|
307
|
+
operation: "all"
|
|
308
|
+
description: "PostgreSQL operation exceeded timeout"
|
|
309
|
+
retriable: true
|
|
310
|
+
- code: "POSTGRES_AUTH_ERROR"
|
|
311
|
+
backend: "postgres"
|
|
312
|
+
operation: "all"
|
|
313
|
+
description: "Authentication with PostgreSQL server failed"
|
|
314
|
+
retriable: false
|
|
315
|
+
# Operation-specific errors
|
|
316
|
+
- code: "POSTGRES_UPSERT_ERROR"
|
|
317
|
+
backend: "postgres"
|
|
318
|
+
operation: "upsert_contract"
|
|
319
|
+
description: "PostgreSQL upsert operation failed"
|
|
320
|
+
retriable: false
|
|
321
|
+
- code: "POSTGRES_TOPIC_UPDATE_ERROR"
|
|
322
|
+
backend: "postgres"
|
|
323
|
+
operation: "update_topic"
|
|
324
|
+
description: "PostgreSQL topic update failed"
|
|
325
|
+
retriable: false
|
|
326
|
+
- code: "POSTGRES_MARK_STALE_ERROR"
|
|
327
|
+
backend: "postgres"
|
|
328
|
+
operation: "mark_stale"
|
|
329
|
+
description: "PostgreSQL mark stale operation failed"
|
|
330
|
+
retriable: false
|
|
331
|
+
- code: "POSTGRES_HEARTBEAT_ERROR"
|
|
332
|
+
backend: "postgres"
|
|
333
|
+
operation: "update_heartbeat"
|
|
334
|
+
description: "PostgreSQL heartbeat update failed"
|
|
335
|
+
retriable: false
|
|
336
|
+
- code: "POSTGRES_DEACTIVATE_ERROR"
|
|
337
|
+
backend: "postgres"
|
|
338
|
+
operation: "deactivate_contract"
|
|
339
|
+
description: "PostgreSQL deactivation failed"
|
|
340
|
+
retriable: false
|
|
341
|
+
- code: "POSTGRES_CLEANUP_ERROR"
|
|
342
|
+
backend: "postgres"
|
|
343
|
+
operation: "cleanup_topic_references"
|
|
344
|
+
description: "PostgreSQL topic cleanup failed"
|
|
345
|
+
retriable: false
|
|
346
|
+
- code: "POSTGRES_UNKNOWN_ERROR"
|
|
347
|
+
backend: "postgres"
|
|
348
|
+
operation: "all"
|
|
349
|
+
description: "Unknown error during PostgreSQL operation"
|
|
350
|
+
retriable: false
|
|
351
|
+
# IO operations (EFFECT node specific)
|
|
352
|
+
io_operations:
|
|
353
|
+
- operation: "upsert_contract"
|
|
354
|
+
description: "Insert or update a contract record"
|
|
355
|
+
input_fields:
|
|
356
|
+
- contract_id
|
|
357
|
+
- node_name
|
|
358
|
+
- version_major
|
|
359
|
+
- version_minor
|
|
360
|
+
- version_patch
|
|
361
|
+
- contract_hash
|
|
362
|
+
- contract_yaml
|
|
363
|
+
- source_node_id
|
|
364
|
+
- is_active
|
|
365
|
+
- registered_at
|
|
366
|
+
- last_seen_at
|
|
367
|
+
output_fields:
|
|
368
|
+
- success
|
|
369
|
+
- contract_id
|
|
370
|
+
- operation
|
|
371
|
+
- rows_affected
|
|
372
|
+
- error
|
|
373
|
+
- error_code
|
|
374
|
+
- operation: "update_topic"
|
|
375
|
+
description: "Update topic routing information"
|
|
376
|
+
input_fields:
|
|
377
|
+
- topic_suffix
|
|
378
|
+
- direction
|
|
379
|
+
- contract_id
|
|
380
|
+
- node_name
|
|
381
|
+
- event_type
|
|
382
|
+
- last_seen_at
|
|
383
|
+
output_fields:
|
|
384
|
+
- success
|
|
385
|
+
- topic_suffix
|
|
386
|
+
- direction
|
|
387
|
+
- rows_affected
|
|
388
|
+
- error
|
|
389
|
+
- error_code
|
|
390
|
+
- operation: "mark_stale"
|
|
391
|
+
description: "Batch mark contracts as stale"
|
|
392
|
+
input_fields:
|
|
393
|
+
- stale_cutoff
|
|
394
|
+
- checked_at
|
|
395
|
+
output_fields:
|
|
396
|
+
- success
|
|
397
|
+
- contracts_marked
|
|
398
|
+
- stale_cutoff
|
|
399
|
+
- error
|
|
400
|
+
- error_code
|
|
401
|
+
- operation: "update_heartbeat"
|
|
402
|
+
description: "Update contract heartbeat timestamp"
|
|
403
|
+
input_fields:
|
|
404
|
+
- contract_id
|
|
405
|
+
- node_name
|
|
406
|
+
- source_node_id
|
|
407
|
+
- last_seen_at
|
|
408
|
+
- uptime_seconds
|
|
409
|
+
- sequence_number
|
|
410
|
+
output_fields:
|
|
411
|
+
- success
|
|
412
|
+
- contract_id
|
|
413
|
+
- last_seen_at
|
|
414
|
+
- error
|
|
415
|
+
- error_code
|
|
416
|
+
- operation: "deactivate_contract"
|
|
417
|
+
description: "Mark contract as inactive"
|
|
418
|
+
input_fields:
|
|
419
|
+
- contract_id
|
|
420
|
+
- node_name
|
|
421
|
+
- reason
|
|
422
|
+
- deactivated_at
|
|
423
|
+
output_fields:
|
|
424
|
+
- success
|
|
425
|
+
- contract_id
|
|
426
|
+
- deactivated_at
|
|
427
|
+
- error
|
|
428
|
+
- error_code
|
|
429
|
+
- operation: "cleanup_topic_references"
|
|
430
|
+
description: "Remove contract from topic references"
|
|
431
|
+
input_fields:
|
|
432
|
+
- contract_id
|
|
433
|
+
- node_name
|
|
434
|
+
- cleaned_at
|
|
435
|
+
output_fields:
|
|
436
|
+
- success
|
|
437
|
+
- contract_id
|
|
438
|
+
- topics_updated
|
|
439
|
+
- error
|
|
440
|
+
- error_code
|
|
441
|
+
# Dependencies (protocols this node requires)
|
|
442
|
+
dependencies:
|
|
443
|
+
- name: "protocol_postgres_adapter"
|
|
444
|
+
type: "protocol"
|
|
445
|
+
class_name: "ProtocolPostgresAdapter"
|
|
446
|
+
module: "omnibase_infra.adapters.protocol_postgres_adapter"
|
|
447
|
+
description: "PostgreSQL database operations"
|
|
448
|
+
- name: "protocol_circuit_breaker_aware"
|
|
449
|
+
type: "protocol"
|
|
450
|
+
class_name: "ProtocolCircuitBreakerAware"
|
|
451
|
+
module: "omnibase_infra.mixins.protocol_circuit_breaker_aware"
|
|
452
|
+
description: "Circuit breaker awareness for backend protection"
|
|
453
|
+
# Capabilities provided by this node
|
|
454
|
+
capabilities:
|
|
455
|
+
- name: "contract_persistence"
|
|
456
|
+
description: "Persist contract records to PostgreSQL"
|
|
457
|
+
- name: "topic_routing"
|
|
458
|
+
description: "Manage topic-to-contract routing mappings"
|
|
459
|
+
- name: "staleness_detection"
|
|
460
|
+
description: "Batch mark stale contracts based on heartbeat"
|
|
461
|
+
- name: "heartbeat_tracking"
|
|
462
|
+
description: "Track contract liveness via heartbeat updates"
|
|
463
|
+
- name: "soft_delete"
|
|
464
|
+
description: "Deactivate contracts without data loss"
|
|
465
|
+
- name: "circuit_breaker_protection"
|
|
466
|
+
description: "Protect PostgreSQL with circuit breaker pattern"
|
|
467
|
+
# Health check configuration
|
|
468
|
+
health_check:
|
|
469
|
+
enabled: true
|
|
470
|
+
endpoint: "/health"
|
|
471
|
+
interval_seconds: 30
|
|
472
|
+
backends:
|
|
473
|
+
- name: "postgres"
|
|
474
|
+
check_type: "connection"
|
|
475
|
+
timeout_ms: 5000
|
|
476
|
+
# Metadata
|
|
477
|
+
metadata:
|
|
478
|
+
author: "OmniNode Team"
|
|
479
|
+
license: "MIT"
|
|
480
|
+
created: "2025-02-02"
|
|
481
|
+
updated: "2025-02-02"
|
|
482
|
+
tags:
|
|
483
|
+
- effect
|
|
484
|
+
- contract-registry
|
|
485
|
+
- postgresql
|
|
486
|
+
- persistence
|
|
487
|
+
- circuit-breaker
|
|
488
|
+
related_tickets:
|
|
489
|
+
- "OMN-1845"
|
|
490
|
+
- "OMN-1653"
|