mirror of
https://github.com/discourse/discourse.git
synced 2024-12-26 16:33:47 +08:00
4406bbb020
Under certain conditions, a recurring automation can end up in a state with no pending automation records, causing it to not execute again until manually triggered.
We use the `RRule` gem to calculate the next execution date and time for recurring automations. The gem takes the interval, frequency, start date, and a time range, and returns all dates/times within this range that meet the recurrence rule. For example:
```ruby
RRule::Rule
.new("FREQ=DAILY;INTERVAL=1", dtstart: Time.parse("2023-01-01 07:30:00 UTC"))
.between(Time.zone.now, Time.zone.now + 2.days)
# => [Sat, 14 Sep 2024 07:30:00.000000000 UTC +00:00, Sun, 15 Sep 2024 07:30:00.000000000 UTC +00:00]
```
However, if the time component of the first point provided to `.between()` is slightly ahead of the start date (e.g., `dtstart`), the first date/time returned by `RRule` can fall outside the specified range by the same subsecond amount. For instance:
```ruby
RRule::Rule
.new("FREQ=DAILY;INTERVAL=1", dtstart: Time.parse("2023-01-01 07:30:00 UTC"))
.between(Time.parse("2023-01-01 07:30:00.999 UTC"), Time.parse("2023-01-03 07:30:00 UTC"))
.first
# => Sun, 01 Jan 2023 07:30:00.000000000 UTC +00:00
```
Here, the start date/time given to `.between()` is 999 milliseconds after 07:30:00, but the first date returned is exactly 07:30:00 without the 999 milliseconds. This causes the next recurring date to fall into the past if the automation executes within a subsecond of the start time, leading to the automation stalling.
I'm not sure why `RRule` does this, but it seems intentional judging by the source of the `.between()` method:
|
||
---|---|---|
.. | ||
after_post_cook_spec.rb | ||
category_created_edited_spec.rb | ||
pm_created_spec.rb | ||
point_in_time_spec.rb | ||
post_created_edited_spec.rb | ||
recurring_spec.rb | ||
stalled_wiki_spec.rb | ||
topic_spec.rb | ||
topic_tags_changed_spec.rb | ||
user_added_to_group_spec.rb | ||
user_badge_granted_spec.rb | ||
user_first_logged_in_spec.rb | ||
user_promoted_spec.rb | ||
user_removed_from_group_spec.rb | ||
user_updated_spec.rb |