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 multiple throttling strategies #623

Open
wants to merge 12 commits into
base: main
Choose a base branch
from

Conversation

monorkin
Copy link

Motivation

I have a job that processes imports. During the import process I'd like to broadcast refreshes to communicate how he import is progressing - stuff like the number of created records, encountered errors, etc.

With the default debounce logic, if the import is updated too frequently, no refresh broadcast will go out until the import is done.

I can manually broadcast refreshes and throttle them to work around this issue, but it would be nice if Turbo Rails offered alternative broadcast throttling strategies like rate limiting or leading-edge debouncing.

Detail

In this PR I've exposed options on broadcasts_refreshes and broadcasts_refreshes_to through which you can change the broadcast throttling strategy.

I've kept the debouncer as the default strategy as it makes the most sense for most applications.

In addition to the debouncer I've added a rate limiter strategy which allows only a set number of broadcasts in a given interval.

class Import
  # Allows at most two refresh within 5 seconds
  broadcasts_refreshes throttle_with: { type: :rate_limiter, max: 2, interval: 5 }
end

For convenience, if you just want to use the defaults, I've also added a short-hand definition syntax

class Import
  # If you want to use the defaults - which is one refresh within two seconds
  broadcasts_refreshes throttle_with: :rate_limiter
end

This interface also allows you to reconfigure the debouncer's default delay of 0.5 sec

class Import
  # Use the debouncer for throttling broadcasts but instead of the default delay of 0.5 use a delay of 0.01
  broadcasts_refreshes throttle_with: { type: :debouncer, delay: 0.01 }
end

In my case, I'd prefere to keep the debounce throttler for requests to my server but would like to switch to the rate limiter when in a background job. For this I've added a Turbo.with_throttler method that changes the default throttler for any code executed within a block passed to it.

class Import
  broadcasts_refreshes
end

class Import::ProcessingJob < ActiveJob
  def perform(import)
    Turbo.with_throttler(type: :rate_limiter, max: 2, interval: 5) do
      import.process
    end
  end
end

Additional information

To make this work I had to change ThreadDebouncer, and in the process I've also renamed it to ThreadThrottler to better communicate its new responsibilities. This might be considered a breaking change to the public interface.

It's possible to define and use custom throttlers

class Import
  broadcasts_refreshes throttle_with: MyApp::CustomThrottler
  # broadcasts_refreshes throttle_with: "MyApp::CustomThrottler"
  # broadcasts_refreshes throttle_with: { type: MyApp::CustomThrottler, arg1: :foo, arg2: :bar }
end

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

Successfully merging this pull request may close these issues.

1 participant