33# Third Party Imports
44from fastapi .routing import APIRoute
55from google .cloud import scheduler_v1
6+ from google .protobuf import duration_pb2
67
78# Imports from this repository
89from fastapi_cloud_tasks .hooks import SchedulerHook
@@ -23,14 +24,21 @@ def __init__(
2324 name : str = "" ,
2425 schedule_create_timeout : float = 10.0 ,
2526 retry_config : scheduler_v1 .RetryConfig = None ,
26- time_zone : str = None
27+ time_zone : str = "UTC" ,
28+ force : bool = False ,
2729 ) -> None :
2830 super ().__init__ (route = route , base_url = base_url )
2931 if name == "" :
3032 name = route .unique_id
3133
3234 if retry_config is None :
33- retry_config = scheduler_v1 .RetryConfig (retry_count = 5 )
35+ retry_config = scheduler_v1 .RetryConfig (
36+ retry_count = 5 ,
37+ max_retry_duration = duration_pb2 .Duration (seconds = 0 ),
38+ min_backoff_duration = duration_pb2 .Duration (seconds = 5 ),
39+ max_backoff_duration = duration_pb2 .Duration (seconds = 120 ),
40+ max_doublings = 5 ,
41+ )
3442
3543 self .retry_config = retry_config
3644 location_parts = client .parse_common_location_path (location_path )
@@ -45,6 +53,7 @@ def __init__(
4553 self .method = schedulerMethod (route .methods )
4654 self .client = client
4755 self .pre_scheduler_hook = pre_scheduler_hook
56+ self .force = force
4857
4958 def schedule (self , ** kwargs ):
5059 # Create http request
@@ -63,25 +72,36 @@ def schedule(self, **kwargs):
6372 http_target = request ,
6473 schedule = self .cron_schedule ,
6574 retry_config = self .retry_config ,
75+ time_zone = self .time_zone ,
6676 )
67- if self .time_zone is not None :
68- job .time_zone = self .time_zone
77+
6978 request = scheduler_v1 .CreateJobRequest (parent = self .location_path , job = job )
7079
7180 request = self .pre_scheduler_hook (request )
7281
73- # Delete and create job
74- self .delete ()
75- return self .client .create_job (
76- request = request , timeout = self .schedule_create_timeout
77- )
82+ if self .force or self ._has_changed (request = request ):
83+ # Delete and create job
84+ self .delete ()
85+ self .client .create_job (request = request , timeout = self .schedule_create_timeout )
86+
87+ def _has_changed (self , request : scheduler_v1 .CreateJobRequest ):
88+ try :
89+ job = self .client .get_job (name = request .job .name )
90+ # Remove things that are either output only or GCP adds by default
91+ job .user_update_time = None
92+ job .state = None
93+ job .schedule_time = None
94+ del job .http_target .headers ["User-Agent" ]
95+ # Proto compare works directly with `__eq__`
96+ return job != request .job
97+ except Exception :
98+ return True
99+ return False
78100
79101 def delete (self ):
80102 # We return true or exception because you could have the delete code on multiple instances
81103 try :
82- self .client .delete_job (
83- name = self .job_id , timeout = self .schedule_create_timeout
84- )
104+ self .client .delete_job (name = self .job_id , timeout = self .schedule_create_timeout )
85105 return True
86106 except Exception as ex :
87107 return ex
0 commit comments