-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'origin/develop'
- Loading branch information
Showing
142 changed files
with
25,318 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# django-background-tasks | ||
|
||
Source: https://github.com/django-background-tasks/django-background-tasks |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# -*- coding: utf-8 -*- | ||
__version__ = '1.2.8' | ||
|
||
default_app_config = 'background_task.apps.BackgroundTasksAppConfig' | ||
|
||
def background(*arg, **kw): | ||
from apps.background_task.tasks import tasks | ||
return tasks.background(*arg, **kw) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
# -*- coding: utf-8 -*- | ||
from django.contrib import admin | ||
from apps.background_task.models import Task | ||
from apps.background_task.models import CompletedTask | ||
|
||
|
||
def inc_priority(modeladmin, request, queryset): | ||
for obj in queryset: | ||
obj.priority += 1 | ||
obj.save() | ||
inc_priority.short_description = "priority += 1" | ||
|
||
def dec_priority(modeladmin, request, queryset): | ||
for obj in queryset: | ||
obj.priority -= 1 | ||
obj.save() | ||
dec_priority.short_description = "priority -= 1" | ||
|
||
class TaskAdmin(admin.ModelAdmin): | ||
display_filter = ['task_name'] | ||
search_fields = ['task_name', 'task_params', ] | ||
list_display = ['task_name', 'task_params', 'run_at', 'priority', 'attempts', 'has_error', 'locked_by', 'locked_by_pid_running', ] | ||
actions = [inc_priority, dec_priority] | ||
|
||
class CompletedTaskAdmin(admin.ModelAdmin): | ||
display_filter = ['task_name'] | ||
search_fields = ['task_name', 'task_params', ] | ||
list_display = ['task_name', 'task_params', 'run_at', 'priority', 'attempts', 'has_error', 'locked_by', 'locked_by_pid_running', ] | ||
|
||
|
||
admin.site.register(Task, TaskAdmin) | ||
admin.site.register(CompletedTask, CompletedTaskAdmin) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
from logging import getLogger | ||
from threading import Thread | ||
|
||
from django.apps import AppConfig | ||
|
||
|
||
logger = getLogger(__name__) | ||
|
||
|
||
class BackgroundTasksAppConfig(AppConfig): | ||
name = 'apps.background_task' | ||
from apps.background_task import __version__ as version_info | ||
verbose_name = 'Background Tasks ({})'.format(version_info) | ||
|
||
def ready(self): | ||
from apps.background_task import signals # noqa | ||
from apps.background_task.management.commands.process_tasks import Command as ProcessTasksCommand | ||
|
||
def task_runner(*args, **kwargs): | ||
logger.info('background tasks thread started') | ||
try: | ||
command = ProcessTasksCommand() | ||
command.handle() | ||
except Exception as exception: | ||
logger.error(exception) | ||
finally: | ||
logger.info('shutting down background task thread.') | ||
|
||
thread = Thread(target=task_runner) | ||
thread.setDaemon(True) | ||
thread.start() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
|
||
class BackgroundTaskError(Exception): | ||
|
||
def __init__(self, message, errors=None): | ||
super(BackgroundTaskError, self).__init__(message) | ||
self.errors = errors | ||
|
||
|
||
class InvalidTaskError(BackgroundTaskError): | ||
""" | ||
The task will not be rescheduled if it fails with this error | ||
""" | ||
pass |
Empty file.
Empty file.
118 changes: 118 additions & 0 deletions
118
app/apps/background_task/management/commands/process_tasks.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
# -*- coding: utf-8 -*- | ||
import logging | ||
import random | ||
import sys | ||
import time | ||
|
||
from django import VERSION | ||
from django.core.management.base import BaseCommand | ||
from django.utils import autoreload | ||
|
||
from apps.background_task.tasks import tasks, autodiscover | ||
from apps.background_task.utils import SignalManager | ||
from django.db import close_old_connections as close_connection | ||
|
||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
def _configure_log_std(): | ||
class StdOutWrapper(object): | ||
def write(self, s): | ||
logger.info(s) | ||
|
||
class StdErrWrapper(object): | ||
def write(self, s): | ||
logger.error(s) | ||
sys.stdout = StdOutWrapper() | ||
sys.stderr = StdErrWrapper() | ||
|
||
|
||
class Command(BaseCommand): | ||
help = 'Run tasks that are scheduled to run on the queue' | ||
|
||
# Command options are specified in an abstract way to enable Django < 1.8 compatibility | ||
OPTIONS = ( | ||
(('--duration', ), { | ||
'action': 'store', | ||
'dest': 'duration', | ||
'type': int, | ||
'default': 0, | ||
'help': 'Run task for this many seconds (0 or less to run forever) - default is 0', | ||
}), | ||
(('--sleep', ), { | ||
'action': 'store', | ||
'dest': 'sleep', | ||
'type': float, | ||
'default': 5.0, | ||
'help': 'Sleep for this many seconds before checking for new tasks (if none were found) - default is 5', | ||
}), | ||
(('--queue', ), { | ||
'action': 'store', | ||
'dest': 'queue', | ||
'help': 'Only process tasks on this named queue', | ||
}), | ||
(('--log-std', ), { | ||
'action': 'store_true', | ||
'dest': 'log_std', | ||
'help': 'Redirect stdout and stderr to the logging system', | ||
}), | ||
) | ||
|
||
if VERSION < (1, 8): | ||
from optparse import make_option | ||
option_list = BaseCommand.option_list + tuple([make_option(*args, **kwargs) for args, kwargs in OPTIONS]) | ||
|
||
# Used in Django >= 1.8 | ||
def add_arguments(self, parser): | ||
for (args, kwargs) in self.OPTIONS: | ||
parser.add_argument(*args, **kwargs) | ||
|
||
def __init__(self, *args, **kwargs): | ||
super(Command, self).__init__(*args, **kwargs) | ||
self.sig_manager = None | ||
self._tasks = tasks | ||
|
||
def run(self, *args, **options): | ||
duration = options.get('duration', 0) | ||
sleep = options.get('sleep', 5.0) | ||
queue = options.get('queue', None) | ||
log_std = options.get('log_std', False) | ||
is_dev = options.get('dev', False) | ||
sig_manager = self.sig_manager | ||
|
||
if is_dev: | ||
# raise last Exception is exist | ||
autoreload.raise_last_exception() | ||
|
||
if log_std: | ||
_configure_log_std() | ||
|
||
autodiscover() | ||
|
||
start_time = time.time() | ||
|
||
while (duration <= 0) or (time.time() - start_time) <= duration: | ||
if sig_manager.kill_now: | ||
# shutting down gracefully | ||
break | ||
|
||
if not self._tasks.run_next_task(queue): | ||
# there were no tasks in the queue, let's recover. | ||
close_connection() | ||
logger.debug('waiting for tasks') | ||
time.sleep(sleep) | ||
else: | ||
# there were some tasks to process, let's check if there is more work to do after a little break. | ||
time.sleep(random.uniform(sig_manager.time_to_wait[0], sig_manager.time_to_wait[1])) | ||
|
||
def handle(self, *args, **options): | ||
# 메인 스레드에서 수행할 것이 아니므로 시그널을 처리하지 못한다. | ||
# 임의로 Placeholder Class만 만들어서 사용한다. | ||
self.sig_manager = FakeSignalManager() | ||
self.run(*args, **options) | ||
|
||
|
||
class FakeSignalManager(SignalManager): | ||
def __init__(self): | ||
self.slow_down(None, None) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
# Generated by Django 4.2.13 on 2024-09-01 07:35 | ||
|
||
from django.db import migrations, models | ||
import django.db.models.deletion | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
initial = True | ||
|
||
dependencies = [ | ||
("contenttypes", "0002_remove_content_type_name"), | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name="Task", | ||
fields=[ | ||
( | ||
"id", | ||
models.BigAutoField( | ||
auto_created=True, | ||
primary_key=True, | ||
serialize=False, | ||
verbose_name="ID", | ||
), | ||
), | ||
("task_name", models.CharField(db_index=True, max_length=190)), | ||
("task_params", models.TextField()), | ||
("task_hash", models.CharField(db_index=True, max_length=40)), | ||
( | ||
"verbose_name", | ||
models.CharField(blank=True, max_length=255, null=True), | ||
), | ||
("priority", models.IntegerField(db_index=True, default=0)), | ||
("run_at", models.DateTimeField(db_index=True)), | ||
( | ||
"repeat", | ||
models.BigIntegerField( | ||
choices=[ | ||
(3600, "hourly"), | ||
(86400, "daily"), | ||
(604800, "weekly"), | ||
(1209600, "every 2 weeks"), | ||
(2419200, "every 4 weeks"), | ||
(0, "never"), | ||
], | ||
default=0, | ||
), | ||
), | ||
("repeat_until", models.DateTimeField(blank=True, null=True)), | ||
( | ||
"queue", | ||
models.CharField( | ||
blank=True, db_index=True, max_length=190, null=True | ||
), | ||
), | ||
("attempts", models.IntegerField(db_index=True, default=0)), | ||
( | ||
"failed_at", | ||
models.DateTimeField(blank=True, db_index=True, null=True), | ||
), | ||
("last_error", models.TextField(blank=True)), | ||
( | ||
"locked_by", | ||
models.CharField( | ||
blank=True, db_index=True, max_length=64, null=True | ||
), | ||
), | ||
( | ||
"locked_at", | ||
models.DateTimeField(blank=True, db_index=True, null=True), | ||
), | ||
( | ||
"creator_object_id", | ||
models.PositiveIntegerField(blank=True, null=True), | ||
), | ||
( | ||
"creator_content_type", | ||
models.ForeignKey( | ||
blank=True, | ||
null=True, | ||
on_delete=django.db.models.deletion.CASCADE, | ||
related_name="background_task", | ||
to="contenttypes.contenttype", | ||
), | ||
), | ||
], | ||
options={ | ||
"db_table": "background_task", | ||
}, | ||
), | ||
migrations.CreateModel( | ||
name="CompletedTask", | ||
fields=[ | ||
( | ||
"id", | ||
models.BigAutoField( | ||
auto_created=True, | ||
primary_key=True, | ||
serialize=False, | ||
verbose_name="ID", | ||
), | ||
), | ||
("task_name", models.CharField(db_index=True, max_length=190)), | ||
("task_params", models.TextField()), | ||
("task_hash", models.CharField(db_index=True, max_length=40)), | ||
( | ||
"verbose_name", | ||
models.CharField(blank=True, max_length=255, null=True), | ||
), | ||
("priority", models.IntegerField(db_index=True, default=0)), | ||
("run_at", models.DateTimeField(db_index=True)), | ||
( | ||
"repeat", | ||
models.BigIntegerField( | ||
choices=[ | ||
(3600, "hourly"), | ||
(86400, "daily"), | ||
(604800, "weekly"), | ||
(1209600, "every 2 weeks"), | ||
(2419200, "every 4 weeks"), | ||
(0, "never"), | ||
], | ||
default=0, | ||
), | ||
), | ||
("repeat_until", models.DateTimeField(blank=True, null=True)), | ||
( | ||
"queue", | ||
models.CharField( | ||
blank=True, db_index=True, max_length=190, null=True | ||
), | ||
), | ||
("attempts", models.IntegerField(db_index=True, default=0)), | ||
( | ||
"failed_at", | ||
models.DateTimeField(blank=True, db_index=True, null=True), | ||
), | ||
("last_error", models.TextField(blank=True)), | ||
( | ||
"locked_by", | ||
models.CharField( | ||
blank=True, db_index=True, max_length=64, null=True | ||
), | ||
), | ||
( | ||
"locked_at", | ||
models.DateTimeField(blank=True, db_index=True, null=True), | ||
), | ||
( | ||
"creator_object_id", | ||
models.PositiveIntegerField(blank=True, null=True), | ||
), | ||
( | ||
"creator_content_type", | ||
models.ForeignKey( | ||
blank=True, | ||
null=True, | ||
on_delete=django.db.models.deletion.CASCADE, | ||
related_name="completed_background_task", | ||
to="contenttypes.contenttype", | ||
), | ||
), | ||
], | ||
), | ||
] |
Empty file.
Oops, something went wrong.