13

I have the problem that my django application accumulates postgres connections over time. It seems that about every 30min a new connection is established and the old connections don't close (see screen). As max connections is set to 100 after some time all connections are blocked.

Does anyone know what is causing this problem?

enter image description here

I discovered this after I integrated some celery tasks. So I am quite sure that it is related to celery.

So I tried to close the connection manually after every Task using after_return method:

from django.db import connection

class DBTask(Task):
    abstract = True

    def after_return(self, *args, **kwargs):
        connection.close()

@task(name='example', base=DBTask)
def example_task(value):
    # do some stuff

But this also doesn't help. Maybe I am totally wrong and it isn't related to celery at all.

My database configuration:

DATABASES = {
    'default': {
        'ENGINE': 'django.contrib.gis.db.backends.postgis',
        'NAME': 'production', 
        'USER': 'production', 
        'HOST': 'some.host', 
        'CONN_MAX_AGE': 0,
    },
}

Installed packages:

  • django 1.8.9
  • pyscopg2 2.6.1
  • celery 3.1.20
  • django-celery 3.1.17

The app is deployed at webfaction (maybe this helps)

I have also seen this question, but setting CONN_MAX_AGE: 0 didn't help.

Update:

Tried adding connection.close() at the end of each celery task, but the number of connection is still increasing.

Update 2:

Tried adding connection.close() at the top of the celery file, but this didn't help either.

Update 3:

Here is the code I am actually using in the celery tasks:

celery_tasks.py

@task(name='push_notifications', base=DBTask)
def push_notifications_task(user_id):
    user = CustomUser.objects.get(id=user_id)
    PusherAPI().push_notifications(user)
    connection.close()

models.py

class PusherAPI(object):

    def push_notifications(self, user):
        from .serializers import NotificationSerializer
        self.pusher.trigger(
            'user_%s' % user.slug,
            'notifications',
            NotificationSerializer(user).data
        )

serializers.py

class NotificationSerializer(object):

    def __init__(self, user=None):
        if user is None:
            self.user = get_current_user()
        else:
            self.user = user

    @property
    def data(self):
        # get notifications from db
        notifications = self.user.notifications.unread()
        # create the notification dict
        ...
        return note_dict

The only db-queries are in CustomUser.objects.get(id=user_id) and notifications = self.user.notifications.unread()

Community
  • 1
  • 1
ilse2005
  • 11,189
  • 5
  • 51
  • 75
  • Do your celery tasks use/create connections? If so, have you tried closing them within the celery tasks instead of later when the task is already done? – das-g Feb 26 '16 at 14:53
  • No, I haven't tried that. Do you think it makes any difference? – ilse2005 Feb 26 '16 at 15:00
  • It could make a difference, as it might be different connections that would get closed in the tasks than the ones you're closing now outside the tasks. But I don't know celery enough to be sure of that. – das-g Feb 26 '16 at 15:05
  • I'll try it. I will let you know if it works – ilse2005 Feb 26 '16 at 15:07
  • @das-g unfortunately this doesn't help – ilse2005 Feb 26 '16 at 16:58
  • Try to close the connection at the begin of the file, not inside the class declaration. – trinchet Feb 28 '16 at 20:09
  • @trinchet. Where exactly do you mean? – ilse2005 Feb 28 '16 at 20:13
  • At the very top of your tasks file. – trinchet Feb 28 '16 at 20:15
  • OK. I'll try that. But why do you think this would help? Isn't this only run once at startup? – ilse2005 Feb 28 '16 at 20:17
  • Yeah, this run just one time, but that will close any missing open connection. – trinchet Feb 28 '16 at 20:18
  • @trinchet, this didn't help. The connections are still increasing over time. – ilse2005 Feb 29 '16 at 10:54
  • can you show where the `connection` object is created in your tasks - the code you show doesn't make sense - how is `connection` passed to `after_return` - is it a module-level variable? – scytale Feb 29 '16 at 11:01
  • @scytale, it is the connection from the `django.db` module. I import it with `from django.db import connection`.I edited my post. – ilse2005 Feb 29 '16 at 11:03
  • and inside your celery tasks are you using the same `connection` object? could you show some sample code from your tasks that does the queries? – scytale Feb 29 '16 at 11:10
  • @scytale, I added all relevant code – ilse2005 Feb 29 '16 at 11:20
  • that's way too much code - it's not all relevant - please reduce to just the bits that import/set up the db connection and some sample queries – scytale Feb 29 '16 at 11:24
  • in particular there's way too much serialization code that does not appear to be relevant at all – scytale Feb 29 '16 at 11:25
  • Sorry, reduced the code. – ilse2005 Feb 29 '16 at 11:32
  • 1
    Are you sure it is actually old connections that are not closed and not new connections that pile up because some part of your application can't handle the load? Have you looked at the individual connections, e.g. with `SELECT * FROM pg_stat_activity;`? – Daniel Hepper Feb 29 '16 at 11:49
  • Thanks @DanielHepper. With this I realized, that the issue is totally unrelated to the celery tasks. In fact it's not even the same databse causing the problems. Sorry for bothering you all! Could you post your suggestion as an answer, that I can reward you the bounty? – ilse2005 Feb 29 '16 at 13:26
  • @ilse2005 Glad I could help! See my answer below. – Daniel Hepper Feb 29 '16 at 13:31

1 Answers1

5

Make sure it is actually old connections that are not closed and not new connections that pile up because some part of your application can't handle the load. Have a look at the individual connections, e.g. with SELECT * FROM pg_stat_activity;

Daniel Hepper
  • 28,981
  • 10
  • 72
  • 75