3
3
import traceback
4
4
from datetime import datetime
5
5
from enum import Enum
6
+ from logging import DEBUG , INFO
6
7
from threading import Thread
7
8
from typing import List , Set , Optional , Sequence , Dict
8
9
9
10
import django
10
11
11
12
from scheduler .helpers .queues import Queue
12
13
from scheduler .helpers .queues import get_queue
14
+ from scheduler .helpers .queues .getters import get_queue_connection
13
15
from scheduler .helpers .utils import current_timestamp
14
16
from scheduler .models import Task
15
17
from scheduler .redis_models import SchedulerLock , JobModel , ScheduledJobRegistry
16
18
from scheduler .settings import SCHEDULER_CONFIG , logger
17
- from scheduler .types import ConnectionType
18
19
19
20
20
21
class SchedulerStatus (str , Enum ):
@@ -31,22 +32,15 @@ def _reschedule_tasks() -> None:
31
32
32
33
33
34
class WorkerScheduler :
34
- def __init__ (
35
- self ,
36
- queues : Sequence [Queue ],
37
- connection : ConnectionType ,
38
- worker_name : str ,
39
- interval : Optional [int ] = None ,
40
- ) -> None :
41
- interval = interval or SCHEDULER_CONFIG .SCHEDULER_INTERVAL
35
+ def __init__ (self , queues : Sequence [Queue ], worker_name : str , interval : Optional [int ] = None ) -> None :
42
36
self ._queues = queues
37
+ if len (queues ) == 0 :
38
+ raise ValueError ("At least one queue must be provided to WorkerScheduler" )
43
39
self ._scheduled_job_registries : List [ScheduledJobRegistry ] = []
44
40
self .lock_acquisition_time : Optional [datetime ] = None
45
- self ._pool_class = connection .connection_pool .connection_class
46
- self ._pool_kwargs = connection .connection_pool .connection_kwargs .copy ()
47
41
self ._locks : Dict [str , SchedulerLock ] = dict ()
48
- self .connection = connection
49
- self .interval = interval
42
+ self .connection = get_queue_connection ( queues [ 0 ]. name )
43
+ self .interval = interval or SCHEDULER_CONFIG . SCHEDULER_INTERVAL
50
44
self ._stop_requested = False
51
45
self .status = SchedulerStatus .STOPPED
52
46
self ._thread : Optional [Thread ] = None
@@ -57,6 +51,9 @@ def __init__(
57
51
def pid (self ) -> Optional [int ]:
58
52
return self ._pid
59
53
54
+ def log (self , level : int , message : str , * args , ** kwargs ) -> None :
55
+ logger .log (level , f"[Scheduler { self .worker_name } /{ self ._pid } ]: { message } " , * args , ** kwargs )
56
+
60
57
def _should_reacquire_locks (self ) -> bool :
61
58
"""Returns True if lock_acquisition_time is longer than 10 minutes ago"""
62
59
if not self .lock_acquisition_time :
@@ -70,12 +67,10 @@ def _acquire_locks(self) -> Set[str]:
70
67
if self .pid is None :
71
68
self ._pid = os .getpid ()
72
69
queue_names = [queue .name for queue in self ._queues ]
73
- logger .debug (
74
- f"""[Scheduler { self .worker_name } /{ self .pid } ] Trying to acquire locks for { ", " .join (queue_names )} """
75
- )
70
+ self .log (DEBUG , f"""Trying to acquire locks for { ", " .join (queue_names )} """ )
76
71
for queue in self ._queues :
77
72
lock = SchedulerLock (queue .name )
78
- if lock .acquire (self .pid , connection = queue .connection , expire = self .interval + 60 ):
73
+ if lock .acquire (self .pid , connection = self .connection , expire = self .interval + 60 ):
79
74
self ._locks [queue .name ] = lock
80
75
successful_locks .add (queue .name )
81
76
@@ -85,7 +80,7 @@ def _acquire_locks(self) -> Set[str]:
85
80
for queue_name in self ._locks :
86
81
queue = get_queue (queue_name )
87
82
self ._scheduled_job_registries .append (queue .scheduled_job_registry )
88
- logger . debug ( f"[Scheduler { self . worker_name } / { self . pid } ] Locks acquired for { ', ' .join (self ._locks .keys ())} " )
83
+ self . log ( DEBUG , f" Locks acquired for { ', ' .join (self ._locks .keys ())} " )
89
84
return successful_locks
90
85
91
86
def start (self ) -> None :
@@ -98,24 +93,22 @@ def start(self) -> None:
98
93
99
94
def request_stop_and_wait (self ) -> None :
100
95
"""Toggle self._stop_requested that's checked on every loop"""
101
- logger . debug ( f"[Scheduler { self . worker_name } / { self . pid } ] Stop Scheduler requested" )
96
+ self . log ( DEBUG , f" Stop Scheduler requested" )
102
97
self ._stop_requested = True
103
98
if self ._thread is not None :
104
99
self ._thread .join ()
105
100
106
101
def heartbeat (self ) -> None :
107
102
"""Updates the TTL on scheduler keys and the locks"""
108
103
lock_keys = ", " .join (self ._locks .keys ())
109
- logger . debug ( f"[Scheduler { self . worker_name } / { self . pid } ] Scheduler updating lock for queue { lock_keys } " )
104
+ self . log ( DEBUG , f" Scheduler updating lock for queue { lock_keys } " )
110
105
with self .connection .pipeline () as pipeline :
111
106
for lock in self ._locks .values ():
112
107
lock .expire (self .connection , expire = self .interval + 60 )
113
108
pipeline .execute ()
114
109
115
110
def stop (self ) -> None :
116
- logger .info (
117
- f"[Scheduler { self .worker_name } /{ self .pid } ] Stopping scheduler, releasing locks for { ', ' .join (self ._locks .keys ())} ..."
118
- )
111
+ self .log (INFO , f"Stopping scheduler, releasing locks for { ', ' .join (self ._locks .keys ())} ..." )
119
112
self .release_locks ()
120
113
self .status = SchedulerStatus .STOPPED
121
114
@@ -128,7 +121,7 @@ def release_locks(self) -> None:
128
121
129
122
def work (self ) -> None :
130
123
queue_names = [queue .name for queue in self ._queues ]
131
- logger . info ( f"""[Scheduler { self . worker_name } / { self . pid } ] Scheduler for { ", " .join (queue_names )} started""" )
124
+ self . log ( INFO , f"""Scheduler for { ", " .join (queue_names )} started""" )
132
125
django .setup ()
133
126
134
127
while True :
0 commit comments