Skip to content

Commit 183ac57

Browse files
committed
fix(operator): two-phase migrations — cold SQL then warm AGE/graph
Split migrations into two phases: - schema/migrations/ — standard SQL (tables, indexes, permissions) - schema/migrations-warm/ — AGE/Cypher DDL (needs running graph engine) Move 058 (precreate_graph_labels) to migrations-warm. After cold migrations, restart postgres so AGE is fully initialized, verify it's healthy, then apply warm migrations. Also: - Migration runner continues past failures instead of stopping - configure.py ai-provider handles missing catalog table gracefully - Runner supports --warm flag to select migration directory
1 parent 6dd7b9e commit 183ac57

4 files changed

Lines changed: 59 additions & 15 deletions

File tree

operator/configure.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -143,16 +143,20 @@ def cmd_ai_provider(self, args):
143143
print(f"📝 Current: {current['provider']} / {current['model_name']}")
144144

145145
# Validate model exists in catalog (ADR-800) — warn but don't block
146-
cur.execute(
147-
"""SELECT id, enabled FROM kg_api.provider_model_catalog
148-
WHERE provider = %s AND model_id = %s AND category = 'extraction'""",
149-
(provider, model),
150-
)
151-
catalog_row = cur.fetchone()
152-
if catalog_row is None:
153-
print(f"⚠️ Model '{model}' not in catalog for {provider}. Run: models refresh {provider}")
154-
elif not catalog_row['enabled']:
155-
print(f"⚠️ Model '{model}' is in catalog but not enabled. Run: models enable {catalog_row['id']}")
146+
try:
147+
cur.execute(
148+
"""SELECT id, enabled FROM kg_api.provider_model_catalog
149+
WHERE provider = %s AND model_id = %s AND category = 'extraction'""",
150+
(provider, model),
151+
)
152+
catalog_row = cur.fetchone()
153+
if catalog_row is None:
154+
print(f"⚠️ Model '{model}' not in catalog for {provider}. Run: models refresh {provider}")
155+
elif not catalog_row['enabled']:
156+
print(f"⚠️ Model '{model}' is in catalog but not enabled. Run: models enable {catalog_row['id']}")
157+
except Exception:
158+
# Table may not exist yet (migrations pending)
159+
conn.rollback()
156160

157161
# Insert/update configuration
158162
cur.execute(

operator/database/migrate-db.sh

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ fi
6060
DRY_RUN=false
6161
AUTO_CONFIRM=false
6262
VERBOSE=false
63+
WARM_MODE=false
6364

6465
for arg in "$@"; do
6566
case $arg in
@@ -72,20 +73,25 @@ for arg in "$@"; do
7273
-v|--verbose)
7374
VERBOSE=true
7475
;;
76+
--warm)
77+
WARM_MODE=true
78+
;;
7579
-h|--help)
7680
echo "Usage: $0 [OPTIONS]"
7781
echo ""
7882
echo "Options:"
7983
echo " --dry-run Show what would be applied without making changes"
8084
echo " -y, --yes Skip confirmation prompt"
8185
echo " -v, --verbose Show detailed migration SQL"
86+
echo " --warm Apply warm migrations (require running AGE/graph engine)"
8287
echo " -h, --help Show this help message"
8388
echo ""
8489
echo "Examples:"
8590
echo " $0 # Interactive mode with confirmation"
8691
echo " $0 --dry-run # Preview pending migrations"
8792
echo " $0 -y # Apply migrations without confirmation"
8893
echo " $0 -y --verbose # Apply with detailed output"
94+
echo " $0 -y --warm # Apply warm migrations (after app start)"
8995
exit 0
9096
;;
9197
*)
@@ -96,6 +102,11 @@ for arg in "$@"; do
96102
esac
97103
done
98104

105+
# Select migration directory based on mode
106+
if [ "$WARM_MODE" = true ]; then
107+
MIGRATIONS_DIR="schema/migrations-warm"
108+
fi
109+
99110
# Header
100111
if [ "$DRY_RUN" = true ]; then
101112
echo -e "${CYAN}📦 Database Migration Runner (DRY RUN)${NC}"
@@ -296,13 +307,15 @@ for pending in "${PENDING_MIGRATIONS[@]}"; do
296307
FAILED_COUNT=$((FAILED_COUNT + 1))
297308

