feat: add local SQLite cache with background sync and HA deployment

- Add SyncEngine for background sync between local SQLite cache and
  remote API with pending_ops queue for offline resilience
- Refactor MCP server to support three modes: SQLite-only, hybrid
  (local cache + sync, new default), and HTTP-only (legacy)
- Add GET /api/memories/sync endpoint for incremental sync
- Change DELETE to soft delete (set deleted_at) for sync support
- Add deleted_at IS NULL filters to all read queries
- Scale API deployment to 2 replicas with pod anti-affinity, PDB,
  and startup probe for high availability
- Add migration 003 for deleted_at column and updated_at index
- Add comprehensive tests for sync engine and API sync endpoint
This commit is contained in:
Viktor Barzin 2026-03-14 12:42:39 +00:00
parent fe55ac634b
commit cd80a67dfa
No known key found for this signature in database
GPG key ID: 0EB088298288D958
7 changed files with 1133 additions and 110 deletions

View file

@ -0,0 +1,40 @@
"""Add soft delete and sync support.
Revision ID: 003
Revises: 002
Create Date: 2026-03-14
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
revision: str = "003"
down_revision: Union[str, None] = "002"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def _column_exists(conn, column_name: str) -> bool:
result = conn.execute(
sa.text(
"SELECT EXISTS(SELECT 1 FROM information_schema.columns "
"WHERE table_name = 'memories' AND column_name = :col)"
),
{"col": column_name},
)
return result.scalar()
def upgrade() -> None:
conn = op.get_bind()
if not _column_exists(conn, "deleted_at"):
op.add_column("memories", sa.Column("deleted_at", sa.DateTime(timezone=True), nullable=True))
op.execute("CREATE INDEX IF NOT EXISTS idx_memories_updated ON memories(updated_at)")
def downgrade() -> None:
op.drop_index("idx_memories_updated")
op.drop_column("memories", "deleted_at")