Skip to content

Commit 35d30e3

Browse files
committed
fix: use cqltype objects as cache keys instead of id() to prevent GC/reuse issues
id()-based cache keys are unsafe for non-singleton parameterized types (e.g., ListType(Int32Type)) which are created fresh by apply_parameters(). If such objects are GC'd, Python could reuse the same id() for a different type, returning the wrong cached Deserializer. Using the cqltype object itself as the dict key holds a strong reference, preventing GC and id() reuse. Since cqltypes use default object identity for __hash__/__eq__, this is semantically equivalent to id()-based keying but safer. Benchmarks show slightly better performance as well (no id() call overhead on the cache-hit path).
1 parent 74e1d35 commit 35d30e3

1 file changed

Lines changed: 11 additions & 7 deletions

File tree

cassandra/deserializers.pyx

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -440,12 +440,15 @@ cdef class GenericDeserializer(Deserializer):
440440
#--------------------------------------------------------------------------
441441
# Helper utilities
442442

443-
# Cache make_deserializers results keyed on the tuple of cqltype ids.
443+
# Cache make_deserializers results keyed on the tuple of cqltype objects.
444+
# Using the cqltype objects themselves (rather than id()) as keys ensures
445+
# the dict holds strong references, preventing GC and id() reuse issues
446+
# with non-singleton parameterized types.
444447
cdef dict _make_deserializers_cache = {}
445448

446449
def make_deserializers(cqltypes):
447450
"""Create an array of Deserializers for each given cqltype in cqltypes"""
448-
cdef tuple key = tuple(id(ct) for ct in cqltypes)
451+
cdef tuple key = tuple(cqltypes)
449452
try:
450453
return _make_deserializers_cache[key]
451454
except KeyError:
@@ -457,15 +460,16 @@ def make_deserializers(cqltypes):
457460

458461
cdef dict classes = globals()
459462

460-
# Cache deserializer instances keyed on cqltype identity to avoid repeated
461-
# class lookups and object creation on every result set.
463+
# Cache deserializer instances keyed on the cqltype object itself to avoid
464+
# repeated class lookups and object creation on every result set.
465+
# Using the object as key (rather than id()) holds a strong reference,
466+
# preventing GC and id() reuse issues with parameterized types.
462467
cdef dict _deserializer_cache = {}
463468

464469
cpdef Deserializer find_deserializer(cqltype):
465470
"""Find a deserializer for a cqltype"""
466-
cdef Py_ssize_t key = id(cqltype)
467471
try:
468-
return <Deserializer>_deserializer_cache[key]
472+
return <Deserializer>_deserializer_cache[cqltype]
469473
except KeyError:
470474
pass
471475

@@ -497,7 +501,7 @@ cpdef Deserializer find_deserializer(cqltype):
497501
cls = GenericDeserializer
498502

499503
cdef Deserializer result = cls(cqltype)
500-
_deserializer_cache[key] = result
504+
_deserializer_cache[cqltype] = result
501505
return result
502506

503507

0 commit comments

Comments
 (0)