298309
# Show error details
299-
echo -e "${RED}Error details:${NC}"
310+
echo -e "${YELLOW}Error details:${NC}"
300311
echo "$MIGRATION_OUTPUT" | grep -i "ERROR:\|ROLLBACK\|LINE" | sed 's/^/ /'
301312

302-
echo ""
303-
echo -e "${RED}✗ Migration $VERSION failed - stopping${NC}"
304-
echo -e "${YELLOW}Fix the migration and try again${NC}"
305-
exit 1
313+
# Continue to next migration instead of stopping.
314+
# Failed migration won't be recorded (ON_ERROR_STOP prevents the
315+
# INSERT INTO schema_migrations from running), so it will be
316+
# retried on the next run when the database may be fully ready.
317+
echo -e "${YELLOW} ⚠️ Skipping — will retry on next migration run${NC}"
318+
continue
306319
fi
307320

308321
echo -e "${GREEN} ✅ Migration $VERSION applied successfully${NC}"

operator/lib/start-infra.sh

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,21 @@ else
124124
fi
125125
cd "$DOCKER_DIR"
126126

127+
# Restart postgres so AGE extension is fully initialized before warm migrations
128+
echo -e "${BLUE} Restarting PostgreSQL for AGE initialization...${NC}"
129+
docker restart $POSTGRES_CONTAINER >/dev/null 2>&1
130+
for i in {1..15}; do
131+
if docker exec $POSTGRES_CONTAINER psql -U ${POSTGRES_USER:-admin} -d ${POSTGRES_DB:-knowledge_graph} -tAc "SELECT 1" >/dev/null 2>&1; then
132+
echo -e "${GREEN}✓ PostgreSQL restarted${NC}"
133+
break
134+
fi
135+
if [ $i -eq 15 ]; then
136+
echo -e "${RED}✗ PostgreSQL did not come back after restart${NC}"
137+
exit 1
138+
fi
139+
sleep 2
140+
done
141+
127142
# Check AGE extension
128143
AGE_EXT=$(docker exec $POSTGRES_CONTAINER psql -U ${POSTGRES_USER:-admin} -d ${POSTGRES_DB:-knowledge_graph} -tAc "SELECT COUNT(*) FROM pg_extension WHERE extname='age'" 2>/dev/null || echo "0")
129144
if [ "$AGE_EXT" -gt 0 ]; then
@@ -132,6 +147,18 @@ else
132147
echo -e "${RED}✗ AGE extension not found${NC}"
133148
fi
134149

150+
# Apply warm migrations (require running AGE/graph engine)
151+
cd "$PROJECT_ROOT"
152+
if [ -f "$PROJECT_ROOT/operator/database/migrate-db.sh" ] && [ -d "schema/migrations-warm" ]; then
153+
WARM_COUNT=$(ls schema/migrations-warm/*.sql 2>/dev/null | wc -l)
154+
if [ "$WARM_COUNT" -gt 0 ]; then
155+
echo -e "${BLUE} Applying warm migrations (AGE/graph)...${NC}"
156+
"$PROJECT_ROOT/operator/database/migrate-db.sh" -y --warm 2>&1 | grep -E "✓|✅|→|⚠️|✗" || true
157+
echo -e "${GREEN}✓ Warm migrations applied${NC}"
158+
fi
159+
fi
160+
cd "$DOCKER_DIR"
161+
135162
# Show applied migrations
136163
MIGRATION_LIST=$(docker exec $POSTGRES_CONTAINER psql -U ${POSTGRES_USER:-admin} -d ${POSTGRES_DB:-knowledge_graph} -tAc "SELECT version || ' - ' || name FROM public.schema_migrations ORDER BY version" 2>/dev/null || echo "")
137164

File renamed without changes.

0 commit comments

Comments
 (0)