# DSOL - Technical Specification

**Author:** TextIQ
**Date:** 2025-12-05
**Project Level:** Quick Flow - Single Story
**Change Type:** Configuration Refactoring
**Development Context:** Brownfield - Azure Redis Cache Migration

---

## Context

### Available Documents

- **Existing Tech-Spec:** docs/tech-spec-redis-auth.md (previous Redis authentication implementation)
- **Existing Story:** docs/sprint-artifacts/story-redis-auth-1.md
- **Architecture:** docs/architecture.md (system architecture)
- **PRD:** docs/prd.md (product requirements)

### Project Stack

**Runtime & Framework:**
- Python 3.11+
- FastAPI 0.122
- uvicorn 0.30+ (ASGI server with standard extras)
- Pydantic 2.0+ with pydantic-settings 2.0+

**Redis Integration:**
- redis 5.0+ (Python client with async support)
- Celery 5.3+ (task queue using Redis as broker/backend)
- redis.asyncio (async Redis client)

**Database & Storage:**
- PostgreSQL with SQLAlchemy 2.0 (asyncpg)
- Milvus 2.4.0+ (vector database)

**Development Tools:**
- pytest 8.0+ with pytest-asyncio 0.23+
- black 24.0+ (line-length: 100)
- ruff 0.5+ (linter)
- mypy 1.10+ (type checker, strict mode)

**Infrastructure:**
- Docker Compose for local development
- Azure Redis Cache for production (SSL/TLS on port 6380)

### Existing Codebase Structure

**Configuration Management:**
- `src/core/config.py` - Pydantic Settings-based configuration
  - Currently uses 4 individual Redis env vars (redis_host, redis_port, redis_password, redis_db)
  - Builds `redis_url` via @property method
  - Settings loaded from .env file with case-insensitive parsing

**Redis Consumers:**
- `src/workers/celery_app.py` - Celery configuration (lines 57-66)
  - Uses `settings.redis_url` for broker and backend
- `src/services/progress_tracker.py` - Progress tracking service (line 37)
  - Initializes: `redis.from_url(redis_url, decode_responses=True)`
- `src/services/rate_limiter.py` - Rate limiting with sliding window
  - Accepts Redis client as dependency

**Testing:**
- `tests/core/test_config.py` - Config tests (lines 8-60)
  - 6 unit tests covering redis_url property builder
  - Tests: with/without password, custom db, custom host/port

**Docker Infrastructure:**
- `docker-compose.yml` - Local development services
  - Redis service on port 6379 (non-SSL)
  - Uses REDIS_PASSWORD env var conditionally

**Environment Configuration:**
- `.env.example` - Template for environment variables
  - Lines 11-20: Redis configuration block with 4 separate variables

---

## The Change

### Problem Statement

The current Redis configuration uses 4 separate environment variables (`REDIS_HOST`, `REDIS_PORT`, `REDIS_PASSWORD`, `REDIS_DB`), which are then assembled into a connection URL via a @property method in the Settings class.

**Issues with current approach:**
1. **Complexity:** Multiple env vars for a single connection string
2. **Azure Redis Cache:** Production uses full connection URL with SSL (`rediss://`)
3. **Configuration Drift:** Disconnect between how Azure provides the URL and how the app consumes it
4. **Maintenance:** Property method adds unnecessary indirection

**Azure Redis Cache provides:**
```
rediss://:password@metis-core-redis.redis.cache.windows.net:6380
```

**Current implementation requires splitting this into 4 separate variables.**

### Proposed Solution

Replace the 4 individual Redis environment variables with a single `REDIS_URL` that accepts the full connection string directly.

**Benefits:**
1. **Simplicity:** Single env var for single connection
2. **Azure-Native:** Use connection string exactly as provided by Azure
3. **Flexibility:** Supports both `redis://` (non-SSL) and `rediss://` (SSL)
4. **Standard:** Follows common pattern used by DATABASE_URL and other connection strings
5. **Less Code:** Remove property method and individual field validation

