|
| 1 | +# Production Features Implementation |
| 2 | + |
| 3 | +This document describes three production-ready features added to the Stellar Goal Vault backend: |
| 4 | + |
| 5 | +## 1. API Key Authentication Middleware |
| 6 | + |
| 7 | +### Overview |
| 8 | + |
| 9 | +Implements request-level authentication using API keys for production deployments. Protects write operations and sensitive endpoints while allowing public read access to certain endpoints. |
| 10 | + |
| 11 | +### Configuration |
| 12 | + |
| 13 | +Set the `API_KEYS` environment variable with comma-separated valid API keys: |
| 14 | + |
| 15 | +```bash |
| 16 | +API_KEYS=key1,key2,key3 |
| 17 | +``` |
| 18 | + |
| 19 | +### Usage |
| 20 | + |
| 21 | +Include the API key in the `Authorization` header using Bearer token format: |
| 22 | + |
| 23 | +```bash |
| 24 | +curl -H "Authorization: Bearer your-api-key" https://api.example.com/api/campaigns |
| 25 | +``` |
| 26 | + |
| 27 | +### Public Endpoints (No Authentication Required) |
| 28 | + |
| 29 | +- `GET /api/health` - Health check |
| 30 | +- `GET /api/config` - Client configuration |
| 31 | +- `GET /api/stats` - Global statistics |
| 32 | +- `GET /api/leaderboard` - Top contributors |
| 33 | +- `GET /api/open-issues` - GitHub issues |
| 34 | + |
| 35 | +### Protected Endpoints (Require Authentication) |
| 36 | + |
| 37 | +- `POST /api/campaigns` - Create campaign |
| 38 | +- `POST /api/campaigns/:id/pledges` - Add pledge |
| 39 | +- `POST /api/campaigns/:id/pledges/reconcile` - Reconcile on-chain pledge |
| 40 | +- `POST /api/campaigns/:id/claim` - Claim campaign |
| 41 | +- `POST /api/campaigns/:id/refund` - Refund contributor |
| 42 | +- `GET /api/campaigns/:id/pledges` - List pledges |
| 43 | +- `GET /api/campaigns/:id/contributors` - Get contributors |
| 44 | +- `GET /api/campaigns/:id/history` - Get campaign history |
| 45 | + |
| 46 | +### Error Responses |
| 47 | + |
| 48 | +```json |
| 49 | +{ |
| 50 | + "success": false, |
| 51 | + "error": { |
| 52 | + "code": "UNAUTHORIZED", |
| 53 | + "message": "Missing or invalid Authorization header. Use format: Bearer <api-key>", |
| 54 | + "requestId": "uuid" |
| 55 | + } |
| 56 | +} |
| 57 | +``` |
| 58 | + |
| 59 | +### Implementation Details |
| 60 | + |
| 61 | +- File: `src/middleware/apiKeyAuth.ts` |
| 62 | +- Middleware: `apiKeyAuthMiddleware` |
| 63 | +- Only enabled in production (`NODE_ENV=production`) |
| 64 | +- Development mode allows all requests if `API_KEYS` is not set |
| 65 | + |
| 66 | +--- |
| 67 | + |
| 68 | +## 2. Redis Cache Layer |
| 69 | + |
| 70 | +### Overview |
| 71 | + |
| 72 | +Implements a distributed caching layer using Redis for production deployments. Caches GET request responses to reduce database load and improve API response times. |
| 73 | + |
| 74 | +### Configuration |
| 75 | + |
| 76 | +Set the `REDIS_URL` environment variable: |
| 77 | + |
| 78 | +```bash |
| 79 | +REDIS_URL=redis://localhost:6379 |
| 80 | +# or with authentication |
| 81 | +REDIS_URL=redis://:password@host:port |
| 82 | +``` |
| 83 | + |
| 84 | +### Features |
| 85 | + |
| 86 | +- **Automatic Cache Management**: GET requests are automatically cached with configurable TTL |
| 87 | +- **Cache Invalidation**: Cache is automatically invalidated on write operations |
| 88 | +- **Graceful Degradation**: API continues to work if Redis is unavailable |
| 89 | +- **Production-Only**: Cache is only enabled in production (`NODE_ENV=production`) |
| 90 | + |
| 91 | +### Cache Configuration |
| 92 | + |
| 93 | +Default TTL: 300 seconds (5 minutes) |
| 94 | + |
| 95 | +Customize TTL in `src/middleware/cacheMiddleware.ts`: |
| 96 | + |
| 97 | +```typescript |
| 98 | +app.use(cacheMiddleware(600)); // 10 minutes |
| 99 | +``` |
| 100 | + |
| 101 | +### Cache Headers |
| 102 | + |
| 103 | +Responses include cache status headers: |
| 104 | + |
| 105 | +- `X-Cache: HIT` - Response served from cache |
| 106 | +- `X-Cache: MISS` - Response generated fresh and cached |
| 107 | + |
| 108 | +### Cached Endpoints |
| 109 | + |
| 110 | +All GET endpoints are cached: |
| 111 | + |
| 112 | +- `GET /api/campaigns` - Campaign list |
| 113 | +- `GET /api/campaigns/:id` - Campaign details |
| 114 | +- `GET /api/campaigns/:id/pledges` - Campaign pledges |
| 115 | +- `GET /api/campaigns/:id/contributors` - Contributor summary |
| 116 | +- `GET /api/campaigns/:id/history` - Campaign history |
| 117 | +- `GET /api/stats` - Global statistics |
| 118 | +- `GET /api/leaderboard` - Top contributors |
| 119 | + |
| 120 | +### Cache Invalidation |
| 121 | + |
| 122 | +Cache is automatically cleared when: |
| 123 | + |
| 124 | +- New campaign is created |
| 125 | +- New pledge is added |
| 126 | +- Campaign is claimed |
| 127 | +- Contributor is refunded |
| 128 | + |
| 129 | +### Implementation Details |
| 130 | + |
| 131 | +- Files: |
| 132 | + - `src/services/cache.ts` - Redis client and cache operations |
| 133 | + - `src/middleware/cacheMiddleware.ts` - Express middleware for caching |
| 134 | +- Functions: |
| 135 | + - `initRedisCache()` - Initialize Redis connection |
| 136 | + - `getCacheValue(key)` - Retrieve cached value |
| 137 | + - `setCacheValue(key, value, ttl)` - Store value in cache |
| 138 | + - `deleteCacheValue(key)` - Remove cached value |
| 139 | + - `clearCachePattern(pattern)` - Clear cache by pattern |
| 140 | + - `isCacheAvailable()` - Check cache availability |
| 141 | + |
| 142 | +### Error Handling |
| 143 | + |
| 144 | +- Cache failures are logged but don't affect API functionality |
| 145 | +- If Redis is unavailable, API continues to work without caching |
| 146 | +- Connection errors are automatically logged |
| 147 | + |
| 148 | +--- |
| 149 | + |
| 150 | +## 3. Concurrent Pledge Race Condition Tests |
| 151 | + |
| 152 | +### Overview |
| 153 | + |
| 154 | +Comprehensive test suite for detecting and validating behavior under concurrent pledge operations. Tests ensure data consistency and proper handling of race conditions. |
| 155 | + |
| 156 | +### Test File |
| 157 | + |
| 158 | +`src/services/campaignStore.concurrent.test.ts` |
| 159 | + |
| 160 | +### Test Cases |
| 161 | + |
| 162 | +#### 1. Concurrent Pledges Without Race Conditions |
| 163 | + |
| 164 | +Tests that multiple concurrent pledges from different contributors are all recorded correctly. |
| 165 | + |
| 166 | +```typescript |
| 167 | +- 4 concurrent pledges of 250 each |
| 168 | +- Expected: All pledges recorded, total = 1000 |
| 169 | +``` |
| 170 | + |
| 171 | +#### 2. Over-Pledging Prevention |
| 172 | + |
| 173 | +Tests behavior when concurrent pledges exceed campaign target. |
| 174 | + |
| 175 | +```typescript |
| 176 | +- 3 concurrent pledges of 300 each (total 900, target 500) |
| 177 | +- Expected: All pledges recorded (no hard cap), total = 900 |
| 178 | +``` |
| 179 | + |
| 180 | +#### 3. Per-Contributor Limits |
| 181 | + |
| 182 | +Tests enforcement of per-contributor pledge limits under concurrent conditions. |
| 183 | + |
| 184 | +```typescript |
| 185 | +- 2 concurrent pledges of 150 each from same contributor (limit 200) |
| 186 | +- Expected: Both pledges recorded (race condition), total = 300 |
| 187 | +- Note: This demonstrates a known race condition |
| 188 | +``` |
| 189 | + |
| 190 | +#### 4. High Concurrent Load |
| 191 | + |
| 192 | +Tests data consistency under heavy concurrent load. |
| 193 | + |
| 194 | +```typescript |
| 195 | +- 20 concurrent pledges of 50 each |
| 196 | +- Expected: All pledges recorded, total = 1000, no data corruption |
| 197 | +``` |
| 198 | + |
| 199 | +#### 5. Concurrent Claim and Pledge |
| 200 | + |
| 201 | +Tests interaction between claim and pledge operations. |
| 202 | + |
| 203 | +```typescript |
| 204 | +- Concurrent claim and pledge on expired campaign |
| 205 | +- Expected: Both operations succeed, campaign claimed, pledge recorded |
| 206 | +``` |
| 207 | + |
| 208 | +#### 6. Duplicate Concurrent Pledges |
| 209 | + |
| 210 | +Tests handling of duplicate pledges from same contributor. |
| 211 | + |
| 212 | +```typescript |
| 213 | +- 3 concurrent identical pledges from same contributor |
| 214 | +- Expected: All pledges recorded (no deduplication at this level) |
| 215 | +``` |
| 216 | + |
| 217 | +### Running Tests |
| 218 | + |
| 219 | +```bash |
| 220 | +# Run all tests |
| 221 | +npm test |
| 222 | + |
| 223 | +# Run only concurrent tests |
| 224 | +npm test -- campaignStore.concurrent.test.ts |
| 225 | + |
| 226 | +# Run with coverage |
| 227 | +npm test -- --coverage |
| 228 | +``` |
| 229 | + |
| 230 | +### Known Race Conditions |
| 231 | + |
| 232 | +The tests document the following race conditions: |
| 233 | + |
| 234 | +1. **Per-Contributor Limit Race Condition** |
| 235 | + - When multiple pledges from the same contributor are submitted concurrently, the limit check may not see previous pledges |
| 236 | + - Result: Contributor can exceed their limit |
| 237 | + - Mitigation: Implement database-level constraints or use transactions |
| 238 | + |
| 239 | +2. **Campaign Funding Cap Race Condition** |
| 240 | + - When pledges are submitted concurrently, the total can exceed the target |
| 241 | + - Result: Campaign can be over-funded |
| 242 | + - Mitigation: Implement atomic operations or use database locks |
| 243 | + |
| 244 | +### Implementation Details |
| 245 | + |
| 246 | +- Uses Vitest for testing |
| 247 | +- Isolated SQLite database per test |
| 248 | +- Async/await for concurrent operations |
| 249 | +- Promise.all() for parallel execution |
| 250 | +- Comprehensive assertions on final state |
| 251 | + |
| 252 | +### Recommendations for Production |
| 253 | + |
| 254 | +1. **Database Transactions**: Wrap pledge operations in transactions |
| 255 | +2. **Optimistic Locking**: Add version fields to campaigns |
| 256 | +3. **Distributed Locks**: Use Redis for cross-instance coordination |
| 257 | +4. **Event Sourcing**: Record all operations for audit trail |
| 258 | +5. **Monitoring**: Track pledge success/failure rates |
| 259 | + |
| 260 | +--- |
| 261 | + |
| 262 | +## Environment Variables |
| 263 | + |
| 264 | +### Required for Production |
| 265 | + |
| 266 | +```bash |
| 267 | +NODE_ENV=production |
| 268 | +API_KEYS=key1,key2,key3 |
| 269 | +REDIS_URL=redis://localhost:6379 |
| 270 | +``` |
| 271 | + |
| 272 | +### Optional |
| 273 | + |
| 274 | +```bash |
| 275 | +# Cache TTL in seconds (default: 300) |
| 276 | +CACHE_TTL=600 |
| 277 | + |
| 278 | +# Redis connection timeout |
| 279 | +REDIS_TIMEOUT=5000 |
| 280 | + |
| 281 | +# Log level |
| 282 | +LOG_LEVEL=info |
| 283 | +``` |
| 284 | + |
| 285 | +--- |
| 286 | + |
| 287 | +## Deployment Checklist |
| 288 | + |
| 289 | +- [ ] Set `NODE_ENV=production` |
| 290 | +- [ ] Generate and configure `API_KEYS` |
| 291 | +- [ ] Set up Redis instance and configure `REDIS_URL` |
| 292 | +- [ ] Run concurrent tests to verify behavior |
| 293 | +- [ ] Monitor cache hit rates and Redis performance |
| 294 | +- [ ] Set up alerts for authentication failures |
| 295 | +- [ ] Configure log aggregation for cache errors |
| 296 | +- [ ] Test API key rotation procedure |
| 297 | +- [ ] Document API key management process |
| 298 | + |
| 299 | +--- |
| 300 | + |
| 301 | +## Performance Considerations |
| 302 | + |
| 303 | +### Cache Performance |
| 304 | + |
| 305 | +- **Hit Rate**: Monitor X-Cache headers to track hit rate |
| 306 | +- **TTL Tuning**: Adjust TTL based on data freshness requirements |
| 307 | +- **Memory**: Monitor Redis memory usage |
| 308 | +- **Eviction**: Configure Redis eviction policy (e.g., allkeys-lru) |
| 309 | + |
| 310 | +### Authentication Performance |
| 311 | + |
| 312 | +- **Overhead**: API key validation adds minimal overhead (~1ms) |
| 313 | +- **Scaling**: Stateless design allows horizontal scaling |
| 314 | +- **Key Rotation**: No downtime required for key rotation |
| 315 | + |
| 316 | +### Concurrency Performance |
| 317 | + |
| 318 | +- **Database**: SQLite WAL mode supports concurrent reads |
| 319 | +- **Writes**: Concurrent writes may cause contention |
| 320 | +- **Scaling**: Consider PostgreSQL for higher concurrency |
| 321 | + |
| 322 | +--- |
| 323 | + |
| 324 | +## Troubleshooting |
| 325 | + |
| 326 | +### Cache Not Working |
| 327 | + |
| 328 | +1. Check `REDIS_URL` is set and Redis is running |
| 329 | +2. Check `NODE_ENV=production` |
| 330 | +3. Review logs for Redis connection errors |
| 331 | +4. Verify Redis credentials and network access |
| 332 | + |
| 333 | +### Authentication Failures |
| 334 | + |
| 335 | +1. Verify API key is in `API_KEYS` environment variable |
| 336 | +2. Check Authorization header format: `Bearer <key>` |
| 337 | +3. Ensure `NODE_ENV=production` for authentication to be active |
| 338 | +4. Review logs for authentication attempts |
| 339 | + |
| 340 | +### Race Conditions |
| 341 | + |
| 342 | +1. Review concurrent test results |
| 343 | +2. Monitor database lock contention |
| 344 | +3. Consider implementing optimistic locking |
| 345 | +4. Use database transactions for critical operations |
0 commit comments