Skip to content

Commit 7c3ee34

Browse files
authored
[ruby/grape] Use autotune for puma and unicorn workers/threads (#8534)
1 parent c56cf03 commit 7c3ee34

File tree

4 files changed

+58
-2
lines changed

4 files changed

+58
-2
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#!/usr/bin/env ruby
2+
# Instantiate about one process per X MiB of available memory, scaling up to as
3+
# close to MAX_THREADS as possible while observing an upper bound based on the
4+
# number of virtual/logical CPUs. If there are fewer processes than
5+
# MAX_THREADS, add threads per process to reach MAX_THREADS.
6+
require 'etc'
7+
8+
KB_PER_WORKER = 64 * 1_024 # average of peak PSS of single-threaded processes (watch smem -k)
9+
MIN_WORKERS = 2
10+
MAX_WORKERS_PER_VCPU = 1.25 # virtual/logical
11+
MIN_THREADS_PER_WORKER = 1
12+
MAX_THREADS = Integer(ENV['MAX_CONCURRENCY'] || 256)
13+
14+
def meminfo(arg)
15+
File.open('/proc/meminfo') do |f|
16+
f.each_line do |line|
17+
key, value = line.split(/:\s+/)
18+
return value.split(/\s+/).first.to_i if key == arg
19+
end
20+
end
21+
22+
raise "Unable to find `#{arg}' in /proc/meminfo!"
23+
end
24+
25+
def auto_tune
26+
avail_mem = meminfo('MemAvailable') * 0.8 - MAX_THREADS * 1_024
27+
28+
workers = [
29+
[(1.0 * avail_mem / KB_PER_WORKER).floor, MIN_WORKERS].max,
30+
[(Etc.nprocessors * MAX_WORKERS_PER_VCPU).ceil, MIN_WORKERS].max
31+
].min
32+
33+
threads_per_worker = [
34+
workers < MAX_THREADS ? (1.0 * MAX_THREADS / workers).ceil : -Float::INFINITY,
35+
MIN_THREADS_PER_WORKER
36+
].max
37+
38+
[workers, threads_per_worker]
39+
end
40+
41+
p auto_tune if $PROGRAM_NAME == __FILE__

frameworks/Ruby/grape/config/puma.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
require_relative 'auto_tune'
2+
3+
# FWBM only... use the puma_auto_tune gem in production!
4+
num_workers, num_threads = auto_tune
5+
6+
workers num_workers
7+
threads num_threads, num_threads
8+
# Use the `preload_app!` method when specifying a `workers` number.
9+
# This directive tells Puma to first boot the application and load code
10+
# before forking the application. This takes advantage of Copy On Write
11+
# process behavior so workers use less memory.
12+
#
13+
preload_app!

frameworks/Ruby/grape/config/unicorn.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
worker_processes 8
1+
require_relative 'auto_tune'
2+
3+
worker_processes, = auto_tune
24
listen "/tmp/unicorn.sock", :backlog => 4096
35

46
preload_app true

frameworks/Ruby/grape/grape.dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ RUN bundle install --jobs=4 --gemfile=/grape/Gemfile --path=/grape/grape/bundle
1010

1111
EXPOSE 8080
1212

13-
CMD bundle exec puma -t 8:32 -w 8 --preload -b tcp://0.0.0.0:8080 -e production
13+
CMD bundle exec puma -C config/puma.rb -b tcp://0.0.0.0:8080 -e production

0 commit comments

Comments
 (0)