Repository navigation
Conversation
Each add_exception_rule de-duplicated with Array#include?, which builds Rule#to_hash for every comparison, and exception_time? checked every candidate occurrence against every exception rule. Building, loading and enumerating a schedule with n exception times was therefore O(n^2). Single exception times are now kept in sets: exact time values for de-duplication (the same equality Rule#== uses) and whole seconds for lookup (the same equality SingleOccurrenceRule#on? uses). Recurring exception rules keep the existing check. The index is rebuilt lazily after an exception is removed.
Set needs an explicit require before Ruby 3.2, which standardrb flags as redundant on newer Rubies.
Object#dup shares the rule arrays between a schedule and its copy, but a copy that removed an exception reset only its own index, so the original kept skipping the removed time. The index is now created with the schedule and only updated in place, like the arrays it mirrors.
zulqarnain-hh
pushed a commit
to hungryhub-team/ice_cube
that referenced
this pull request
Oct 26, 2026
Backport of the follow-up on ice-cube-ruby#572.
This file contains hidden or 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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Schedules with many exception times get slow fast. A typical case is blocking out every slot of a recurring schedule for a few weeks, e.g. one
add_exception_timeper 15-minute slot, which comes to a few thousand exception times.Two things make it quadratic:
add_exception_rulede-duplicates withArray#include?, andRule#==buildsto_hashfor every comparison, so adding n exception times (directly, or throughfrom_yaml/from_hash) is O(n²).exception_time?checks every candidate occurrence against every exception rule.This PR keeps single exception times in two hashes:
time.to_r) for de-duplication, which is the same equalityRule#==givesSingleOccurrenceRuletime.to_i) for lookup, which is the same equalitySingleOccurrenceRule#on?usesAdding and checking an exception time is now O(1). Recurring exception rules keep the existing
include?andon?checks. The index is created with the schedule and rebuilt in place onremove_exception_rule/remove_exception_time. Updating it in place meansdup/clonecopies share it the same way they already share the rule arrays. Schedules loaded from Marshal dumps made by older versions build it on first use. Nothing in the public API changes.Benchmark
A 15-minute schedule over 27 days with 2,400 exception times (Ruby 2.7):
from_yamlall_occurrencesScript
Behaviour
I compared master and this branch over a set of schedules: UTC, local,
ActiveSupport::TimeWithZoneand subsecond start times;until,countandhour_of_dayrules; and duplicate exception times given in other zones or with fractional seconds, mixed with recurring exception rules. For each I checked occurrences,occurs_at?,occurring_between?,next_occurrencesandprevious_occurrence,to_ical, YAML, hash and Marshal round trips, and removing and re-adding exceptions. The output was identical.Specs
The new specs in
schedule_spec.rbcover:dupcopy and its original staying consistent with their exception times