**User provides complete URL including database number if needed:**
- Local: `redis://localhost:6379`
- Azure: `rediss://:password@domain:6380`
- With DB: `redis://localhost:6379/2`

### Scope

**In Scope:**

1. **Configuration Changes:**
   - Remove `redis_host`, `redis_port`, `redis_password`, `redis_db` from Settings class
   - Add `redis_url` as direct string field
   - Remove `redis_url` @property method
   - Update `.env.example` with new REDIS_URL format

2. **Docker Infrastructure:**
   - Update `docker-compose.yml` to use REDIS_URL environment variable
   - Maintain local development Redis service (non-SSL)

3. **Testing:**
   - Remove old unit tests for redis_url property builder
   - Add new unit tests for direct REDIS_URL validation
   - Test both `redis://` and `rediss://` URL schemes

4. **Documentation:**
   - Update README.md with new REDIS_URL configuration
   - Update any integration guides referencing Redis config

**Out of Scope:**

1. Database number defaults or parsing - user provides complete URL
2. URL format validation - Redis client will handle malformed URLs
3. Migration script for existing deployments (manual update)
4. Backward compatibility with old env vars
5. Connection pooling changes
6. Redis client library upgrades

---

## Implementation Details

### Source Tree Changes

**MODIFY:**
- `src/core/config.py` (lines 13-16, 48-59)
  - Remove: `redis_host`, `redis_port`, `redis_password`, `redis_db` fields
  - Remove: `redis_url` @property method (lines 48-59)
  - Add: `redis_url: str` field with default value

**MODIFY:**
- `.env.example` (lines 11-20)
  - Replace 4-variable Redis configuration block
  - Add single REDIS_URL with examples for local and Azure

**MODIFY:**
- `docker-compose.yml` (lines 21-36)
  - Update Redis service environment variable handling
  - Change from REDIS_PASSWORD to REDIS_URL

**MODIFY:**
- `tests/core/test_config.py` (lines 8-60)
  - Remove 6 old redis_url property tests
  - Add 3 new tests for direct redis_url field

**MODIFY:**
- `README.md`
  - Update Redis configuration section with new REDIS_URL

**OPTIONAL:**
- `docs/integration-guide-structured-store.md`
  - Update if it references Redis configuration

### Technical Approach

**1. Settings Class Refactoring:**

Remove property-based URL builder, replace with direct field:

```python
class Settings(BaseSettings):
    # Old approach (REMOVE):
    redis_host: str = "localhost"
    redis_port: int = 6379
    redis_password: str = ""
    redis_db: int = 0

    @property
    def redis_url(self) -> str:
        if self.redis_password:
            return f"redis://:{self.redis_password}@{self.redis_host}:{self.redis_port}/{self.redis_db}"
        else:
            return f"redis://{self.redis_host}:{self.redis_port}/{self.redis_db}"

    # New approach (ADD):
    redis_url: str = "redis://localhost:6379"
```

**2. No URL Parsing or Validation:**

The Redis client library (`redis.asyncio` and `redis` for Celery) handles URL parsing natively:
- Both support `redis://` and `rediss://` schemes
- Both extract host, port, password, db, and SSL from URL
- Connection errors will surface if URL is malformed

**3. Docker Compose Update:**

```yaml
redis:
  image: redis:7
  container_name: dsol-redis
  # Simplified - just expose service, no password for local dev
  ports:
    - "6379:6379"
```

Application container uses: `REDIS_URL=redis://redis:6379`

**4. Environment Configuration Examples:**

`.env.example` will show both use cases:
```bash
# Local development (non-SSL)
REDIS_URL=redis://localhost:6379

# Azure Redis Cache (SSL with authentication)
REDIS_URL=rediss://:your-password@your-cache.redis.cache.windows.net:6380
```

### Existing Patterns to Follow

