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
1 change: 1 addition & 0 deletions frameworks/rails/Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ source 'https://rubygems.org'
gem 'rails', '~> 8.0'
gem 'puma', '~> 8.0'
gem 'pg', '~> 1.5'
gem 'redis'
gem 'bootsnap', require: false
gem 'connection_pool'
7 changes: 7 additions & 0 deletions frameworks/rails/Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,10 @@ GEM
erb
psych (>= 4.0.0)
tsort
redis (5.4.1)
redis-client (>= 0.22.0)
redis-client (0.29.0)
connection_pool
reline (0.6.3)
io-console (~> 0.5)
securerandom (0.4.1)
Expand Down Expand Up @@ -210,6 +214,7 @@ DEPENDENCIES
pg (~> 1.5)
puma (~> 8.0)
rails (~> 8.0)
redis

CHECKSUMS
action_text-trix (2.1.18) sha256=3fdb83f8bff4145d098be283cdd47ac41caf5110bfa6df4695ed7127d7fb3642
Expand Down Expand Up @@ -272,6 +277,8 @@ CHECKSUMS
railties (8.1.3) sha256=913eb0e0cb520aac687ffd74916bd726d48fa21f47833c6292576ef6a286de22
rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701
rdoc (7.2.0) sha256=8650f76cd4009c3b54955eb5d7e3a075c60a57276766ebf36f9085e8c9f23192
redis (5.4.1) sha256=b5e675b57ad22b15c9bcc765d5ac26f60b675408af916d31527af9bd5a81faae
redis-client (0.29.0) sha256=0c65bf1f8f6dca22063ddb085c0bb2054feef6f03a84869f4161b18a9a15bea3
reline (0.6.3) sha256=1198b04973565b36ec0f11542ab3f5cfeeec34823f4e54cebde90968092b1835
securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1
stringio (3.2.0) sha256=c37cb2e58b4ffbd33fe5cd948c05934af997b36e0b6ca6fdf43afa234cf222e1
Expand Down
45 changes: 45 additions & 0 deletions frameworks/rails/app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
class ApplicationController < ActionController::API

