Skip to content

Commit 41b32fb

Browse files
feat: fix file eviction using sqlalchemy events (#621)
* add * Delete test.py * feat: split lua codes into own file * feat: fix file eviction using sqlalchemy events
1 parent 3d8d004 commit 41b32fb

File tree

1 file changed

+29
-1
lines changed

1 file changed

+29
-1
lines changed

src/backend/app/models/files.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
from uuid import UUID
44

55
from pydantic import model_validator
6-
from sqlalchemy import BigInteger, Column, DateTime, UniqueConstraint, text
6+
from sqlalchemy import BigInteger, Column, DateTime, UniqueConstraint, event, text
7+
from sqlalchemy.engine import Connection
8+
from sqlalchemy.orm import Mapper
79
from sqlmodel import Field, SQLModel
810

911

@@ -60,3 +62,29 @@ def validate_expire(self) -> Self:
6062
if self.expires_at < self.created_at:
6163
raise ValueError("Expiration time cannot be earlier than creation time")
6264
return self
65+
66+
67+
@event.listens_for(File, "after_delete")
68+
def on_file_delete(mapper: Mapper, connection: Connection, target: File):
69+
"""
70+
Event hook to evict the file from the global app state (Redis) after it is deleted from the DB.
71+
This ensures that total_space_used is updated and the file is removed from active_uploads.
72+
"""
73+
import asyncio
74+
75+
from app.states.app import AppState
76+
77+
# Capture values to avoid issues with target being detached/deleted
78+
file_key = target.key
79+
file_size = target.size
80+
81+
async def do_evict():
82+
await AppState.evict_files(file_keys=[file_key], freed_bytes=file_size)
83+
84+
try:
85+
loop = asyncio.get_running_loop()
86+
loop.create_task(do_evict())
87+
except RuntimeError:
88+
# If no loop is running, we might be in a sync context.
89+
# In this app, it's expected to have a loop in FastAPI or Celery async tasks.
90+
pass

0 commit comments

Comments
 (0)