-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Send pulse triggers should respect report timezone #42502
Changes from 3 commits
7c20853
e76e819
a23e0c1
907da2d
6ef0844
78e24b1
757093e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -265,7 +265,7 @@ | |
:priority (.getPriority trigger) | ||
:start-time (.getStartTime trigger) | ||
:may-fire-again? (.mayFireAgain trigger) | ||
:data (.getJobDataMap trigger)}) | ||
:data (into {} (.getJobDataMap trigger))}) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why did you need to do this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we need this in tests because we can't compare JobDataMap with clojure map |
||
|
||
(defmethod trigger->info CronTrigger | ||
[^CronTrigger trigger] | ||
|
@@ -274,6 +274,9 @@ | |
:schedule | ||
(.getCronExpression trigger) | ||
|
||
:timezone | ||
(.getID (.getTimeZone trigger)) | ||
|
||
:misfire-instruction | ||
;; not 100% sure why `case` doesn't work here... | ||
(condp = (.getMisfireInstruction trigger) | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -11,15 +11,18 @@ | |||||||||||||||
[clojurewerkz.quartzite.jobs :as jobs] | ||||||||||||||||
[clojurewerkz.quartzite.schedule.cron :as cron] | ||||||||||||||||
[clojurewerkz.quartzite.triggers :as triggers] | ||||||||||||||||
[metabase.driver :as driver] | ||||||||||||||||
[metabase.models.pulse :as pulse] | ||||||||||||||||
[metabase.models.task-history :as task-history] | ||||||||||||||||
[metabase.pulse] | ||||||||||||||||
[metabase.query-processor.timezone :as qp.timezone] | ||||||||||||||||
[metabase.task :as task] | ||||||||||||||||
[metabase.util.cron :as u.cron] | ||||||||||||||||
[metabase.util.log :as log] | ||||||||||||||||
[metabase.util.malli :as mu] | ||||||||||||||||
[toucan2.core :as t2]) | ||||||||||||||||
(:import | ||||||||||||||||
(java.util TimeZone) | ||||||||||||||||
(org.quartz CronTrigger TriggerKey))) | ||||||||||||||||
|
||||||||||||||||
(set! *warn-on-reflection* true) | ||||||||||||||||
|
@@ -35,12 +38,13 @@ | |||||||||||||||
u.cron/schedule-map->cron-string | ||||||||||||||||
(str/replace " " "_"))))) | ||||||||||||||||
|
||||||||||||||||
(defn- send-pulse-trigger-key->schedule-map | ||||||||||||||||
(defn- send-pulse-trigger-key->info | ||||||||||||||||
[trigger-key] | ||||||||||||||||
(let [[_ _pulse-id schedule-str] (re-matches #"metabase\.task\.send-pulse\.trigger\.(\d+)\.(.*)" trigger-key)] | ||||||||||||||||
(-> schedule-str | ||||||||||||||||
(str/replace "_" " ") | ||||||||||||||||
u.cron/cron-string->schedule-map))) | ||||||||||||||||
(let [[_ pulse-id schedule-str] (re-matches #"metabase\.task\.send-pulse\.trigger\.(\d+)\.(.*)" trigger-key)] | ||||||||||||||||
{:pulse-id (parse-long pulse-id) | ||||||||||||||||
:schedule-map (-> schedule-str | ||||||||||||||||
(str/replace "_" " ") | ||||||||||||||||
u.cron/cron-string->schedule-map)})) | ||||||||||||||||
|
||||||||||||||||
(defn- send-pulse! | ||||||||||||||||
[pulse-id channel-ids] | ||||||||||||||||
|
@@ -56,16 +60,24 @@ | |||||||||||||||
(catch Throwable e | ||||||||||||||||
(log/errorf e "Error sending Pulse %d to channel ids: %s" pulse-id (str/join ", " channel-ids))))) | ||||||||||||||||
|
||||||||||||||||
(defn- send-trigger-timezone | ||||||||||||||||
[] | ||||||||||||||||
(or (driver/report-timezone) | ||||||||||||||||
(qp.timezone/system-timezone-id) | ||||||||||||||||
"UTC")) | ||||||||||||||||
Comment on lines
+63
to
+67
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This feels like it could be DRY'd up with other parts of the codebase outside of pulses. It doesn't feel right to have a notion of the trigger time zone specific to send pulses task. Also, before we were just using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it's the fallback timezone id we used in QP so I think we should apply it for pulse too:
It's also used in other tasks like persist refresh : https://github.com/metabase/metabase/blob/send-pulse-trigger-respect-timezone/src/metabase/task/persist_refresh.clj#L286 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, I'm suggesting to DRY this up then. Otherwise they can get out of sync. Instead of duplicating this logic again, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Wdyt if we move this to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't mind where it goes too much. But I'm seeing a couple more places this could be DRYed up too: metabase/src/metabase/email/messages.clj Lines 577 to 579 in e6f4795
metabase/src/metabase/task/index_values.clj Lines 82 to 84 in e6f4795
|
||||||||||||||||
|
||||||||||||||||
(mu/defn ^:private send-pulse-trigger | ||||||||||||||||
"Build a Quartz trigger to send a pulse to a list of channel-ids." | ||||||||||||||||
^CronTrigger | ||||||||||||||||
([pulse-id :- pos-int? | ||||||||||||||||
schedule-map :- u.cron/ScheduleMap | ||||||||||||||||
pc-ids :- [:set pos-int?]] | ||||||||||||||||
(send-pulse-trigger pulse-id schedule-map pc-ids 6)) | ||||||||||||||||
pc-ids :- [:set pos-int?] | ||||||||||||||||
timezone :- :string] | ||||||||||||||||
(send-pulse-trigger pulse-id schedule-map pc-ids timezone 6)) | ||||||||||||||||
([pulse-id :- pos-int? | ||||||||||||||||
schedule-map :- u.cron/ScheduleMap | ||||||||||||||||
pc-ids :- [:set pos-int?] | ||||||||||||||||
timezone :- :string | ||||||||||||||||
priority :- pos-int?] | ||||||||||||||||
(triggers/build | ||||||||||||||||
(triggers/with-identity (send-pulse-trigger-key pulse-id schedule-map)) | ||||||||||||||||
|
@@ -75,6 +87,7 @@ | |||||||||||||||
(triggers/with-schedule | ||||||||||||||||
(cron/schedule | ||||||||||||||||
(cron/cron-schedule (u.cron/schedule-map->cron-string schedule-map)) | ||||||||||||||||
(cron/in-time-zone (TimeZone/getTimeZone ^String timezone)) | ||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is the fix |
||||||||||||||||
;; if we miss a sync for one reason or another (such as system being down) do not try to run the sync again. | ||||||||||||||||
;; Just wait until the next sync cycle. | ||||||||||||||||
;; | ||||||||||||||||
|
@@ -129,21 +142,39 @@ | |||||||||||||||
priority (ms-duration->priority (- end start))] | ||||||||||||||||
(when (= :done result) | ||||||||||||||||
(log/infof "Updating priority of trigger %s to %d" (.getName ^TriggerKey (send-pulse-trigger-key pulse-id schedule-map)) priority) | ||||||||||||||||
(task/reschedule-trigger! (send-pulse-trigger pulse-id schedule-map channel-ids priority)))) | ||||||||||||||||
(task/reschedule-trigger! (send-pulse-trigger pulse-id schedule-map channel-ids (send-trigger-timezone) priority)))) | ||||||||||||||||
(log/infof "Skip sending pulse %d because all channels have no recipients" pulse-id)))) | ||||||||||||||||
|
||||||||||||||||
;; called in [driver/report-timezone] setter | ||||||||||||||||
(defn update-send-pulse-triggers-timezone! | ||||||||||||||||
"Update the timezone of all SendPulse triggers if the report timezone changes." | ||||||||||||||||
[] | ||||||||||||||||
(let [triggers (-> send-pulse-job-key task/job-info :triggers) | ||||||||||||||||
new-timezone (send-trigger-timezone)] | ||||||||||||||||
(doseq [trigger triggers | ||||||||||||||||
:when (not= new-timezone (:timezone trigger))] ; skip if timezone is the same | ||||||||||||||||
(let [trigger-key (:key trigger) | ||||||||||||||||
channel-ids (get-in trigger [:data "channel-ids"]) | ||||||||||||||||
{:keys [pulse-id | ||||||||||||||||
schedule-map]} (send-pulse-trigger-key->info trigger-key)] | ||||||||||||||||
(log/infof "Updating timezone of trigger %s to %s. Was: %s" trigger-key new-timezone (:timezone trigger)) | ||||||||||||||||
(task/reschedule-trigger! (send-pulse-trigger pulse-id schedule-map channel-ids new-timezone (:priority trigger))))))) | ||||||||||||||||
|
||||||||||||||||
(jobs/defjob ^{:doc "Triggers that send a pulse to a list of channels at a specific time"} | ||||||||||||||||
SendPulse | ||||||||||||||||
[context] | ||||||||||||||||
(let [{:strs [pulse-id channel-ids]} (qc/from-job-data context) | ||||||||||||||||
trigger-key (.. context getTrigger getKey getName)] | ||||||||||||||||
(send-pulse!* (send-pulse-trigger-key->schedule-map trigger-key) pulse-id channel-ids))) | ||||||||||||||||
(send-pulse!* (:schedule-map (send-pulse-trigger-key->info trigger-key)) pulse-id channel-ids))) | ||||||||||||||||
|
||||||||||||||||
;;; ------------------------------------------------ Job: InitSendPulseTriggers ---------------------------------------------------- | ||||||||||||||||
|
||||||||||||||||
(declare update-send-pulse-trigger-if-needed!) | ||||||||||||||||
|
||||||||||||||||
(defn- init-send-pulse-triggers! | ||||||||||||||||
(defn init-send-pulse-triggers! | ||||||||||||||||
"Update send pulse triggers for all active pulses. | ||||||||||||||||
Called once when Metabase starts up to create triggers for all existing PulseChannels | ||||||||||||||||
and whenever the report timezone changes." | ||||||||||||||||
[] | ||||||||||||||||
(let [trigger-slot->pc-ids (as-> (t2/select :model/PulseChannel :enabled true) results | ||||||||||||||||
(group-by #(select-keys % [:pulse_id :schedule_type :schedule_day :schedule_hour :schedule_frame]) results) | ||||||||||||||||
|
@@ -215,7 +246,7 @@ | |||||||||||||||
(do | ||||||||||||||||
(log/infof "Updating Send Pulse trigger %s for pulse %d with new pc-ids: %s, was: %s " trigger-key pulse-id new-pc-ids existing-pc-ids) | ||||||||||||||||
(task/delete-trigger! trigger-key) | ||||||||||||||||
(task/add-trigger! (send-pulse-trigger pulse-id schedule-map new-pc-ids)))))) | ||||||||||||||||
(task/add-trigger! (send-pulse-trigger pulse-id schedule-map new-pc-ids (send-trigger-timezone))))))) | ||||||||||||||||
|
||||||||||||||||
;;; -------------------------------------------------- Task init ------------------------------------------------ | ||||||||||||||||
|
||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It'll be nice to replace this sneak hidden cross-module dependency with an event system - maybe in scope for the notifications refactor?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, it's one of my annoyance as well!