Skip to content

Commit

Permalink
celery: Use django-celery-beat for storing schedule
Browse files Browse the repository at this point in the history
The beat file is problematic and it is better to have it in the database
for distributed environments.

This removes one dependency on shared filesystem, see WeblateOrg#732.
  • Loading branch information
nijel committed Jan 19, 2023
1 parent 94be0e8 commit a7eef93
Show file tree
Hide file tree
Showing 14 changed files with 51 additions and 73 deletions.
4 changes: 2 additions & 2 deletions docs/admin/admin.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ The Django admin interface

.. warning::

Will be removed in the future,
as its use is discouraged—most features can be managed directly in Weblate.
Use with caution as this is the low level interface. You should not need it
in most cases.

Here you can manage objects stored in the database, such as users, translations
and other settings:
Expand Down
9 changes: 5 additions & 4 deletions docs/admin/continuous.rst
Original file line number Diff line number Diff line change
Expand Up @@ -377,10 +377,11 @@ fulfilled:
:ref:`addon-weblate.git.squash` add-on in that case.

If you want to commit changes more frequently and without checking of age, you
can schedule a regular task to perform a commit:

.. literalinclude:: ../../weblate/examples/beat-settings.py
:language: python
can schedule a regular task to perform a commit. This can be done using
:guilabel:`Periodic Tasks` in :ref:`admin-interface`. First create desired
:guilabel:`Iterval` (for example 120 seconds). Then add new periodic task and
choose ``weblate.trans.tasks.commit_pending`` as :guilabel:`Task` with
``{"hours": 0}`` as :guilabel:`Keyword Arguments` and desired interval.

.. _processing:

Expand Down
11 changes: 11 additions & 0 deletions docs/admin/upgrade.rst
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,17 @@ Please follow :ref:`generic-upgrade-instructions` in order to perform update.

.. seealso:: :ref:`generic-upgrade-instructions`

Upgrade from 4.15 to 4.16
~~~~~~~~~~~~~~~~~~~~~~~~~

Please follow :ref:`generic-upgrade-instructions` in order to perform update.

* Celery beat is now stroring the tasks schedule in the database,
``CELERY_BEAT_SCHEDULER`` and :setting:`django:INSTALLED_APPS` need to be
changed for that.

.. seealso:: :ref:`generic-upgrade-instructions`

.. _py3:

Upgrading from Python 2 to Python 3
Expand Down
2 changes: 2 additions & 0 deletions docs/changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ Weblate 4.16

Not yet released.

* Celery beat is now stroring the tasks schedule in the database.

`All changes in detail <https://github.com/WeblateOrg/weblate/milestone/89?closed=1>`__.

Weblate 4.15.1
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ cssselect>=1.2,<1.3
Cython>=0.29.14,<0.30
diff-match-patch==20200713
django-appconf>=1.0.3,<1.1
django-celery-beat>=2.4.0,<2.4
django-compressor>=2.4,<5
django-crispy-forms>=1.9.0,<1.15
django-filter>=2.4.0,<23.0
Expand Down
12 changes: 0 additions & 12 deletions weblate/examples/beat-settings.py

This file was deleted.

3 changes: 2 additions & 1 deletion weblate/settings_docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,7 @@
"rest_framework",
"rest_framework.authtoken",
"django_filters",
"django_celery_beat",
]

modify_env_list(INSTALLED_APPS, "APPS")
Expand Down Expand Up @@ -1279,7 +1280,7 @@

# Celery settings, it is not recommended to change these
CELERY_WORKER_MAX_MEMORY_PER_CHILD = 200000
CELERY_BEAT_SCHEDULE_FILENAME = os.path.join(DATA_DIR, "celery", "beat-schedule")
CELERY_BEAT_SCHEDULER = "django_celery_beat.schedulers:DatabaseScheduler"
CELERY_TASK_ROUTES = {
"weblate.trans.tasks.auto_translate*": {"queue": "translate"},
"weblate.accounts.tasks.notify_*": {"queue": "notify"},
Expand Down
3 changes: 2 additions & 1 deletion weblate/settings_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,7 @@
"rest_framework",
"rest_framework.authtoken",
"django_filters",
"django_celery_beat",
]

# Custom exception reporter to include some details
Expand Down Expand Up @@ -896,7 +897,7 @@

