Commit 9f2f3ff
committed
(improvement) row_parser: cache ParseDesc for prepared statements
Cache the ParseDesc object constructed in recv_results_rows() so that
repeated executions of the same prepared statement skip the list
comprehensions, ColDesc construction, and make_deserializers() call.
The cache is keyed by id(column_metadata). For prepared statements the
result_metadata list is stored on PreparedStatement and reused, so id()
is stable. On cache hit we verify object identity (cached_ref is
column_metadata) and that session-level settings (column_encryption_policy,
protocol_version) still match.
Implementation details:
- _get_or_build_parse_desc: cached path, used only when column_metadata
comes from result_metadata (prepared statements with stable id()).
- _build_parse_desc: uncached path, used for inline metadata from
non-prepared queries that creates a fresh list every execution.
- Cache is bounded to 256 entries; cleared entirely when full.
- Returns only (desc, column_names, column_types) to the caller,
avoiding exposure of internal cache fields.
- Thread-safety: dict get/set are atomic in CPython (GIL), but
concurrent cache misses may cause redundant construction (benign).
A clear_parse_desc_cache() and get_parse_desc_cache_size() function
are exposed for testing.
## Benchmark results (median, pytest-benchmark)
### ParseDesc construction only (reference benchmarks)
| Columns | **Before** (original) | **After** (with cache) |
|---------|-----------------------|------------------------|
| 5 cols | 3,966 ns | 191 ns |
| 10 cols | 5,730 ns | 175 ns |
| 20 cols | 9,266 ns | 166 ns |
| 50 cols | 19,388 ns | 193 ns |
### Full pipeline integration (recv_results_rows through Cython)
| Scenario | **Before** (original) | **After** (with cache) |
|-------------------|-----------------------|------------------------|
| 1 row x 10 col | 40,867 ns | 2,977 ns |
| 100 rows x 5 col | 145,584 ns | 73,206 ns |
| 1000 rows x 5 col | 1,099,825 ns | 999,517 ns |
For small result sets (single-row lookups common with prepared statements),
ParseDesc construction is a large fraction of the total response-path cost.
Caching eliminates it entirely after the first execution.
All 623 unit tests pass (16 skipped - pre-existing).1 parent 9c53d78 commit 9f2f3ff
2 files changed
Lines changed: 639 additions & 5 deletions
0 commit comments