Skip to content

Task rate limits#3363

Open
mbertrand wants to merge 4 commits into
mainfrom
mb/task_rate_limits
Open

Task rate limits#3363
mbertrand wants to merge 4 commits into
mainfrom
mb/task_rate_limits

Conversation

@mbertrand
Copy link
Copy Markdown
Member

@mbertrand mbertrand commented May 20, 2026

What are the relevant tickets?

Closes https://github.com/mitodl/hq/issues/11394

Description (What does it do?)

Adds a cooldown_task decorator that makes use of redis lock keys to prevent a task from running more than once every n seconds (3600 for get_mit_edx_data, 900 for get_xpro_data, get_oll_data, and get_mitxonline_data).

How can this be tested?

  • docker compose up

  • Run ./manage.py backpopulate_mitxonline_data

  • Try running it again afterward. You should see this in the logs:

    Skipping ratelimit:learning_resources.tasks.get_mitxonline_data: rate-limited (900s)

    And the command should finish quickly:

    Population of MITX Online data finished, took 0.041112 seconds
    Task skipped (rate-limited). See celery logs for details.
  • Try again with --force. This time it should process normally:

    Population of MITX Online data finished, took 77.92632 seconds
    Populated 268 resources. See celery logs for details.
    

Additional Context

Copilot AI review requested due to automatic review settings May 20, 2026 18:12
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 20, 2026

OpenAPI Changes

No changes detected

View full changelog

Unexpected changes? Ensure your branch is up-to-date with main (consider rebasing).

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a reusable task rate-limiting decorator (backed by the configured Redis cache) and applies it to several ETL Celery tasks to prevent repeated invocations within a fixed time window.

Changes:

  • Introduces rate_limited_task(wait_time, key=None) in main/decorators.py using an atomic cache.add on the redis cache.
  • Adds unit tests covering rate-limiting behavior and key selection.
  • Applies the decorator to selected ETL tasks in learning_resources/tasks.py (3600s for MIT edX; 900s for MITxOnline/xPro/OLL).

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
main/decorators.py Adds the Redis-cache-backed rate limit decorator used to skip frequent task invocations.
main/decorators_test.py Adds tests for rate-limiting behavior and cache key selection.
learning_resources/tasks.py Wraps several ETL tasks with the new rate-limiting decorator.

Comment thread main/decorators.py Outdated
Comment thread main/decorators_test.py Outdated
Comment thread learning_resources/tasks.py
Comment thread learning_resources/tasks.py Outdated
@mbertrand mbertrand added Needs Review An open Pull Request that is ready for review and removed Work in Progress labels May 20, 2026
@mbertrand mbertrand force-pushed the mb/task_rate_limits branch from 5f48bee to 04f3099 Compare May 20, 2026 19:50
@rhysyngsun rhysyngsun self-assigned this May 21, 2026
Copy link
Copy Markdown
Contributor

@rhysyngsun rhysyngsun left a comment

Choose a reason for hiding this comment

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

Generally looking good, I'll test tomorrow

Comment thread main/decorators.py
caches["redis"].set(lock_key, "1", timeout=wait_time)
elif not caches["redis"].add(lock_key, "1", timeout=wait_time):
log.info("Skipping %s: cooldown active (%ss)", lock_key, wait_time)
return None
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm going to sleep on it overnight but I'm a little concerned that letting the task succeed is going to cause confusion later when tasks appear to be successful but they aren't actually getting run.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think we'll just leave it as-is and this should come up in the logs if anyone investigates.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think I might have just come up with a way to make it more obvious in the celery monitoring dashboard - raise Reject so the status is REJECTED instead of SUCCEEDED - I think that should not raise a sentry error (which is what this is intended to avoid). And the mgmt commands should probably override the cooldown by default without needing to supply --force - what do you think of that idea?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I went ahead and pushed those changes in a new commit, can revert if you prefer the original approach.

@mbertrand mbertrand force-pushed the mb/task_rate_limits branch from f102cb6 to 8bc243f Compare May 22, 2026 19:28
Copy link
Copy Markdown
Contributor

@rhysyngsun rhysyngsun left a comment

Choose a reason for hiding this comment

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

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Needs Review An open Pull Request that is ready for review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants