Extremely long delay grabbing type info for string array (and likely other types) on CockroachDB
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 5/5
- Tiempo estimado
- Más de una semana
- Aptitud para principiantes
- 35/100
Línea de trabajo
El issue identifica asyncpg.Connection._introspect_types como el punto de entrada y reporta retrasos graves en la introspección de tipos de CockroachDB; empieza por ahí y reproduce el comportamiento contra CockroachDB. La solución alternativa de almacenamiento en caché propuesta muestra una posible dirección, pero la solución prevista y los criterios de finalización requieren el acuerdo de los maintainers.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
I was having atrocious and unacceptable delays in my production environment that I wasn't seeing locally, using CockroachDB cloud. Found the cause was the introspection of types. I'm using the latest version of the CockroachDB drivers.
I wrote this hack to work around the issue. It caches the result in local memory and also caches it to redis so that new instances don't see it. You can change the key for the redis cache using an environment variable so that new versions aren't locked to old values.
If someone wants to turn it into part of the product, please be my guest. I won't have time for it. In the mean time. here's the hack that does monkey patching:
introspection_result_cache: dict[tuple[str, int, str], Any] = {}
orig_introspection_types = asyncpg.Connection._introspect_types
INTROSPECTION_KEY = os.environ.get(
"ASYNCPG_INTROSPECTION_CACHE_KEY", "ASYNCPG_INTROSPECTION_CACHE_KEY"
)
introspection_lock = asyncio.Lock()
class FauxResult:
_binary_fields = ("kind", "elemdelim")
column_order = [
"oid",
"ns",
"name",
"kind",
"basetype",
"elemtype",
"elemdelim",
"range_subtype",
"attrtypoids",
"attrnames",
"depth",
"basetype_name",
"elemtype_name",
"range_subtype_name",
]
def __init__(self, row=None, data: dict | None = None) -> None:
if row:
self.data = dict(row)
else:
assert data
self.data = data
def __getattr__(self, name: str) -> Any:
return self.data[name]
def __getitem__(self, idx_or_column_name: int | str) -> Any:
if isinstance(idx_or_column_name, int):
return self.data[self.column_order[idx_or_column_name]]
return self.data[idx_or_column_name]
def for_serialization(self) -> dict:
result = copy.copy(self.data)
for field in self._binary_fields:
if (value := self.data.get(field)) is not None:
result[field] = value.decode()
return result
@classmethod
def from_serialization(cls, data: dict) -> Self:
for field in cls._binary_fields:
if (value := data.get(field)) is not None:
data[field] = value.encode()
return cls(data=data)
class FauxPreparedStatementState:
def __init__(self, name) -> None:
self.name = name
async def to_redis_cache(
host: str, port: int, database: str, inspection_types: tuple[list, Any]
) -> None:
pss = FauxPreparedStatementState(inspection_types[1].name)
results = [FauxResult(row) for row in inspection_types[0]]
await redis_client().set(
INTROSPECTION_KEY + f"-{host}-{port}-{database}",
orjson.dumps([[result.for_serialization() for result in results], pss.name]),
)
async def from_redis_cache(host: str, port: int, database: str) -> tuple[list, Any] | None:
data = await redis_client().get(INTROSPECTION_KEY + f"-{host}-{port}-{database}")
if data is None:
return None
results, pss_name = orjson.loads(data)
pss = FauxPreparedStatementState(pss_name)
return [FauxResult.from_serialization(row) for row in results], pss
def apply_introspection_caching():
async def new_introspect_types(self, *args, **kwargs) -> Any:
host: str
port: int
database: str
host, port = self._addr
database = self._params.database
if (cached_val := introspection_result_cache.get((host, port, database))) is not None:
return cached_val
async with introspection_lock:
redis_cached_value = await from_redis_cache(host, port, database)
if redis_cached_value is not None:
introspection_result_cache[host, port, database] = redis_cached_value
return redis_cached_value
result = await orig_introspection_types(self, *args, **kwargs)
await to_redis_cache(host, port, database, result)
return result
asyncpg.Connection._introspect_types = new_introspect_types
- Lenguaje dominante
- Python
- Estrellas
- 8.1k
- Forks
- 469
- Merge medio
- 2 d 20 h
- PR fusionados (30 d)
- 9
Guía de contribución
No hay ninguna guía de contribución indexada para este repositorio
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de MagicStack/asyncpg
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
MagicStack/asyncpg#1357 · 1 comentario · 1 reacción ·
-
Dificultad 3/5 1-2 días Aptitud para principiantes 72/100
MagicStack/asyncpg#1342 ·
-
Dificultad 3/5 1-2 días Aptitud para principiantes 56/100
MagicStack/asyncpg#1340 · 1 comentario ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 28/100
MagicStack/asyncpg#1337 ·
-
Dificultad 4/5 3-5 días Aptitud para principiantes 42/100
MagicStack/asyncpg#1330 · 1 comentario ·
Todos los issues de MagicStack/asyncpg
Issues similares
-
bug confirmed issue
Dificultad 2/5 1-3 horas Aptitud para principiantes 75/100
open-webui/open-webui#30750 · 1 comentario ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 75/100
-
enhancement
Dificultad 2/5 1-3 horas Aptitud para principiantes 75/100
OpenwaterHealth/openmotion-bloodflow-app#604 · 1 comentario ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 70/100
-
good first issue
Dificultad 1/5 Menos de una hora Aptitud para principiantes 90/100