**Pydantic Settings Conventions:**
- Use lowercase field names with underscores (already following with `redis_url`)
- Provide sensible defaults for local development
- Use `SettingsConfigDict` for .env file loading (already configured)
- Set `extra="ignore"` to handle transition (already set)

**Testing Patterns:**
- Test file naming: `test_*.py` (following existing pattern)
- Pytest fixtures for settings instances
- Assertion style: `assert` (not expect/should)
- Test organization: `tests/core/` for core module tests

**Code Style:**
- Line length: 100 characters (black + ruff configured)
- Type hints: Required (mypy strict mode enabled)
- Quotes: Double quotes (black default)
- Indentation: 4 spaces

### Integration Points

**Internal Dependencies:**
1. **Celery Application** (`src/workers/celery_app.py:59-60`)
   - Uses: `settings.redis_url` for broker and backend
   - No changes needed - already uses redis_url

2. **Progress Tracker** (`src/services/progress_tracker.py:170`)
   - Uses: `settings.redis_url` in `get_progress_tracker()`
   - No changes needed - already uses redis_url

3. **Rate Limiter** (`src/services/rate_limiter.py`)
   - Accepts Redis client as dependency (initialized elsewhere)
   - No changes needed

**External Dependencies:**
- Redis server (docker-compose for local, Azure Redis Cache for production)
- No API changes or external service integration impacts

---

## Development Context

### Relevant Existing Code

**Configuration Pattern:**
- See `database_url` field in `src/core/config.py:10` - uses same direct URL pattern
- See `sync_database_url` property (lines 43-45) - property used only for URL transformation, not building

**Redis Client Initialization:**
- `redis.asyncio.from_url()` pattern in `src/services/progress_tracker.py:37`
- Celery broker/backend URL pattern in `src/workers/celery_app.py:59-60`

**Settings Testing:**
- Existing test pattern in `tests/core/test_config.py` creates Settings instances with kwargs
- Tests assert on field values and property outputs

### Dependencies

**Framework/Libraries:**
- redis 5.0+ - Native URL parsing with `from_url()` method
- Celery 5.3+ - Native URL support for broker/backend configuration
- pydantic-settings 2.0+ - Environment variable loading and type coercion
- FastAPI 0.122 - No direct Redis dependency
- pytest 8.0+ - Testing framework

**Internal Modules:**
- `src.core.config.Settings` - Configuration class (modified)
- `src.core.config.settings` - Global settings instance (no change)
- All modules importing `settings` - No changes needed (redis_url remains accessible)

### Configuration Changes

**Environment Variables:**

REMOVE:
```bash
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=your_secure_password_here
REDIS_DB=0
```

ADD:
```bash
REDIS_URL=redis://localhost:6379
```

**Docker Compose:**
- Simplify Redis service (remove conditional password handling)
- Application services use `REDIS_URL=redis://redis:6379` (docker network hostname)

**No application code changes** in services consuming `settings.redis_url` - the property name becomes a field but remains accessible the same way.

### Existing Conventions (Brownfield)

**Code Style (pyproject.toml lines 46-80):**
- Black formatter with 100 char line length
- Ruff linter with pycodestyle, pyflakes, isort, bugbear, comprehensions, pyupgrade
- E501 ignored (line length handled by black)
- Python 3.11 target version

**Type Checking (pyproject.toml lines 82-89):**
- mypy strict mode enabled
- ignore_missing_imports: true
- Type hints required on all functions and methods

**Testing (pyproject.toml lines 91-96):**
- pytest with asyncio_mode="auto"
- Test files: `test_*.py` pattern
- Test functions: `test_*` pattern
- Verbose output with short tracebacks
- Tests in `tests/` directory mirroring `src/` structure

**Import Style:**
- Absolute imports from `src.*`
- Standard library, third-party, local imports order (ruff isort)

**Documentation Style:**
- Module docstrings at top of each file (triple quotes)
- Function/method docstrings with Args/Returns sections (Google style)
- Type hints in code, not docstrings

---

