mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-16 11:49:28 +00:00
fix(cpg): end an engine session with the transaction that owns it (M2)
The first session-enabled replay produced correct detections and no speedup, reporting session_query_failed:census:session_request_failed on both sides. A targeted probe ruled out the sequence I suspected: an overlay followed by a census in one session both answer correctly, with the census confirming the overlays were saved. What the probe exposed instead is a leak. A build runs on a copy of the backend, so the session started during the build belonged to that copy and was never closed, leaving a JVM holding the configured 2 GB heap for the life of the process. Two builds per transaction meant two of them, which is enough to exhaust a 3 GB container and make every later request fail. It also matches the host running out of memory during these runs. The build now closes any session its copy started, and the driver ends the session with the transaction, since that is what transaction-owned means. Controls cover both: a build leaves nothing alive on the copy that started it, and a completed scan closes the backend's session. Closing never fails a scan that found things. This is a genuine defect found by measurement rather than by the suite, which passed throughout. Whether it was the whole cause of the census failure is not yet established; the next session-enabled run will say. 1,399 tests passed, nine skipped. Rollout stays NO-GO. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
This commit is contained in:
co-authored by
Claude Opus 5
parent
d2362cd18d
commit
566b862b0a
@@ -290,3 +290,30 @@ def test_a_transport_failure_carries_the_server_log(tmp_path, fake):
|
||||
assert live.failure == "session_request_failed"
|
||||
assert "OutOfMemoryError" in live.last_body
|
||||
assert "OutOfMemoryError" in live.log_tail()
|
||||
|
||||
|
||||
def test_a_build_closes_the_session_it_started(tmp_path, fake, monkeypatch):
|
||||
"""M2: an unclosed session leaks a JVM holding the configured heap for the whole process."""
|
||||
from openultrasast.cpg.backend import JoernBackend
|
||||
|
||||
closed: list[str] = []
|
||||
monkeypatch.setattr(EngineSession, "start", lambda self: True)
|
||||
monkeypatch.setattr(EngineSession, "close", lambda self: closed.append(str(self.scratch)))
|
||||
backend = JoernBackend(session_transport=True)
|
||||
# The build runs on a copy, so the session it starts is the copy's to close.
|
||||
monkeypatch.setattr(JoernBackend, "_build_impl", lambda self, root, **k: self._session_for(tmp_path / "cpg.bin"))
|
||||
backend.build(tmp_path, execution_budget=ExecutionBudget(time.monotonic() + 20, 1.0))
|
||||
assert closed, "the build copy left its session open, which leaks a JVM holding the heap"
|
||||
assert backend._session is None
|
||||
|
||||
|
||||
def test_the_driver_ends_the_session_with_the_transaction(tmp_path):
|
||||
from openultrasast.cpg.backend import JoernBackend
|
||||
from openultrasast.model.scan import scan_repository
|
||||
|
||||
backend = JoernBackend()
|
||||
closed: list[bool] = []
|
||||
backend.close_session = lambda: closed.append(True) # type: ignore[method-assign]
|
||||
(tmp_path / "a.js").write_text("const x = 1;\n")
|
||||
scan_repository(tmp_path, [], backend=backend)
|
||||
assert closed == [True]
|
||||
|
||||
Reference in New Issue
Block a user