sethmcknight commited on
Commit
45cf08e
·
1 Parent(s): 7e43525

refactor: enhance gunicorn startup script with health checks and config handling

Browse files
Files changed (3) hide show
  1. Dockerfile +1 -0
  2. POSTGRES_MIGRATION.md +51 -13
  3. run.sh +35 -9
Dockerfile CHANGED
@@ -25,6 +25,7 @@ COPY src ./src
25
  COPY data ./data
26
  COPY scripts ./scripts
27
  COPY run.sh ./run.sh
 
28
 
29
  RUN chmod +x run.sh && chmod +x scripts/init_pgvector.py || true
30
 
 
25
  COPY data ./data
26
  COPY scripts ./scripts
27
  COPY run.sh ./run.sh
28
+ COPY gunicorn.conf.py ./gunicorn.conf.py
29
 
30
  RUN chmod +x run.sh && chmod +x scripts/init_pgvector.py || true
31
 
POSTGRES_MIGRATION.md CHANGED
@@ -1,11 +1,13 @@
1
  # PostgreSQL Migration Guide
2
 
3
  ## Overview
 
4
  This branch implements PostgreSQL with pgvector as an alternative to ChromaDB for vector storage. This reduces memory usage from 400MB+ to ~50-100MB by storing vectors on disk instead of in RAM.
5
 
6
  ## What's Been Implemented
7
 
8
  ### 1. PostgresVectorService (`src/vector_db/postgres_vector_service.py`)
 
9
  - Full PostgreSQL integration with pgvector extension
10
  - Automatic table creation and indexing
11
  - Similarity search using cosine distance
@@ -13,25 +15,30 @@ This branch implements PostgreSQL with pgvector as an alternative to ChromaDB fo
13
  - Health monitoring and collection info
14
 
15
  ### 2. PostgresVectorAdapter (`src/vector_db/postgres_adapter.py`)
 
16
  - Compatibility layer for existing ChromaDB interface
17
  - Ensures seamless migration without code changes
18
  - Converts between PostgreSQL and ChromaDB result formats
19
 
20
  ### 3. Updated Configuration (`src/config.py`)
 
21
  - Added `VECTOR_STORAGE_TYPE` environment variable
22
  - PostgreSQL connection settings
23
  - Memory optimization parameters
24
 
25
  ### 4. Factory Pattern (`src/vector_store/vector_db.py`)
 
26
  - `create_vector_database()` function selects backend automatically
27
  - Supports both ChromaDB and PostgreSQL based on configuration
28
 
29
  ### 5. Migration Script (`scripts/migrate_to_postgres.py`)
 
30
  - Data optimization (text summarization, metadata cleaning)
31
  - Batch processing with memory management
32
  - Handles 4GB → 1GB data reduction for free tier
33
 
34
  ### 6. Tests (`tests/test_vector_store/test_postgres_vector.py`)
 
35
  - Unit tests with mocked dependencies
36
  - Integration tests for real database
37
  - Compatibility tests for ChromaDB interface
@@ -39,15 +46,18 @@ This branch implements PostgreSQL with pgvector as an alternative to ChromaDB fo
39
  ## Setup Instructions
40
 
41
  ### Step 1: Create Render PostgreSQL Database
 
42
  1. Go to Render Dashboard
43
  2. Create → PostgreSQL
44
  3. Choose "Free" plan (1GB storage, 30 days)
45
  4. Save the connection details
46
 
47
  ### Step 2: Enable pgvector Extension
 
48
  You have several options to enable pgvector:
49
 
50
  **Option A: Use the initialization script (Recommended)**
 
51
  ```bash
52
  # Set your database URL
53
  export DATABASE_URL="postgresql://user:password@host:port/database"
@@ -58,16 +68,19 @@ python scripts/init_pgvector.py
58
 
59
  **Option B: Manual SQL**
60
  Connect to your database and run:
 
61
  ```sql
62
  CREATE EXTENSION IF NOT EXISTS vector;
63
  ```
64
 
65
  **Option C: From Render Dashboard**
 
66
  1. Go to your PostgreSQL service → Info tab
67
  2. Use the "PSQL Command" to connect
68
  3. Run: `CREATE EXTENSION IF NOT EXISTS vector;`
69
 
70
  The initialization script (`scripts/init_pgvector.py`) will:
 
71
  - Test database connection
72
  - Check PostgreSQL version compatibility (13+)
73
  - Install pgvector extension safely
@@ -75,7 +88,9 @@ The initialization script (`scripts/init_pgvector.py`) will:
75
  - Provide detailed logging and error messages
76
 
77
  ### Step 3: Update Environment Variables
 
78
  Add to your Render environment variables:
 
79
  ```bash