## Implementation Stack

**Runtime:**
- Python 3.11+

**Web Framework:**
- FastAPI 0.122
- uvicorn 0.30+ (ASGI server)

**Configuration:**
- pydantic 2.0+
- pydantic-settings 2.0+

**Redis:**
- redis 5.0+ (with async support)
- Celery 5.3+ (task queue)

**Testing:**
- pytest 8.0+
- pytest-asyncio 0.23+

**Code Quality:**
- black 24.0+ (formatter)
- ruff 0.5+ (linter)
- mypy 1.10+ (type checker)

**Infrastructure:**
- Docker Compose (local dev)
- Azure Redis Cache (production)

---

## Technical Details

**Redis URL Format Support:**

Both `redis` and `celery` libraries natively support these URL formats:

```
redis://[[username]:password@]host[:port][/database]
rediss://[[username]:password@]host[:port][/database]
```

- `redis://` - Non-SSL connection (local dev)
- `rediss://` - SSL/TLS connection (Azure Redis Cache)
- `:password` - Password authentication (Azure requires this)
- `/database` - Database number (0-15, optional, user specifies if needed)

**URL Parsing (handled by redis library):**
- `redis.asyncio.from_url(url)` - Async client for FastAPI services
- `Celery(broker=url, backend=url)` - Celery automatically parses

**No validation needed** - both libraries raise connection errors on invalid URLs:
- `redis.exceptions.ConnectionError` - For connection failures
- Malformed URLs cause immediate startup failure (fail-fast)

**SSL/TLS Support:**
- `rediss://` scheme automatically enables SSL
- Azure Redis Cache requires SSL (port 6380)
- Local Docker Redis uses non-SSL (port 6379)

**Connection Pooling:**
- redis library handles pooling automatically via `from_url()`
- No changes to connection pool configuration
- Existing connection behavior preserved

**Environment Variable Substitution:**
- Pydantic Settings automatically loads from .env
- Field name `redis_url` maps to env var `REDIS_URL` (case-insensitive)
- No custom parsing or validation logic needed

---

## Development Setup

**Local Development:**

```bash
# 1. Clone repo (if not already)
git clone <repo-url>
cd dsol

# 2. Create Python virtual environment
python3.11 -m venv .venv
source .venv/bin/activate

# 3. Install dependencies
pip install -e ".[dev]"

# 4. Copy environment template
cp .env.example .env

# 5. Update .env with Redis URL
# For local: REDIS_URL=redis://localhost:6379
# For Azure: REDIS_URL=rediss://:password@your-cache:6380

# 6. Start Docker services
docker-compose up -d

# 7. Run database migrations
alembic upgrade head

# 8. Start API server
uvicorn src.api.main:app --reload --host 0.0.0.0 --port 8000

# 9. Start Celery worker (separate terminal)
celery -A src.workers.celery_app worker --loglevel=info

# 10. Run tests
pytest
```

**Verification:**
```bash
# Test Redis connection
python -c "from src.core.config import settings; print(settings.redis_url)"

# Check Redis connectivity
redis-cli -h localhost -p 6379 ping
```

---

## Implementation Guide

### Setup Steps

1. **Create feature branch**
   ```bash
   git checkout -b feature/redis-url-simplification
   ```

2. **Verify current tests pass**
   ```bash
   pytest tests/core/test_config.py -v
   ```

3. **Verify development environment**
   ```bash
   docker-compose ps  # Ensure Redis is running
   ```

### Implementation Steps

**Step 1: Update Settings Class**
- File: `src/core/config.py`
- Action: Remove 4 individual Redis fields (lines 13-16)
- Action: Add `redis_url: str = "redis://localhost:6379"` field
- Action: Remove `@property redis_url()` method (lines 48-59)
- Action: Update comment on line 39 (remove reference to old REDIS_URL)

**Step 2: Update Environment Template**
- File: `.env.example`
- Action: Replace Redis configuration block (lines 11-20)
- Action: Add new REDIS_URL with local and Azure examples
- Action: Add comments explaining format

