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

Add support for interval cron expressions #93

Open
wants to merge 11 commits into
base: develop
Choose a base branch
from

Conversation

jbrody1
Copy link

@jbrody1 jbrody1 commented Dec 8, 2015

I am using graphite-beacon to monitor a production service that sees varying load patterns throughout the day and by day of week. I've added the ability to use cron syntax to schedule checks, rather than a fixed interval. This allows me to have alerts with certain thresholds for weekdays and work hours, with different thresholds for off-peak hours.

@jbrody1
Copy link
Author

jbrody1 commented Dec 16, 2015

Bump for @klen @garrettheel, any interest?

@garrettheel
Copy link
Collaborator

Thanks for the PR @jbrody1. This looks useful - I'm just waiting to see how some other changes fall out before looking at this more seriously.

Would the ability to write more expressive rules also fit your need? (E.g "warning: < 200MB AND it is Wednesday")

@jbrody1
Copy link
Author

jbrody1 commented Jan 11, 2016

More expressive rules would be great. In my case, I need rules that are enforced not only on certain days (weekdays), but certain times of day (working hours), excluding specific days (holidays), etc. Thinking about the best way to express these conditions, it could be either a new, custom syntax for chronological expressions, or cron itself. Given this choice, I went with cron.

I'm interested to see what else is coming for graphite-beacon. In the meantime I'll resolve the conflicts in case you want this PR.

@jbrody1
Copy link
Author

jbrody1 commented May 6, 2016

Bump. Any interest? I've been running this in production for several months now and it has been working well.

@jbrody1
Copy link
Author

jbrody1 commented Oct 5, 2016

Bump @garrettheel. We're still using this in production. I'd love to get back on the main codeline.

Copy link
Collaborator

@garrettheel garrettheel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution, this looks really useful! Would you also be able to add some docs for this to the README?

"""Schedule the next run of this callback."""
if self.is_running:
now = datetime.now()
next = self.cron.get_next(datetime)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

next shadows the python builtin, please use a different name

interval = options.get('interval', self.reactor.options['interval'])
time_window = options.get('time_window', None)

if is_cron(interval):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't set history_size in here which will break any uses of historical

time_window = options.get('time_window', None)

if is_cron(interval):
self.interval = interval
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you check that this is a valid cron expression before proceeding here? It's best to fail early on bad input

@@ -15,6 +15,9 @@
import math
from collections import deque, defaultdict
from itertools import islice
from croniter import croniter
from datetime import datetime
from threading import Lock
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is unused

next = self.cron.get_next(datetime)
while next <= now:
next = self.cron.get_next(datetime)
LOGGER.debug("now: %s", now)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add context that this belongs to CronCallback

@coveralls
Copy link

Coverage Status

Changes Unknown when pulling 32e3c75 on jbrody1:develop into * on klen:develop*.

@coveralls
Copy link

Coverage Status

Changes Unknown when pulling f36744c on jbrody1:develop into * on klen:develop*.

@jbrody1
Copy link
Author

jbrody1 commented Oct 19, 2016

Thanks for the comments @garrettheel. I've implemented the changes you requested. As you can see, there was an issue with implementing historical values for cron expressions. The possible solutions were:

  1. Continue using only time-based historical windows everywhere (minimizes syntax changes, but adds complexity for cron intervals).
  2. Support either time-based or size-based historical windows (adds syntax changes and complexity).
  3. Support time-based or size-based historical windows for fixed intervals, but only size-based historical windows from cron intervals (adds syntax changes, but minimizes complexity).

I went with #1 to minimize the surface area of the change on syntax/API. However, this has the side effect of potentially using fewer data points if there are large gaps in the cron schedule. I think #3 might be functionally better, though it puts more burden on the user.

Please take a look, and let me know if you have a preference.

Thanks,
John


def is_running(self):
"""Is running."""
return self.is_running
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use the same name for the function and the variable here

if self.is_running:
now = datetime.now()
next_time = self.cron.get_next(datetime)
while next_time <= now:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think it's worth having a limit on how far this will search before erroring/failing early?

@garrettheel
Copy link
Collaborator

Ideally I'd like to see (2) but (3) would be fine for now also. Can you leave all of the history changes out of this PR and address them in a separate one? Let's stick to size-based and figure out a way to add time-based in a non-breaking way later.

I'm happy with you to error with "Unsupported" if you don't want to implement history for cron intervals right now (just be sure to document it).

Otherwise this is really great and pretty much ready to merge!

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.

4 participants