80
  DATABASE_URL=postgresql://username:password@host:port/database
81
  VECTOR_STORAGE_TYPE=postgres
@@ -83,12 +98,15 @@ MEMORY_LIMIT_MB=400
83
  ```
84
 
85
  ### Step 4: Install Dependencies
 
86
  ```bash
87
  pip install psycopg2-binary==2.9.7
88
  ```
89
 
90
  ### Step 5: Run Migration (Optional)
 
91
  If you have existing ChromaDB data:
 
92
  ```bash
93
  python scripts/migrate_to_postgres.py --database-url="your-connection-string"
94
  ```
@@ -96,12 +114,15 @@ python scripts/migrate_to_postgres.py --database-url="your-connection-string"
96
  ## Usage
97
 
98
  ### Switch to PostgreSQL
 
99
  Set environment variable:
 
100
  ```bash
101
  export VECTOR_STORAGE_TYPE=postgres
102
  ```
103
 
104
  ### Use in Code (No Changes Required!)
 
105
  ```python
106
  from src.vector_store.vector_db import create_vector_database
107
 
@@ -113,22 +134,24 @@ results = vector_db.search(query_embedding, top_k=5)
113
 
114
  ## Expected Memory Reduction
115
 
116
- | Component | Before (ChromaDB) | After (PostgreSQL) | Savings |
117
- |-----------|------------------|-------------------|---------|
118
- | Vector Storage | 200-300MB | 0MB (disk) | 200-300MB |
119
- | Embedding Model | 100MB | 50MB (smaller model) | 50MB |
120
- | Application Code | 50-100MB | 50-100MB | 0MB |
121
- | **Total** | **350-500MB** | **50-150MB** | **300-350MB** |
122
 
123
  ## Migration Optimizations
124
 
125
  ### Data Size Reduction
 
126
  - **Text Summarization**: Documents truncated to 1000 characters
127
  - **Metadata Cleaning**: Only essential fields kept
128
  - **Dimension Reduction**: Can use smaller embedding models
129
  - **Quality Filtering**: Skip very short or low-quality documents
130
 
131
  ### Memory Management
 
132
  - **Batch Processing**: Process documents in small batches
133
  - **Garbage Collection**: Aggressive cleanup between operations
134
  - **Streaming**: Process data without loading everything into memory
@@ -136,17 +159,20 @@ results = vector_db.search(query_embedding, top_k=5)
136
  ## Testing
137
 
138
  ### Unit Tests
 
139
  ```bash
140
  pytest tests/test_vector_store/test_postgres_vector.py -v
141
  ```
142
 
143
  ### Integration Tests (Requires Database)
 
144
  ```bash
145
  export TEST_DATABASE_URL="postgresql://test:test@localhost:5432/test_db"
146
  pytest tests/test_vector_store/test_postgres_vector.py -m integration -v
147
  ```
148
 
149
  ### Migration Test
 
150
  ```bash
151
  python scripts/migrate_to_postgres.py --test-only
152
  ```
@@ -154,13 +180,17 @@ python scripts/migrate_to_postgres.py --test-only
154
  ## Deployment
155
 
156
  ### Local Development
 
157
  Keep using ChromaDB:
 
158
  ```bash
159
  export VECTOR_STORAGE_TYPE=chroma
160
  ```
161
 
162
  ### Production (Render)
 
163
  Switch to PostgreSQL:
 
164
  ```bash
165
  export VECTOR_STORAGE_TYPE=postgres
166
  export DATABASE_URL="your-render-postgres-url"
@@ -169,10 +199,13 @@ export DATABASE_URL="your-render-postgres-url"
169
  ## Troubleshooting
170
 
171
  ### Common Issues
 
172
  1. **"pgvector extension not found"**
 
173
  - Run `CREATE EXTENSION vector;` in your database
174
 
175
  2. **Connection errors**
 
176
  - Verify DATABASE_URL format: `postgresql://user:pass@host:port/db`
177
  - Check firewall/network connectivity
178
 
@@ -181,6 +214,7 @@ export DATABASE_URL="your-render-postgres-url"
181
  - Check that old ChromaDB files aren't being loaded
182
 
183
  ### Monitoring
 
184
  ```python
185
  from src.vector_db.postgres_vector_service import PostgresVectorService
186
 
@@ -190,23 +224,27 @@ print(health) # Shows connection status, document count, etc.
190
  ```
191
 
192
  ## Rollback Plan
 
193
  If issues occur, simply change back to ChromaDB:
 