# Celery settings, it is not recommended to change these
CELERY_WORKER_MAX_MEMORY_PER_CHILD = 200000
CELERY_BEAT_SCHEDULE_FILENAME = os.path.join(DATA_DIR, "celery", "beat-schedule")
CELERY_BEAT_SCHEDULER = "django_celery_beat.schedulers:DatabaseScheduler"
CELERY_TASK_ROUTES = {
"weblate.trans.tasks.auto_translate*": {"queue": "translate"},
"weblate.accounts.tasks.notify_*": {"queue": "notify"},
Expand Down
1 change: 0 additions & 1 deletion weblate/settings_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@
DATA_DIR = os.path.join(BASE_DIR, "data-test")
MEDIA_ROOT = os.path.join(DATA_DIR, "media")
STATIC_ROOT = os.path.join(DATA_DIR, "static")
CELERY_BEAT_SCHEDULE_FILENAME = os.path.join(DATA_DIR, "celery", "beat-schedule")
CELERY_TASK_ALWAYS_EAGER = True
CELERY_BROKER_URL = "memory://"
CELERY_TASK_EAGER_PROPAGATES = True
Expand Down
1 change: 0 additions & 1 deletion weblate/utils/checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,6 @@ def check_data_writable(app_configs=None, **kwargs):
data_dir("home"),
data_dir("ssh"),
data_dir("vcs"),
data_dir("celery"),
data_dir("backups"),
data_dir("fonts"),
data_dir("cache", "fonts"),
Expand Down
30 changes: 5 additions & 25 deletions weblate/utils/management/commands/cleanup_celery.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,15 @@
#
# SPDX-License-Identifier: GPL-3.0-or-later

import os
# TODO: Drop in Weblate 4.17

from celery.beat import Service
from django.conf import settings

from weblate.utils.celery import app
from weblate.utils.management.base import BaseCommand


class Command(BaseCommand):
help = "removes incompatible celery schedule file"

@staticmethod
def try_remove(filename):
if os.path.exists(filename):
os.remove(filename)

@staticmethod
def setup_schedule():
service = Service(app=app)
scheduler = service.get_scheduler()
scheduler.setup_schedule()
help = "deprecated, will be removed in Weblate 4.17"

def handle(self, *args, **options):
try:
self.setup_schedule()
except Exception as error:
if os.path.exists(settings.CELERY_BEAT_SCHEDULE_FILENAME):
self.stderr.write(f"Removing corrupted schedule file: {error!r}")
self.try_remove(settings.CELERY_BEAT_SCHEDULE_FILENAME)
self.try_remove(settings.CELERY_BEAT_SCHEDULE_FILENAME + ".db")
self.setup_schedule()
self.stderr.write(
"This command does nothing, it will be removed in Weblate 4.17!"
)
1 change: 1 addition & 0 deletions weblate/utils/requirements.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"rapidfuzz",
"openpyxl",
"celery",
"django-celery-beat",
"kombu",
"translation-finder",
"weblate-language-data",
Expand Down
30 changes: 4 additions & 26 deletions weblate/utils/tests/test_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,19 @@
#
# SPDX-License-Identifier: GPL-3.0-or-later

import os
from glob import glob
from io import StringIO

from django.core.management import call_command
from django.test import SimpleTestCase, TestCase
from django.test.utils import override_settings

from weblate.trans.tests.utils import TempDirMixin


class CommandTests(SimpleTestCase, TempDirMixin):
def setUp(self):
self.create_temp()
self.beat = os.path.join(self.tempdir, "beat")
self.beat_db = os.path.join(self.tempdir, "beat.db")

def tearDown(self):
self.remove_temp()

def check_beat(self):
self.assertTrue(glob(self.beat + "*"))

def test_none(self):
with override_settings(CELERY_BEAT_SCHEDULE_FILENAME=self.beat):
call_command("cleanup_celery")
self.check_beat()

def test_broken(self):
for name in (self.beat, self.beat_db):
with open(name, "wb") as handle:
handle.write(b"\x00")
with override_settings(CELERY_BEAT_SCHEDULE_FILENAME=self.beat):
call_command("cleanup_celery")
self.check_beat()
def test_cleanup(self):
output = StringIO()
call_command("cleanup_celery", stderr=output)
self.assertIn(" it will be removed in Weblate", output.getvalue())

def test_queues(self):
output = StringIO()
Expand Down
16 changes: 16 additions & 0 deletions weblate/wladmin/sites.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@
from django.utils.decorators import method_decorator
from django.utils.translation import gettext_lazy as _
from django.views.decorators.cache import never_cache
from django_celery_beat.admin import (
ClockedSchedule,
ClockedScheduleAdmin,
CrontabSchedule,
IntervalSchedule,
PeriodicTask,
PeriodicTaskAdmin,
SolarSchedule,
)
from rest_framework.authtoken.admin import TokenAdmin
from rest_framework.authtoken.models import Token
from social_django.admin import AssociationOption, NonceOption, UserSocialAuthOption
Expand Down Expand Up @@ -160,6 +169,13 @@ def discover(self):
# Django REST Framework
self.register(Token, TokenAdmin)

# Django Celery Beat
self.register(IntervalSchedule)
self.register(CrontabSchedule)
self.register(SolarSchedule)
self.register(ClockedSchedule, ClockedScheduleAdmin)
self.register(PeriodicTask, PeriodicTaskAdmin)

# Simple SSO
if "simple_sso.sso_server" in settings.INSTALLED_APPS:
from simple_sso.sso_server.models import Consumer
Expand Down

0 comments on commit a7eef93

Please sign in to comment.