• Stars
    star
    58
  • Rank 498,182 (Top 11 %)
  • Language
    Ruby
  • License
    MIT License
  • Created 4 months ago
  • Updated 28 days ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Sidekiq middleware to re-route “greedy” clients’ jobs to slower queues

Sidekiq::FairTenant

Throttle “greedy” clients’ jobs to ensure more or less fair distribution of resources between clients.

This tiny Sidekiq middleware will re-route client's jobs after certain threshold to throttled queues (defined by you), where they will be processed with reduced priority.

“Weighted queues” feature of Sidekiq allows to de-prioritize jobs in throttled queues, so they will not block jobs from other clients, at the same time preserving overall throughput.

Sponsored by Evil Martians

Installation

  1. Install the gem and add to the application's Gemfile by executing:

    bundle add sidekiq-fair_tenant
  2. Add fair_tenant_queues section to sidekiq_options in your job class:

     class SomeJob
       sidekiq_options \
         queue: 'default',
    +    fair_tenant_queues: [
    +     { queue: 'throttled_2x', threshold: 100, per: 1.hour },
    +     { queue: 'throttled_4x', threshold:  10, per: 1.minute },
    +    ]
     end
  3. Add tenant detection login into your job class:

     class SomeJob
    +  def self.fair_tenant(*_perform_arguments)
    +    # Return any string that will be used as tenant name
    +    "tenant_1"
    +  end
     end
  4. Add throttled queues with reduced weights to your Sidekiq configuration:

     # config/sidekiq.yml
     :queues:
       - [default, 4]
    +  - [throttled_2x, 2]
    +  - [throttled_4x, 1]

    See Sidekiq Advanced options for Queues to learn more about queue weights.

Usage

Specifying throttling rules

In your job class, add fair_tenant_queues section to sidekiq_options as array of hashes with following keys:

  • queue - throttled queue name to re-route jobs into.
  • threshold - maximum number of jobs allowed to be enqueued within per seconds.
  • per - sliding time window in seconds to count jobs (you can use ActiveSupport Durations in Rails).

You can specify multiple rules and they all will be checked. Last matching rule will be used, so order rules from least to most restrictive.

Example:

sidekiq_options \
  queue: 'default',
  fair_tenant_queues: [
    # First rule is less restrictive, reacting to a large number of jobs enqueued in a long time window
    { queue: 'throttled_2x', threshold: 1_000, per: 1.day },
    # Next rule is more restrictive, reacting to spikes of jobs in a short time window
    { queue: 'throttled_4x', threshold:    10, per: 1.minute },
  ]

Specifying tenant

  1. Explicitly during job enqueuing:

    SomeJob.set(fair_tenant: 'tenant_1').perform_async
  2. Dynamically using fair_tenant class-level method in your job class (receives same arguments as perform)

    class SomeJob
      def self.fair_tenant(*_perform_arguments)
        # Return any string that will be used as tenant name
        "tenant_1"
      end
    end
  3. Set fair_tenant job option in a custom middleware earlier in the stack.

  4. Or let this gem automatically pick tenant name from apartment-sidekiq if you're using apartment gem.

Configuration

Configuration is handled by anyway_config gem. With it you can load settings from environment variables (which names are constructed from config key upcased and prefixed with SIDEKIQ_FAIR_TENANT_), YAML files, and other sources. See anyway_config docs for details.

Config key Type Default Description
max_throttling_window integer 86_400 (1 day) Maximum throttling window in seconds
enqueues_key string sidekiq-fair_tenant:enqueued:%<job_class>s:tenant:%<fair_tenant>s Ruby format string used as a name for Redis key holding job ids for throttling window. Available placeholders: queue, job_class, fair_tenant
logger logger Sidekiq.logger Logger instance used for warning logging.

How it works

If number of jobs enqueued by a single client exceeds some threshold per a sliding time window, their jobs would be re-routed to another queue, with lower priority.

This gem tracks single client's jobs in a Redis sorted set with job id as a key and enqueuing timestamp as a score. When a new job is enqueued, it is added to the set, and then the set is trimmed to contain only jobs enqueued within the last max_throttling_window seconds.

On every enqueue attempt, the set is checked for number of jobs enqueued within the last per seconds of every rule. If the number of jobs in this time window exceeds threshold, the job is enqueued to a throttled queue, otherwise it is enqueued to the default queue. If multiple rules match, last one is used.