194
  ```bash
195
  export VECTOR_STORAGE_TYPE=chroma
196
  ```
 
197
  The factory pattern ensures seamless switching between backends.
198
 
199
  ## Performance Comparison
200
 
201
- | Operation | ChromaDB | PostgreSQL | Notes |
202
- |-----------|----------|------------|-------|
203
- | Insert | Fast | Medium | Network overhead |
204
- | Search | Very Fast | Fast | pgvector is optimized |
205
- | Memory | High | Low | Vectors stored on disk |
206
- | Persistence | File-based | Database | More reliable |
207
- | Scaling | Limited | Excellent | Can upgrade storage |
208
 
209
  ## Next Steps
 
210
  1. Test locally with PostgreSQL
211
  2. Create Render PostgreSQL database
212
  3. Run migration script
 
1
  # PostgreSQL Migration Guide
2
 
3
  ## Overview
4
+
5
  This branch implements PostgreSQL with pgvector as an alternative to ChromaDB for vector storage. This reduces memory usage from 400MB+ to ~50-100MB by storing vectors on disk instead of in RAM.
6
 
7
  ## What's Been Implemented
8
 
9
  ### 1. PostgresVectorService (`src/vector_db/postgres_vector_service.py`)
10
+
11
  - Full PostgreSQL integration with pgvector extension
12
  - Automatic table creation and indexing
13
  - Similarity search using cosine distance
 
15
  - Health monitoring and collection info
16
 
17
  ### 2. PostgresVectorAdapter (`src/vector_db/postgres_adapter.py`)
18
+
19
  - Compatibility layer for existing ChromaDB interface
20
  - Ensures seamless migration without code changes
21
  - Converts between PostgreSQL and ChromaDB result formats
22
 
23
  ### 3. Updated Configuration (`src/config.py`)
24
+
25
  - Added `VECTOR_STORAGE_TYPE` environment variable
26
  - PostgreSQL connection settings
27
  - Memory optimization parameters
28
 
29
  ### 4. Factory Pattern (`src/vector_store/vector_db.py`)
30
+
31
  - `create_vector_database()` function selects backend automatically
32
  - Supports both ChromaDB and PostgreSQL based on configuration
33
 
34
  ### 5. Migration Script (`scripts/migrate_to_postgres.py`)
35
+
36
  - Data optimization (text summarization, metadata cleaning)
37
  - Batch processing with memory management
38
  - Handles 4GB → 1GB data reduction for free tier
39
 
40
  ### 6. Tests (`tests/test_vector_store/test_postgres_vector.py`)
41
+
42
  - Unit tests with mocked dependencies
43
  - Integration tests for real database
44
  - Compatibility tests for ChromaDB interface
 
46
  ## Setup Instructions
47
 
48
  ### Step 1: Create Render PostgreSQL Database
49
+
50
  1. Go to Render Dashboard
51
  2. Create → PostgreSQL
52
  3. Choose "Free" plan (1GB storage, 30 days)
53
  4. Save the connection details
54
 
55
  ### Step 2: Enable pgvector Extension
56
+
57
  You have several options to enable pgvector:
58
 
59
  **Option A: Use the initialization script (Recommended)**
60
+
61
  ```bash
62
  # Set your database URL
63
  export DATABASE_URL="postgresql://user:password@host:port/database"
 
68
 
69
  **Option B: Manual SQL**
70
  Connect to your database and run:
71
+
72
  ```sql
73
  CREATE EXTENSION IF NOT EXISTS vector;
74
  ```
75
 
76
  **Option C: From Render Dashboard**
77
+
78
  1. Go to your PostgreSQL service → Info tab
79
  2. Use the "PSQL Command" to connect
80
  3. Run: `CREATE EXTENSION IF NOT EXISTS vector;`
81
 
82
  The initialization script (`scripts/init_pgvector.py`) will:
83
+
84
  - Test database connection
85
  - Check PostgreSQL version compatibility (13+)
86
  - Install pgvector extension safely
 
88
  - Provide detailed logging and error messages
89
 
90
  ### Step 3: Update Environment Variables
91
+
92
  Add to your Render environment variables:
93
+
94
  ```bash
95
  DATABASE_URL=postgresql://username:password@host:port/database
96
  VECTOR_STORAGE_TYPE=postgres
 
98
  ```
99
 
100
  ### Step 4: Install Dependencies
101
+
102
  ```bash
103
  pip install psycopg2-binary==2.9.7
104
  ```
105
 
106
  ### Step 5: Run Migration (Optional)
107
+
108
  If you have existing ChromaDB data:
109
+
110
  ```bash
