145 lines
4.9 KiB
Markdown
145 lines
4.9 KiB
Markdown
|
|
# Sesja 2026-06-25 — KB etap 2: bulk import Gmail uruchomiony
|
|||
|
|
|
|||
|
|
## Cel
|
|||
|
|
|
|||
|
|
Domknięcie etapu 2 bazy wiedzy. Poprzednia sesja (2026-06-24) zostawiła kod gotowy,
|
|||
|
|
ale dane na SOLARIA. Ta sesja = transfer + uruchomienie + weryfikacja na produkcji (PIHA).
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Co zrobione
|
|||
|
|
|
|||
|
|
### Relokacja kb-postgres SOLARIA→PIHA — domknięta i zweryfikowana
|
|||
|
|
|
|||
|
|
Baza stoi na PIHA (always-on); zapytania KB działają 24/7 niezależnie od SOLARIA.
|
|||
|
|
|
|||
|
|
### Przygotowanie PIHA pod import
|
|||
|
|
|
|||
|
|
- **cgroup memory enable**: `cgroup_enable=memory cgroup_memory=1` dopisane do
|
|||
|
|
`/boot/firmware/cmdline.txt` + reboot; backup w `cmdline.txt.bak`.
|
|||
|
|
- **Swap 4 GB**: wpisany do `/etc/fstab` (był już skonfigurowany wcześniej, weryfikacja).
|
|||
|
|
- **SSH key perms**: `chmod 600 ~/.ssh/id_rsa` — prawa `0640` blokowały `git fetch`
|
|||
|
|
(OpenSSH odrzuca klucze `group-readable`).
|
|||
|
|
|
|||
|
|
### Transfer Takeout SOLARIA→PIHA
|
|||
|
|
|
|||
|
|
`rsync` po LAN (nie Tailscale, oba dostępne): 15 GB zip → po rozpakowaniu
|
|||
|
|
**27 GB `allmail.mbox`** wylądowało w `~/kb/dropbox/`.
|
|||
|
|
`venv` pod PEP668 w `~/kb/venv` (Pi OS wymusza brak systemowych pip-instalacji).
|
|||
|
|
|
|||
|
|
### Walidacja przed pełnym wlewem
|
|||
|
|
|
|||
|
|
1. `--dry-run --limit 200` — parsowanie bez zapisów, weryfikacja nagłówków.
|
|||
|
|
2. `--limit 1000` z realnym zapisem — próbka; potwierdzono że RAM stoi płasko:
|
|||
|
|
zużycie to koszt zbudowania indeksu `mailbox.mbox` przy otwarciu, nie akumulacja
|
|||
|
|
per-mail w pętli.
|
|||
|
|
|
|||
|
|
### Pełny import
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
nohup nice -n 15 ionice -c2 -n7 \
|
|||
|
|
gmail-bulk-import \
|
|||
|
|
--mbox ~/kb/dropbox/allmail.mbox \
|
|||
|
|
--archive ~/kb/mail/archive \
|
|||
|
|
--dsn postgresql://kb:<pw>@localhost:5433/kb \
|
|||
|
|
>> ~/kb/import.log 2>&1 &
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Czas: **~29 minut**. Brak OOM, swap stabilny przez cały przebieg.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Wynik importu
|
|||
|
|
|
|||
|
|
| Metryka | Wartość |
|
|||
|
|
|---------|---------|
|
|||
|
|
| processed | 226 318 |
|
|||
|
|
| imported (pliki .eml) | 225 057 |
|
|||
|
|
| skipped (duplikaty Message-ID) | 1 260 |
|
|||
|
|
| errors (zepsute maile) | 1 |
|
|||
|
|
| epoch_fallback (brak daty) | 2 559 |
|
|||
|
|
| msgs_with_attachments | 32 495 |
|
|||
|
|
| total_attachments | 70 193 |
|
|||
|
|
| total_attachment_bytes | 15,4 GB |
|
|||
|
|
|
|||
|
|
**Baza (envelope):** `SELECT count(DISTINCT id) FROM envelope` → **225 030** unikalnych kopert.
|
|||
|
|
**Archiwum:** 225 057 plików `.eml`, **27 GB na NVMe PIHA** (`~/kb/mail/archive`).
|
|||
|
|
**Zakres dat:** 2002→2026-06-19; pik 2011–2012 (~23 k/rok); bez dziur.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Lekcje
|
|||
|
|
|
|||
|
|
### `imported` ≠ wiersze w DB — NORMA, nie bug
|
|||
|
|
|
|||
|
|
Importer liczy `imported` jako zapisane pliki `.eml`.
|
|||
|
|
`count(DISTINCT id)` w `envelope` jest mniejsze o duplikaty Message-ID:
|
|||
|
|
`ON CONFLICT (id) DO NOTHING` je poprawnie odrzuca.
|
|||
|
|
|
|||
|
|
Różnica: 225 057 archiwum − 225 030 DB = **27 duplikatów klucza** — zero utraty danych.
|
|||
|
|
|
|||
|
|
Zmarnowano ~30 minut na re-run całego mboxa szukając tych „27", zanim przeczytano kod
|
|||
|
|
importera. **Reguła: verify-before-fix** — najpierw sprawdź co kod faktycznie robi,
|
|||
|
|
zanim zaczniesz pisać fixa.
|
|||
|
|
|
|||
|
|
### `mailbox.mbox` buduje indeks przy każdym otwarciu
|
|||
|
|
|
|||
|
|
`python mailbox.mbox` skanuje całe 27 GB przed pierwszą wiadomością.
|
|||
|
|
Na Pi5 to kilka minut startu — **nie zawis, nie błąd**. Re-run płaci ten koszt w całości
|
|||
|
|
(wszystkie 226 k wiadomości przelatują ponownie przez idempotencję).
|
|||
|
|
Jeśli trzeba dosłać kilka kopert, taniej będzie osobny skrypt niż re-run mboxa.
|
|||
|
|
|
|||
|
|
### RAM/swap stabilne
|
|||
|
|
|
|||
|
|
Swap ~47% podczas importu = stałe tło Home Assistant, nie skok od importera.
|
|||
|
|
Hard `mem_limit: 1g` na kb-postgres chronił HA przez cały przebieg.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Sprzątanie po imporcie
|
|||
|
|
|
|||
|
|
- `allmail.mbox` + oryginalny zip skasowane z `~/kb/dropbox/` na PIHA → odzysk **~42 GB NVMe**.
|
|||
|
|
- Oryginalny zip Takeout (15 GB) przeniesiony na SOLARIA `~/kb/_cold/` jako cold backup
|
|||
|
|
(do końca fazy 2, potem do skasowania).
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Stan etapu 2
|
|||
|
|
|
|||
|
|
**ZAMKNIĘTY.**
|
|||
|
|
|
|||
|
|
Pełna historia Gmaila 2002→2026 w bazie wiedzy:
|
|||
|
|
- **225 030** unikalnych kopert w `kb-postgres` (PIHA)
|
|||
|
|
- **27 GB** archiwum `.eml` (PIHA NVMe)
|
|||
|
|
- manifest **70 193** załączników w `entities[]` (ekstrakcja/OCR = faza 2)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Backlog / następne kroki
|
|||
|
|
|
|||
|
|
- **Faza 2 załączników**: ekstrakcja i OCR (PDF/skany → Paperless lub dedykowany job).
|
|||
|
|
- **Odzysk 2559 dat epoch-fallback**: nagłówki `Received:` / `X-GM-RECEIVED` mogą dać
|
|||
|
|
prawdziwe timestamps dla maili bez `Date:`.
|
|||
|
|
- **Etap 3**: Fastmail JMAP live ingest → `jobs/fastmail-poller/`.
|
|||
|
|
- **Etap 4**: Gmail IMAP live sync → `jobs/gmail-imap-poller/`.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Higiena git
|
|||
|
|
|
|||
|
|
Praca w worktree `task/kb-session-0625`; główny checkout `~/homelab-codex-ws` czysty.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Commits tej sesji
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
c0ffb6a docs(kb): sesja 2026-06-22 — spine relokowany na PIHA + przygotowanie hosta
|
|||
|
|
c32e050 docs(kb): sesja 2026-06-24 — importer Gmail gotowy + konwencja jobs/
|
|||
|
|
254a680 fix(piha): capabilities — realny RAM/NVMe + gitignore build dirs
|
|||
|
|
f234280 refactor(kb-mail): importer Gmail — entities załączników, --limit, batch, bez Dockera, DSN→PIHA
|
|||
|
|
6dc1d32 feat(kb-mail): etap 2 — jednorazowy bulk importer Gmail (mbox → archiwum)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
(Commitów implementacyjnych w tej sesji brak — dokumentacja domknięcia etapu 2.)
|