|
| 1 | +# IRIS Connection Architecture Analysis |
| 2 | + |
| 3 | +**Date**: 2025-11-23 |
| 4 | +**Feature**: 051-simplify-iris-connection |
| 5 | +**Status**: Specification Complete |
| 6 | + |
| 7 | +## Executive Summary |
| 8 | + |
| 9 | +Current IRIS connection architecture has **6 separate components** across multiple layers, causing significant developer confusion and onboarding friction. This analysis maps the current complexity and proposes simplification to **1-2 components**. |
| 10 | + |
| 11 | +**Key Finding**: 156 files import connection-related modules, but most just need a simple "give me a connection" API. |
| 12 | + |
| 13 | +## Current Architecture (6 Components) |
| 14 | + |
| 15 | +### 1. iris_dbapi_connector.py |
| 16 | +**Location**: `iris_vector_rag/common/iris_dbapi_connector.py` |
| 17 | +**Lines of Code**: ~250 |
| 18 | +**Purpose**: Low-level IRIS DBAPI module import with UV environment fallback |
| 19 | +**Key Function**: `get_iris_dbapi_connection()` |
| 20 | +**Critical**: Contains UV compatibility fix (Issue #5) - MUST preserve |
| 21 | + |
| 22 | +```python |
| 23 | +# Current API |
| 24 | +from iris_vector_rag.common.iris_dbapi_connector import get_iris_dbapi_connection |
| 25 | + |
| 26 | +conn = get_iris_dbapi_connection() |
| 27 | +``` |
| 28 | + |
| 29 | +### 2. ConnectionManager (Generic Abstraction) |
| 30 | +**Location**: `iris_vector_rag/common/connection_manager.py` |
| 31 | +**Lines of Code**: ~150 |
| 32 | +**Purpose**: High-level connection management with JDBC/ODBC/DBAPI fallback |
| 33 | +**Key Class**: `ConnectionManager(connection_type="odbc")` |
| 34 | + |
| 35 | +**Issues**: |
| 36 | +- Unnecessary abstraction for most use cases |
| 37 | +- Fallback logic adds complexity |
| 38 | +- Overlaps with IRISConnectionManager |
| 39 | + |
| 40 | +```python |
| 41 | +# Current API |
| 42 | +from iris_vector_rag.common.connection_manager import ConnectionManager |
| 43 | + |
| 44 | +manager = ConnectionManager(connection_type="dbapi") |
| 45 | +conn = manager.connect() |
| 46 | +``` |
| 47 | + |
| 48 | +### 3. IRISConnectionManager (IRIS-Specific) |
| 49 | +**Location**: `iris_vector_rag/common/iris_connection_manager.py` |
| 50 | +**Lines of Code**: ~200 |
| 51 | +**Purpose**: DBAPI-first connection with automatic environment detection |
| 52 | +**Key Class**: `IRISConnectionManager(prefer_dbapi=True)` |
| 53 | + |
| 54 | +**Issues**: |
| 55 | +- Duplicates functionality from ConnectionManager |
| 56 | +- DBAPI vs JDBC priority adds confusion |
| 57 | +- Environment detection complexity |
| 58 | + |
| 59 | +```python |
| 60 | +# Current API |
| 61 | +from iris_vector_rag.common.iris_connection_manager import IRISConnectionManager |
| 62 | + |
| 63 | +manager = IRISConnectionManager(prefer_dbapi=True) |
| 64 | +conn = manager.get_connection() |
| 65 | +``` |
| 66 | + |
| 67 | +### 4. IRISConnectionPool (API/Production) |
| 68 | +**Location**: `iris_vector_rag/common/connection_pool.py` |
| 69 | +**Lines of Code**: ~250 |
| 70 | +**Purpose**: Thread-safe connection pooling for REST API server |
| 71 | +**Key Class**: `IRISConnectionPool(pool_size=20, max_overflow=10)` |
| 72 | + |
| 73 | +**Issues**: |
| 74 | +- Designed for production API, but concept bleeds into tests |
| 75 | +- Most tests don't need real pooling |
| 76 | + |
| 77 | +```python |
| 78 | +# Current API (production) |
| 79 | +from iris_vector_rag.common.connection_pool import IRISConnectionPool |
| 80 | + |
| 81 | +pool = IRISConnectionPool( |
| 82 | + host="localhost", |
| 83 | + port=1972, |
| 84 | + namespace="USER", |
| 85 | + username="user", |
| 86 | + password="pass", |
| 87 | + pool_size=20, |
| 88 | + max_overflow=10 |
| 89 | +) |
| 90 | + |
| 91 | +with pool.acquire() as conn: |
| 92 | + # Use connection |
| 93 | + pass |
| 94 | +``` |
| 95 | + |
| 96 | +### 5. ConnectionPool (Testing - Backend Mode) |
| 97 | +**Location**: `iris_vector_rag/testing/connection_pool.py` |
| 98 | +**Lines of Code**: ~150 |
| 99 | +**Purpose**: Mode-aware connection pooling for tests (Community vs Enterprise) |
| 100 | +**Key Class**: `ConnectionPool(mode=BackendMode.COMMUNITY)` |
| 101 | + |
| 102 | +**Issues**: |
| 103 | +- Semaphore-based pooling is a mock - doesn't provide real connections |
| 104 | +- Confusing name: "pool" but it's really just a limit enforcer |
| 105 | +- Test fixtures named `connection_pool` but don't actually pool |
| 106 | + |
| 107 | +```python |
| 108 | +# Current API (testing) |
| 109 | +from iris_vector_rag.testing.connection_pool import ConnectionPool |
| 110 | +from iris_vector_rag.testing.backend_manager import BackendMode |
| 111 | + |
| 112 | +pool = ConnectionPool(mode=BackendMode.COMMUNITY) # max 1 connection |
| 113 | +with pool.acquire(timeout=30.0) as conn: |
| 114 | + # conn is a mock - actual connection comes from iris_dbapi_connector |
| 115 | + pass |
| 116 | +``` |
| 117 | + |
| 118 | +### 6. Backend Mode Configuration |
| 119 | +**Location**: `.specify/config/backend_modes.yaml` + loading utilities |
| 120 | +**Purpose**: Configure Community (1 connection) vs Enterprise (999 connections) limits |
| 121 | + |
| 122 | +**Issues**: |
| 123 | +- Adds configuration layer that should be automatic |
| 124 | +- Edition detection should query database, not config file |
| 125 | +- Forces manual configuration |
| 126 | + |
| 127 | +```yaml |
| 128 | +# .specify/config/backend_modes.yaml |
| 129 | +mode: community |
| 130 | +max_connections: 1 |
| 131 | +``` |
| 132 | +
|
| 133 | +## Test Fixture Confusion |
| 134 | +
|
| 135 | +**Problem**: Test fixture named `connection_pool` doesn't actually provide pooled connections. |
| 136 | + |
| 137 | +```python |
| 138 | +# tests/conftest.py |
| 139 | +@pytest.fixture(scope="session") |
| 140 | +def connection_pool(backend_configuration): |
| 141 | + """Creates ConnectionPool with mode-appropriate limits""" |
| 142 | + from iris_vector_rag.testing.connection_pool import ConnectionPool |
| 143 | + return ConnectionPool(mode=backend_configuration.mode) |
| 144 | +
|
| 145 | +@pytest.fixture |
| 146 | +def iris_connection(connection_pool): |
| 147 | + """Acquires connection from pool""" |
| 148 | + with connection_pool.acquire(timeout=30.0) as conn: |
| 149 | + yield conn # conn is a mock semaphore slot, not a real connection |
| 150 | +``` |
| 151 | + |
| 152 | +**Developer Confusion**: |
| 153 | +- "Is `iris_connection` a real connection or mocked?" |
| 154 | +- "What does `connection_pool.acquire()` actually return?" |
| 155 | +- "Where does the actual IRIS connection come from?" |
| 156 | + |
| 157 | +## Usage Analysis |
| 158 | + |
| 159 | +**Files Importing Connection Modules**: 156 files |
| 160 | + |
| 161 | +**Breakdown by Component**: |
| 162 | +- `iris_dbapi_connector`: ~50 files (direct low-level usage) |
| 163 | +- `ConnectionManager`: ~30 files (generic abstraction) |
| 164 | +- `IRISConnectionManager`: ~20 files (IRIS-specific) |
| 165 | +- `ConnectionPool` (testing): ~40 files (pytest fixtures) |
| 166 | +- `IRISConnectionPool` (production): ~10 files (API server only) |
| 167 | +- Backend configuration: ~6 files (test setup) |
| 168 | + |
| 169 | +**Key Insight**: Most usage (90%+) just needs: "give me a working IRIS connection" |
| 170 | + |
| 171 | +## Proposed Simplified Architecture (1-2 Components) |
| 172 | + |
| 173 | +### Component 1: iris_connection.py (Unified Module) |
| 174 | + |
| 175 | +Single module with two APIs: |
| 176 | +1. **Simple Connection API** (90% use case) |
| 177 | +2. **Optional Pooling API** (10% use case - high concurrency) |
| 178 | + |
| 179 | +```python |
| 180 | +# New simplified API |
| 181 | +from iris_vector_rag.common import get_iris_connection |
| 182 | +
|
| 183 | +# Simple usage (90% of cases) |
| 184 | +conn = get_iris_connection() # Auto-detect edition, use env vars |
| 185 | +
|
| 186 | +# With explicit params |
| 187 | +conn = get_iris_connection( |
| 188 | + host="localhost", |
| 189 | + port=1972, |
| 190 | + namespace="USER", |
| 191 | + username="user", |
| 192 | + password="pass" |
| 193 | +) |
| 194 | +
|
| 195 | +# Optional pooling (10% of cases - API server) |
| 196 | +from iris_vector_rag.common import IRISConnectionPool |
| 197 | +
|
| 198 | +pool = IRISConnectionPool(max_connections=20) |
| 199 | +with pool.acquire() as conn: |
| 200 | + # Use connection |
| 201 | + pass |
| 202 | +``` |
| 203 | + |
| 204 | +**Features**: |
| 205 | +- Automatic edition detection (Community vs Enterprise) |
| 206 | +- Environment variable fallback |
| 207 | +- Preserves UV compatibility fix |
| 208 | +- Clear separation: basic vs pooling |
| 209 | +- No configuration files required |
| 210 | + |
| 211 | +### Component 2: Preserved UV Handling |
| 212 | + |
| 213 | +Keep `_get_iris_dbapi_module()` function from iris_dbapi_connector.py, integrated into new module. |
| 214 | + |
| 215 | +## Migration Strategy |
| 216 | + |
| 217 | +### Phase 1: Create Unified Module |
| 218 | +- Create `iris_vector_rag/common/iris_connection.py` |
| 219 | +- Implement `get_iris_connection()` API |
| 220 | +- Implement `IRISConnectionPool` class |
| 221 | +- Preserve UV compatibility fix |
| 222 | +- Add automatic edition detection |
| 223 | + |
| 224 | +### Phase 2: Update Test Fixtures |
| 225 | +- Replace `connection_pool` fixture with direct `get_iris_connection()` |
| 226 | +- Rename fixtures for clarity: `iris_connection` → `real_iris_connection` |
| 227 | +- Remove backend mode configuration dependency |
| 228 | + |
| 229 | +### Phase 3: Gradual Migration |
| 230 | +- Keep old APIs working (deprecation warnings) |
| 231 | +- Update core modules first (storage, pipelines) |
| 232 | +- Update test suite incrementally |
| 233 | +- Update documentation and examples |
| 234 | + |
| 235 | +### Phase 4: Deprecation |
| 236 | +- Add clear deprecation warnings to old APIs |
| 237 | +- Provide migration guide |
| 238 | +- Set timeline for removal (e.g., 3 versions) |
| 239 | + |
| 240 | +### Phase 5: Cleanup |
| 241 | +- Remove old connection modules |
| 242 | +- Remove backend mode configuration |
| 243 | +- Update all imports |
| 244 | + |
| 245 | +## Files to Modify |
| 246 | + |
| 247 | +### New Files: |
| 248 | +1. `iris_vector_rag/common/iris_connection.py` (new unified module) |
| 249 | + |
| 250 | +### Files to Deprecate: |
| 251 | +1. `iris_vector_rag/common/connection_manager.py` |
| 252 | +2. `iris_vector_rag/common/iris_connection_manager.py` |
| 253 | +3. `iris_vector_rag/testing/connection_pool.py` |
| 254 | +4. `.specify/config/backend_modes.yaml` |
| 255 | + |
| 256 | +### Files to Preserve (with integration): |
| 257 | +1. `iris_vector_rag/common/iris_dbapi_connector.py` (UV fix logic) |
| 258 | +2. `iris_vector_rag/common/connection_pool.py` (production API pooling) |
| 259 | + |
| 260 | +### Test Files to Update: |
| 261 | +1. `tests/conftest.py` (update fixtures) |
| 262 | +2. All test files using `connection_pool` fixture (~40 files) |
| 263 | + |
| 264 | +## Backward Compatibility Plan |
| 265 | + |
| 266 | +**Keep Both APIs Working During Transition**: |
| 267 | + |
| 268 | +```python |
| 269 | +# Old API (deprecated, but works) |
| 270 | +from iris_vector_rag.common.connection_manager import ConnectionManager |
| 271 | +manager = ConnectionManager(connection_type="dbapi") |
| 272 | +conn = manager.connect() |
| 273 | +# Warning: "ConnectionManager deprecated. Use get_iris_connection() instead." |
| 274 | +
|
| 275 | +# New API (recommended) |
| 276 | +from iris_vector_rag.common import get_iris_connection |
| 277 | +conn = get_iris_connection() |
| 278 | +``` |
| 279 | + |
| 280 | +**Timeline**: |
| 281 | +- Version N: Introduce new API, deprecate old |
| 282 | +- Version N+1: Continue supporting both, louder warnings |
| 283 | +- Version N+2: Continue supporting both, very loud warnings |
| 284 | +- Version N+3: Remove old APIs (breaking change, major version bump) |
| 285 | + |
| 286 | +## Success Metrics |
| 287 | + |
| 288 | +1. **Code Reduction**: 6 components → 1-2 components ✅ |
| 289 | +2. **File Count**: ~1100 LOC → ~300 LOC (73% reduction) |
| 290 | +3. **Import Clarity**: Single obvious import for 90% of cases ✅ |
| 291 | +4. **Understanding Time**: <5 minutes for new developers ✅ |
| 292 | +5. **Test Clarity**: Obviously real connections vs mocks ✅ |
| 293 | +6. **Zero Breaking Changes**: During migration period ✅ |
| 294 | + |
| 295 | +## Open Questions |
| 296 | + |
| 297 | +1. **Edition Detection**: How to reliably detect Community vs Enterprise? |
| 298 | + - Query system table? `SELECT * FROM %SYS.License` |
| 299 | + - Check license key format? |
| 300 | + - Configuration file fallback? |
| 301 | + |
| 302 | +2. **Automatic Connection Limits**: Should we enforce limits automatically? |
| 303 | + - Community: Warn if >1 connection attempt? |
| 304 | + - Enterprise: No limits? |
| 305 | + - Or let developer manage? |
| 306 | + |
| 307 | +3. **Pooling Strategy**: Should basic API include implicit pooling? |
| 308 | + - Current thinking: No - keep pooling explicit and optional |
| 309 | + - Rationale: Most tests/scripts don't need pooling |
| 310 | + |
| 311 | +4. **Connection Sharing**: Should we cache connections at module level? |
| 312 | + - Pro: Reduces connection overhead |
| 313 | + - Con: Adds state management complexity |
| 314 | + - Recommendation: Start without caching, add if needed |
| 315 | + |
| 316 | +## Next Steps |
| 317 | + |
| 318 | +1. ✅ Complete specification (this document) |
| 319 | +2. [ ] Prototype `iris_connection.py` module |
| 320 | +3. [ ] Design edition detection mechanism |
| 321 | +4. [ ] Write contract tests for new API |
| 322 | +5. [ ] Implement new module with UV fix preserved |
| 323 | +6. [ ] Update test fixtures |
| 324 | +7. [ ] Create migration guide |
| 325 | +8. [ ] Gradual rollout to codebase |
| 326 | + |
| 327 | +## References |
| 328 | + |
| 329 | +- **Feature Branch**: `051-simplify-iris-connection` |
| 330 | +- **Specification**: `specs/051-simplify-iris-connection/spec.md` |
| 331 | +- **TODO Item**: `TODO.md` lines 70-109 |
| 332 | +- **UV Compatibility Fix**: `iris_dbapi_connector.py:252` (commit `478d3f1b`) |
| 333 | +- **Backend Mode Feature**: Feature 035-make-2-modes |
0 commit comments