• Stars
    star
    227
  • Rank 175,900 (Top 4 %)
  • Language
    Ruby
  • Created almost 11 years ago
  • Updated over 5 years ago

Reviews

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

Repository Details

Puma performance without all the (T)pain

Puma Auto Tune

Build Status

Stability

Please do not use this library in production until this schneems/get_process_mem#7 has been fixed.

What

Performance without the (T)pain: puma_auto_tune will automatically adjust the number of puma workers to optimize the performance of your Ruby web application.

How

Puma is a web server that allows you to adjust the amount of processes and threads it uses to process requests. At a very simple level the more processes and threads you have the more requests you can process concurrently. However this comes at a cost, more processes means more RAM and more threads means more CPU usage. You want to get as close to maxing out your resources without going over.

The amount of memory and CPU your program consumes is also a factor of your code, as well as the amount of load it is under. Larger applications require more RAM. More requests mean more Ruby objects are created and garbage collected as your application generates web pages. Because of these factors, there is no one size fits all number for workers and threads, that's where Puma Auto Tune comes in.

Run Puma Auto Tune in production under load, or in staging while simulating load with tools like siege, blitz.io, or flood.io for a long enough time and we will compute and set your application numbers to maximize concurrent requests without going over your system limits.

Currently Puma Auto Tune will optimize the number of workers (processes) based on RAM.

Install

In your Gemfile add:

gem 'puma_auto_tune'

Then run $ bundle install.

Use

In your application call:

PumaAutoTune.start

In Rails you could place this in an initializer such as config/initializers/puma_auto_tune.rb.

Puma Auto Tune will attempt to find an ideal number of workers for your application.

Config

You will need to configure your Puma Auto Tune to be aware of the maximum amount of RAM it can use.

PumaAutoTune.config do |config|
  config.ram = 512 # mb: available on system
end

We will attempt to detect your RAM size if you are running on Heroku. If we cannot, the default is 512 mb. There are a few other advanced config options:

PumaAutoTune.config do |config|
  config.ram           = 1024 # mb: available on system
  config.frequency     = 20   # seconds: the duration to check memory usage
  config.reap_duration = 30   # seconds: how long `reap_cycle` will be run for
end

To see defaults check out puma_auto_tune.rb

Hitting the Sweet Spot

Puma Auto Tune is designed to tune the number of workers for a given application while it is running. Once you restart the program the tuning must start over. Once the algorithm has found the "sweet spot" you can maximize your application throughput by manually setting the number of workers that puma starts with. To help you do this Puma Auto Tune outputs semi-regular logs with formatted values.

measure#puma.resource_ram_mb=476.6328125 measure#puma.current_cluster_size=5

You can use a service such as librato to pull values out of your logs and graph them. When you see over time that your server settles on a given cluster_size you should set this as your default puma -w $PUMA_WORKERS if you're using the CLI to start your app or if you're using a config/puma.rb file:

workers Integer(ENV['PUMA_WORKERS'] || 3)

Puma Worker Killer

Do not use with puma_worker_killer gem. Puma Auto Tune takes care of memory leaks in addition to tuning your puma workers.

How it Works: Tuning Algorithm (RAM)

Simple by default, custom for true Puma hackers. The best way to think of the tuner is to start with the different states of memory consumption Puma can be under:

  • Unused RAM: we can add a worker
  • Memory leak (too much RAM usage): we should restart a worker
  • Too much RAM usage: we can remove a worker
  • Just right: No need to scale up or down.

The algorithm will periodically get the total memory used by Puma and take action appropriately.

Memory States: Unused RAM

The memory of the smallest worker is recorded. If adding another worker does not put the total memory over the threshold then one will be added.

Memory States: Memory Leak (too much RAM usage)

When the amount of memory is more than that on the system, we assume a memory leak and restart the largest worker. This will trigger a check to determine if the result was due to a memory leak or because we have too many workers.

Memory States: Too much RAM Usage

