feat: implement SPEC-11 API performance optimizations by phernandez · Pull Request #315 · basicmachines-co/basic-memory · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
245 changes: 245 additions & 0 deletions specs/SPEC-11 Basic Memory API Performance Optimization.md
11 changes: 8 additions & 3 deletions src/basic_memory/api/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,23 @@
webdav,
)
from basic_memory.config import ConfigManager
from basic_memory.services.initialization import initialize_app, initialize_file_sync
from basic_memory.services.initialization import initialize_file_sync


@asynccontextmanager
async def lifespan(app: FastAPI): # pragma: no cover
"""Lifecycle manager for the FastAPI app."""

app_config = ConfigManager().config
# Initialize app and database
logger.info("Starting Basic Memory API")
print(f"fastapi {app_config.projects}")
await initialize_app(app_config)

# Cache database connections in app state for performance (no project reconciliation)
logger.info("Initializing database and caching connections...")
engine, session_maker = await db.get_or_create_db(app_config.database_path)
app.state.engine = engine
app.state.session_maker = session_maker
logger.info("Database connections cached in app state")

logger.info(f"Sync changes enabled: {app_config.sync_changes}")
if app_config.sync_changes:
Expand Down
7 changes: 5 additions & 2 deletions src/basic_memory/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@ class BasicMemoryConfig(BaseSettings):
description="Format for generated filenames. False preserves spaces and special chars, True converts them to hyphens for consistency with permalinks",
)

skip_initialization_sync: bool = Field(
default=False,
description="Skip expensive initialization synchronization. Useful for cloud/stateless deployments where project reconciliation is not needed.",
)

# API connection configuration
api_url: Optional[str] = Field(
default=None,
Expand Down Expand Up @@ -341,8 +346,6 @@ def save_basic_memory_config(file_path: Path, config: BasicMemoryConfig) -> None
logger.error(f"Failed to save config: {e}")




# setup logging to a single log file in user home directory
user_home = Path.home()
log_dir = user_home / DATA_DIR_NAME
Expand Down
21 changes: 18 additions & 3 deletions src/basic_memory/deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from typing import Annotated
from loguru import logger

from fastapi import Depends, HTTPException, Path, status
from fastapi import Depends, HTTPException, Path, status, Request
from sqlalchemy.ext.asyncio import (
AsyncSession,
AsyncEngine,
Expand Down Expand Up @@ -78,9 +78,24 @@ async def get_project_config(


async def get_engine_factory(
app_config: AppConfigDep,
request: Request,
) -> tuple[AsyncEngine, async_sessionmaker[AsyncSession]]: # pragma: no cover
"""Get engine and session maker."""
"""Get cached engine and session maker from app state.

For API requests, returns cached connections from app.state for optimal performance.
For non-API contexts (CLI), falls back to direct database connection.
"""
# Try to get cached connections from app state (API context)
if (
hasattr(request, "app")
and hasattr(request.app.state, "engine")
and hasattr(request.app.state, "session_maker")
):
return request.app.state.engine, request.app.state.session_maker

# Fallback for non-API contexts (CLI)
logger.debug("Using fallback database connection for non-API context")
app_config = get_app_config()
engine, session_maker = await db.get_or_create_db(app_config.database_path)
return engine, session_maker

Expand Down
1 change: 1 addition & 0 deletions src/basic_memory/mcp/tools/__init__.py
Loading
Loading