apm-agent-python icon indicating copy to clipboard operation
apm-agent-python copied to clipboard

django dbapi2 instumentation results in InterfaceError connection already closed

Open soudhaf opened this issue 3 years ago • 6 comments

Describe the bug: django requests are failing because dbapi2 instumentation seems to result in an InterfaceError Exception, Exception Value: connection already closed

Traceback:

File "/opt/venv/lib/python3.9/site-packages/django/db/backends/base/base.py" in _cursor
  237.             return self._prepare_cursor(self.create_cursor(name))

File "/opt/venv/lib/python3.9/site-packages/django/utils/asyncio.py" in inner
  33.             return func(*args, **kwargs)

File "/opt/venv/lib/python3.9/site-packages/django/db/backends/postgresql/base.py" in create_cursor
  236.             cursor = self.connection.cursor()

File "/opt/venv/lib/python3.9/site-packages/elasticapm/instrumentation/packages/dbapi2.py" in cursor
  263.         return self.cursor_proxy(self.__wrapped__.cursor(*args, **kwargs), self._self_destination_info)

The above exception (connection already closed) was the direct cause of the following exception:

File "/opt/venv/lib/python3.9/site-packages/django/core/handlers/exception.py" in inner
  47.                 response = get_response(request)

File "/opt/venv/lib/python3.9/site-packages/django/core/handlers/base.py" in _get_response
  181.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "/usr/local/lib/python3.9/contextlib.py" in inner
  79.                 return func(*args, **kwds)

File "/opt/venv/lib/python3.9/site-packages/django/views/decorators/csrf.py" in wrapped_view
  54.         return view_func(*args, **kwargs)

File "/opt/venv/lib/python3.9/site-packages/django/views/generic/base.py" in view
  70.             return self.dispatch(request, *args, **kwargs)

File "/opt/venv/lib/python3.9/site-packages/rest_framework/views.py" in dispatch
  509.             response = self.handle_exception(exc)

File "/opt/venv/lib/python3.9/site-packages/rest_framework/views.py" in handle_exception
  469.             self.raise_uncaught_exception(exc)

File "/opt/venv/lib/python3.9/site-packages/rest_framework/views.py" in raise_uncaught_exception
  480.         raise exc

File "/opt/venv/lib/python3.9/site-packages/rest_framework/views.py" in dispatch
  506.             response = handler(request, *args, **kwargs)

File "/code/utils/decorators.py" in decorated_method
  73.             return old_method(self, request, serializer)

File "/code/api/views.py" in post
  177.             serializer.update(my_user, serializer.validated_data)

File "/code/api/serializers.py" in update
  683.             device = Device.objects.get(tracking_id=my_user.tracking_id,

File "/opt/venv/lib/python3.9/site-packages/django/db/models/manager.py" in manager_method
  85.                 return getattr(self.get_queryset(), name)(*args, **kwargs)

File "/opt/venv/lib/python3.9/site-packages/django/db/models/query.py" in get
  431.         num = len(clone)

File "/opt/venv/lib/python3.9/site-packages/django/db/models/query.py" in __len__
  262.         self._fetch_all()

File "/opt/venv/lib/python3.9/site-packages/django/db/models/query.py" in _fetch_all
  1324.             self._result_cache = list(self._iterable_class(self))

File "/opt/venv/lib/python3.9/site-packages/django/db/models/query.py" in __iter__
  51.         results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)

File "/opt/venv/lib/python3.9/site-packages/django/db/models/sql/compiler.py" in execute_sql
  1173.             cursor = self.connection.cursor()

File "/opt/venv/lib/python3.9/site-packages/django/utils/asyncio.py" in inner
  33.             return func(*args, **kwargs)

File "/opt/venv/lib/python3.9/site-packages/django/db/backends/base/base.py" in cursor
  259.         return self._cursor()

File "/opt/venv/lib/python3.9/site-packages/django/db/backends/base/base.py" in _cursor
  237.             return self._prepare_cursor(self.create_cursor(name))

