Repository navigation
fix: bound JSON output shutdown after interrupt - #7578
harsh21234i wants to merge 5 commits into
Conversation
[Medium impact] Fixes JSON output shutdown when interrupted. Fix the nested-JSON output-thread failure before merging. Findings
SummaryThis PR bounds JSON-output shutdown from the first interrupt.
Both numbered previous findings are fixed. The new recursive truncation can stop an output thread on deeply nested JSON. Reviews (3) · Last reviewed commit: "fix: preserve fields in oversized log re..." · Reviewed by Greptile |
Merging this PR will not alter performance
|
There was a problem hiding this comment.
All reported issues were addressed across 3 files
Reply with feedback, questions, or to request a fix.
View guided diff | Re-trigger cubic
There was a problem hiding this comment.
All reported issues were addressed
Reply with feedback, questions, or to request a fix.
View guided diff | Re-trigger cubic
There was a problem hiding this comment.
All reported issues were addressed across 3 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Re-trigger cubic
| if isinstance(value, dict): | ||
| return { | ||
| key: truncate_strings(item, max_chars) for key, item in value.items() | ||
| } | ||
| if isinstance(value, list): | ||
| return [truncate_strings(item, max_chars) for item in value] |
There was a problem hiding this comment.
On supported Python 3.11, an oversized JSON record with roughly 600 nested containers can pass json.loads but fail inside truncate_strings. Each level adds both a function call and a comprehension frame, exhausting the allowed call depth sooner.
The resulting RecursionError escapes _write, which only catches OSError, and stops the output thread. Later child output can fill the unread pipe and hang the command. Use a walk that does not recurse, or catch this failure and emit a bounded fallback record.
There was a problem hiding this comment.
3 issues found across 2 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/reflex-base/src/reflex_base/utils/log.py">
<violation number="1" location="packages/reflex-base/src/reflex_base/utils/log.py:426">
P1: Replace this recursive traversal with an iterative walk so deeply nested JSON records cannot raise `RecursionError` and terminate the output pump.</violation>
<violation number="2" location="packages/reflex-base/src/reflex_base/utils/log.py:434">
P2: A near-limit record dominated by non-string fields can trigger thousands of full copies and serializations before reaching the fallback, stalling output draining. Detect when retries cannot shrink the record or cap the retry work.</violation>
<violation number="3" location="packages/reflex-base/src/reflex_base/utils/log.py:435">
P2: Oversized records containing `NaN` or `Infinity` are emitted as non-standard JSON because this serialization uses the default `allow_nan=True`. Normalize non-finite values before dumping so strict JSON consumers can parse every emitted line.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Re-trigger cubic
| return value if len(value) <= max_chars else value[:max_chars] + marker | ||
| if isinstance(value, dict): | ||
| return { | ||
| key: truncate_strings(item, max_chars) for key, item in value.items() |
There was a problem hiding this comment.
P1: Replace this recursive traversal with an iterative walk so deeply nested JSON records cannot raise RecursionError and terminate the output pump.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At packages/reflex-base/src/reflex_base/utils/log.py, line 426:
<comment>Replace this recursive traversal with an iterative walk so deeply nested JSON records cannot raise `RecursionError` and terminate the output pump.</comment>
<file context>
@@ -416,32 +416,30 @@ def _truncate_json_record(record: bytes, max_bytes: int) -> bytes:
+ return value if len(value) <= max_chars else value[:max_chars] + marker
+ if isinstance(value, dict):
+ return {
+ key: truncate_strings(item, max_chars) for key, item in value.items()
+ }
+ if isinstance(value, list):
</file context>
|
|
||
| payload["truncated"] = True | ||
| max_chars = max_bytes | ||
| while True: |
There was a problem hiding this comment.
P2: A near-limit record dominated by non-string fields can trigger thousands of full copies and serializations before reaching the fallback, stalling output draining. Detect when retries cannot shrink the record or cap the retry work.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At packages/reflex-base/src/reflex_base/utils/log.py, line 434:
<comment>A near-limit record dominated by non-string fields can trigger thousands of full copies and serializations before reaching the fallback, stalling output draining. Detect when retries cannot shrink the record or cap the retry work.</comment>
<file context>
@@ -416,32 +416,30 @@ def _truncate_json_record(record: bytes, max_bytes: int) -> bytes:
+
+ payload["truncated"] = True
+ max_chars = max_bytes
+ while True:
+ result = json.dumps(truncate_strings(payload, max_chars)).encode() + b"\n"
+ if len(result) <= max_bytes:
</file context>
| payload["truncated"] = True | ||
| max_chars = max_bytes | ||
| while True: | ||
| result = json.dumps(truncate_strings(payload, max_chars)).encode() + b"\n" |
There was a problem hiding this comment.
P2: Oversized records containing NaN or Infinity are emitted as non-standard JSON because this serialization uses the default allow_nan=True. Normalize non-finite values before dumping so strict JSON consumers can parse every emitted line.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At packages/reflex-base/src/reflex_base/utils/log.py, line 435:
<comment>Oversized records containing `NaN` or `Infinity` are emitted as non-standard JSON because this serialization uses the default `allow_nan=True`. Normalize non-finite values before dumping so strict JSON consumers can parse every emitted line.</comment>
<file context>
@@ -416,32 +416,30 @@ def _truncate_json_record(record: bytes, max_bytes: int) -> bytes:
+ payload["truncated"] = True
+ max_chars = max_bytes
+ while True:
+ result = json.dumps(truncate_strings(payload, max_chars)).encode() + b"\n"
+ if len(result) <= max_bytes:
+ return result
</file context>
Summary
Tests
tests/units/reflex_base/utils/test_log.py: 81 passed, 5 skipped on Windows (POSIX-only cases).git diff --checkpassed.Fixes #7510