Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 43 additions & 1 deletion loopx/control_plane/effect_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,11 @@ def __init__(self) -> None:
self._revision: _RuntimeRevision | None = None
self._scope_count = 1
self._closed = False
# The Todo context handler is a pure lens over supplied records. Share
# its exact results only inside this already revision-pinned request;
# authority reads, clocks, mutation and settlement never enter here.
self._todo_context: dict[bytes, bytes] = {}
self._todo_context_bytes = 0

def try_join(self) -> bool:
with self._lock:
Expand All @@ -143,6 +148,20 @@ def leave(self) -> None:
if self._scope_count == 0:
self._closed = True
self._revision = None
self._todo_context.clear()
self._todo_context_bytes = 0

def todo_context_result(self, key: bytes) -> bytes | None:
with self._lock:
return None if self._closed else self._todo_context.get(key)

def remember_todo_context(self, key: bytes, result: bytes) -> None:
with self._lock:
size = len(key) + len(result)
if (not self._closed and key not in self._todo_context
and self._todo_context_bytes + size <= MAX_REQUEST_BYTES):
self._todo_context[key] = result
self._todo_context_bytes += size


_REQUEST_RUNTIME_REVISION: ContextVar[_RequestRuntimeRevision | None] = (
Expand Down Expand Up @@ -1581,13 +1600,36 @@ def effect_runtime_result(
retry_safe: bool = True,
large_local_snapshot: bool = False,
) -> Any:
return effect_runtime_request(
state = _REQUEST_RUNTIME_REVISION.get() if method == "todo.context.page" else None
key = None
if state is not None:
# Preserve JSON order and scalar types, including bool/int, signed zero
# and exact integers. Do not infer equality from a digest or Todo id.
try:
key = json.dumps([params, timeout, retry_safe, large_local_snapshot],
ensure_ascii=False, allow_nan=False, separators=(",", ":")).encode("utf-8")
except (TypeError, ValueError, OverflowError, UnicodeError, RecursionError):
pass # Invalid/non-JSON input still reaches the original validator.
if key is not None:
prior = state.todo_context_result(key)
if prior is not None:
return json.loads(prior) # A caller cannot mutate another view.
result = effect_runtime_request(
method,
params,
timeout=timeout,
retry_safe=retry_safe,
large_local_snapshot=large_local_snapshot,
).get("result")
if state is not None and key is not None and isinstance(result, dict):
try:
encoded = json.dumps(result, ensure_ascii=False, allow_nan=False,
separators=(",", ":")).encode("utf-8")
except (TypeError, ValueError, OverflowError, UnicodeError, RecursionError):
pass
else:
state.remember_todo_context(key, encoded)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Narrow the request state before inserting a memoized result

配置内的 uv run --extra test mypy 在不可变基线通过 17 个文件,在这个提交新增 union-attr:state 仍是 _RequestRuntimeRevision | None。前面 key is not None 的逻辑关联不能为这里提供类型收窄,因此必需检查失败;这不等于已复现运行时 None 异常。请在写入 guard 显式检查 state is not None 或作等价收窄,保留完整键、预算与重试语义,并重跑配置内 mypy 及受影响套件。

return result


def effect_runtime_projection_result(method: str, params: Mapping[str, Any]) -> Any:
Expand Down
77 changes: 77 additions & 0 deletions tests/control_plane/test_effect_runtime_request_scope.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,3 +291,80 @@ def collect(**_kwargs: object) -> dict[str, object]:

assert payload == {"first": "revision-1", "second": "revision-1"}
assert fingerprints == ["revision-1"]


def test_repeated_todo_lenses_share_only_exact_request_inputs(monkeypatch):
calls = []

def request(method, params, **kwargs):
calls.append((method, params))
return {"result": {"items": [{"text": params["text"]}]}}

monkeypatch.setattr(effect_runtime, "effect_runtime_request", request)
with effect_runtime.effect_runtime_request_scope():
first = effect_runtime.effect_runtime_result("todo.context.page", {"text": "original"})
first["items"][0]["text"] = "caller edit"
assert effect_runtime.effect_runtime_result("todo.context.page", {"text": "original"}) == {
"items": [{"text": "original"}]}
assert effect_runtime.effect_runtime_result("todo.context.page", {"text": "revised"}) == {
"items": [{"text": "revised"}]}
# JSON equality must not use Python's bool/int equality or lose -0.0.
for value in (True, 1, 1.0, 0.0, -0.0, 2**60, 2**60 + 1):
effect_runtime.effect_runtime_result("todo.context.page", {"text": value})
assert len(calls) == 9


def test_todo_reuse_ends_with_request_even_for_a_copied_context(monkeypatch):
calls = []

def request(method, params, **kwargs):
calls.append(method)
return {"result": {"observed": len(calls)}}

monkeypatch.setattr(effect_runtime, "effect_runtime_request", request)
with effect_runtime.effect_runtime_request_scope():
first = effect_runtime.effect_runtime_result("todo.context.page", {})
inherited = copy_context()
assert inherited.run(effect_runtime.effect_runtime_result, "todo.context.page", {}) == first
assert inherited.run(effect_runtime.effect_runtime_result, "todo.context.page", {}) != first
with effect_runtime.effect_runtime_request_scope():
assert effect_runtime.effect_runtime_result("todo.context.page", {}) != first
assert len(calls) == 3


def test_authority_reads_settlement_and_failed_lenses_are_never_reused(monkeypatch):
calls = []

def request(method, params, **kwargs):
calls.append(method)
if method == "todo.context.page" and calls.count(method) == 1:
raise effect_runtime.EffectRuntimeRejected("invalid fixture")
return {"result": {"observed": len(calls)}}

monkeypatch.setattr(effect_runtime, "effect_runtime_request", request)
with effect_runtime.effect_runtime_request_scope():
for method in ("coordination.local_authority.todo_snapshot", "quota.settlement.read", "quota.spend.commit"):
assert effect_runtime.effect_runtime_result(method, {}) != effect_runtime.effect_runtime_result(method, {})
with pytest.raises(effect_runtime.EffectRuntimeRejected):
effect_runtime.effect_runtime_result("todo.context.page", {})
assert effect_runtime.effect_runtime_result("todo.context.page", {}) == {"observed": 8}
assert effect_runtime.effect_runtime_result("todo.context.page", {}) == {"observed": 8}
assert len(calls) == 8


def test_todo_reuse_is_bounded_and_unscoped_calls_keep_original_behavior(monkeypatch):
calls = []

def request(method, params, **kwargs):
calls.append(method)
return {"result": {"text": "preserve all original output"}}

monkeypatch.setattr(effect_runtime, "effect_runtime_request", request)
monkeypatch.setattr(effect_runtime, "MAX_REQUEST_BYTES", 1)
with effect_runtime.effect_runtime_request_scope():
for _ in range(2):
assert effect_runtime.effect_runtime_result("todo.context.page", {}) == {
"text": "preserve all original output"}
for _ in range(2):
effect_runtime.effect_runtime_result("todo.context.page", {})
assert len(calls) == 4
Loading