newrelic / newrelic-python-agent

New Relic Python Agent
https://docs.newrelic.com/docs/agents/python-agent
Apache License 2.0
178 stars 102 forks source link

Issue with redis call being interjected by aioredis hook #1035

Open SanketDG opened 10 months ago

SanketDG commented 10 months ago

Description When redis and aioredis==1.3.1 is used together, newrelic tries to pickup the redis.asyncio.Redis as that of aioredis and throws the traceback as mentioned ๐Ÿ‘‡๐Ÿผ

aioredis==2.0.1 works fine.

Expected Behavior Ideally, this hook should not be called, since the call is for redis.asyncio

Troubleshooting or NR Diag results

Traceback (most recent call last):
  File "/Users/sanketdasgupta/.pyenv/versions/quick-poc/lib/python3.9/site-packages/uvicorn/protocols/http/h11_impl.py", line 408, in run_asgi
    result = await app(  # type: ignore[func-returns-value]
  File "/Users/sanketdasgupta/.pyenv/versions/quick-poc/lib/python3.9/site-packages/uvicorn/middleware/proxy_headers.py", line 84, in __call__
    return await self.app(scope, receive, send)
  File "/Users/sanketdasgupta/.pyenv/versions/quick-poc/lib/python3.9/site-packages/newrelic/api/asgi_application.py", line 363, in nr_async_asgi
    return await coro
  File "/Users/sanketdasgupta/.pyenv/versions/quick-poc/lib/python3.9/site-packages/newrelic/common/async_proxy.py", line 148, in __next__
    return self.send(None)
  File "/Users/sanketdasgupta/.pyenv/versions/quick-poc/lib/python3.9/site-packages/newrelic/common/async_proxy.py", line 120, in send
    return self.__wrapped__.send(value)
  File "/Users/sanketdasgupta/.pyenv/versions/quick-poc/lib/python3.9/site-packages/fastapi/applications.py", line 1054, in __call__
    await super().__call__(scope, receive, send)
  File "/Users/sanketdasgupta/.pyenv/versions/quick-poc/lib/python3.9/site-packages/newrelic/api/asgi_application.py", line 363, in nr_async_asgi
    return await coro
  File "/Users/sanketdasgupta/.pyenv/versions/quick-poc/lib/python3.9/site-packages/newrelic/common/async_proxy.py", line 148, in __next__
    return self.send(None)
  File "/Users/sanketdasgupta/.pyenv/versions/quick-poc/lib/python3.9/site-packages/newrelic/common/async_proxy.py", line 120, in send
    return self.__wrapped__.send(value)
  File "/Users/sanketdasgupta/.pyenv/versions/quick-poc/lib/python3.9/site-packages/starlette/applications.py", line 123, in __call__
    await self.middleware_stack(scope, receive, send)
  File "<string>", line 5, in wrapper
  File "/Users/sanketdasgupta/.pyenv/versions/quick-poc/lib/python3.9/site-packages/starlette/middleware/errors.py", line 186, in __call__
    raise exc
  File "/Users/sanketdasgupta/.pyenv/versions/quick-poc/lib/python3.9/site-packages/starlette/middleware/errors.py", line 164, in __call__
    await self.app(scope, receive, _send)
  File "<string>", line 5, in wrapper
  File "/Users/sanketdasgupta/.pyenv/versions/quick-poc/lib/python3.9/site-packages/starlette/middleware/exceptions.py", line 62, in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
  File "/Users/sanketdasgupta/.pyenv/versions/quick-poc/lib/python3.9/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
    raise exc
  File "/Users/sanketdasgupta/.pyenv/versions/quick-poc/lib/python3.9/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
    await app(scope, receive, sender)
  File "/Users/sanketdasgupta/.pyenv/versions/quick-poc/lib/python3.9/site-packages/starlette/routing.py", line 762, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/Users/sanketdasgupta/.pyenv/versions/quick-poc/lib/python3.9/site-packages/starlette/routing.py", line 782, in app
    await route.handle(scope, receive, send)
  File "/Users/sanketdasgupta/.pyenv/versions/quick-poc/lib/python3.9/site-packages/starlette/routing.py", line 297, in handle
    await self.app(scope, receive, send)
  File "/Users/sanketdasgupta/.pyenv/versions/quick-poc/lib/python3.9/site-packages/starlette/routing.py", line 77, in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
  File "/Users/sanketdasgupta/.pyenv/versions/quick-poc/lib/python3.9/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
    raise exc
  File "/Users/sanketdasgupta/.pyenv/versions/quick-poc/lib/python3.9/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
    await app(scope, receive, sender)
  File "/Users/sanketdasgupta/.pyenv/versions/quick-poc/lib/python3.9/site-packages/starlette/routing.py", line 72, in app
    response = await func(request)
  File "/Users/sanketdasgupta/.pyenv/versions/quick-poc/lib/python3.9/site-packages/fastapi/routing.py", line 299, in app
    raise e
  File "/Users/sanketdasgupta/.pyenv/versions/quick-poc/lib/python3.9/site-packages/fastapi/routing.py", line 294, in app
    raw_response = await run_endpoint_function(
  File "/Users/sanketdasgupta/.pyenv/versions/quick-poc/lib/python3.9/site-packages/fastapi/routing.py", line 191, in run_endpoint_function
    return await dependant.call(**values)
  File "/Users/sanketdasgupta/work/fynd/stormbreaker/main.py", line 42, in read_root
    value = await app.state.redis2.get("example_key")
  File "/Users/sanketdasgupta/.pyenv/versions/quick-poc/lib/python3.9/site-packages/newrelic/hooks/datastore_aioredis.py", line 67, in _nr_wrapper_AioRedis_method_
    if isinstance(instance._pool_or_conn, _RedisBuffer):
AttributeError: 'Redis' object has no attribute '_pool_or_conn'

Steps to Reproduce

import os
from fastapi import FastAPI
import redis.asyncio as aredis
import aioredis
from contextlib import asynccontextmanager
import newrelic.agent

# Initialize New Relic agent
newrelic.agent.initialize()

# Initialize FastAPI app

# New Relic application wrapper
# app = newrelic.agent.WSGIApplicationWrapper(app)

# New Relic transaction naming
# @newrelic.agent.transaction(name="FastAPI Server Transaction")
async def startup_event():
    # Connect to Redis using aioredis
    redis_url = os.getenv("REDIS_URL", "redis://localhost:6379")
    app.state.redis = await aioredis.create_redis_pool(redis_url, encoding="utf-8")
    app.state.redis2 = await aredis.from_url(redis_url, encoding="utf-8")

@asynccontextmanager
async def lifespan(app: FastAPI):
    # Load the ML model
    await startup_event()
    yield
    # Clean up the ML models and release the resources
    app.state.redis.close()
    await app.state.redis.wait_closed()

app = FastAPI(lifespan=lifespan)

@app.get("/")
async def read_root():
    keys = ["all_countries_dict"]
    # Example route using the initialized Redis connection
    value = await app.state.redis2.get("example_key")
    value = await app.state.redis.get("example_key")
    value = await app.state.redis2.mget(*keys)
    value = await app.state.redis2.mget(keys)
    value = await app.state.redis.mget(*keys)

    return {"message": "Hello, FastAPI!", "redis_value": value}

if __name__ == "__main__":
    import uvicorn

    uvicorn.run(app, host="0.0.0.0", port=8001)

Your Environment

$ pip freeze
aioredis==1.3.1
annotated-types==0.6.0
anyio==4.2.0
asttokens==2.4.1
async-timeout==4.0.3
click==8.1.7
decorator==5.1.1
exceptiongroup==1.2.0
executing==2.0.1
fastapi==0.109.0
h11==0.14.0
hiredis==2.3.2
idna==3.6
ipython==8.18.1
jedi==0.19.1
matplotlib-inline==0.1.6
newrelic==8.8.0
parso==0.8.3
pexpect==4.9.0
prompt-toolkit==3.0.43
ptyprocess==0.7.0
pure-eval==0.2.2
pydantic==2.5.3
pydantic_core==2.14.6
Pygments==2.17.2
redis==4.6.0
six==1.16.0
sniffio==1.3.0
stack-data==0.6.3
starlette==0.35.1
traitlets==5.14.1
typing_extensions==4.9.0
uvicorn==0.25.0
wcwidth==0.2.13

Additional context Note: Only happens with aioredis==1.3.1, works fine with aioredis==2.0.1