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

#574 Avoid Recomputing Schedules #616

Merged
merged 39 commits into from Apr 14, 2023
Merged

Conversation

victorgarcia98
Copy link
Contributor

Closes #574

This commit includes the definition of the decorator @redis_cache, which allows to cache job creation whe a function is called with the same arguments.

Signed-off-by: victor <victor@seita.nl>
@victorgarcia98 victorgarcia98 changed the title Issue #574 #574 Avoid Recomputing Schedules Mar 27, 2023
@coveralls
Copy link
Collaborator

coveralls commented Mar 27, 2023

Pull Request Test Coverage Report for Build 4700657181

  • 51 of 52 (98.08%) changed or added relevant lines in 5 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+0.2%) to 64.669%

Changes Missing Coverage Covered Lines Changed/Added Lines %
flexmeasures/data/services/utils.py 41 42 97.62%
Totals Coverage Status
Change from base Build 4688597922: 0.2%
Covered Lines: 6989
Relevant Lines: 10061

💛 - Coveralls

Copy link
Contributor

@Flix6x Flix6x left a comment

Choose a reason for hiding this comment

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

Very nice work! Just making sure: the job id itself is unaffected right? (We hand out the job id through the API as a return value for triggering a schedule.)

flexmeasures/data/services/utils.py Outdated Show resolved Hide resolved
flexmeasures/data/services/utils.py Outdated Show resolved Hide resolved
flexmeasures/data/services/utils.py Outdated Show resolved Hide resolved
flexmeasures/data/tests/test_scheduling_repeated_jobs.py Outdated Show resolved Hide resolved
flexmeasures/data/services/utils.py Outdated Show resolved Hide resolved
flexmeasures/data/services/utils.py Outdated Show resolved Hide resolved
flexmeasures/data/tests/test_scheduling_repeated_jobs.py Outdated Show resolved Hide resolved
@victorgarcia98
Copy link
Contributor Author

Very nice work! Just making sure: the job id itself is unaffected right? (We hand out the job id through the API as a return value for triggering a schedule.)

Yes! In the first trigger, we just return the Job. In the following triggers, we fetch the job and return it (the same job with the same job id).

Signed-off-by: victor <victor@seita.nl>
… provide a tuple with the paramterers to be considered for the hash. Added some extra info to the docstrings.

Signed-off-by: victor <victor@seita.nl>
… has failed.

Signed-off-by: victor <victor@seita.nl>
…lds different hashes.

Signed-off-by: victor <victor@seita.nl>
@Flix6x
Copy link
Contributor

Flix6x commented Mar 28, 2023

This PR is coming along nicely. Don't forget to add the changelog entry. Other than that, it's just a matter of settling what to do with failed jobs.

Signed-off-by: victor <victor@seita.nl>
…e creation of a new job.

Signed-off-by: victor <victor@seita.nl>
Signed-off-by: victor <victor@seita.nl>
Copy link
Contributor

@nhoening nhoening left a comment

Choose a reason for hiding this comment

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

Cool stuff. I have some deeper questions and small asks.

flexmeasures/data/services/utils.py Outdated Show resolved Hide resolved
flexmeasures/data/services/utils.py Outdated Show resolved Hide resolved
flexmeasures/data/tests/test_scheduling_repeated_jobs.py Outdated Show resolved Hide resolved
flexmeasures/data/tests/test_scheduling_repeated_jobs.py Outdated Show resolved Hide resolved
flexmeasures/data/services/utils.py Show resolved Hide resolved
Fixed some typos.
Simplified a test.
Renamed @redis_cache -> @job_cache.
Updated docstrings.

Signed-off-by: victor <victor@seita.nl>
Copy link
Contributor

@nhoening nhoening left a comment

Choose a reason for hiding this comment

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

Just more typos :D

flexmeasures/data/services/utils.py Outdated Show resolved Hide resolved
flexmeasures/data/services/utils.py Outdated Show resolved Hide resolved
Signed-off-by: F.N. Claessen <felix@seita.nl>
…ments of the function being decorated

Signed-off-by: F.N. Claessen <felix@seita.nl>
Signed-off-by: F.N. Claessen <felix@seita.nl>
Signed-off-by: F.N. Claessen <felix@seita.nl>
Signed-off-by: F.N. Claessen <felix@seita.nl>
… assigns a queue, and in case of "requeuing", a queue doesn't need to be reassigned; the job already knows which queue it should go in when it is requeued.

Signed-off-by: F.N. Claessen <felix@seita.nl>
Signed-off-by: F.N. Claessen <felix@seita.nl>
Signed-off-by: F.N. Claessen <felix@seita.nl>
Signed-off-by: F.N. Claessen <felix@seita.nl>
@Flix6x
Copy link
Contributor

Flix6x commented Apr 8, 2023

I made some commits myself, primarily to address my remaining issues with the documentation and code legibility. I tried to make them small, so you can follow my rationale behind each one.

Sorry about the unverified status of my commits (it seems like I somehow managed to make that worse than before).

While doing these I also came to the conclusion that the hash keys are not actually stored under some queue-specific segment of our Redis database. Therefore, I believe we can't be sure yet that the hash is unique if we apply the @job_cache decorator to more job-creating functions in the future. We have more functions that create jobs (at the moment, just 1 per queue I think), so that is something to be mindful of, but doesn't need to be resolved now (a possible resolution: perhaps the function that the job is supposed to execute can be added to the hash, and/or the queue name).

