Skip to content

Commit 64383ac

Browse files
committed
Implement old session cleanup
1 parent 9404125 commit 64383ac

File tree

6 files changed

+52
-2
lines changed

6 files changed

+52
-2
lines changed

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ dependencies = [
1111
### Invariant extra dependencies
1212
"presidio-analyzer>=2.2.354",
1313
"spacy>=3.7.5",
14+
"fastapi-utils>=0.7.0",
15+
"typing-inspect>=0.9.0",
1416
]
1517
readme = "README.md"
1618
requires-python = ">= 3.10"

requirements-dev.lock

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,12 @@ exceptiongroup==1.2.1
6767
face==22.0.0
6868
# via glom
6969
fastapi==0.111.0
70+
# via fastapi-utils
7071
# via invariant-server
7172
fastapi-cli==0.0.4
7273
# via fastapi
74+
fastapi-utils==0.7.0
75+
# via invariant-server
7376
filelock==3.15.4
7477
# via tldextract
7578
glom==22.1.0
@@ -118,6 +121,8 @@ murmurhash==1.0.10
118121
# via preshed
119122
# via spacy
120123
# via thinc
124+
mypy-extensions==1.0.0
125+
# via typing-inspect
121126
numpy==1.26.4
122127
# via blis
123128
# via spacy
@@ -143,9 +148,12 @@ preshed==3.0.9
143148
# via thinc
144149
presidio-analyzer==2.2.354
145150
# via invariant-server
151+
psutil==5.9.8
152+
# via fastapi-utils
146153
pydantic==2.8.2
147154
# via confection
148155
# via fastapi
156+
# via fastapi-utils
149157
# via invariant
150158
# via pydantic-settings
151159
# via spacy
@@ -240,6 +248,9 @@ typing-extensions==4.12.2
240248
# via semgrep
241249
# via sqlalchemy
242250
# via typer
251+
# via typing-inspect
252+
typing-inspect==0.9.0
253+
# via invariant-server
243254
ujson==5.10.0
244255
# via fastapi
245256
urllib3==2.2.2

requirements.lock

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,12 @@ exceptiongroup==1.2.1
6767
face==22.0.0
6868
# via glom
6969
fastapi==0.111.0
70+
# via fastapi-utils
7071
# via invariant-server
7172
fastapi-cli==0.0.4
7273
# via fastapi
74+
fastapi-utils==0.7.0
75+
# via invariant-server
7376
filelock==3.15.4
7477
# via tldextract
7578
glom==22.1.0
@@ -116,6 +119,8 @@ murmurhash==1.0.10
116119
# via preshed
117120
# via spacy
118121
# via thinc
122+
mypy-extensions==1.0.0
123+
# via typing-inspect
119124
numpy==1.26.4
120125
# via blis
121126
# via spacy
@@ -138,9 +143,12 @@ preshed==3.0.9
138143
# via thinc
139144
presidio-analyzer==2.2.354
140145
# via invariant-server
146+
psutil==5.9.8
147+
# via fastapi-utils
141148
pydantic==2.8.2
142149
# via confection
143150
# via fastapi
151+
# via fastapi-utils
144152
# via invariant
145153
# via pydantic-settings
146154
# via spacy
@@ -233,6 +241,9 @@ typing-extensions==4.12.2
233241
# via semgrep
234242
# via sqlalchemy
235243
# via typer
244+
# via typing-inspect
245+
typing-inspect==0.9.0
246+
# via invariant-server
236247
ujson==5.10.0
237248
# via fastapi
238249
urllib3==2.2.2

server/config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
class Settings(BaseSettings):
55
production: bool = False
6+
idle_timeout: int = 10 * 60 # 10 minutes of inactivity before stopping the process
67

78

89
settings = Settings()

server/ipc/controller.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import json
44
import os
55
from server.config import settings
6+
import time
67

78

89
class IpcController:
@@ -25,6 +26,7 @@ def _execute_function(self, session_id, func_name, *args, **kwargs):
2526
rpc_info = self.rpc_map[session_id]
2627
message = json.dumps({"func_name": func_name, "args": args, "kwargs": kwargs})
2728
try:
29+
rpc_info["timestamp"] = time.time()
2830
rpc_info["stdin"].write(message + "\n")
2931
rpc_info["stdin"].flush()
3032
result = rpc_info["stdout"].readline().strip()
@@ -67,7 +69,11 @@ def start_process(self, session_id):
6769
text=True,
6870
)
6971
self.process_map[session_id] = process
70-
self.rpc_map[session_id] = {"stdin": process.stdin, "stdout": process.stdout}
72+
self.rpc_map[session_id] = {
73+
"stdin": process.stdin,
74+
"stdout": process.stdout,
75+
"timestamp": time.time(),
76+
}
7177

7278
def call_function(self, session_id, func_name, *args, **kwargs):
7379
if session_id not in self.rpc_map:
@@ -80,9 +86,19 @@ def stop_process(self, session_id):
8086
self.rpc_map[session_id]["stdin"].close()
8187
self.rpc_map[session_id]["stdout"].close()
8288
process = self.process_map.pop(session_id, None)
89+
del self.rpc_map[session_id]
8390
if process:
8491
process.terminate()
8592

93+
def cleanup(self):
94+
for session_id in list(self.rpc_map.keys()):
95+
if (
96+
session_id in self.rpc_map
97+
and time.time() - self.rpc_map[session_id]["timestamp"]
98+
> settings.idle_timeout
99+
):
100+
self.stop_process(session_id)
101+
86102

87103
class IpcControllerSingleton:
88104
def __init__(self):

server/main.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
from fastapi import FastAPI
1+
from fastapi import Depends, FastAPI
2+
from server.ipc.controller import IpcController, get_ipc_controller
23
from server.routers import session, policy, monitor
34
from server.database import engine
45
from server.models import Base
6+
from fastapi_utils.tasks import repeat_every
57

68
Base.metadata.create_all(bind=engine)
79

@@ -20,3 +22,10 @@ def index():
2022
app.include_router(session.router, prefix="/session", tags=["session"])
2123
app.include_router(policy.router, prefix="/policy", tags=["policy"])
2224
app.include_router(monitor.router, prefix="/monitor", tags=["monitor"])
25+
26+
27+
@app.on_event("startup")
28+
@repeat_every(seconds=60) # 1 minute
29+
def cleanup_old_ipc() -> None:
30+
ipc_controller = get_ipc_controller()
31+
ipc_controller.cleanup()

0 commit comments

Comments
 (0)