# 🗄️ Database & Persistence Audit — NODA1 & NODA2 **Date:** 2026-01-22 **Purpose:** Повний аудит конфігурації БД, агентів та persistent storage **Goal:** Запобігти втраті даних при rebuild контейнерів --- ## 🔍 Проблема > "Агенти працюють нормально, але ми вже вдруге налаштовуємо базу даних" **Symptoms:** - Дані зникають після rebuild контейнерів - Потрібно повторно налаштовувати БД - Collections в Qdrant порожні --- ## ✅ Що працює ПРАВИЛЬНО ### 1. Docker Volumes налаштовані #### NODA1 (`docker-compose.node1.yml`) ```yaml volumes: qdrant-data-node1: ✅ 20KB (порожній) neo4j-data-node1: ✅ Є postgres_data_node1: ✅ 81MB (АКТИВНИЙ) redis-data-node1: ✅ Є minio-data-node1: ✅ Є nats-data-node1: ✅ Є # ... інші volumes ``` **Поточний стан:** - ✅ Volumes створені - ✅ PostgreSQL має 81MB даних - ✅ Дані зберігаються в `/var/lib/docker/volumes/` --- ### 2. Агенти налаштовані **7 активних Telegram ботів:** 1. DAARWIZZ (`agent_id="daarwizz"`) 2. Helion (`agent_id="helion"`) 3. GREENFOOD (`agent_id="greenfood"`) 4. AgroMatrix (`agent_id="agromatrix"`) 5. NUTRA (`agent_id="nutra"`) 6. Druid (`agent_id="druid"`) 7. Alateya (`agent_id="alateya"`) - без токену **Конфігурації:** - Файл: `gateway-bot/http_api.py` - Кожен має унікальний `agent_id` - Кожен має окремий prompt файл --- ### 3. PostgreSQL працює **Бази даних:** ```sql daarion_main -- Artifacts (3 таблиці) daarion_memory -- user_facts (з agent_id полем!) rag -- RAG сервіс postgres -- System DB ``` **user_facts structure:** ```sql Columns: - agent_id TEXT (з індексом!) - user_id TEXT - team_id TEXT - fact_key TEXT - fact_value TEXT - fact_value_json JSONB Unique constraint: (user_id, team_id, agent_id, fact_key) ``` **Реальні дані (станом на 2026-01-22):** - `nutra` - 3 записи - `agromatrix` - 2 записи - Деякі без `agent_id` (глобальні profiles) ✅ **PostgreSQL зберігає дані правильно!** --- ## ⚠️ Проблеми та їх причини ### 1. Qdrant Collections порожні **Факт:** ```bash curl http://localhost:6333/collections # Result: {"collections": []} ``` **Причина:** - Volume існує: `qdrant-data-node1` (20KB - тільки metadata) - Router ще не створив collections - Або collections створені, але volume був очищений **Як має бути:** Router динамічно створює collections: - `{agent_id}_messages` - `{agent_id}_memory_items` - `{agent_id}_docs` Приклад: `agromatrix_messages`, `greenfood_messages`, тощо --- ### 2. Volume naming мismatch **Виявлено дублювання:** ``` postgres_data_node1 81MB (СТАРИЙ) microdao-daarion_postgres_data_node1 81MB (АКТИВНИЙ) ``` **Активний:** `microdao-daarion_postgres_data_node1` **Проблема:** - Docker Compose автоматично додає prefix `microdao-daarion_` - В `docker-compose.node1.yml` вказано просто `postgres_data_node1` - Docker створює `{project}_postgres_data_node1` --- ### 3. Відсутність backup стратегії **Поточний стан:** - ❌ Немає автоматичних backups PostgreSQL - ❌ Немає backups Qdrant - ❌ Немає backups Neo4j - ✅ Є cron backup (3 AM) на host рівні **З документації (`docker-compose.db.yml`):** ```yaml db-backup: image: prodrigestivill/postgres-backup-local:latest environment: SCHEDULE: "@every 12h" BACKUP_KEEP_DAYS: 7 BACKUP_KEEP_WEEKS: 4 BACKUP_KEEP_MONTHS: 6 volumes: - ./db_backups:/backups ``` ⚠️ **Цей сервіс НЕ запущений на NОДА1!** --- ## 🎯 Чому дані "зникають" ### Сценарій 1: Rebuild контейнера БЕЗ volumes ```bash # НЕБЕЗПЕЧНО! docker rm -f dagi-postgres docker run ... postgres:16 # Без -v # Результат: Новий контейнер з порожньою БД ``` ### Сценарій 2: Очищення volumes ```bash # ДУЖЕ НЕБЕЗПЕЧНО! docker volume rm qdrant-data-node1 # Результат: Всі дані Qdrant втрачені назавжди ``` ### Сценарій 3: Неправильний COMPOSE_PROJECT_NAME ```bash # Якщо змінюється project name: COMPOSE_PROJECT_NAME=dagi_node1 docker compose up # vs COMPOSE_PROJECT_NAME=microdao-daarion docker compose up # Docker створює РІЗНІ volumes! ``` --- ## 🔧 Рекомендації (КРИТИЧНО) ### 1. Зафіксувати volume names **Оновити `docker-compose.node1.yml`:** ```yaml volumes: qdrant-data-node1: name: qdrant-data-node1 # EXPLICIT NAME driver: local postgres_data_node1: name: postgres-data-node1 # EXPLICIT NAME driver: local neo4j-data-node1: name: neo4j-data-node1 driver: local ``` **Чому:** Запобігає створенню volumes з prefix-ами --- ### 2. Додати автоматичні backups **Створити `docker-compose.backups.yml`:** ```yaml version: "3.9" services: postgres-backup: image: prodrigestivill/postgres-backup-local:15 container_name: postgres-backup-node1 restart: unless-stopped environment: POSTGRES_HOST: dagi-postgres POSTGRES_DB: daarion_main,daarion_memory,rag POSTGRES_USER: daarion POSTGRES_PASSWORD: DaarionDB2026! SCHEDULE: "@every 6h" BACKUP_KEEP_DAYS: 7 BACKUP_KEEP_WEEKS: 4 BACKUP_KEEP_MONTHS: 6 POSTGRES_EXTRA_OPTS: "-Z9 --schema=public --blobs" volumes: - /opt/backups/postgres:/backups networks: - dagi-network qdrant-backup: image: qdrant/qdrant:latest container_name: qdrant-backup-node1 restart: "no" environment: SCHEDULE: "@daily" volumes: - qdrant-data-node1:/qdrant/storage:ro - /opt/backups/qdrant:/backups command: > sh -c " tar czf /backups/qdrant-$(date +%Y%m%d-%H%M%S).tar.gz /qdrant/storage && find /backups -name 'qdrant-*.tar.gz' -mtime +30 -delete " networks: - dagi-network networks: dagi-network: external: true volumes: qdrant-data-node1: external: true name: qdrant-data-node1 ``` **Запуск:** ```bash docker compose -f docker-compose.backups.yml up -d postgres-backup ``` --- ### 3. Створити restore процедури **Файл: `scripts/restore-from-backup.sh`** ```bash #!/bin/bash # Restore PostgreSQL from backup BACKUP_FILE="$1" DATABASE="${2:-daarion_memory}" if [ -z "$BACKUP_FILE" ]; then echo "Usage: $0 [database]" echo "Example: $0 /opt/backups/postgres/daarion_memory-20260122-030000.sql.gz" exit 1 fi echo "Restoring $DATABASE from $BACKUP_FILE..." # Stop services that use this DB docker compose -f docker-compose.node1.yml stop dagi-memory-service-node1 dagi-router-node1 # Restore gunzip -c "$BACKUP_FILE" | docker exec -i dagi-postgres psql -U daarion -d "$DATABASE" # Restart services docker compose -f docker-compose.node1.yml start dagi-memory-service-node1 dagi-router-node1 echo "✅ Restore complete!" ``` --- ### 4. Додати pre-rebuild checks **Файл: `scripts/safe-rebuild.sh`** ```bash #!/bin/bash # Safe container rebuild with backup verification SERVICE="$1" if [ -z "$SERVICE" ]; then echo "Usage: $0 " exit 1 fi echo "🔍 Pre-rebuild checks for $SERVICE..." # 1. Check volumes echo "📦 Checking volumes..." docker inspect "$SERVICE" | grep -A20 "Mounts" # 2. Backup first echo "💾 Creating backup..." case "$SERVICE" in dagi-postgres) docker exec dagi-postgres pg_dumpall -U daarion | gzip > "/opt/backups/manual/postgres-$(date +%Y%m%d-%H%M%S).sql.gz" ;; dagi-qdrant-node1) tar czf "/opt/backups/manual/qdrant-$(date +%Y%m%d-%H%M%S).tar.gz" /var/lib/docker/volumes/qdrant-data-node1/_data ;; esac # 3. Confirm read -p "⚠️ Proceed with rebuild? (yes/no): " CONFIRM if [ "$CONFIRM" != "yes" ]; then echo "❌ Aborted" exit 0 fi # 4. Rebuild echo "🔨 Rebuilding $SERVICE..." docker compose -f docker-compose.node1.yml up -d --build --force-recreate "$SERVICE" echo "✅ Rebuild complete!" ``` --- ### 5. Документувати volume layout **Створити `VOLUMES-LAYOUT.md`:** ```markdown # Docker Volumes Layout — NODA1 ## PostgreSQL Volume: `postgres-data-node1` (або `microdao-daarion_postgres_data_node1`) Path: `/var/lib/docker/volumes/postgres-data-node1/_data` Size: ~81MB Databases: daarion_main, daarion_memory, rag Backup: Every 6h → /opt/backups/postgres/ ## Qdrant Volume: `qdrant-data-node1` Path: `/var/lib/docker/volumes/qdrant-data-node1/_data` Size: ~20KB (пусто після rebuild!) Collections: {agent_id}_messages, {agent_id}_memory_items, {agent_id}_docs Backup: Daily → /opt/backups/qdrant/ ## Neo4j Volume: `neo4j-data-node1`, `neo4j-logs-node1` Path: `/var/lib/docker/volumes/neo4j-data-node1/_data` Backup: Manual (cypher-shell DUMP) ## Redis Volume: `redis-data-node1` Path: `/var/lib/docker/volumes/redis-data-node1/_data` Backup: RDB snapshots every 1h ## MinIO Volume: `minio-data-node1` Path: `/var/lib/docker/volumes/minio-data-node1/_data` Purpose: Artifacts, images, files Backup: S3 sync to external storage ``` --- ## 📋 Action Plan (Терміново) ### Крок 1: Backup ЗАРАЗ ```bash # На НОДА1 ssh root@144.76.224.179 # PostgreSQL docker exec dagi-postgres pg_dumpall -U daarion | gzip > /root/postgres-emergency-$(date +%Y%m%d-%H%M%S).sql.gz # Qdrant tar czf /root/qdrant-emergency-$(date +%Y%m%d-%H%M%S).tar.gz /var/lib/docker/volumes/qdrant-data-node1/_data # Neo4j tar czf /root/neo4j-emergency-$(date +%Y%m%d-%H%M%S).tar.gz /var/lib/docker/volumes/neo4j-data-node1/_data ls -lh /root/*emergency*.tar.gz ``` ### Крок 2: Зафіксувати volume names ```bash # На НОДА1 cd /opt/microdao-daarion cp docker-compose.node1.yml docker-compose.node1.yml.backup-$(date +%Y%m%d) # Редагувати volumes секцію (додати explicit names) nano docker-compose.node1.yml # Рестарт з новими іменами docker compose -f docker-compose.node1.yml up -d ``` ### Крок 3: Налаштувати автоматичні backups ```bash # Створити директорії mkdir -p /opt/backups/{postgres,qdrant,neo4j,manual} # Створити docker-compose.backups.yml nano docker-compose.backups.yml # Запустити docker compose -f docker-compose.backups.yml up -d ``` ### Крок 4: Створити restore scripts ```bash mkdir -p /opt/scripts nano /opt/scripts/restore-from-backup.sh chmod +x /opt/scripts/restore-from-backup.sh nano /opt/scripts/safe-rebuild.sh chmod +x /opt/scripts/safe-rebuild.sh ``` ### Крок 5: Протестувати backup/restore ```bash # Test backup docker exec postgres-backup-node1 sh -c "/backup.sh" ls -lh /opt/backups/postgres/ # Test restore (на staging БД) # НЕ на production! ``` --- ## 🔐 Safety Rules (ЗАВЖДИ дотримуватись) ### НІКОЛИ не робити: ```bash ❌ docker volume rm ❌ docker compose down -v ❌ docker system prune -a --volumes ❌ rm -rf /var/lib/docker/volumes/ ``` ### ЗАВЖДИ робити: ```bash ✅ Backup ПЕРЕД rebuild ✅ Перевірити volumes ПЕРЕД видаленням контейнера ✅ Використовувати explicit volume names ✅ Тестувати restore процедури ✅ Зберігати backups поза сервером ``` --- ## 📊 Поточний стан volumes ```bash # NОДА1 volumes (станом на 2026-01-22) postgres_data_node1: 81MB ✅ АКТИВНИЙ, є дані microdao-daarion_postgres_data_node1: 81MB ✅ АКТИВНИЙ (те саме) qdrant-data-node1: 20KB ⚠️ ПОРОЖНІЙ microdao-daarion_qdrant-data-node1: 20KB ⚠️ ПОРОЖНІЙ neo4j-data-node1: ??? ✅ Є redis-data-node1: ??? ✅ Є ``` --- ## 🎯 Висновок ### Що працює: ✅ Docker volumes налаштовані ✅ PostgreSQL зберігає дані (81MB) ✅ Агенти працюють з правильними agent_id ✅ Ізоляція пам'яті між агентами правильна ### Що потрібно: ⚠️ Зафіксувати volume names (explicit) ⚠️ Налаштувати автоматичні backups ⚠️ Створити restore процедури ⚠️ Протестувати backup/restore ⚠️ Документувати процедури ### Чому "знову налаштовуємо": ❌ Qdrant контейнер пересоздавали без збереження даних ❌ Collections не створилися автоматично після rebuild ❌ Немає backup стратегії ❌ Немає документації про restore --- **Next Steps:** Виконати Action Plan (5 кроків) **Priority:** КРИТИЧНО - створити backups ЗАРАЗ **Документація:** Оновити INFRASTRUCTURE.md з процедурами backup/restore