**Step 3: Update Docker Compose**
- File: `docker-compose.yml`
- Action: Simplify Redis service configuration (lines 21-36)
- Action: Remove conditional password handling
- Action: Keep healthcheck (adjust if using password in local dev)

**Step 4: Update Configuration Tests**
- File: `tests/core/test_config.py`
- Action: Remove tests for old redis_url property (lines 8-60, all 6 tests)
- Action: Add 3 new tests:
  - `test_redis_url_default()` - Verify default value
  - `test_redis_url_local()` - Test non-SSL URL
  - `test_redis_url_azure()` - Test SSL URL with password

**Step 5: Update Documentation**
- File: `README.md`
- Action: Find Redis configuration section
- Action: Replace with new REDIS_URL format and examples

**Step 6: Verify Integration**
- Action: Run all tests: `pytest`
- Action: Start services: `docker-compose up -d`
- Action: Start API: `uvicorn src.api.main:app --reload`
- Action: Start worker: `celery -A src.workers.celery_app worker`
- Action: Test health endpoint: `curl http://localhost:8000/health`

### Testing Strategy

**Unit Tests (tests/core/test_config.py):**

1. **Test Default Value:**
   ```python
   def test_redis_url_default():
       """Test redis_url has correct default value."""
       settings = Settings()
       assert settings.redis_url == "redis://localhost:6379"
   ```

2. **Test Local Development URL:**
   ```python
   def test_redis_url_local():
       """Test redis_url accepts local non-SSL URL."""
       settings = Settings(redis_url="redis://localhost:6379/2")
       assert settings.redis_url == "redis://localhost:6379/2"
   ```

3. **Test Azure SSL URL:**
   ```python
   def test_redis_url_azure_ssl():
       """Test redis_url accepts Azure SSL URL."""
       url = "rediss://:mypass@cache.redis.cache.windows.net:6380"
       settings = Settings(redis_url=url)
       assert settings.redis_url == url
   ```

**Integration Tests:**
- Existing integration tests in `tests/extraction/test_integration.py` should pass unchanged
- Existing progress tracker tests in `tests/services/test_progress_tracker.py` should pass unchanged
- No new integration tests needed (behavior unchanged)

**Manual Testing Checklist:**
- [ ] API starts successfully with new REDIS_URL
- [ ] Celery worker starts and connects to Redis
- [ ] Health endpoint returns Redis healthy status
- [ ] Document upload triggers Celery task successfully
- [ ] Progress tracking updates visible via API
- [ ] Rate limiting works (test with multiple rapid requests)

### Acceptance Criteria

1. **Configuration Simplification:**
   - [ ] Settings class has single `redis_url` field (no individual fields)
   - [ ] No `@property redis_url()` method exists
   - [ ] `.env.example` shows REDIS_URL format with examples

2. **Backward Compatibility Removed:**
   - [ ] Old env vars (REDIS_HOST, REDIS_PORT, etc.) are not referenced
   - [ ] Settings class does not accept old field names

3. **URL Format Support:**
   - [ ] Supports `redis://` URLs (non-SSL local)
   - [ ] Supports `rediss://` URLs (SSL Azure)
   - [ ] Works with and without password
   - [ ] User provides database number in URL if needed

4. **Existing Functionality Preserved:**
   - [ ] Celery connects to Redis using settings.redis_url
   - [ ] Progress tracker initializes correctly
   - [ ] Rate limiter works (Redis client initialized elsewhere)
   - [ ] Health checks report Redis status

5. **Testing:**
   - [ ] All new unit tests pass
   - [ ] All existing tests pass (no regressions)
   - [ ] Manual verification checklist complete

6. **Documentation:**
   - [ ] README.md updated with new REDIS_URL format
   - [ ] .env.example has clear examples
   - [ ] Comments in code reference new approach

---

## Developer Resources

