Skip to content

Commit b83bfb5

Browse files
committed
Rage: Add framework
1 parent 6f5864d commit b83bfb5

9 files changed

Lines changed: 200 additions & 0 deletions

File tree

frameworks/rage/Dockerfile

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
FROM ruby:4.0-slim
2+
3+
RUN apt-get update && \
4+
apt-get install -y --no-install-recommends build-essential libsqlite3-dev libyaml-dev libjemalloc2 && \
5+
rm -rf /var/lib/apt/lists/*
6+
7+
# Use Jemalloc
8+
ENV LD_PRELOAD=libjemalloc.so.2
9+
10+
ENV RUBY_YJIT_ENABLE=1
11+
ENV RACK_ENV=production
12+
13+
WORKDIR /app
14+
15+
COPY Gemfile .
16+
RUN bundle install --jobs=$(nproc)
17+
18+
COPY . .
19+
20+
EXPOSE 8080
21+
22+
CMD ["bundle", "exec", "rage", "server", "-p", "8080", "-b", "0.0.0.0"]

frameworks/rage/Gemfile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
source 'https://rubygems.org'
2+
3+
gem "rage-rb", "~> 1.19"
4+
gem 'sqlite3', '~> 2.6'
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# frozen_string_literal: true
2+
3+
require 'zlib'
4+
5+
class BenchmarkController < RageController::API
6+
# Pre-load datasets at class level (shared across workers via preload)
7+
DATASET_PATH = ENV.fetch('DATASET_PATH', '/data/dataset.json')
8+
LARGE_DATASET_PATH = '/data/dataset-large.json'
9+
10+
@db_available = File.exist?('/data/benchmark.db')
11+
12+
if File.exist?(DATASET_PATH)
13+
@dataset_items = JSON.parse(File.read(DATASET_PATH))
14+
end
15+
16+
if File.exist?(LARGE_DATASET_PATH)
17+
raw = JSON.parse(File.read(LARGE_DATASET_PATH))
18+
items = raw.map { |d| d.merge('total' => (d['price'] * d['quantity'] * 100).round / 100.0) }
19+
@large_json_payload = JSON.generate({ 'items' => items, 'count' => items.length })
20+
end
21+
22+
DB_QUERY = 'SELECT id, name, category, price, quantity, active, tags, rating_score, rating_count FROM items WHERE price BETWEEN ? AND ? LIMIT 50'
23+
24+
def self.db_available = @db_available
25+
def self.large_json_payload = @large_json_payload
26+
def self.dataset_items = @dataset_items
27+
28+
before_action do
29+
headers["server"] = "rage"
30+
end
31+
32+
def pipeline
33+
render plain: 'ok'
34+
end
35+
36+
def baseline_one
37+
total = 0
38+
request.query_parameters.each_value do |v|
39+
total += v.to_i
40+
end
41+
if request.post?
42+
body_str = request.body.read.to_s.strip
43+
total += body_str.to_i
44+
end
45+
render plain: total.to_s
46+
end
47+
48+
def baseline_two
49+
total = 0
50+
request.query_parameters.each_value do |v|
51+
total += v.to_i
52+
end
53+
render plain: total.to_s
54+
end
55+
56+
def json_endpoint
57+
if self.class.dataset_items
58+
items = self.class.dataset_items.map { |d| d.merge('total' => (d['price'] * d['quantity'] * 100).round / 100.0) }
59+
body = JSON.generate({ 'items' => items, 'count' => items.length })
60+
response.headers['Content-Type'] = 'application/json'
61+
render plain: body
62+
else
63+
head 500
64+
end
65+
end
66+
67+
def compression
68+
if self.class.large_json_payload
69+
sio = StringIO.new
70+
gz = Zlib::GzipWriter.new(sio, 1)
71+
gz.write(self.class.large_json_payload)
72+
gz.close
73+
response.headers['Content-Type'] = 'application/json'
74+
response.headers['Content-Encoding'] = 'gzip'
75+
send_data sio.string, disposition: :inline
76+
else
77+
head 500
78+
end
79+
end
80+
81+
def db
82+
unless self.class.db_available
83+
render json: { items: [], count: 0 }
84+
return
85+
end
86+
87+
min_val = (params[:min] || 10).to_f
88+
max_val = (params[:max] || 50).to_f
89+
conn = get_db
90+
rows = conn.execute(DB_QUERY, [min_val, max_val])
91+
items = rows.map do |r|
92+
{
93+
'id' => r['id'], 'name' => r['name'], 'category' => r['category'],
94+
'price' => r['price'], 'quantity' => r['quantity'], 'active' => r['active'] == 1,
95+
'tags' => JSON.parse(r['tags']),
96+
'rating' => { 'score' => r['rating_score'], 'count' => r['rating_count'] }
97+
}
98+
end
99+
render json: { items: items, count: items.length }
100+
end
101+
102+
def upload
103+
data = request.body.read
104+
render plain: data.bytesize.to_s
105+
end
106+
107+
def not_found
108+
head 404
109+
end
110+
111+
private
112+
113+
def get_db
114+
Fiber.current[:rage_db] ||= begin
115+
db = SQLite3::Database.new('/data/benchmark.db', readonly: true)
116+
db.execute('PRAGMA mmap_size=268435456')
117+
db.results_as_hash = true
118+
db
119+
end
120+
end
121+
end

frameworks/rage/config.ru

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
require_relative 'config/application'
2+
run Rage.application
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# frozen_string_literal: true
2+
3+
require 'bundler/setup'
4+
Bundler.require(:default)
5+
6+
require 'rage/all'
7+
8+
Rage.configure do
9+
# use this to add settings that are constant across all environments
10+
end
11+
12+
require "rage/setup"
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Rage.configure do
2+
config.server.workers_count = -1
3+
config.logger = Rage::Logger.new(STDOUT)
4+
end
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Rage.configure do
2+
config.logger = Rage::Logger.new(STDOUT)
3+
end

frameworks/rage/config/routes.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Rage.routes.draw do
2+
get '/pipeline', to: 'benchmark#pipeline'
3+
get '/baseline11', to: 'benchmark#baseline_one'
4+
post '/baseline11', to: 'benchmark#baseline_one'
5+
get '/baseline2', to: 'benchmark#baseline_two'
6+
get '/json', to: 'benchmark#json_endpoint'
7+
get '/compression', to: 'benchmark#compression'
8+
get '/db', to: 'benchmark#db'
9+
post '/upload', to: 'benchmark#upload'
10+
11+
# Catch-all for unknown paths → 404
12+
get '*', to: 'benchmark#not_found'
13+
end

frameworks/rage/meta.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"display_name": "Rage",
3+
"language": "Ruby",
4+
"type": "framework",
5+
"engine": "puma",
6+
"description": "Rage is a modern Ruby framework designed for non-blocking I/O and simpler infrastructure",
7+
"repo": "https://github.com/rage-rb/rage",
8+
"enabled": true,
9+
"tests": [
10+
"baseline",
11+
"pipelined",
12+
"noisy",
13+
"limited-conn",
14+
"json",
15+
"upload",
16+
"compression",
17+
"mixed"
18+
]
19+
}

0 commit comments

Comments
 (0)