Commit 0315a68
feat: implement connection pooling for supported providers (#194)
* docs: add connection pooling implementation plan
Create detailed implementation plan for HTTP client connection pooling
based on the client-pooling-analysis document. The plan outlines:
- Phase 1: Infrastructure (HttpClientPool class, config settings, state integration)
- Phase 2: Provider integration (Voyage AI, Cohere, Qdrant considerations)
- Testing strategy and rollback plan
- Provider-specific connection pool settings
This addresses connection overhead, potential exhaustion during high-load
indexing, and Qdrant timeout issues (httpcore.ReadError).
* feat: implement HTTP client connection pooling for providers
Add centralized HTTP client pooling infrastructure to improve connection
reuse across Voyage AI and Cohere providers during high-load operations.
Key changes:
- Create HttpClientPool class with singleton pattern for application-wide
connection reuse (src/codeweaver/common/http_pool.py)
- Integrate http_pool into CodeWeaverState with proper cleanup on shutdown
- Update ProviderRegistry to inject pooled httpx clients for Voyage/Cohere
- Add provider-specific settings (50 max connections, 90s read timeout)
- Add comprehensive unit tests for HttpClientPool
This addresses:
- Connection overhead during batch embedding operations
- Potential connection exhaustion during large indexing jobs
- Improved reliability with HTTP/2 support for better multiplexing
The implementation includes graceful fallbacks - if pooling fails,
providers will create their own clients as before.
* refactor: address PR #194 review suggestions for HTTP connection pooling
- Add thread safety via asyncio.Lock for coroutine-safe client creation
- Make reset_http_pool() async to properly close clients before reset
- Add reset_http_pool_sync() for testing fixtures without cleanup
- Unify dual singleton pattern (remove redundant module-level instance)
- Change INFO logging to DEBUG for client creation (reduce noise)
- Narrow exception types (httpx.HTTPError, OSError) instead of Exception
- Merge duplicate VOYAGE/COHERE conditionals using _POOLED_HTTP_PROVIDERS
- Use get_client_sync() in provider registry for sync context
- Add comprehensive tests:
- Test override settings are actually applied to clients
- Test aclose() error handling (graceful degradation)
- Test PoolTimeouts immutability
- Test concurrent get_client calls (thread safety)
- Test _get_pooled_httpx_client for various providers
- Update implementation plan status to Implemented
- Export reset_http_pool_sync in common/__init__.py
* feat: expand HTTP connection pooling to OpenAI-compatible and Mistral providers
Extend connection pooling support to 11 providers:
- OpenAI-compatible (8): OpenAI, Azure, Fireworks, Groq, Together, Ollama, Cerebras, Heroku
- Voyage and Cohere (existing)
- Mistral (new)
Changes:
- OpenAI factory: Accept http_client parameter for AsyncOpenAI
- Mistral provider: Accept httpx_client mapped to async_client
- Provider registry: Convert _POOLED_HTTP_PROVIDERS to dict mapping provider -> param name
- Registry _instantiate_client: Use provider-specific parameter names
- Tests: Add tests for OpenAI, Mistral pooling; update provider mapping tests
- Docs: Update implementation plan with full provider table
Provider parameter mapping:
- httpx_client: Voyage, Cohere, Mistral
- http_client: OpenAI, Azure, Fireworks, Groq, Together, Ollama, Cerebras, Heroku
Not supported (no httpx injection):
- Bedrock (uses boto3)
- HuggingFace (global factory pattern)
- Google GenAI (args only, not full client)
* fix: add thread safety to singleton and sync client creation
Address Copilot review suggestions from PR #194:
1. get_instance() race condition: Add double-checked locking with
threading.Lock to prevent multiple singleton instances when called
concurrently from different threads
2. get_client_sync() race condition: Add thread-safe double-checked
locking pattern using _sync_lock to prevent duplicate client creation
when called concurrently during provider initialization
3. Remove unused 'patch' import from test_http_pool.py
Thread safety guarantees are now documented in the module docstring:
- Singleton: threading.Lock with double-checked locking
- Async clients: asyncio.Lock for coroutine safety
- Sync clients: threading.Lock for thread safety
* fix: resolve test failures for HTTP connection pooling
- Mark _POOLED_HTTP_PROVIDERS as ClassVar to prevent Pydantic treating
it as a private attribute
- Fix import path: use codeweaver.providers.provider for ProviderKind
instead of non-existent codeweaver.providers.kinds
- Remove _limits assertions from tests since httpx.AsyncClient doesn't
expose limits directly (timeout assertions are sufficient)
* Apply suggestions from code review
Co-authored-by: Copilot <[email protected]>
Signed-off-by: Adam Poulemanos <[email protected]>
* Apply suggestions from code review
Co-authored-by: Copilot <[email protected]>
Signed-off-by: Adam Poulemanos <[email protected]>
* Apply suggestions from code review
Co-authored-by: Copilot <[email protected]>
Signed-off-by: Adam Poulemanos <[email protected]>
* docs: document client caching behavior for HTTP pool
Add Note section to get_client() and get_client_sync() docstrings clarifying
that clients are cached by name only, not by override parameters. Subsequent
calls with different overrides will return the originally cached client.
This is intentional behavior for connection pooling - each provider should
use consistent settings, which ProviderRegistry ensures.
* fix: address Copilot review suggestions for HTTP pool
1. asyncio.Lock event loop binding issue:
- Changed _async_lock to be lazily created on first use
- asyncio.Lock is bound to the event loop it's created in, so lazy
creation ensures it's bound to the correct loop
- Renamed _lock to _async_lock for clarity
2. Race condition in fast-path checks:
- Changed if-check to try-except pattern in get_client() and
get_client_sync() to handle client removal between check and return
- Prevents KeyError if close_client/close_all removes client mid-check
3. Sync/async mixing warning:
- Added Warning section to get_client_sync() docstring explaining
that sync and async methods should not be mixed
4. Walrus operator clarity:
- Replaced walrus operator with explicit get() and None check in
provider.py for better readability
---------
Signed-off-by: Adam Poulemanos <[email protected]>
Co-authored-by: Claude <[email protected]>
Co-authored-by: Copilot <[email protected]>1 parent 043d49a commit 0315a68
File tree
8 files changed
+1345
-1
lines changed- claudedocs
- src/codeweaver
- common
- registry
- providers/embedding/providers
- server
- tests/unit/common
8 files changed
+1345
-1
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
15 | 23 | | |
16 | 24 | | |
17 | 25 | | |
| |||
103 | 111 | | |
104 | 112 | | |
105 | 113 | | |
| 114 | + | |
106 | 115 | | |
107 | 116 | | |
108 | 117 | | |
109 | 118 | | |
110 | 119 | | |
111 | 120 | | |
112 | 121 | | |
| 122 | + | |
| 123 | + | |
113 | 124 | | |
114 | 125 | | |
115 | 126 | | |
| |||
140 | 151 | | |
141 | 152 | | |
142 | 153 | | |
| 154 | + | |
143 | 155 | | |
144 | 156 | | |
145 | 157 | | |
| |||
169 | 181 | | |
170 | 182 | | |
171 | 183 | | |
| 184 | + | |
| 185 | + | |
172 | 186 | | |
173 | 187 | | |
174 | 188 | | |
| |||
203 | 217 | | |
204 | 218 | | |
205 | 219 | | |
| 220 | + | |
206 | 221 | | |
207 | 222 | | |
208 | 223 | | |
209 | 224 | | |
210 | 225 | | |
211 | 226 | | |
212 | 227 | | |
| 228 | + | |
| 229 | + | |
213 | 230 | | |
214 | 231 | | |
215 | 232 | | |
| |||
240 | 257 | | |
241 | 258 | | |
242 | 259 | | |
| 260 | + | |
243 | 261 | | |
244 | 262 | | |
245 | 263 | | |
| |||
269 | 287 | | |
270 | 288 | | |
271 | 289 | | |
| 290 | + | |
| 291 | + | |
272 | 292 | | |
273 | 293 | | |
274 | 294 | | |
| |||
0 commit comments