commandnet 0.5.0__tar.gz → 0.5.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {commandnet-0.5.0 → commandnet-0.5.1}/PKG-INFO +1 -1
- {commandnet-0.5.0 → commandnet-0.5.1}/commandnet/engine/runtime.py +21 -10
- {commandnet-0.5.0 → commandnet-0.5.1}/pyproject.toml +1 -1
- {commandnet-0.5.0 → commandnet-0.5.1}/README.md +0 -0
- {commandnet-0.5.0 → commandnet-0.5.1}/commandnet/__init__.py +0 -0
- {commandnet-0.5.0 → commandnet-0.5.1}/commandnet/core/graph.py +0 -0
- {commandnet-0.5.0 → commandnet-0.5.1}/commandnet/core/models.py +0 -0
- {commandnet-0.5.0 → commandnet-0.5.1}/commandnet/core/node.py +0 -0
- {commandnet-0.5.0 → commandnet-0.5.1}/commandnet/interfaces/event_bus.py +0 -0
- {commandnet-0.5.0 → commandnet-0.5.1}/commandnet/interfaces/observer.py +0 -0
- {commandnet-0.5.0 → commandnet-0.5.1}/commandnet/interfaces/persistence.py +0 -0
|
@@ -70,8 +70,10 @@ class Engine:
|
|
|
70
70
|
|
|
71
71
|
async def _run_node_logic(self, event: Event):
|
|
72
72
|
subject_id = event.subject_id
|
|
73
|
-
|
|
73
|
+
|
|
74
|
+
# 1. READ & LOCK
|
|
74
75
|
node_name, ctx_dict = await self.db.lock_and_load(subject_id)
|
|
76
|
+
|
|
75
77
|
if not node_name or (
|
|
76
78
|
node_name != event.node_name and node_name != "AWAITING_CALL"
|
|
77
79
|
):
|
|
@@ -92,33 +94,42 @@ class Engine:
|
|
|
92
94
|
if hasattr(ctx_type, "model_validate")
|
|
93
95
|
else ctx_dict
|
|
94
96
|
)
|
|
97
|
+
|
|
95
98
|
payload = (
|
|
96
99
|
payload_type.model_validate(event.payload)
|
|
97
100
|
if (event.payload and hasattr(payload_type, "model_validate"))
|
|
98
101
|
else event.payload
|
|
99
102
|
)
|
|
100
103
|
|
|
101
|
-
#
|
|
104
|
+
# Soft cancel check BEFORE compute
|
|
102
105
|
if hasattr(ctx, "is_cancelled"):
|
|
103
106
|
ctx.is_cancelled = await self.db.is_cancelled(subject_id)
|
|
104
107
|
|
|
108
|
+
# 2. RELEASE LOCK before long-running compute
|
|
109
|
+
await self.db.unlock_subject(subject_id)
|
|
110
|
+
|
|
111
|
+
# 3. COMPUTE (no DB lock held)
|
|
105
112
|
start_t = asyncio.get_event_loop().time()
|
|
106
113
|
result = await node_cls().run(ctx, payload)
|
|
114
|
+
duration = (asyncio.get_event_loop().time() - start_t) * 1000
|
|
115
|
+
|
|
116
|
+
# 4. RE-LOCK before writing
|
|
117
|
+
node_name_check, _ = await self.db.lock_and_load(subject_id)
|
|
118
|
+
|
|
119
|
+
# 🔴 IMPORTANT SAFETY CHECK (prevents stale writes)
|
|
120
|
+
if node_name_check != event.node_name and node_name_check != "AWAITING_CALL":
|
|
121
|
+
await self.db.unlock_subject(subject_id)
|
|
122
|
+
return
|
|
123
|
+
|
|
124
|
+
await self._apply_target(subject_id, ctx, result, duration)
|
|
107
125
|
|
|
108
|
-
await self._apply_target(
|
|
109
|
-
subject_id,
|
|
110
|
-
ctx,
|
|
111
|
-
result,
|
|
112
|
-
(asyncio.get_event_loop().time() - start_t) * 1000,
|
|
113
|
-
)
|
|
114
126
|
except Exception as e:
|
|
115
127
|
await self.observer.on_error(subject_id, event.node_name, e)
|
|
116
128
|
raise
|
|
117
129
|
finally:
|
|
130
|
+
# Ensure we never leave a lock hanging
|
|
118
131
|
await self.db.unlock_subject(subject_id)
|
|
119
132
|
|
|
120
|
-
# --- RECURSIVE TARGET RESOLVER ---
|
|
121
|
-
|
|
122
133
|
async def _apply_target(
|
|
123
134
|
self,
|
|
124
135
|
subject_id: str,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "commandnet"
|
|
3
|
-
version = "0.5.
|
|
3
|
+
version = "0.5.1"
|
|
4
4
|
description = "A lightweight, Pydantic-powered, distributed event-driven state machine and typed node graph runtime."
|
|
5
5
|
authors = [
|
|
6
6
|
{ name = "Christopher Vaz", email = "christophervaz160@gmail.com" }
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|