111
  python scripts/migrate_to_postgres.py --database-url="your-connection-string"
112
  ```
 
114
  ## Usage
115
 
116
  ### Switch to PostgreSQL
117
+
118
  Set environment variable:
119
+
120
  ```bash
121
  export VECTOR_STORAGE_TYPE=postgres
122
  ```
123
 
124
  ### Use in Code (No Changes Required!)
125
+
126
  ```python
127
  from src.vector_store.vector_db import create_vector_database
128
 
 
134
 
135
  ## Expected Memory Reduction
136
 
137
+ | Component | Before (ChromaDB) | After (PostgreSQL) | Savings |
138
+ | ---------------- | ----------------- | -------------------- | ------------- |
139
+ | Vector Storage | 200-300MB | 0MB (disk) | 200-300MB |
140
+ | Embedding Model | 100MB | 50MB (smaller model) | 50MB |
141
+ | Application Code | 50-100MB | 50-100MB | 0MB |
142
+ | **Total** | **350-500MB** | **50-150MB** | **300-350MB** |
143
 
144
  ## Migration Optimizations
145
 
146
  ### Data Size Reduction
147
+
148
  - **Text Summarization**: Documents truncated to 1000 characters
149
  - **Metadata Cleaning**: Only essential fields kept
150
  - **Dimension Reduction**: Can use smaller embedding models
151
  - **Quality Filtering**: Skip very short or low-quality documents
152
 
153
  ### Memory Management
154
+
155
  - **Batch Processing**: Process documents in small batches
156
  - **Garbage Collection**: Aggressive cleanup between operations
157
  - **Streaming**: Process data without loading everything into memory
 
159
  ## Testing
160
 
161
  ### Unit Tests
162
+
163
  ```bash
164
  pytest tests/test_vector_store/test_postgres_vector.py -v
165
  ```
166
 
167
  ### Integration Tests (Requires Database)
168
+
169
  ```bash
170
  export TEST_DATABASE_URL="postgresql://test:test@localhost:5432/test_db"
171
  pytest tests/test_vector_store/test_postgres_vector.py -m integration -v
172
  ```
173
 
174
  ### Migration Test
175
+
176
  ```bash
177
  python scripts/migrate_to_postgres.py --test-only
178
  ```
 
180
  ## Deployment
181
 
182
  ### Local Development
183
+
184
  Keep using ChromaDB:
185
+
186
  ```bash
187
  export VECTOR_STORAGE_TYPE=chroma
188
  ```
189
 
190
  ### Production (Render)
191
+
192
  Switch to PostgreSQL:
193
+
194
  ```bash
195
  export VECTOR_STORAGE_TYPE=postgres
196
  export DATABASE_URL="your-render-postgres-url"
 
199
  ## Troubleshooting
200
 
201
  ### Common Issues
202
+
203
  1. **"pgvector extension not found"**
204
+
205
  - Run `CREATE EXTENSION vector;` in your database
206
 
207
  2. **Connection errors**
208
+
209
  - Verify DATABASE_URL format: `postgresql://user:pass@host:port/db`
210
  - Check firewall/network connectivity
211
 
 
214
  - Check that old ChromaDB files aren't being loaded
215
 
216
  ### Monitoring
217
+
218
  ```python
219
  from src.vector_db.postgres_vector_service import PostgresVectorService
220
 
 
224
  ```
225
 
226
  ## Rollback Plan
227
+
228
  If issues occur, simply change back to ChromaDB:
229
+
230
  ```bash
231
  export VECTOR_STORAGE_TYPE=chroma
232
  ```
233
+
234
  The factory pattern ensures seamless switching between backends.
235
 
236
  ## Performance Comparison
237
 
238
+ | Operation | ChromaDB | PostgreSQL | Notes |
239
+ | ----------- | ---------- | ---------- | ---------------------- |
240
+ | Insert | Fast | Medium | Network overhead |
241
+ | Search | Very Fast | Fast | pgvector is optimized |
242
+ | Memory | High | Low | Vectors stored on disk |
243
+ | Persistence | File-based | Database | More reliable |
244
+ | Scaling | Limited | Excellent | Can upgrade storage |
245
 
246
  ## Next Steps
247
+
248
  1. Test locally with PostgreSQL
249
  2. Create Render PostgreSQL database
250
  3. Run migration script
run.sh CHANGED
@@ -25,16 +25,24 @@ done
25
  echo "Starting gunicorn on port ${PORT_VALUE} with ${WORKERS_VALUE} workers and timeout ${TIMEOUT_VALUE}s"