After a worker has been restarted we will aggressively check for memory usage for a fixed period of time, default is 90 seconds(PumaAutoTune.reap_reap_duration). If memory goes over the limit, it is assumed that the cause is due to excess workers. The number of workers will be decreased by one. Puma Auto Tune will record the number of total workers that were present when we went over and set this as a new maximum worker number. After removing a process, Puma Auto Tune again checks for memory overages for the same duration and continues to decrement the number of workers until the total memory consumed is under the maximum.

Memory States: Just Right

Periodically the tuner will wake up and take note of memory usage. If it cannot scale up, and doesn't need to scale down it goes back to sleep.

Customizing the Algorithm

Here's the fun part. You can write your own algorithm using the included hook system. The default algorithm is implemented as a series of pre-defined hooks.

You can over-write one or more of the hooks to add custom behavior. To define hooks call:

PumaAutoTune.hooks(:ram) do |auto|

end

Each hook has a name and can be over-written by calling set and passing in the symbol of the hook you wish to over-write. These are the default RAM hooks:

  • :cycle
  • :reap_cycle
  • :out_of_memory
  • :under_memory
  • :add_worker
  • :remove_worker

Once you have the hook object you can use the call method to jump to other hooks.

Cycle

This is the main event loop of your program. This code will be called every PumaAutoTune.frequency seconds. To over-write you can do this:

PumaAutoTune.hooks(:ram) do |auto|
  auto.set(:cycle) do |memory, master, workers|
    if memory > PumaAutoTune.ram # mb
      auto.call(:out_of_memory)
    else
      auto.call(:under_memory) if memory + workers.last.memory
    end
  end
end

Reap Cycle

When you think you might run out of memory call the reap_cycle. The code in this hook will be called in a loop for PumaAutoTune.reap_duration seconds.

PumaAutoTune.hooks do |auto|
  auto.set(:reap_cycle) do |memory, master, workers|
    if memory > PumaAutoTune.ram
      auto.call(:remove_worker)
    end
  end
end

Add Worker

Bumps up the worker size by one.

PumaAutoTune.hooks do |auto|
  auto.set(:add_worker) do |memory, master, workers|
    auto.log "Cluster too small. Resizing to add one more worker"
    master.add_worker
    auto.call(:reap_cycle)
  end
end

Here we're calling :reap_cycle just in case we accidentally went over our memory limit after the increase.

Remove Worker

Removes a worker. When remove_worker is called it will automatically set PumaAutoTune.max_workers to be one less than the current number of workers.

PumaAutoTune.hooks do |hook|
  auto.set(:remove_worker) do |memory, master, workers|
    auto.log "Cluster too large. Resizing to remove one worker"
    master.remove_worker
    auto.call(:reap_cycle)
  end
end

In case removing one worker wasn't enough we call reap_cycle again. Once a worker has been flagged with restart it will report zero RAM usage even if it has not completely terminated.

License

MIT

More Repositories

1

Octave

my octave exercises for 2011 stanford machine learning class, posted after the due date of course
MATLAB
818
star
2

sextant

find your route on a long journey over Rails with Sextant
Ruby
569
star
3

likeable

Use Redis to make your Ruby objects Likeable
Ruby
330
star
4

sprockets_better_errors

Ruby
264
star
5

reddit_on_rails

A Hero's Journey
195
star
6

explain_shell

Ruby
113
star
7

let_it_go

Make sure your string literals are Frozen
Ruby
88
star
8

method_cacheable

Cache method calls and speed up your Ruby on Rails application with MethodCacheable.
Ruby
79
star
9

rrrretry

did I stutter?
Ruby
72
star
10

going_the_distance

Distance Measurements are Awesome!
Ruby
61
star
11

attendance

Speed up ActiveRecord#present? calls with the power of monkeypatching!
Ruby
60
star
12

q

A universal interface for Ruby queueing backends.
Ruby
39
star
13

ruby_view_server

Ruby
34
star
14

ruby_javascript_data_viz

Ruby
29
star
15

wicked_example

an example for the wicked gem
Ruby
29
star
16

hey_you

Use postgres to trigger custom events
Ruby
23
star
17

threaded_in_memory_queue

A non-durable in memory threaded worker queue for Ruby
Ruby
22
star
18

keytar

Make your keys awesome again with this simple library
Ruby
21
star
19

routes_controller_exercise

Ruby
19
star
20

