Skip to content
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

Improve recurrence handling, remove restrictions #627

Merged
merged 6 commits into from
Nov 4, 2024

Conversation

axunonb
Copy link
Collaborator

@axunonb axunonb commented Oct 28, 2024

  • Deprecated RecurrenceRestrictionType and RecurrenceEvaluationModeType enums.

Updated RecurrencePattern.cs:

  • Deprecated RestrictionType and EvaluationMode properties.
  • Set default for RestrictionType.

Fix RecurrenceUtil:

  • None of GetOccurrences() overloads convert IDateTime arguments with AsSystemLocal. This partial conversion is considered a bug. The change may break user code that contains a fix for the bug.

Refined RecurrencePatternEvaluator:

  • Convert existing Java style comments for methods to XML documentation comments.

Updated Calendar.cs:

  • Deprecated properties and methods associated with RestrictionType and EvaluationMode
  • None of GetOccurrences() overloads convert IDateTime arguments with AsSystemLocal. This partial conversion is considered a bug. The change may break user code that contains a fix for the bug.
  • Removed obsolete Evaluate method (just threw when called)

Enhanced RecurrenceTests.cs with more robust test cases:

  • Replaced Assert.That with Assert.Multiple.
  • Changed assertion for occurrences count.
  • Renamed test methods for clarity.

Corrected typos and improved formatting for better readability.

Fixes #622

- Deprecated `RecurrenceRestrictionType` and `RecurrenceEvaluationModeType` enums.

Updated RecurrencePattern.cs:
- Deprecated `RestrictionType` and `EvaluationMode` properties.
- Set default for `RestrictionType`.

Enhanced RecurrenceUtil:
- Added new using directives.
- Introduced `DefaultTimeout`.
- Updated `GetOccurrences` with timeout logic.

Refined RecurrencePatternEvaluator:
- Removed `_maxIncrementCount` with connected logic in favor of `RecurrenceUtil.DefaultTimeout`
- Improved XML documentation comments.

Updated Calendar.cs:
- Simplified `Load` method.
- Deprecated certain properties and methods associated with `RestrictionType` and `EvaluationMode`
- Removed obsolete `Evaluate` method.

Enhanced RecurrenceTests.cs with more robust test cases:
- Replaced `Assert.That` with `Assert.Multiple`.
- Changed assertion for occurrences count.
- Renamed test methods for clarity.
- Added timeout settings and updated expected exceptions.
- Adjusted test data and added comments.

Corrected typos and improved formatting for better readability.

Fixes ical-org#622
@axunonb
Copy link
Collaborator Author

axunonb commented Oct 28, 2024

@minichma
Sorry for trashing your inbox with this PR.
I couldn't reproduce the timeouts locally, so it was a trial and error work to get them fixed.
The RecurrenceUtil.RecurrenceTimeout now has a default of RecurrenceUtil.DefaultTimeout (1000ms), which is increased to 1500ms for some test. This is really a lot for calculating recurrences, isn't it?

@minichma
Copy link
Collaborator

@axunonb no worries 😄 Didn't have time to go into detail so far, just my two cents on timeouts in general. I think it would be a good idea to keep the behavior of the algorithms deterministic. By introducing a timeout, we lose determinism, which can cause quite some issues of different kind. E.g. debugging is getting more challenging, as the timeout will expire quickly after a breakpoint was hit. More importantly the libs will behave differently depending on the system they are running on and the load that system currently experiences. So on server systems people might start seeing exceptions at peak hours in code that normally runs just fine. So all in all I would rather discourage the implementation of time-based behavior. I think the limitation based on increments was not too bad, because its fully deterministic, but certainly should be configurable. An alternative could be to allow users to configure a callback that's invoked frequently (probably on every increment). Users can then implement a timeout on their own. Or maybe pass a CancellationToken to the evaluation function that allows the caller to cancel based on a timeout, though this the callback would be preferable, as the CancellationToken wouldn't allow implementing deterministic behavior either.

@axunonb
Copy link
Collaborator Author

axunonb commented Oct 28, 2024

Thanks for the quick feedback!

