4.2 KiB
4.2 KiB
Postgres + pgvector migration — planning questions
Status: awaiting input from Travis before scoping the migration.
Trigger: user pivoted away from SQLite — wants to use the existing Postgres + pgvector instance, plus the shared /projects/shared/python/embedding_chunking module.
1. Connection / secrets
- Host, port, database name, user
- Where does the connection string live? Options:
- env var (e.g.
SECOND_BRAIN_DB_URL) config/settings.toml(would need a.local.tomloverride so the password doesn't get committed).pgpass- other
- env var (e.g.
- Is the Postgres instance on
herbydev, the NAS, or elsewhere? Tailscale-only or LAN-reachable? - Driver preference:
psycopgv3 sync (default suggestion — pipeline + scheduler are sync today)asyncpg(only worth it if we also rewrite the web layer to async)
2. Schema layout
- Same Postgres instance as
vault-mcp'spetalbrain.wiki? Yes / No - If yes — same database with a dedicated schema (e.g.
second_brain.sources,second_brain.extractions)? Or its own database entirely? - Naming conventions to match:
- snake_case tables — assumed yes
created_at/updated_atastimestamptz(vs current naive UTC) — preference?- PK type:
bigserial/bigint identity/ UUID?
- Should second-brain integrate with
vault-mcpat all?- Option A: standalone — second-brain owns its tables, the wiki compiler still writes Obsidian markdown files
- Option B: dual-write — published extractions land in
petalbrain.wikiviavault-mcpso they show up in the unified wiki - Option C: replace the file-based compiler entirely with
vault-mcpwrites
3. Embeddings
- Public API of
/projects/shared/python/embedding_chunking:- function signatures
- what it returns (raw text chunks? chunks + vectors? chunker only, embeddings done separately?)
- embedding model + vector dimension (drives the
vector(N)column type) - any batching / rate-limit behavior we need to respect
- Where should embeddings attach?
- (a) on
Extractionrows — semantic search over summaries / key_points / claims - (b) on chunked transcripts — chunk-level retrieval for "find the part of the video that says X"
- (c) both (separate tables:
extraction_embeddings+transcript_chunks)
- (a) on
- pgvector index strategy:
- HNSW — better recall, slower build, recommended default for personal-scale corpus
- IVFFlat — faster build, needs periodic
ANALYZE+ a meaningfullistsvalue
- Distance metric: cosine (typical), L2, or inner product?
4. Migration tooling
- Schema is small and not yet in production. Cleanest path:
- SQLAlchemy
Base.metadata.create_all()against Postgres - one-off import script to move existing SQLite rows over (if any are worth keeping)
- SQLAlchemy
- Alternative: stand up Alembic now while the cost is low. Pays off the first time you
ALTERa table in anger; costs ~1 session of setup. - Travis's call — defaulting to
create_all+ import script unless you say otherwise.
5. Pooling
- Three consumers share the DB: CLI (
process,compile), the FastAPI web server, the scheduler. - Default plan: SQLAlchemy
QueuePoolwithpool_size=5, max_overflow=5. - Is PgBouncer already in front of this instance? If yes — switch to
NullPoolon our side and let PgBouncer pool.
6. Other open items (carryover from prior session)
- Vault location decision: I recommended
/opt/projects/second-brain-vault/as a peer dir. Not yet confirmed. - NAS mount: is it mounted on
herbydev, and at what path? Relevant if media (downloaded videos, transcripts) should live there rather than on the herbydev SSD.
What I will produce once these are answered
- Updated
src/second_brain/database.pywith a Postgres engine + URL resolution - Schema migration: new tables for embeddings (and chunks if we go with option (c))
- Wiring of
embedding_chunkinginto the extractor (or into a post-extraction step in the scheduler) config/settings.toml.exampleupdates with the new[database]and[embeddings]blocks- README / CLAUDE.md updates noting the Postgres dependency
- One-shot SQLite → Postgres import script (only if there are rows worth keeping)