django-cms icon indicating copy to clipboard operation
django-cms copied to clipboard

CacheKey model in menus causing database locks

Open marksweb opened this issue 8 years ago • 3 comments

Summary

In the past few days we've been seeing OperationalError: (1205, 'Lock wait timeout exceeded; try restarting transaction') coming from one of our CMS systems. Our infrastructure at this point is running about 5 servers through a load balancer with a MySQL database server.

This has been triggered from our clients trying to publish content & seems to be a result of the menu app & it's use of cache keys in a table.

Expected behaviour

I'd expect that any part of the system using cache would prepare a key and then check if that key exists in the cache, if not, set whatever data it needs in the cache with that key.

If for whatever reason there is a need to be storing cache keys in the database, I'd expect the key field to be unique.

Actual behaviour

Cache keys are stored in the database & errors appear to be triggered based on clearing those keys. Keys don't appear to be unique so we're seeing multiple objects with the same information; screen shot 2018-03-05 at 11 54 26

Stacktrace;

OperationalError: (1205, 'Lock wait timeout exceeded; try restarting transaction')
  File "django/core/handlers/exception.py", line 42, in inner
    response = get_response(request)
  File "django/core/handlers/base.py", line 249, in _legacy_get_response
    response = self._get_response(request)
  File "django/core/handlers/base.py", line 187, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "django/core/handlers/base.py", line 185, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "django/utils/decorators.py", line 149, in _wrapped_view
    response = view_func(request, *args, **kwargs)
  File "django/views/decorators/cache.py", line 57, in _wrapped_view_func
    response = view_func(request, *args, **kwargs)
  File "django/contrib/admin/sites.py", line 211, in inner
    return view(request, *args, **kwargs)
  File "django/utils/decorators.py", line 67, in _wrapper
    return bound_func(*args, **kwargs)
  File "django/views/decorators/http.py", line 40, in inner
    return func(request, *args, **kwargs)
  File "django/utils/decorators.py", line 63, in bound_func
    return func.__get__(self, type(self))(*args2, **kwargs2)
  File "django/utils/decorators.py", line 185, in inner
    return func(*args, **kwargs)
  File "cms/admin/pageadmin.py", line 983, in publish_page
    all_published = page.publish(language)
  File "cms/models/pagemodel.py", line 713, in publish
    public_page = self._publisher_save_public(public_page)
  File "cms/models/pagemodel.py", line 1386, in _publisher_save_public
    obj.save()
  File "cms/models/pagemodel.py", line 589, in save
    super(Page, self).save(**kwargs)
  File "django/db/models/base.py", line 796, in save
    force_update=force_update, update_fields=update_fields)
  File "cms/models/pagemodel.py", line 604, in save_base
    return super(Page, self).save_base(*args, **kwargs)
  File "django/db/models/base.py", line 820, in save_base
    update_fields=update_fields)
  File "django/dispatch/dispatcher.py", line 191, in send
    response = receiver(signal=self, sender=sender, **named)
  File "cms/signals/page.py", line 21, in pre_save_page
    menu_pool.clear(instance.site_id)
  File "menus/menu_pool.py", line 343, in clear
    cache_keys.delete()
  File "django/db/models/query.py", line 609, in delete
    deleted, _rows_count = collector.delete()
  File "django/db/models/deletion.py", line 306, in delete
    count = query.delete_batch(pk_list, self.using)
  File "django/db/models/sql/subqueries.py", line 46, in delete_batch
    num_deleted += self.do_query(self.get_meta().db_table, self.where, using=using)
  File "django/db/models/sql/subqueries.py", line 28, in do_query
    cursor = self.get_compiler(using).execute_sql(CURSOR)
  File "django/db/models/sql/compiler.py", line 835, in execute_sql
    cursor.execute(sql, params)
  File "raven/contrib/django/client.py", line 123, in execute
    return real_execute(self, sql, params)
  File "django/db/backends/utils.py", line 64, in execute
    return self.cursor.execute(sql, params)
  File "django/db/utils.py", line 94, in __exit__
    six.reraise(dj_exc_type, dj_exc_value, traceback)
  File "django/db/backends/utils.py", line 64, in execute
    return self.cursor.execute(sql, params)
  File "django/db/backends/mysql/base.py", line 110, in execute
    return self.cursor.execute(query, args)
  File "MySQLdb/cursors.py", line 205, in execute
    self.errorhandler(self, exc, value)
  File "MySQLdb/connections.py", line 36, in defaulterrorhandler
    raise errorclass, errorvalue

Environment

  • Python version: 2.7
  • Django version: 1.10.8
  • django CMS version: 3.4.5

marksweb avatar Mar 06 '18 10:03 marksweb

@czpython given the related tickets, is there any work on this yet or any thought on a potential solution? I'm starting a load test tomorrow & if I see anymore issues coming from that related to this table I might start digging into how the CacheKeys table is used & a solution to purely generate cache keys avoiding the database.

marksweb avatar Mar 06 '18 19:03 marksweb

@jrief heres my issue raised when I hit the issue with the menu cache keys.

marksweb avatar Jan 28 '22 19:01 marksweb

As I said during the meeting today, we shall use Redis with wildcard key deletion. There is a workaround in memcached which achieves the same result.

jrief avatar Jan 28 '22 22:01 jrief