CRUD_COLUMNS = 'id, name, category, price, quantity, active, tags, rating_score, rating_count'
SELECT_QUERY = "SELECT #{CRUD_COLUMNS} FROM items WHERE price BETWEEN $1 AND $2 LIMIT $3"
CRUD_GET_SQL = "SELECT #{CRUD_COLUMNS} FROM items WHERE id = $1 LIMIT 1"
CRUD_LIST_SQL = "SELECT #{CRUD_COLUMNS} FROM items WHERE category = $1 ORDER BY id LIMIT $2 OFFSET $3"
CRUD_UPDATE_SQL = "UPDATE items SET name = $1, price = $2, quantity = $3 WHERE id = $4"
CRUD_UPSERT_SQL = <<~SQL
INSERT INTO items
(#{CRUD_COLUMNS})
VALUES ($1, $2, $3, $4, $5, true, '[\"bench\"]', 0, 0)
ON CONFLICT (id) DO UPDATE SET name = $2, price = $4, quantity = $5
RETURNING id
SQL

private

def self.get_async_db
@async_db ||= begin
return unless ENV['DATABASE_URL']
ConnectionPool.new(size: pool_size, timeout: 5) do
db = PG.connect(ENV['DATABASE_URL'])
db.prepare('select', SELECT_QUERY)
db.prepare('crud_get', CRUD_GET_SQL)
db.prepare('crud_list', CRUD_LIST_SQL)
db.prepare('crud_update', CRUD_UPDATE_SQL)
db.prepare('crud_upsert', CRUD_UPSERT_SQL)
db
end
end
end

def self.redis
@redis ||= begin
return unless ENV['REDIS_URL']
ConnectionPool::Wrapper.new(size: pool_size, timeout: 10) do
Redis.new(url: ENV['REDIS_URL'])
end
end
end

def self.pool_size
ENV.fetch('RAILS_MAX_THREADS', 4).to_i
end
end
18 changes: 1 addition & 17 deletions frameworks/rails/app/controllers/benchmark_controller.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
require 'zlib'
require 'pg'

class BenchmarkController < ActionController::API
class BenchmarkController < ApplicationController
mattr_accessor :dataset

DATA_DIR = ENV.fetch('DATA_DIR', '/data')
Expand All @@ -14,8 +14,6 @@ class BenchmarkController < ActionController::API

FileUtils.cp_r(File.join(DATA_DIR, 'static'), Rails.root.join('public', 'static'))

PG_QUERY = 'SELECT id, name, category, price, quantity, active, tags, rating_score, rating_count FROM items WHERE price BETWEEN $1 AND $2 LIMIT $3'.freeze

def baseline11
total = params[:a].to_i + params[:b].to_i
if request.post?
Expand Down Expand Up @@ -77,18 +75,4 @@ def upload
def not_found
head 404
end

private

def self.get_async_db
@async_db ||= begin
return unless ENV['DATABASE_URL'].present?
max_connections = ENV.fetch('RAILS_MAX_THREADS', 4).to_i
ConnectionPool.new(size: max_connections, timeout: 5) do
db = PG.connect(ENV['DATABASE_URL'])
db.prepare('select', PG_QUERY)
db
end
end
end
end
110 changes: 110 additions & 0 deletions frameworks/rails/app/controllers/items_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
class ItemsController < ApplicationController
def index
category = params[:category] || 'electronics'
page = (params[:page] || 1).to_i
limit = (params[:limit] || 10).to_i
offset = (page - 1) * limit

rows = self.class.get_async_db&.with do |connection|
connection.exec_prepared('crud_list', [category, limit, offset])
end || []

items = rows.map do |row|
map_row(row)
end
render json: { items: items, total: items.length, page: page, limit: limit }
end

def show
id = params[:id]
json = self.class.redis&.with do |connection|
connection.get(id.to_s)
end
if json
headers['x-cache'] = 'HIT'
return render json: json
else
headers['x-cache'] = 'MISS'
end

rows = self.class.get_async_db&.with do |connection|
connection.exec_prepared('crud_get', [id])
end || []

if row = rows.first
item = map_row(row)
json = JSON.generate(item)
self.class.redis&.with do |connection|
connection.set(id.to_s, json)
end
render json: item
else
head 404
end
end

def create
id = params[:id]
name = params[:name] || 'New Product'
category = params[:category] || 'electronics'
price = (params[:price] || 0).to_i
quantity = (params[:quantity] || 0).to_i

self.class.get_async_db&.with do |connection|
connection.exec_prepared('crud_upsert', [id, name, category, price, quantity])
end

self.class.redis&.with do |connection|
connection.del(id.to_s)
end

item = {
'id' => id,
'name' => name,
'category' => category,
'price' => price,
'quantity' => quantity
}

render json: item, status: 201
end

def update
id = params[:id]
name = params[:name] || 'New Product'
price = (params[:price] || 0).to_i
quantity = (params[:quantity] || 0).to_i

row = self.class.get_async_db&.with do |connection|
connection.exec_prepared('crud_update', [name, price, quantity, id])
end || []

self.class.redis&.with do |connection|
connection.del(id.to_s)
end

item = {
'id' => id,
'name' => name,
'price' => price,
'quantity' => quantity
}
render json: item
end

private

def map_row(row)
mapped_row = {
id: row['id'],
name: row['name'],
category: row['category'],
price: row['price'],
quantity: row['quantity'],
active: row['active'] == 1,
}
mapped_row[:tags] = JSON.parse(row['tags']) if row['tags']
mapped_row[:rating] = { score: row['rating_score'], count: row['rating_count'] } if row['rating_score'] && row['rating_count']
mapped_row
end
end
4 changes: 4 additions & 0 deletions frameworks/rails/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
get '/json/:count', to: 'benchmark#json_endpoint'
get '/async-db', to: 'benchmark#async_db'
post '/upload', to: 'benchmark#upload'
get '/crud/items', to: 'items#index'
get '/crud/items/:id', to: "items#show"
post '/crud/items', to: 'items#create'
put '/crud/items/:id', to: 'items#update'

# Catch-all for unknown paths → 404
match '*path', to: 'benchmark#not_found', via: :all
Expand Down
1 change: 1 addition & 0 deletions frameworks/rails/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"api-4",
"api-16",
"async-db",
"crud",
"static"
],
"maintainers": ["p8"]
Expand Down