Another question that came up is whether there is any mechanism keeping the cache size at bay. Jobs have a limited lifetime, but here we are adding keys to Redis ourselves, and I suspect that the list just grows unlimited. Is that the case?

Finally, I found a if/else case for which no logic is specifically implemented. What happens (/ should happen) if the hash exists but the job id no longer exists in Redis (e.g. after the job's time to live has passed)?

@nhoening
Copy link
Contributor

nhoening commented Apr 8, 2023

  • I believe Redis keys have a TTL by design
  • we could store the hash on the job meta data and create a post-hook function on jobs to delete that key when they are done or cancelled. Not on a laptop now, but I feel that RQ allows for that.

@Flix6x
Copy link
Contributor

Flix6x commented Apr 8, 2023

I believe Redis keys have a TTL by design

Alas, it looks like they don't. (source)

@nhoening
Copy link
Contributor

nhoening commented Apr 8, 2023

Thanks. Then maybe my second idea would help.

@FlexMeasures FlexMeasures deleted a comment from nhoening Apr 8, 2023
… FLEXMEASURES_JOB_CACHE_TTL.

Signed-off-by: victor <victor@seita.nl>
@victorgarcia98
Copy link
Contributor Author

Thanks for the reviews!

I've added the FLEXMEASURES_JOB_CACHE_TTL config variable to set a TTL for the caching keys.

Regarding the addition of the post-hook functions on_success and on_failiure, I think it's not needed because with the decorator we have the following functionality covered:

  • In case of job failure: currently, with requeu=True, we requeu the same job after being called with the same arguments than before.
  • In case of job success: we want to avoid recomputing the job to some extend. Moreover, on expiration of the the 'caching pair', the following call will trigger the creation of a new job.

In both cases, this behavior can be overridden by setting the flag force_new_job_creation to True, which forces the creation of a new Job.

With the post-hook functions, we had the following disadvantages:

  • We had to define and add them them to each job creation function.
  • We want to requeu the same job in case of a job failiure, If we delete the cache flag, we'll create a fresh new one (with a new Job Id).
  • On success, we want to keep the cache flag so that repeating calls are cached.

Regarding the case that the Job has expired, we handle this as follow:

  1. If the caching pair exists, we retrieve the Job Id
  2. We check if it exists
    • If it does: we retrieve the Job object and return it.
    • If it doesn't: we exit both if's and proceed to create a new Job

Please, let me know If I'm missing something.

Regarding the hash collisions for functions with the same arguments, indeed that could happen. Even though unlikely, to avoid collisions in the future, I'm changing the key to be {queue_name}:{func_name}:{input_arguments_hash}.

@Flix6x
Copy link
Contributor

Flix6x commented Apr 10, 2023

I find that the new default TTL of an hour carries with it considerable weight, given that it scopes the whole caching logic. Two places I could think of that could mention that scope are: the changelog entries, and the documentation page for config variables. The new config variable should be added to that documentation page in any case.

Copy link
Contributor

@Flix6x Flix6x left a comment

Choose a reason for hiding this comment

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

🎉

Copy link
Contributor

@nhoening nhoening left a comment

Choose a reason for hiding this comment

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

A very cool PR, thanks!

…elevant state change.

Squashed commit of the following:

commit 79ef71a
Author: victor <victor@seita.nl>
Date:   Thu Mar 30 15:38:55 2023 +0200

    Fixing typos on @deprecated decorator and trigger warnings through loggere.

    Signed-off-by: victor <victor@seita.nl>

commit 7faa6e6
Author: victor <victor@seita.nl>
Date:   Wed Mar 29 23:28:00 2023 +0200

    Adding version of sunset of a function/method to the @deprecated decorator

    Signed-off-by: victor <victor@seita.nl>

commit fe228d2
Author: victor <victor@seita.nl>
Date:   Mon Mar 27 11:57:32 2023 +0200

    Getting location of the new funciton directly from the importable object.

    Signed-off-by: victor <victor@seita.nl>

commit c1be8db
Author: victor <victor@seita.nl>
Date:   Sun Mar 26 23:26:28 2023 +0200

    Adding deprecated messages for the functions that were moved.

    Signed-off-by: victor <victor@seita.nl>

commit ac4a232
Author: victor <victor@seita.nl>
Date:   Thu Mar 23 22:29:48 2023 +0100

    Issue #599

    Forgot to add `from __future__ import annotations`. Local testing worked as I'm uing Python v3.10.

    Signed-off-by: victor <victor@seita.nl>

commit 3ff0571
Author: victor <victor@seita.nl>
Date:   Thu Mar 23 22:02:11 2023 +0100

    Issue #599
    Moving get_asset_group_queries from data/services to data/queries

    Signed-off-by: victor <victor@seita.nl>

commit 53fc214
Author: victor <victor@seita.nl>
Date:   Thu Mar 23 20:30:31 2023 +0100

    Issue #599
    Moving DataSources fetching from query to services.

    Signed-off-by: victor <victor@seita.nl>

Signed-off-by: victor <victor@seita.nl>
…hout a relevant state change."

This reverts commit a694443.
@victorgarcia98 victorgarcia98 merged commit fd62ed3 into main Apr 14, 2023
5 checks passed
@victorgarcia98 victorgarcia98 deleted the 574-avoid-recomputing-schedules branch April 14, 2023 14:13
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.

Avoid recomputing schedules
4 participants