### File Paths Reference

**Core Implementation:**
- `/Users/nguyen/Work/DSOL/src/core/config.py` - Settings class (MODIFY)

**Environment Configuration:**
- `/Users/nguyen/Work/DSOL/.env.example` - Environment template (MODIFY)

**Infrastructure:**
- `/Users/nguyen/Work/DSOL/docker-compose.yml` - Docker services (MODIFY)

**Testing:**
- `/Users/nguyen/Work/DSOL/tests/core/test_config.py` - Config unit tests (MODIFY)

**Documentation:**
- `/Users/nguyen/Work/DSOL/README.md` - Main documentation (MODIFY)
- `/Users/nguyen/Work/DSOL/docs/integration-guide-structured-store.md` - Integration guide (CHECK/MODIFY)

### Key Code Locations

**Settings Class:**
- `src/core/config.py:6-40` - Settings class definition
- `src/core/config.py:13-16` - Current Redis fields (REMOVE)
- `src/core/config.py:48-59` - Current redis_url property (REMOVE)
- `src/core/config.py:63` - Global settings instance (no change)

**Redis Consumers:**
- `src/workers/celery_app.py:59` - Celery broker configuration (uses settings.redis_url)
- `src/workers/celery_app.py:60` - Celery backend configuration (uses settings.redis_url)
- `src/services/progress_tracker.py:37` - Progress tracker initialization (uses redis_url param)
- `src/services/progress_tracker.py:170` - Progress tracker factory (uses settings.redis_url)

**Environment Configuration:**
- `.env.example:11-20` - Redis configuration block (REPLACE)
- `docker-compose.yml:21-36` - Redis service definition (SIMPLIFY)

**Tests:**
- `tests/core/test_config.py:8-60` - All redis_url tests (REPLACE)

### Testing Locations

**Unit Tests:**
- `tests/core/test_config.py` - Configuration unit tests

**Integration Tests:**
- `tests/extraction/test_integration.py` - Pipeline integration (uses Redis)
- `tests/services/test_progress_tracker.py` - Progress tracker (uses Redis)

**API Tests:**
- `tests/api/test_document_status.py` - Document status endpoints (indirect Redis usage)

**Manual Testing:**
- Health endpoint: `GET http://localhost:8000/health`
- Redis CLI: `redis-cli -h localhost -p 6379`

### Documentation to Update

**Primary Documentation:**
- `README.md` - Redis configuration section
  - Show new REDIS_URL format
  - Update environment setup instructions
  - Add examples for local and Azure

**Environment Template:**
- `.env.example` - Redis configuration
  - Replace 4-variable block with single REDIS_URL
  - Add comment explaining format
  - Show both local and Azure examples

**Integration Guides (if applicable):**
- `docs/integration-guide-structured-store.md`
  - Check if it references Redis configuration
  - Update if needed

**Optional Documentation:**
- Add migration note for existing deployments
- Update any deployment guides with new env var

---

## UX/UI Considerations

No UI/UX impact - backend configuration change only.

**Developer Experience Improvements:**
- Simpler configuration (1 env var instead of 4)
- Copy-paste Azure connection string directly
- Less room for configuration errors
- Consistent with DATABASE_URL pattern

---

## Testing Approach

### Test Framework & Standards

**Framework:** pytest 8.0+ with pytest-asyncio 0.23+

**Configuration (pyproject.toml lines 91-96):**
- Test discovery: `test_*.py` files
- Test functions: `test_*` pattern
- Async mode: auto
- Verbose output with short tracebacks

**Test Organization:**
- Unit tests: `tests/` directory mirroring `src/` structure
- Test file per source file: `tests/core/test_config.py` for `src/core/config.py`

**Assertion Style:**
- Use `assert` statements (not expect/should)
- Clear assertion messages when needed
- Type hints on test functions

**Coverage Requirements:**
- All new code paths covered
- Existing tests must pass (no regressions)
- Integration tests verify end-to-end behavior

