Spaces:
Running
π§ Fix SQLAlchemy Connection Pool Error in HF Spaces
Browse filesβ Critical Issue Fixed:
β’ AssertionError: can't 'checkout' a detached connection fairy
β’ Multiple 500 errors on /api/traces/ endpoint
β’ Database connection pool failures in async environment
π― Root Cause Analysis:
β’ sqlite:///:memory: doesn't work well with FastAPI async operations
β’ In-memory databases create separate instances per connection
β’ Connection pool management conflicts in multi-threaded environment
β’ SQLAlchemy connection fairy detachment issues
β‘ Solution Implemented:
β’ Replaced in-memory database with temporary file database
β’ Enhanced SQLite engine configuration for async operations
β’ Added proper connection pool settings:
- pool_pre_ping=True (verify connections before use)
- pool_recycle=3600 (recycle connections every hour)
- timeout=30, isolation_level=None (autocommit mode)
β’ Each HF Spaces session gets unique temporary database file
π Privacy Protection Maintained:
β’ Still provides user isolation (unique temp file per session)
β’ Data cleared on container restart
β’ No cross-user data sharing
β’ Better reliability than in-memory approach
π Expected Results:
β’ /api/traces/ endpoint should work without 500 errors
β’ Stable database connections in async environment
β’ Better performance and reliability
β’ Maintained user privacy and data isolation
- backend/app.py +2 -1
- backend/database/__init__.py +21 -7
|
@@ -80,8 +80,9 @@ async def startup_event():
|
|
| 80 |
|
| 81 |
# Show database type info
|
| 82 |
if os.getenv("SPACE_ID"):
|
| 83 |
-
logger.info("π HF Spaces: Using
|
| 84 |
logger.info("π Note: Data will be cleared when container restarts")
|
|
|
|
| 85 |
else:
|
| 86 |
logger.info("πΎ Local development: Using persistent database")
|
| 87 |
|
|
|
|
| 80 |
|
| 81 |
# Show database type info
|
| 82 |
if os.getenv("SPACE_ID"):
|
| 83 |
+
logger.info("π HF Spaces: Using temporary file database for user privacy")
|
| 84 |
logger.info("π Note: Data will be cleared when container restarts")
|
| 85 |
+
logger.info("π‘οΈ User isolation: Each session has its own database")
|
| 86 |
else:
|
| 87 |
logger.info("πΎ Local development: Using persistent database")
|
| 88 |
|
|
@@ -11,22 +11,36 @@ from sqlalchemy.orm import sessionmaker, scoped_session
|
|
| 11 |
# Get the absolute path to the project root directory
|
| 12 |
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
| 13 |
|
| 14 |
-
# Database URL - Use
|
| 15 |
# This ensures each container restart gets a fresh database
|
| 16 |
import uuid
|
|
|
|
| 17 |
session_id = str(uuid.uuid4())[:8]
|
| 18 |
|
| 19 |
-
# For HF Spaces: Use
|
| 20 |
-
# For local development: Use file database
|
| 21 |
if os.getenv("SPACE_ID"): # HF Spaces environment
|
| 22 |
-
|
| 23 |
-
|
|
|
|
|
|
|
|
|
|
| 24 |
else:
|
| 25 |
DATABASE_URL = f"sqlite:///{os.path.join(ROOT_DIR, 'datasets/db/agent_monitoring.db')}"
|
| 26 |
print(f"πΎ Local: Using persistent database")
|
| 27 |
|
| 28 |
-
# Create engine
|
| 29 |
-
engine = create_engine(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
|
| 31 |
# Create session factory
|
| 32 |
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
|
|
|
| 11 |
# Get the absolute path to the project root directory
|
| 12 |
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
| 13 |
|
| 14 |
+
# Database URL - Use temporary file SQLite for HF Spaces to avoid data sharing between users
|
| 15 |
# This ensures each container restart gets a fresh database
|
| 16 |
import uuid
|
| 17 |
+
import tempfile
|
| 18 |
session_id = str(uuid.uuid4())[:8]
|
| 19 |
|
| 20 |
+
# For HF Spaces: Use temporary file database to prevent user data sharing
|
| 21 |
+
# For local development: Use persistent file database
|
| 22 |
if os.getenv("SPACE_ID"): # HF Spaces environment
|
| 23 |
+
# Use temporary file instead of in-memory to avoid connection pool issues
|
| 24 |
+
temp_db_path = f"/tmp/agentgraph_{session_id}.db"
|
| 25 |
+
DATABASE_URL = f"sqlite:///{temp_db_path}"
|
| 26 |
+
print(f"π HF Spaces: Using temporary database (session: {session_id})")
|
| 27 |
+
print(f"π Database path: {temp_db_path}")
|
| 28 |
else:
|
| 29 |
DATABASE_URL = f"sqlite:///{os.path.join(ROOT_DIR, 'datasets/db/agent_monitoring.db')}"
|
| 30 |
print(f"πΎ Local: Using persistent database")
|
| 31 |
|
| 32 |
+
# Create engine with proper SQLite configuration for async operations
|
| 33 |
+
engine = create_engine(
|
| 34 |
+
DATABASE_URL,
|
| 35 |
+
connect_args={
|
| 36 |
+
"check_same_thread": False,
|
| 37 |
+
"timeout": 30,
|
| 38 |
+
"isolation_level": None, # Autocommit mode
|
| 39 |
+
},
|
| 40 |
+
pool_pre_ping=True, # Verify connections before use
|
| 41 |
+
pool_recycle=3600, # Recycle connections every hour
|
| 42 |
+
echo=False # Disable SQL logging for production
|
| 43 |
+
)
|
| 44 |
|
| 45 |
# Create session factory
|
| 46 |
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|