Browse Source

Ruby: Address the perf issues exposed by R14P1.1 (#2622)

[ci fw-only Ruby/sinatra Ruby/sinatra-sequel Ruby/rack-sequel Ruby/roda-sequel]
Mike Pastore 8 years ago
parent
commit
ce745856fa

+ 0 - 1
frameworks/Ruby/rack-sequel/Gemfile

@@ -5,7 +5,6 @@ gem 'passenger', '~> 5.1', :platforms=>[:ruby, :mswin], :require=>false
 gem 'puma', '~> 3.6', :require=>false
 gem 'sequel', '~> 4.44'
 gem 'rack', '~> 2.0'
-gem 'sysrandom', '~> 1.0'
 gem 'torquebox-web', '>= 4.0.0.beta3', '< 5', :platforms=>:jruby, :require=>false
 gem 'unicorn', '~> 5.2', :platforms=>[:ruby, :mswin], :require=>false
 

+ 7 - 7
frameworks/Ruby/rack-sequel/boot.rb

@@ -1,5 +1,5 @@
 # frozen_string_literal: true
-require 'bundler'
+require 'bundler/setup'
 require 'time'
 
 MAX_PK = 10_000
@@ -33,10 +33,10 @@ def connect(dbtype)
 
   # Determine threading/thread pool size and timeout
   if defined?(JRUBY_VERSION)
-    opts[:max_connections] = Integer(ENV.fetch('MAX_CONCURRENCY'))
+    opts[:max_connections] = (2 * Math.log(Integer(ENV.fetch('MAX_CONCURRENCY')))).floor
     opts[:pool_timeout] = 10
-  elsif defined?(Puma)
-    opts[:max_connections] = Puma.cli_config.options.fetch(:max_threads)
+  elsif defined?(Puma) && (threads = Puma.cli_config.options.fetch(:max_threads)) > 1
+    opts[:max_connections] = (2 * Math.log(threads)).floor
     opts[:pool_timeout] = 10
   else
     Sequel.single_threaded = true
@@ -54,8 +54,7 @@ end
 
 DB = connect(ENV.fetch('DBTYPE').to_sym).tap do |db|
   db.extension(:freeze_datasets)
-  db.optimize_model_load if db.respond_to?(:optimize_model_load)
-  db.freeze
+  db.optimize_model_load = true if db.respond_to?(:optimize_model_load=)
 end
 
 # Define ORM models
@@ -68,4 +67,5 @@ class Fortune < Sequel::Model(:Fortune)
   unrestrict_primary_key
 end
 
-Sequel::Model.freeze
+[World, Fortune].each(&:freeze)
+DB.freeze

+ 1 - 1
frameworks/Ruby/rack-sequel/config/auto_tune.rb

@@ -8,7 +8,7 @@ require 'etc'
 KB_PER_WORKER = 64 * 1_024 # average of peak PSS of single-threaded processes (watch smem -k)
 MIN_WORKERS = 2
 MAX_WORKERS_PER_VCPU = 1.25 # virtual/logical
-MIN_THREADS_PER_WORKER = 4
+MIN_THREADS_PER_WORKER = 1
 MAX_THREADS = Integer(ENV['MAX_CONCURRENCY'] || 256)
 
 def meminfo(arg)

+ 18 - 12
frameworks/Ruby/rack-sequel/hello_world.rb

@@ -17,16 +17,21 @@ class HelloWorld
 
   # Return a random number between 1 and MAX_PK
   def rand1
-    Sysrandom.random_number(MAX_PK).succ
+    rand(MAX_PK).succ
   end
 
+  WORLD_BY_ID = World.naked.where(:id=>:$id).prepare(:first, :world_by_id)
+  WORLD_UPDATE = World.where(:id=>:$id).prepare(:update, :world_update, :randomnumber=>:$randomnumber)
+
   def db
-    World.with_pk(rand1).values
+    WORLD_BY_ID.(:id=>rand1)
   end
 
   def queries(env)
-    Array.new(bounded_queries(env)) do
-      World.with_pk(rand1).values
+    DB.synchronize do
+      Array.new(bounded_queries(env)) do
+        WORLD_BY_ID.(:id=>rand1)
+      end
     end
   end
 
@@ -71,14 +76,13 @@ class HelloWorld
     HTML
   end
 
-  WORLD_BY_ID = World.naked.where(:id=>:$id).prepare(:first, :world_by_id)
-  WORLD_UPDATE = World.where(:id=>:$id).prepare(:update, :world_update, :randomnumber=>:$randomnumber)
-
   def updates(env)
-    Array.new(bounded_queries(env)) do
-      world = WORLD_BY_ID.(:id=>rand1)
-      WORLD_UPDATE.(:id=>world[:id], :randomnumber=>(world[:randomnumber] = rand1))
-      world
+    DB.synchronize do
+      Array.new(bounded_queries(env)) do
+        world = WORLD_BY_ID.(:id=>rand1)
+        WORLD_UPDATE.(:id=>world[:id], :randomnumber=>(world[:randomnumber] = rand1))
+        world
+      end
     end
   end
 
@@ -105,11 +109,13 @@ class HelloWorld
         ['text/plain', 'Hello, World!']
       end
 
-    return 200,
+    [
+      200,
       DEFAULT_HEADERS.merge(
         'Content-Type'=>content_type,
         'Date'=>Time.now.httpdate
       ),
       body
+    ]
   end
 end

+ 1 - 1
frameworks/Ruby/rack-sequel/run_jruby_torquebox.sh

@@ -10,4 +10,4 @@ rvm use jruby-$JRUBY_VERSION
 
 . $(dirname $0)/config/bundle_install.sh
 
-bundle exec torquebox run --io-threads $MAX_CONCURRENCY --worker-threads $MAX_CONCURRENCY -b 0.0.0.0 -p 8080 -e production &
+bundle exec torquebox run --io-threads $(( MAX_CONCURRENCY / 2 )) --worker-threads $MAX_CONCURRENCY -b 0.0.0.0 -p 8080 -e production &

+ 0 - 1
frameworks/Ruby/roda-sequel/Gemfile

@@ -6,7 +6,6 @@ gem 'passenger', '~> 5.1', :platforms=>[:ruby, :mswin], :require=>false
 gem 'puma', '~> 3.6', :require=>false
 gem 'sequel', '~> 4.44'
 gem 'roda', '~> 2.24'
-gem 'sysrandom', '~> 1.0'
 gem 'tilt', '~> 2.0', :require=>'tilt/erb'
 gem 'torquebox-web', '>= 4.0.0.beta3', '< 5', :platforms=>:jruby, :require=>false
 gem 'unicorn', '~> 5.2', :platforms=>[:ruby, :mswin], :require=>false

+ 7 - 7
frameworks/Ruby/roda-sequel/boot.rb

@@ -1,5 +1,5 @@
 # frozen_string_literal: true
-require 'bundler'
+require 'bundler/setup'
 require 'time'
 
 MAX_PK = 10_000
@@ -33,10 +33,10 @@ def connect(dbtype)
 
   # Determine threading/thread pool size and timeout
   if defined?(JRUBY_VERSION)
-    opts[:max_connections] = Integer(ENV.fetch('MAX_CONCURRENCY'))
+    opts[:max_connections] = (2 * Math.log(Integer(ENV.fetch('MAX_CONCURRENCY')))).floor
     opts[:pool_timeout] = 10
-  elsif defined?(Puma)
-    opts[:max_connections] = Puma.cli_config.options.fetch(:max_threads)
+  elsif defined?(Puma) && (threads = Puma.cli_config.options.fetch(:max_threads)) > 1
+    opts[:max_connections] = (2 * Math.log(threads)).floor
     opts[:pool_timeout] = 10
   else
     Sequel.single_threaded = true
@@ -54,8 +54,7 @@ end
 
 DB = connect(ENV.fetch('DBTYPE').to_sym).tap do |db|
   db.extension(:freeze_datasets)
-  db.optimize_model_load if db.respond_to?(:optimize_model_load)
-  db.freeze
+  db.optimize_model_load = true if db.respond_to?(:optimize_model_load=)
 end
 
 # Define ORM models
@@ -68,4 +67,5 @@ class Fortune < Sequel::Model(:Fortune)
   unrestrict_primary_key
 end
 
-Sequel::Model.freeze
+[World, Fortune].each(&:freeze)
+DB.freeze

+ 1 - 1
frameworks/Ruby/roda-sequel/config/auto_tune.rb

@@ -8,7 +8,7 @@ require 'etc'
 KB_PER_WORKER = 64 * 1_024 # average of peak PSS of single-threaded processes (watch smem -k)
 MIN_WORKERS = 2
 MAX_WORKERS_PER_VCPU = 1.25 # virtual/logical
-MIN_THREADS_PER_WORKER = 4
+MIN_THREADS_PER_WORKER = 1
 MAX_THREADS = Integer(ENV['MAX_CONCURRENCY'] || 256)
 
 def meminfo(arg)

+ 19 - 9
frameworks/Ruby/roda-sequel/hello_world.rb

@@ -18,7 +18,7 @@ class HelloWorld < Roda
 
   # Return a random number between 1 and MAX_PK
   def rand1
-    Sysrandom.random_number(MAX_PK).succ
+    rand(MAX_PK).succ
   end
 
   after do
@@ -37,9 +37,14 @@ class HelloWorld < Roda
 
   # Test type 3: Multiple database queries
   static_get '/queries' do
-    Array.new(bounded_queries) do
-      World.with_pk(rand1).values
-    end
+    worlds =
+      DB.synchronize do
+        Array.new(bounded_queries) do
+          World.with_pk(rand1)
+        end
+      end
+
+    worlds.map!(&:values)
   end
 
   # Test type 4: Fortunes
@@ -56,11 +61,16 @@ class HelloWorld < Roda
 
   # Test type 5: Database updates
   static_get '/updates' do
-    Array.new(bounded_queries) do
-      world = World.with_pk(rand1)
-      world.update(:randomnumber=>rand1)
-      world.values
-    end
+    worlds =
+      DB.synchronize do
+        Array.new(bounded_queries) do
+          world = World.with_pk(rand1)
+          world.update(:randomnumber=>rand1)
+          world
+        end
+      end
+
+    worlds.map!(&:values)
   end
 
   # Test type 6: Plaintext

+ 1 - 1
frameworks/Ruby/roda-sequel/run_jruby_torquebox.sh

@@ -10,4 +10,4 @@ rvm use jruby-$JRUBY_VERSION
 
 . $(dirname $0)/config/bundle_install.sh
 
-bundle exec torquebox run --io-threads $MAX_CONCURRENCY --worker-threads $MAX_CONCURRENCY -b 0.0.0.0 -p 8080 -e production &
+bundle exec torquebox run --io-threads $(( MAX_CONCURRENCY / 2 )) --worker-threads $MAX_CONCURRENCY -b 0.0.0.0 -p 8080 -e production &

+ 0 - 1
frameworks/Ruby/sinatra-sequel/Gemfile

@@ -6,7 +6,6 @@ gem 'puma', '~> 3.6', :require=>false
 gem 'sequel', '~> 4.44'
 gem 'sinatra', '>= 2.0.0.rc1', '< 3.0', :require=>'sinatra/base'
 gem 'slim', '~> 3.0'
-gem 'sysrandom', '~> 1.0'
 gem 'torquebox-web', '>= 4.0.0.beta3', '< 5', :platforms=>:jruby, :require=>false
 gem 'unicorn', '~> 5.2', :platforms=>[:ruby, :mswin], :require=>false
 

+ 7 - 7
frameworks/Ruby/sinatra-sequel/boot.rb

@@ -1,5 +1,5 @@
 # frozen_string_literal: true
-require 'bundler'
+require 'bundler/setup'
 require 'time'
 
 MAX_PK = 10_000
@@ -33,10 +33,10 @@ def connect(dbtype)
 
   # Determine threading/thread pool size and timeout
   if defined?(JRUBY_VERSION)
-    opts[:max_connections] = Integer(ENV.fetch('MAX_CONCURRENCY'))
+    opts[:max_connections] = (2 * Math.log(Integer(ENV.fetch('MAX_CONCURRENCY')))).floor
     opts[:pool_timeout] = 10
-  elsif defined?(Puma)
-    opts[:max_connections] = Puma.cli_config.options.fetch(:max_threads)
+  elsif defined?(Puma) && (threads = Puma.cli_config.options.fetch(:max_threads)) > 1
+    opts[:max_connections] = (2 * Math.log(threads)).floor
     opts[:pool_timeout] = 10
   else
     Sequel.single_threaded = true
@@ -54,8 +54,7 @@ end
 
 DB = connect(ENV.fetch('DBTYPE').to_sym).tap do |db|
   db.extension(:freeze_datasets)
-  db.optimize_model_load if db.respond_to?(:optimize_model_load)
-  db.freeze
+  db.optimize_model_load = true if db.respond_to?(:optimize_model_load=)
 end
 
 # Define ORM models
@@ -68,4 +67,5 @@ class Fortune < Sequel::Model(:Fortune)
   unrestrict_primary_key
 end
 
-Sequel::Model.freeze
+[World, Fortune].each(&:freeze)
+DB.freeze

+ 1 - 1
frameworks/Ruby/sinatra-sequel/config/auto_tune.rb

@@ -8,7 +8,7 @@ require 'etc'
 KB_PER_WORKER = 64 * 1_024 # average of peak PSS of single-threaded processes (watch smem -k)
 MIN_WORKERS = 2
 MAX_WORKERS_PER_VCPU = 1.25 # virtual/logical
-MIN_THREADS_PER_WORKER = 4
+MIN_THREADS_PER_WORKER = 1
 MAX_THREADS = Integer(ENV['MAX_CONCURRENCY'] || 256)
 
 def meminfo(arg)

+ 19 - 13
frameworks/Ruby/sinatra-sequel/hello_world.rb

@@ -32,7 +32,7 @@ class HelloWorld < Sinatra::Base
 
     # Return a random number between 1 and MAX_PK
     def rand1
-      Sysrandom.random_number(MAX_PK).succ
+      rand(MAX_PK).succ
     end
   end
 
@@ -56,11 +56,14 @@ class HelloWorld < Sinatra::Base
 
   # Test type 3: Multiple database queries
   get '/queries' do
-    worlds = Array.new(bounded_queries) do
-      World.with_pk(rand1).values
-    end
-
-    json worlds
+    worlds =
+      DB.synchronize do
+        Array.new(bounded_queries) do
+          World.with_pk(rand1)
+        end
+      end
+
+    json worlds.map!(&:values)
   end
 
   # Test type 4: Fortunes
@@ -77,13 +80,16 @@ class HelloWorld < Sinatra::Base
 
   # Test type 5: Database updates
   get '/updates' do
-    worlds = Array.new(bounded_queries) do
-      world = World.with_pk(rand1)
-      world.update(:randomnumber=>rand1)
-      world.values
-    end
-
-    json worlds
+    worlds =
+      DB.synchronize do
+        Array.new(bounded_queries) do
+          world = World.with_pk(rand1)
+          world.update(:randomnumber=>rand1)
+          world
+        end
+      end
+
+    json worlds.map!(&:values)
   end
 
   # Test type 6: Plaintext

+ 1 - 1
frameworks/Ruby/sinatra-sequel/run_jruby_torquebox.sh

@@ -10,4 +10,4 @@ rvm use jruby-$JRUBY_VERSION
 
 . $(dirname $0)/config/bundle_install.sh
 
-bundle exec torquebox run --io-threads $MAX_CONCURRENCY --worker-threads $MAX_CONCURRENCY -b 0.0.0.0 -p 8080 -e production &
+bundle exec torquebox run --io-threads $(( MAX_CONCURRENCY / 2 )) --worker-threads $MAX_CONCURRENCY -b 0.0.0.0 -p 8080 -e production &

+ 0 - 1
frameworks/Ruby/sinatra/Gemfile

@@ -6,7 +6,6 @@ gem 'json', '~> 2.0'
 gem 'passenger', '~> 5.1', :platforms=>[:ruby, :mswin], :require=>false
 gem 'puma', '~> 3.6', :require=>false
 gem 'sinatra', '>= 2.0.0.rc1', '< 3.0', :require=>'sinatra/base'
-gem 'sysrandom', '~> 1.0'
 gem 'torquebox-web', '>= 4.0.0.beta3', '< 5', :platforms=>:jruby, :require=>false
 gem 'unicorn', '~> 5.2', :platforms=>[:ruby, :mswin], :require=>false
 

+ 8 - 8
frameworks/Ruby/sinatra/boot.rb

@@ -1,5 +1,5 @@
 # frozen_string_literal: true
-require 'bundler'
+require 'bundler/setup'
 require 'time'
 
 MAX_PK = 10_000
@@ -33,15 +33,15 @@ def connect(dbtype)
 
   # Determine threading/thread pool size and timeout
   if defined?(JRUBY_VERSION)
-    opts[:pool] = Integer(ENV.fetch('MAX_CONCURRENCY'))
-    opts[:check_timeout] = 10
-  elsif defined?(Puma)
-    opts[:pool] = Puma.cli_config.options.fetch(:max_threads)
-    opts[:check_timeout] = 10
+    opts[:pool] = (2 * Math.log(Integer(ENV.fetch('MAX_CONCURRENCY')))).floor
+    opts[:checkout_timeout] = 10
+  elsif defined?(Puma) && (threads = Puma.cli_config.options.fetch(:max_threads)) > 1
+    opts[:pool] = (2 * Math.log(threads)).floor
+    opts[:checkout_timeout] = 10
   else
     # TODO: ActiveRecord doesn't have a single-threaded mode?
     opts[:pool] = 1
-    opts[:check_timeout] = 0
+    opts[:checkout_timeout] = 0
   end
 
   ActiveRecord::Base.establish_connection(opts)
@@ -54,7 +54,7 @@ class World < ActiveRecord::Base
   self.table_name = name
 
   alias_attribute(:randomnumber, :randomNumber) \
-    if ActiveRecord::Base.connection.adapter_name.downcase.start_with?('mysql')
+    if connection.adapter_name.downcase.start_with?('mysql')
 end
 
 class Fortune < ActiveRecord::Base

+ 1 - 1
frameworks/Ruby/sinatra/config/auto_tune.rb

@@ -8,7 +8,7 @@ require 'etc'
 KB_PER_WORKER = 96 * 1_024 # average of peak PSS of single-threaded processes (watch smem -k)
 MIN_WORKERS = 2
 MAX_WORKERS_PER_VCPU = 1.25 # virtual/logical
-MIN_THREADS_PER_WORKER = 4
+MIN_THREADS_PER_WORKER = 1
 MAX_THREADS = Integer(ENV['MAX_CONCURRENCY'] || 256)
 
 def meminfo(arg)

+ 22 - 17
frameworks/Ruby/sinatra/hello_world.rb

@@ -29,7 +29,7 @@ class HelloWorld < Sinatra::Base
 
     # Return a random number between 1 and MAX_PK
     def rand1
-      Sysrandom.random_number(MAX_PK).succ
+      rand(MAX_PK).succ
     end
   end
 
@@ -48,27 +48,31 @@ class HelloWorld < Sinatra::Base
 
   # Test type 2: Single database query
   get '/db' do
-    world = ActiveRecord::Base.connection_pool.with_connection do
-      World.find(rand1).attributes
-    end
+    world =
+      ActiveRecord::Base.connection_pool.with_connection do
+        World.find(rand1).attributes
+      end
 
     json world
   end
 
   # Test type 3: Multiple database queries
   get '/queries' do
-    worlds = ActiveRecord::Base.connection_pool.with_connection do
-      Array.new(bounded_queries) { World.find(rand1).attributes }
-    end
+    worlds =
+      ActiveRecord::Base.connection_pool.with_connection do
+        Array.new(bounded_queries) do
+          World.find(rand1)
+        end
+      end
 
-    json worlds
+    json worlds.map!(&:attributes)
   end
 
   # Test type 4: Fortunes
   get '/fortunes' do
     @fortunes = ActiveRecord::Base.connection_pool.with_connection do
-      Fortune.all.to_a
-    end
+      Fortune.all
+    end.to_a
     @fortunes << Fortune.new(
       :id=>0,
       :message=>'Additional fortune added at request time.'
@@ -80,15 +84,16 @@ class HelloWorld < Sinatra::Base
 
   # Test type 5: Database updates
   get '/updates' do
-    worlds = ActiveRecord::Base.connection_pool.with_connection do |conn|
-      Array.new(bounded_queries) do
-        world = World.find(rand1)
-        world.update(:randomnumber=>rand1)
-        world.attributes
+    worlds =
+      ActiveRecord::Base.connection_pool.with_connection do
+        Array.new(bounded_queries) do
+          world = World.find(rand1)
+          world.update(:randomnumber=>rand1)
+          world
+        end
       end
-    end
 
-    json worlds
+    json worlds.map!(&:attributes)
   end
 
   # Test type 6: Plaintext

+ 1 - 1
frameworks/Ruby/sinatra/run_jruby_torquebox.sh

@@ -10,4 +10,4 @@ rvm use jruby-$JRUBY_VERSION
 
 . $(dirname $0)/config/bundle_install.sh
 
-bundle exec torquebox run --io-threads $MAX_CONCURRENCY --worker-threads $MAX_CONCURRENCY -b 0.0.0.0 -p 8080 -e production &
+bundle exec torquebox run --io-threads $(( MAX_CONCURRENCY / 2 )) --worker-threads $MAX_CONCURRENCY -b 0.0.0.0 -p 8080 -e production &