"Deterministic algorithms" is a valid point, agree.
After disabling RecurrenceRestrictionType and RecurrenceEvaluationModeType there were several tests running for ever. This was resolved with the timeout. But of course these unpredictable results are the downside that I dislike.

  1. Configurable number of increments:
    • Con:GetOccurrences will block for an unpredictable time, if not configured properly (forever, with default "infinite").
    • Con: With recurrences not known at compile time, configuration is hard, may lead to unexpected results.
    • Pro: simple.
    • How to implement: An optional "max increments" argument to GetOccurrences, with "infinite" as the default.
  2. Callback:
    • Con: GetOccurrences` will block for an unpredictable time, maybe forever, if not configured properly.
    • Con: A bit more complex for users.
    • Pro: On top of option 1, it gives the user the chance to cancel.
    • How to implement: An optional callback argument to GetOccurrences, meaning "infinite" if not set differently by the user.
  3. Not to forget: Return IEnumberable<Occurrence> instead of HashSet<Occurrence>

Really hard to decide. What would you prefer?

@minichma
Copy link
Collaborator

Not to forget: Return IEnumberable instead of HashSet

This should be implemented anyways, because there's really no need (from a user's perspective) to iterate over all occurrences if just a few are needed. And more importantly, AFAIK the order in which HashSet returns items is not defined, so the user would have to sort them before using. But this would just be an addition, not an alternative to 1/2, right? Because if no occurrences would be returned, it could still run forever.

From the other two options I would probably prefer 1, because its very straightforward. Question would be, how to do the configuration. I'd suggest to pass something like EvaluationOptions to GetOccurrences (probably default to null), which can contain all the params that should be adjustable, also for the future.

That GetOccurrences could block for an indefinite amount of time is not something I see critical. To some degree we could say, this is how computers work. The time processing takes is most often dependent on the CPU and system load, so this is not something special here. Its not on us to decide how long a task is supposed to take. And generally the duration should be low. If its noticable, there's something wrong anyways.

One other thought I have is whether it should be possible that the iteration runs for a noticable amount of time at all. It should probably be possible to simplify the whole algorithm in a way, that it will only process time values that will eventually be returned to the user (exception: very large COUNT or SETPOS, but those should be forbidden anyways), which would prevent the whole thing from blocking. But that's probably a bigger story.

@axunonb
Copy link
Collaborator Author

axunonb commented Oct 28, 2024

I'm fine going this way, thanks. EvaluationOptions will give us the flexibility for any future extensions. I'll work on an update. Except for the last paragraph, which is a bigger story indeed.

- Removed `SetupBeforeEachTest` and timeout settings from tests.
- Re-introduced `_maxIncrementCount` in `RecurrencePatternEvaluator`.
- Modified `GetDates` to use `_maxIncrementCount` for loop control.
- Removed timeout handling from `RecurrenceUtil` class.
@axunonb axunonb changed the title Implement timeout for recurrence handling, remove restrictions Improve recurrence handling, remove restrictions Oct 28, 2024
- Updated test method names to reflect checking for at least a defined number of occurrences.
- Removed redundant summary comments to test methods for clarity.
- Remove remaining mentions of "Timeout"
@axunonb
Copy link
Collaborator Author

axunonb commented Oct 28, 2024

@minichma
The PR now serves the following targets:

  1. Mark all code related to RecurrenceRestrictionType and RecurrenceEvaluationModeType enums as obsolete.
  2. Update test methods accordingly
  3. Close Get Occurrences fails on a RRULE FREQ=SECONDLY #622

What if we merged it and published a new minor version?
This would be a bug fix release for 2 issues.
Using it in existing code has no effect and is better than before, because restrictions were widely broken. We explain details in the release notes.

@axunonb axunonb marked this pull request as ready for review October 28, 2024 22:37
@axunonb axunonb requested a review from minichma October 28, 2024 22:37
* Secondly_DefinedNumberOfOccurrences_ShouldSucceed(), Minutely_DefinedNumberOfOccurrences_ShouldSucceed(), Hourly_DefinedNumberOfOccurrences_ShouldSucceed() assert exact number of occurences.

* Updated test methods to dynamically generate expected occurrences using loops
@axunonb
Copy link
Collaborator Author

axunonb commented Nov 4, 2024

@minichma Do you think this PR needs some more tasks to be completed? Otherwise I'd like to proceed as described.

minichma
minichma previously approved these changes Nov 4, 2024
Ical.Net/Constants.cs Show resolved Hide resolved
Ical.Net/DataTypes/RecurrencePattern.cs Outdated Show resolved Hide resolved
Ical.Net/Evaluation/RecurrenceUtil.cs Show resolved Hide resolved
@minichma
Copy link
Collaborator

minichma commented Nov 4, 2024

Do you think this PR needs some more tasks to be completed? Otherwise I'd like to proceed #627 (comment).

I think it's good as is. Left just a few minor comments.

Copy link

sonarqubecloud bot commented Nov 4, 2024

@axunonb axunonb merged commit 533aeeb into ical-org:main Nov 4, 2024
3 checks passed
@axunonb axunonb deleted the pr/remove-recurrence-restriction branch November 4, 2024 23:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Get Occurrences fails on a RRULE FREQ=SECONDLY
2 participants