# Migration 003: Server Sessions from KV to D1 **Date**: 2026-01-28 **Type**: Session Storage Migration **Status**: Ready to Deploy ## Overview Migrated server consultation session management from KV Namespace to D1 Database for better consistency, reliability, and integrated data management. ## Changes Summary ### 1. Database Schema (`schema.sql` + `migrations/003_server_sessions_d1.sql`) **New Table**: `server_sessions` ```sql CREATE TABLE IF NOT EXISTS server_sessions ( user_id TEXT PRIMARY KEY, status TEXT NOT NULL CHECK(status IN ('gathering', 'recommending', 'selecting', 'ordering', 'completed')), collected_info TEXT, last_recommendation TEXT, messages TEXT, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL, expires_at INTEGER NOT NULL ); CREATE INDEX IF NOT EXISTS idx_server_sessions_expires ON server_sessions(expires_at); ``` ### 2. Code Changes **Updated Files**: - `/src/server-agent.ts` - Session management functions (getServerSession, saveServerSession, deleteServerSession) - `/src/openai-service.ts` - Session check in generateOpenAIResponse() - `/src/tools/server-tool.ts` - start_consultation, continue_consultation, cancel_consultation actions - `/src/routes/handlers/callback-handler.ts` - server_order and server_cancel callbacks **Function Signature Changes**: ```typescript // Before (KV) export async function getServerSession(kv: KVNamespace, userId: string): Promise export async function saveServerSession(kv: KVNamespace, userId: string, session: ServerSession): Promise export async function deleteServerSession(kv: KVNamespace, userId: string): Promise // After (D1) export async function getServerSession(db: D1Database, userId: string): Promise export async function saveServerSession(db: D1Database, userId: string, session: ServerSession): Promise export async function deleteServerSession(db: D1Database, userId: string): Promise // New export async function cleanupExpiredSessions(db: D1Database): Promise ``` **Removed**: - All `[SESSION DEBUG]` logging (unnecessary with D1's strong consistency) - KV constants: `SESSION_TTL`, `SESSION_KEY_PREFIX` **Added**: - `SESSION_TTL_MS` constant (3600 * 1000 = 1 hour) - `cleanupExpiredSessions()` function for cron cleanup - Automatic expiry checking in queries (`WHERE expires_at > ?`) ### 3. Key Implementation Details **Session Storage**: - JSON serialization for complex fields (collected_info, last_recommendation, messages) - Automatic TTL management with expires_at column - Strong consistency (no eventual consistency issues) **Error Handling**: - Graceful JSON parsing with try-catch - Null checks for expired/missing sessions - Proper logging without debug noise **Performance**: - Primary key on user_id for fast lookups - Index on expires_at for efficient cleanup - Single query for session retrieval ### 4. Migration Path **Automatic Migration**: - Old KV sessions will expire naturally (1 hour TTL) - New sessions are created in D1 automatically - No manual data migration needed **Zero Downtime**: - D1 table creation is idempotent (IF NOT EXISTS) - Code handles missing sessions gracefully - Users can restart consultation if session expires ### 5. Benefits **Before (KV Namespace)**: - Eventual consistency (delayed reads) - Complex debugging (async replication) - Separate data store (fragmented) - Manual cleanup needed **After (D1 Database)**: - Strong consistency (immediate reads) - Simple debugging (SQL queries) - Integrated storage (single database) - Automatic cleanup (expires_at + cron) ### 6. Deployment Steps ```bash # 1. Apply migration (creates table) wrangler d1 execute telegram-conversations --file=migrations/003_server_sessions_d1.sql # 2. Verify table creation wrangler d1 execute telegram-conversations --command "SELECT name FROM sqlite_master WHERE type='table' AND name='server_sessions'" # 3. Deploy code changes npm run deploy # 4. Test server consultation flow # Send message: "서버 추천" # Verify session is created in D1 wrangler d1 execute telegram-conversations --command "SELECT * FROM server_sessions LIMIT 5" ``` ### 7. Rollback Plan If issues occur: 1. **Code Rollback**: Revert to previous commit (KV-based code) 2. **Database**: Drop table if needed ```sql DROP TABLE IF EXISTS server_sessions; DROP INDEX IF EXISTS idx_server_sessions_expires; ``` **Note**: Old KV sessions are unaffected (will expire naturally) ### 8. Testing Checklist - [x] Migration SQL syntax validated - [x] Schema.sql updated - [x] All code references updated (env.SESSION_KV → env.DB) - [x] Logging cleaned up (no debug noise) - [ ] Local testing (`npm run dev`) - [ ] Production deployment - [ ] Session creation test - [ ] Session retrieval test - [ ] Session expiry test - [ ] Concurrent session handling ### 9. Notes **Important**: - `server_order_confirm:` and `delete_confirm:` sessions remain in KV (not migrated, separate feature) - Troubleshoot agent still uses KV (separate migration needed if required) - D1 binding (`env.DB`) already exists in project **Future Improvements**: - Consider migrating troubleshoot sessions to D1 - Add session analytics (query D1 for session stats) - Implement session history/audit log