Git Product home page Git Product logo

activejob-traffic_control's Introduction

ActiveJob::TrafficControl Build Status Code Climate Test Coverage Gem Version

Rate controls for your ActiveJobs, powered by Suo, a distributed semaphore library backed by Redis or Memcached.

Installation

Add this line to your application's Gemfile:

gem 'activejob-traffic_control'

And then execute:

$ bundle

Or install it yourself as:

$ gem install activejob-traffic_control

Usage

ActiveJob::TrafficControl adds three modules you can mixin to your job classes as needed, or to ApplicationJob if you are using ActiveJob 5+ (or you have created a base job class yourself).

# to initialize the type of locking client (memcached vs. redis):
ActiveJob::TrafficControl.client = ConnectionPool.new(size: 5, timeout: 5) { Redis.new } # set poolthresholds as needed
# or, ActiveJob::TrafficControl.client = ConnectionPool.new(size: 5, timeout: 5) { Dalli::Client.new }
# or if not multithreaded, ActiveJob::TrafficControl.client = Redis.new

Throttle

class CanThrottleJob < ActiveJob::Base
  throttle threshold: 2, period: 1.second

  def perform
    # no more than two of `CanThrottleJob` will run every second
    # if more than that attempt to run, they will be re-enqueued to run in a random time
    # ranging from 1 - 5x the period (so, 1-5 seconds in this case)
  end
end

If you do not care about the job being re-enqueued (if it's scheduled to run otherwise, or dropping will have no ill effect), you can specify drop: true instead. The drop: true flag also applies to Concurrency, below.

class CanThrottleAndDropJob < ActiveJob::Base
  throttle threshold: 2, period: 1.second, drop: true

  def perform
    # no more than two of `CanThrottleJob` will run every second
    # if more than that attempt to run, they will be dropped
  end
end

Concurrency

class ConcurrencyTestJob < ActiveJob::Base
  concurrency 5, drop: false

  def perform
    # only five `ConcurrencyTestJob` will ever run simultaneously
  end
end

Disable

For Disable, you also need to configure the cache client:

ActiveJob::TrafficControl.cache_client = Rails.cache.dalli # if using :dalli_store
# or ActiveJob::TrafficControl.cache_client = ActiveSupport::Cache.lookup_store(:dalli_store, "localhost:11211")
class CanDisableJob < ActiveJob::Base
  def perform
    # you can pause this job from running by executing `CanDisableJob.disable!` (which will cause the job to be re-enqueued),
    # or have it be dropped entirely via `CanDisableJob.disable!(drop: true)`
    # enable it again via `CanDisableJob.enable!`
  end
end

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake test to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/nickelser/activejob-traffic_control. Please look at the .rubocop.yml for the style guide.

activejob-traffic_control's People

Contributors

ankane avatar avokhmin avatar nickelser avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

activejob-traffic_control's Issues

disable! & enable! job perpetually stuck

How is enable! and disable! supposed to work?

I'm using sidekiq and check a certain condition throughout the execution of the job and disable or enable it based ons said condition. However the job seems to get perpetually stuck in a planned state.

I'm using memory_store as cache client.

ActiveJob::TrafficControl.cache_client = ActiveSupport::Cache.lookup_store(:memory_store)

code managing the state:

Job.disable! unless @asset.ready?
Job.enable!  if     @asset.ready?

[Question/Suggestion] Replace instead of drop?

Just found this gem and it looks really neat. I'm looking for a possible gem to provide a solution to an issue we recently encountered. Our issue is quite similar to the one mentioned in #4 but slightly different. Let me explain:

One of our systems(our Android/iOS app) reports both some user data and client data(like its current version) with each API call to our main system. We do not have any request limitation on this API so we can receive lots of calls per second. This information is stored in the system on the user model but also reported to another external system. The external system has rate limitation though.

The throttling functionality provided by this gem is perfect to not exceed the rate limit of the external system. Though part of the user data we submit to the external system is the timestamp of the last API request for each user. Therefore we would like to replace any previous queued jobs rather then drop new jobs that are exceeding the rate limit.

I looked through the code briefly to see if something like this could easily be achieved but it does not seem like that. Or do you have any suggestion how this could be achieved with this gem?

In general I think this could be very useful for any kind of REST based API with rate limitation when posting data to that API since one wants the last state transfered and none of the previous ones.

Suo::LockClientError

Hi @nickelser

This error is happening on production only. Could this be a cause of missing this code below in the configs?

ActiveJob::TrafficControl.client = ConnectionPool.new(size: 5, timeout: 5) { Redis.new }

Question: Where to put the initializer

In the docs it mentions we need to run:

ActiveJob::TrafficControl.client = ConnectionPool.new(size: 5, timeout: 5) { Redis.new } # set poolthresholds as needed

I assume it can just go in any initializer. I have put that in my config/initializers/active_job.rb.

Is this correct? I'm happy to submit a PR to update the docs.

[Question] Stacking throttle?

I haven't tried it myself, but I am wondering if there is a way to stack throttle definitions. Here is a use case:
We have a portion of a system that receives callbacks about incoming calls from provider A, and hangs up the call, and then schedules a job to start a call from provider B. Provider B has a rate limit of 2 requests per second. However, quite often provider A hits us twice with a callback for the same call, 3-15 seconds apart. So I would like to have 2 throttle guards:

  1. Standard 2 per second limit
  2. Parameter based throttle (i.e don't call the same number twice in a 20 second window)

How could I get this accomplished?

Suo && unlock token

Hello.

Should we do unlock of token?

see:

# manually locking/unlocking
# the return value from lock without a block is a unique token valid only for the current lock
# which must be unlocked manually
token = suo.lock
foo.baz!
suo.unlock(token)

What license governs activejob-traffic_control?

Hi @nickelser . I've looked through the codebase but have not been able to find a license file or similar information regarding the license for activejob-traffic_control. Could you please advise what license governs this package? Many thanks.

Named arguments not passed correctly

From @psteininger (#3):

I did experience a problem with a Sidekiq backed job, which had named (not positional) parameters. The job would be called seemingly w/o parameters. I did change the signature of the perform method and got around it. Perhaps I should open another issue?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.