26
  export PYTHONPATH="/app${PYTHONPATH:+:$PYTHONPATH}"
27
 
 
 
 
 
 
 
 
 
28
  # Start gunicorn in background so we can trap signals and collect diagnostics
29
  gunicorn \
30
  --bind 0.0.0.0:${PORT_VALUE} \
31
  --workers "${WORKERS_VALUE}" \
32
  --timeout "${TIMEOUT_VALUE}" \
33
- --log-level debug \
34
  --access-logfile - \
35
  --error-logfile - \
36
  --capture-output \
37
- --config gunicorn.conf.py \
38
  app:app &
39
 
40
  GUNICORN_PID=$!
@@ -55,18 +63,36 @@ handle_term() {
55
  }
56
  trap 'handle_term' SIGTERM SIGINT
57
 
58
- # Give gunicorn a moment to start before pre-warm
59
- echo "Waiting for server to start to pre-warm..."
60
- sleep 5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
 
62
- # Pre-warm application (best-effort; don't fail startup if warm request fails)
63
- echo "Pre-warming application..."
64
  curl -sS -X POST http://localhost:${PORT_VALUE}/chat \
65
  -H "Content-Type: application/json" \
66
  -d '{"message":"pre-warm"}' \
67
- --max-time 180 --fail >/dev/null 2>&1 || echo "Pre-warm request failed but continuing..."
68
 
69
- echo "Server is running."
70
 
71
  # Wait for gunicorn to exit and forward its exit code
72
  wait "${GUNICORN_PID}"
 
25
  echo "Starting gunicorn on port ${PORT_VALUE} with ${WORKERS_VALUE} workers and timeout ${TIMEOUT_VALUE}s"
26
  export PYTHONPATH="/app${PYTHONPATH:+:$PYTHONPATH}"
27
 
28
+ # Determine gunicorn config usage
29
+ GUNICORN_CONFIG_ARG=""
30
+ if [ -f gunicorn.conf.py ]; then
31
+ GUNICORN_CONFIG_ARG="--config gunicorn.conf.py"
32
+ else
33
+ echo "Warning: gunicorn.conf.py not found; starting with inline CLI options only."
34
+ fi
35
+
36
  # Start gunicorn in background so we can trap signals and collect diagnostics
37
  gunicorn \
38
  --bind 0.0.0.0:${PORT_VALUE} \
39
  --workers "${WORKERS_VALUE}" \
40
  --timeout "${TIMEOUT_VALUE}" \
41
+ --log-level info \
42
  --access-logfile - \
43
  --error-logfile - \
44
  --capture-output \
45
+ ${GUNICORN_CONFIG_ARG} \
46
  app:app &
47
 
48
  GUNICORN_PID=$!
 
63
  }
64
  trap 'handle_term' SIGTERM SIGINT
65
 
66
+ # Readiness probe loop
67
+ echo "Waiting for application readiness (health endpoint)..."
68
+ READY_TIMEOUT="${READY_TIMEOUT:-60}" # total seconds to wait
69
+ READY_INTERVAL="${READY_INTERVAL:-3}" # seconds between checks
70
+ ELAPSED=0
71
+ READY=0
72
+ while [ "$ELAPSED" -lt "$READY_TIMEOUT" ]; do
73
+ if ! kill -0 "${GUNICORN_PID}" 2>/dev/null; then
74
+ echo "Gunicorn process exited prematurely during startup; aborting." >&2
75
+ exit 1
76
+ fi
77
+ if curl -fsS "http://localhost:${PORT_VALUE}/health" >/dev/null 2>&1; then
78
+ READY=1
79
+ break
80
+ fi
81
+ sleep "$READY_INTERVAL"
82
+ ELAPSED=$((ELAPSED + READY_INTERVAL))
83
+ done
84
+ if [ "$READY" -ne 1 ]; then
85
+ echo "Health endpoint not ready after ${READY_TIMEOUT}s; continuing but marking as degraded." >&2
86
+ fi
87
 
88
+ # Pre-warm (chat) if health is ready
89
+ echo "Pre-warming application via /chat endpoint..."
90
  curl -sS -X POST http://localhost:${PORT_VALUE}/chat \
91
  -H "Content-Type: application/json" \
92
  -d '{"message":"pre-warm"}' \
93
+ --max-time 30 --fail >/dev/null 2>&1 || echo "Pre-warm request failed but continuing..."
94
 
95
+ echo "Server is running (PID ${GUNICORN_PID})."
96
 
97
  # Wait for gunicorn to exit and forward its exit code
98
  wait "${GUNICORN_PID}"