You are expected to configure Sidekiq to process throttled queues with lower priority using queue weights.

Advantages

  • If fast queues are empty then slow queues are processed at full speed (no artificial delays)
  • If fast queues are full, slow queues are still processed, but slower (configurable), so application doesn’t “stall” for throttled users
  • Minimal changes to the application code are required.

Disadvantages

  • As Sidekiq does not support mixing ordered and weighted queue modes (as stated in Sidekiq Wiki on queue configuration), you can’t make the same worker process execute some super important queue always first, ignoring other queues. Run separate worker to solve this.
  • You have to keep track of all your queues and their weights.

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake spec 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 the created tag, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/Envek/sidekiq-fair_tenant.

License

The gem is available as open source under the terms of the MIT License.

More Repositories

1

after_commit_everywhere

Use ActiveRecord transactional callbacks outside of models, literally everywhere in your application.
Ruby
660
star
2

aws-sam-typescript-layers-example

Example project for developing AWS Lambda functions on TypeScript with all goodies: local development, tests, debugging, shared layers (3rd party and your own), and deploy.
TypeScript
231
star
3

obs-studio-node-example

Learn how to use OBS Studio from your Electron app for screen video recording
JavaScript
92
star
4

cookiecutter-aws-sam-typescript-layers

AWS SAM template to quickly set up Lambda application using TypeScript on Node.js 14 with shared layers for dependencies
TypeScript
21
star
5

dockerized-browser-streamer

Stream video of any website (but WebRTC things works best) to RTMP endpoint. Launch it in Docker. Pray if it works.
Shell
21
star
6

jquery-datetimepicker-rails

jQuery date and time picker by @xdan bundled for rails asset pipeline
JavaScript
18
star
7

Amestris

Demo page for interaction between SVG, HTML and JavaScript
JavaScript
14
star
8

lefthook-crystalball-example

Learn how to make git hooks to do most routine tasks for you: install gems, migrate the database, run tests, and linters.
Ruby
11
star
9

graphql-flamegraph

Collects data for visualizing performance of your GraphQL request resolving for applications built with GraphQL-Ruby
Ruby
10
star
10

postindexapi.ru

Very small service for providing russian post codes in JSON
Ruby
8
star
11

yard-dry-initializer

Generates documentation for your params and options
Ruby
7
star
12

httpi-adapter-openssl_gost

HTTPI adapter for accessing HTTPS servers with GOST algorithms and certificates
Ruby
7
star
13

mkpasswd

Command to generate password hashes, compatible by options with command from GNU/Linux whois package.
Ruby
5
star
14

durable-websockets

Example of guaranteed delivery of messages from backend to web browser with RabbitMQ
Ruby
4
star
15

fann

Personal fork of Fast Artificial Neural Network library
C++
3
star
16

puppet-pgtune

Puppet module for configuring PostgreSQL installations for optimal performance.
Puppet
3
star
17

saintprubyconf-db-indexes-talk

Slidev slides for “Indexing database: how to do good and not to do bad” talk at Fall Saint P 2021 meetup
Vue
3
star
18

snils

Generating, validating and formatting SNILS number / Генерирует, проверяет и форматирует СНИЛС
Ruby
3
star
19

jquery-kladr-rails

KLADR API JS client for Ruby on Rails asset pipeline
Ruby
2
star
20

terraform-eks-example

Played around with Terraform to create Amazon EKS Kubernetes cluster and deploy something to it
HCL
2
star
21

puppet-erlang

Puppet module to install up to date Erlang on all systems from Erlang Solutions repositories.
Ruby
1
star
22

spellchecker

Example of spellchecking HTTP microservice in Ruby and Hunspell
Ruby
1
star
23

etatata

Dead simple microservice ETA calculator. Proud member of bycicle parade!
Ruby
1
star
24

ECGKW

Envek Computer Graphics (K)Course Work
C++
1
star
25

psyche

Software for Amur regional, psychological, medical and pedagogical committee (and my course project, btw)
Ruby
1
star
26

AmurMap

Project to create vector maps of Amur region, Russian Federation.
1
star
27

slidev-theme-envek

Personal theme for presentation slides made with sli.dev
Vue
1
star