simulate_ruby_web_memory_use

Simulates a web server running multiple threads and the amount of memory it needs
Ruby
17
star
21

tiny_queue

a tiny example of a threadsafe queue in C
C
16
star
22

schneems

The blog of schneems. Written by schneems, it is a blog. An internet web log.
HTML
16
star
23

living_dead

ITS ALIVE!!!!!!! (or is it?)
Ruby
15
star
24

arrays_and_active_record

Exercise for Week 7 of Class.
Ruby
14
star
25

WhySpam

Take a big bite out of Spam with Ruby On Rails
JavaScript
13
star
26

move_logic_to_controllers

Ruby
12
star
27

threaded

Lighter than actors, simpler then threads. Get threaded!
Ruby
10
star
28

resque_def

Love Resque? Hate Boilerplate? Use resque_def!
Ruby
9
star
29

united-dictionary

A multi-lingual user submitted dictionary
Ruby
8
star
30

Potential-Friend-Finder

Ruby
7
star
31

likeable_example

this is an example app for the likeable gem
Ruby
6
star
32

rate_throttle_clients

Ruby
6
star
33

Gowalla-Style-Photo-Browser

Page through your photos with ease using jquery
JavaScript
5
star
34

heroku-bouncer-demo

Ruby
5
star
35

heroku_ulimit_to_ram

Ughhhh, don't use this
Shell
4
star
36

active-storage-attachment-example

Ruby
4
star
37

non-developers-on-rails

Ruby
4
star
38

Selenium-RC-Beta-2

Selenium RC Beta 2
4
star
39

git_test

test your code, store it in git, share with teammates
Ruby
4
star
40

dotfiles

Vim Script
4
star
41

Roulette

Pre-Sharding so simple, you'll shoot yourself : p
Ruby
4
star
42

proc_to_lambda

Convert your Procs to Lambdas
Ruby
3
star
43

minimumviablerubywebserver

it's a web server, it's ruby, it's minimum viable and Heroku compatible, check it out
Ruby
3
star
44

minimal-ruby

Ruby
2
star
45

require_array_source_memory_problem

Ruby
2
star
46

repl_runner

Runs REPL like things, cause you know. REPL
Ruby
2
star
47

which_problem

Rust
2
star
48

heap_problem

Demonstrates that a non-retained object is sometimes still present in the heap
Ruby
2
star
49

ruby-26-bundler-issue

Ruby
2
star
50

MagicArg

Make our arguments magical
Ruby
2
star
51

rspec_test_project

Ruby
1
star
52

mama_schneems

Ruby
1
star
53

memory_notes_and_whatnot

Ruby
1
star
54

dotenv-buildpack

A cloud native buildpack (CNB) for injecting contents of your .env into your app at build time for local development
Rust
1
star
55

cant_stop

Programs used to demonstrate a solution of "optimal stoping"
Ruby
1
star
56

rails_monkeypatch_require_issue

Ruby
1
star
57

postgresql

Ruby
1
star
58

UpDown

Markdown editor
JavaScript
1
star
59

implement_ruby_hash_syntax_with_parslet_example

Ruby
1
star
60

codetriage-ko1-test-app

Ruby
1
star
61

advent_2021

Rust
1
star
62

basic_rust_lol_cnb_buildpack

Rust
1
star
63

kraken

🐙 Kraken MMO Distributed Framework :octocat:
Ruby
1
star
64

type_script_yargs_playground

TypeScript
1
star
65

max_manhattan_distance

An algorithm of my own design
C
1
star
66

heroku-buildpack-force-compile

Shell
1
star
67

omgcnb

Ruby
1
star
68

MailFactory

a tool for generating a mail object that you can use to test your apps email parsing behavior.
Ruby
1
star
69

rails-6-simple-2023-06-06

Ruby
1
star
70

rails51_webpacker

Ruby
1
star
71

buildit-gems

builds gems
Shell
1
star
72

Wordpress-For-Deployment

Wordpress Install for Capistrano Deploy
PHP
1
star
73

bullet_stream

Rust
1
star
74

kodyok

Best Free Wordpress Theme
JavaScript
1
star
75

DelayedLog

1
star