Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions frameworks/rails/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
FROM ruby:3.4-slim

RUN apt-get update && \
apt-get install -y --no-install-recommends build-essential libsqlite3-dev libyaml-dev && \
rm -rf /var/lib/apt/lists/*

WORKDIR /app

COPY Gemfile .
RUN bundle install --jobs=$(nproc)

COPY . .

EXPOSE 8080

CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]
5 changes: 5 additions & 0 deletions frameworks/rails/Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
source 'https://rubygems.org'

gem 'rails', '~> 8.0'
gem 'puma', '~> 6.5'
gem 'sqlite3', '~> 2.6'
121 changes: 121 additions & 0 deletions frameworks/rails/app/controllers/benchmark_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
require 'json'
require 'zlib'
require 'stringio'
require 'sqlite3'

class BenchmarkController < ActionController::API
# Pre-load datasets at class level (shared across workers via preload)
DATASET_PATH = ENV.fetch('DATASET_PATH', '/data/dataset.json')
LARGE_DATASET_PATH = '/data/dataset-large.json'

@@json_payload = nil
@@compressed_payload = nil
@@db_available = File.exist?('/data/benchmark.db')

if File.exist?(DATASET_PATH)
raw = JSON.parse(File.read(DATASET_PATH))
items = raw.map { |d| d.merge('total' => (d['price'] * d['quantity'] * 100).round / 100.0) }
@@json_payload = JSON.generate({ 'items' => items, 'count' => items.length })
end

if File.exist?(LARGE_DATASET_PATH)
raw = JSON.parse(File.read(LARGE_DATASET_PATH))
items = raw.map { |d| d.merge('total' => (d['price'] * d['quantity'] * 100).round / 100.0) }
payload = JSON.generate({ 'items' => items, 'count' => items.length })
sio = StringIO.new
gz = Zlib::GzipWriter.new(sio, 1)
gz.write(payload)
gz.close
@@compressed_payload = sio.string
end

DB_QUERY = 'SELECT id, name, category, price, quantity, active, tags, rating_score, rating_count FROM items WHERE price BETWEEN ? AND ? LIMIT 50'

def pipeline
response.headers['Server'] = 'rails'
render plain: 'ok'
end

def baseline11
total = 0
request.query_parameters.each_value do |v|
total += v.to_i if v =~ /\A-?\d+\z/
end
if request.post?
body_str = request.body.read.to_s.strip
total += body_str.to_i if body_str =~ /\A-?\d+\z/
end
response.headers['Server'] = 'rails'
render plain: total.to_s
end

def baseline2
total = 0
request.query_parameters.each_value do |v|
total += v.to_i if v =~ /\A-?\d+\z/
end
response.headers['Server'] = 'rails'
render plain: total.to_s
end

def json_endpoint
if @@json_payload
response.headers['Server'] = 'rails'
response.headers['Content-Type'] = 'application/json'
render plain: @@json_payload
else
head 500
end
end

def compression
if @@compressed_payload
response.headers['Server'] = 'rails'
response.headers['Content-Type'] = 'application/json'
response.headers['Content-Encoding'] = 'gzip'
send_data @@compressed_payload, disposition: :inline
else
head 500
end
end

def db
unless @@db_available
response.headers['Server'] = 'rails'
render json: { items: [], count: 0 }
return
end

min_val = (params[:min] || 10).to_f
max_val = (params[:max] || 50).to_f
conn = get_db
rows = conn.execute(DB_QUERY, [min_val, max_val])
items = rows.map do |r|
{
'id' => r['id'], 'name' => r['name'], 'category' => r['category'],
'price' => r['price'], 'quantity' => r['quantity'], 'active' => r['active'] == 1,
'tags' => JSON.parse(r['tags']),
'rating' => { 'score' => r['rating_score'], 'count' => r['rating_count'] }
}
end
response.headers['Server'] = 'rails'
render json: { items: items, count: items.length }
end

def upload
data = request.body.read
response.headers['Server'] = 'rails'
render plain: data.bytesize.to_s
end

private

def get_db
Thread.current[:rails_db] ||= begin
db = SQLite3::Database.new('/data/benchmark.db', readonly: true)
db.execute('PRAGMA mmap_size=268435456')
db.results_as_hash = true
db
end
end
end
2 changes: 2 additions & 0 deletions frameworks/rails/config.ru
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
require_relative 'config/environment'
run Rails.application
24 changes: 24 additions & 0 deletions frameworks/rails/config/application.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
require 'rails'
require 'action_controller/railtie'

class BenchmarkApp < Rails::Application
config.load_defaults 8.0
config.eager_load = true
config.api_only = true
config.secret_key_base = 'benchmark-not-secret'
config.hosts.clear
config.consider_all_requests_local = false

# Disable all middleware we don't need
config.middleware.delete ActionDispatch::HostAuthorization
config.middleware.delete ActionDispatch::Callbacks
config.middleware.delete ActionDispatch::ActionableExceptions
config.middleware.delete ActionDispatch::RemoteIp
config.middleware.delete ActionDispatch::RequestId
config.middleware.delete Rails::Rack::Logger
config.middleware.delete ActionDispatch::ShowExceptions

# Silence logging
config.logger = Logger.new('/dev/null')
config.log_level = :fatal
end
2 changes: 2 additions & 0 deletions frameworks/rails/config/environment.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
require_relative 'application'
Rails.application.initialize!
13 changes: 13 additions & 0 deletions frameworks/rails/config/puma.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
require 'etc'

cores = Etc.nprocessors
workers cores
threads 4, 4

bind 'tcp://0.0.0.0:8080'

preload_app!

before_fork do
# Close any inherited DB connections
end
10 changes: 10 additions & 0 deletions frameworks/rails/config/routes.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Rails.application.routes.draw do
get '/pipeline', to: 'benchmark#pipeline'
get '/baseline11', to: 'benchmark#baseline11'
post '/baseline11', to: 'benchmark#baseline11'
get '/baseline2', to: 'benchmark#baseline2'
get '/json', to: 'benchmark#json_endpoint'
get '/compression', to: 'benchmark#compression'
get '/db', to: 'benchmark#db'
post '/upload', to: 'benchmark#upload'
end
19 changes: 19 additions & 0 deletions frameworks/rails/meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"display_name": "Rails",
"language": "Ruby",
"type": "framework",
"engine": "puma",
"description": "Ruby on Rails (API mode) on Puma, multi-worker with one worker per CPU core.",
"repo": "https://github.com/rails/rails",
"enabled": true,
"tests": [
"baseline",
"pipelined",
"noisy",
"limited-conn",
"json",
"upload",
"compression",
"mixed"
]
}
Loading