Skip to content

refactor multithreading into WattTimeBase#35

Merged
sam-watttime merged 2 commits intofuture-releasefrom
multithreading-refactor
Mar 12, 2025
Merged

refactor multithreading into WattTimeBase#35
sam-watttime merged 2 commits intofuture-releasefrom
multithreading-refactor

Conversation

@sam-watttime
Copy link
Contributor

@sam-watttime sam-watttime commented Feb 11, 2025

This is a refactor of the multithreading changes introduced in #34

Notably, we wanted to remove the use of a MixIn object. This was achieved by adding new methods to WattTimeBase including:

  • _make_rate_limited_request: this should be used in place of request.get() across all child classes. For both single threaded and multithreaded applications, it will ensure that no more than 10 requests per second can be submitted, and if they are, it will sleep (0.1s) until the limit is no longer violated. Significant DRY code was moved into this method to get, raise_for_status(), raise any exceptions, and print any warnings.
  • _apply_rate_limit is used by make_rate_limited_request. This method checks a class attribute tracking the times of the last submitted requests, and ensures that no more than self.rate_limit are submitted per second. This is achieved by sleeping in a singlethreaded context, and using threading.Condition when multithreading to wait the appropriate amount of time. No use of the threading module is required at all if self.multithreaded==False
  • _fetch_data will call _make_rate_limited_request in a for loop with a list of parameters (e.g. chunks of dates). if multithreading is enabled, then a threadpool executor is used and futures are examined as_completed()

Sorry for the messy git history here... I was working with this repo as upstream and when I went to push to a new branch (multithreading-refactor) it pushed to main. I wouldn't think this would be possible due to branch protection rules... I'll look into how to tighten those up. In any case, I reverted the commit on main, rebased main into this branch and then cherry-picked the commit to show the differences here while maintaining the history.

self._rate_limit_lock = (
threading.Lock()
) # prevent multiple threads from modifying _last_request_times simultaneously
self._rate_limit_condition = threading.Condition(self._rate_limit_lock)
Copy link
Contributor

@jcofield jcofield Feb 12, 2025

Choose a reason for hiding this comment

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

Is there anyway to move this into the method(s) that need it to avoid having a loosely defined class structure? If not me may need to consider some more structure, like methods to set and access these attributes.

else:
rsp.raise_for_status()
return rsp.json()
j = self._make_rate_limited_request(url, params=params)
Copy link
Contributor

@jcofield jcofield Feb 12, 2025

Choose a reason for hiding this comment

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

Is this a change in functionality? If so can can avoid doing it during the refactor?

@sam-watttime sam-watttime changed the base branch from main to future-release February 13, 2025 23:47
@sam-watttime sam-watttime merged commit 425c2e3 into future-release Mar 12, 2025
1 check passed
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.

3 participants