### Testing Approach

**Unit Tests (New):**

1. **Default Value Test:**
   - Verify Settings() creates redis_url with default value
   - Assert: `settings.redis_url == "redis://localhost:6379"`

2. **Local URL Test:**
   - Pass custom local URL with database number
   - Assert: URL stored correctly

3. **Azure SSL URL Test:**
   - Pass `rediss://` URL with password and custom port
   - Assert: URL stored correctly

**Unit Tests (Existing - should still pass):**
- Database URL tests (unchanged)
- Settings loading tests (unchanged)

**Integration Tests (Existing - should still pass):**
- `tests/extraction/test_integration.py` - Pipeline uses Redis via Celery
- `tests/services/test_progress_tracker.py` - Progress tracker Redis operations
- `tests/api/test_document_status.py` - API endpoints using Redis indirectly

**Manual Verification:**
1. Start all services (docker-compose up)
2. Check health endpoint returns Redis connected
3. Upload document and verify Celery task executes
4. Check progress tracking endpoint updates
5. Test rate limiting with rapid requests

**Regression Prevention:**
- All existing tests must pass
- No changes to Redis client behavior
- Connection pooling unchanged
- Error handling unchanged

---

## Deployment Strategy

### Deployment Steps

**Development Environment:**
1. Update `.env` file with new REDIS_URL format
2. Restart services: `docker-compose down && docker-compose up -d`
3. Restart API: `uvicorn src.api.main:app --reload`
4. Restart worker: `celery -A src.workers.celery_app worker --loglevel=info`

**Staging/Production:**
1. Update environment variables in deployment platform:
   - Remove: `REDIS_HOST`, `REDIS_PORT`, `REDIS_PASSWORD`, `REDIS_DB`
   - Add: `REDIS_URL=rediss://:password@cache.redis.cache.windows.net:6380`
2. Deploy new code version
3. Verify health endpoint reports Redis connected
4. Monitor logs for Redis connection errors
5. Test document upload and processing

**Azure-Specific:**
- Get connection string from Azure Portal → Redis Cache → Access keys → Primary connection string
- Use the provided `rediss://` URL directly
- Ensure port 6380 is accessible (default for Azure Redis SSL)

### Rollback Plan

**If deployment fails:**

1. **Immediate Rollback:**
   ```bash
   # Revert to previous git commit
   git revert <commit-hash>
   git push
   ```

2. **Environment Variable Rollback:**
   - Restore old environment variables:
     ```bash
     REDIS_HOST=your-cache.redis.cache.windows.net
     REDIS_PORT=6380
     REDIS_PASSWORD=your-password
     REDIS_DB=0
     ```
   - Remove new REDIS_URL variable

3. **Verify Services:**
   - Check health endpoint
   - Test document processing
   - Monitor error logs

**Rollback is simple** because:
- No database schema changes
- No data migrations
- Redis connection behavior unchanged
- Only configuration format changed

### Monitoring

**Post-Deployment Monitoring:**

1. **Application Logs:**
   - Watch for Redis connection errors
   - Monitor Celery worker startup logs
   - Check for SettingsConfigDict validation errors

2. **Health Checks:**
   - API health endpoint: `GET /health`
   - Should return: `{"redis": "connected", ...}`
   - Alert if Redis status changes to "disconnected"

3. **Redis Metrics (Azure Portal):**
   - Connection count (should remain stable)
   - Hit rate (should remain consistent)
   - CPU usage (should remain similar)

4. **Application Metrics:**
   - Task queue length (should not grow)
   - Document processing latency (should remain consistent)
   - API response times (should be unaffected)

**Red Flags:**
- Redis connection errors in logs
- Celery workers failing to start
- Health endpoint reporting Redis disconnected
- Tasks not being processed
- Progress tracking not updating

**Monitoring Duration:**
- First 15 minutes: Active monitoring
- First hour: Periodic checks
- First 24 hours: Standard monitoring with alerts

---