File "/opt/venv/lib/python3.9/site-packages/django/db/utils.py" in __exit__
  90.                 raise dj_exc_value.with_traceback(traceback) from exc_value

File "/opt/venv/lib/python3.9/site-packages/django/db/backends/base/base.py" in _cursor
  237.             return self._prepare_cursor(self.create_cursor(name))

File "/opt/venv/lib/python3.9/site-packages/django/utils/asyncio.py" in inner
  33.             return func(*args, **kwargs)

File "/opt/venv/lib/python3.9/site-packages/django/db/backends/postgresql/base.py" in create_cursor
  236.             cursor = self.connection.cursor()

File "/opt/venv/lib/python3.9/site-packages/elasticapm/instrumentation/packages/dbapi2.py" in cursor
  263.         return self.cursor_proxy(self.__wrapped__.cursor(*args, **kwargs), self._self_destination_info)

Exception Type: InterfaceError at /api/xyz
Exception Value: connection already closed

To Reproduce

I don't know how to reproduce, this doesn't happen very often like once or twice a day

Environment (please complete the following information)

  • OS: debian bullseye
  • Python version: 3.9
  • Framework and version: Django 3.2.13
  • Agent version: 6.8.1

Note: This error appeared first time after upgrading from elastic-apm==6.6.1 to elastic-apm==6.7.2.

soudhaf avatar May 26 '22 08:05 soudhaf

Does the error occur regularly enough that you're confident it wasn't happening at all in 6.6.1? I'm wondering whether we should look at this as a regression or an implementation bug.

basepi avatar Jun 01 '22 15:06 basepi

Yes, 6.6.1 version run over 3 months, no errors. Just to be sure I will be downgrading to 6.6.1 I'll let you know the result after some time. On another note, I tried disabling dbapi2 by setting environment to SKIP_INSTRUMENT_DBAPI2=True, but I still get the same exception.

soudhaf avatar Jun 09 '22 10:06 soudhaf

Thanks for the confirmation.

basepi avatar Jun 09 '22 17:06 basepi

I am having a similar problem. I have downgraded the elasticapm version in my environment to 6.6.1 but am still experiencing the issue.

Describe the bug

Occasionally calls are failing with either InterfaceError('connection already closed') or OperationalError('SSL SYSCALL error: EOF detected\n'). Stack trace can be found here.

Reproducing

Unfortunately this is similar behavior, as in not reliably reproducable. It mostly (95%) happens with code that runs at night when there has been a couple of hours of low to no usage on the system if that helps. I wasn't yet able to verify if either of the two errors disappear once I completely disable elasticapm, that is another point on my troubleshooting list.

Environment

OS: debian bullseye (inside of a container)
Python version: 3.8.13
Framework and version: Django 3.2.14
Agent version: 6.6.1 and 6.9.1 confirmed

Kircheneer avatar Jul 22 '22 08:07 Kircheneer

Can confirm issue still happens after downgrading to 6.6.1.

soudhaf avatar Jul 22 '22 14:07 soudhaf

After diving this a bit, I don't think it's a bug in our agent.

In our dbapi2 instrumentation, we wrap the Connection and the Cursor objects so we can create spans when queries are executed through the cursor.

Django is requesting a cursor object from the postgres connection:

cursor = self.connection.cursor()

Our ConnectionProxy object passes that through to the wrapped connection (self.__wrapped__.cursor(*args, **kwargs)):

https://github.com/elastic/apm-agent-python/blob/a81a90f1ffd256a974bdd6206e778d47179fc407/elasticapm/instrumentation/packages/dbapi2.py#L265-L266

At which point postgres throws the connection already closed error.

The presence of elasticapm in the stacktrace is a red herring, I think. Can you try removing the agent for a time and see if the problem persists?

basepi avatar Aug 11 '22 19:08 basepi

Closing for now. Please keep me posted if you are having this issue with the agent present, and it goes away when you remove the agent. Otherwise I think it's worth trying to track down why the connection is closing unexpectedly.

basepi avatar Aug 16 '22 21:08 basepi