Skip to content

Commit 0706776

Browse files
Merge branch 'MDA2AV:main' into sch/native-sha1
2 parents 4ad8f8e + bd9ba99 commit 0706776

97 files changed

Lines changed: 2884 additions & 2196 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

frameworks/roda/Gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ source 'https://rubygems.org'
33
gem 'roda', '~> 3.102'
44
gem 'puma', '~> 8.0'
55
gem 'pg', '~> 1.5'
6+
gem 'redis'
67
gem 'json'
78
gem 'concurrent-ruby'
89
gem 'connection_pool'

frameworks/roda/Gemfile.lock

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ GEM
1111
puma (8.0.1)
1212
nio4r (~> 2.0)
1313
rack (3.2.5)
14+
redis (5.4.1)
15+
redis-client (>= 0.22.0)
16+
redis-client (0.29.0)
17+
connection_pool
1418
roda (3.102.0)
1519
rack
1620

@@ -33,6 +37,7 @@ DEPENDENCIES
3337
json
3438
pg (~> 1.5)
3539
puma (~> 8.0)
40+
redis
3641
roda (~> 3.102)
3742

3843
CHECKSUMS
@@ -45,7 +50,9 @@ CHECKSUMS
4550
pg (1.6.3-x86_64-linux) sha256=5d9e188c8f7a0295d162b7b88a768d8452a899977d44f3274d1946d67920ae8d
4651
puma (8.0.1) sha256=7b94e50c07655718c1fb8ae41a11fc06c7d61293208b3aa608ff71a46d3ad37c
4752
rack (3.2.5) sha256=4cbd0974c0b79f7a139b4812004a62e4c60b145cba76422e288ee670601ed6d3
53+
redis (5.4.1) sha256=b5e675b57ad22b15c9bcc765d5ac26f60b675408af916d31527af9bd5a81faae
54+
redis-client (0.29.0) sha256=0c65bf1f8f6dca22063ddb085c0bb2054feef6f03a84869f4161b18a9a15bea3
4855
roda (3.102.0) sha256=b2156fff6d2b1b52bfac39e4ccde0d820a26594f069c3d9e99cc0853f7ee7dcc
4956

5057
BUNDLED WITH
51-
4.0.6
58+
4.0.10

frameworks/roda/app.rb

Lines changed: 142 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,25 @@ class App < Roda
2424
opts[:dataset_items] = items
2525
end
2626

27-
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
27+
CRUD_COLUMNS = 'id, name, category, price, quantity, active, tags, rating_score, rating_count'
28+
SELECT_QUERY = "SELECT #{CRUD_COLUMNS} FROM items WHERE price BETWEEN $1 AND $2 LIMIT $3".freeze
29+
CRUD_GET_SQL = "SELECT #{CRUD_COLUMNS} FROM items WHERE id = $1 LIMIT 1"
30+
CRUD_LIST_SQL = "SELECT #{CRUD_COLUMNS} FROM items WHERE category = $1 ORDER BY id LIMIT $2 OFFSET $3"
31+
CRUD_UPDATE_SQL = "UPDATE items SET name = $1, price = $2, quantity = $3 WHERE id = $4"
32+
CRUD_UPSERT_SQL = <<~SQL
33+
INSERT INTO items
34+
(#{CRUD_COLUMNS})
35+
VALUES ($1, $2, $3, $4, $5, true, '[\"bench\"]', 0, 0)
36+
ON CONFLICT (id) DO UPDATE SET name = $2, price = $4, quantity = $5
37+
RETURNING id
38+
SQL
2839

2940
plugin :public, root: DATA_DIR, gzip: true, brotli: true
3041
plugin :request_headers
3142
plugin :plain_hash_response_headers
3243
plugin :halt
3344
plugin :send_file
45+
plugin :all_verbs
3446

3547
route do |r|
3648
r.root { 'ok' }
@@ -83,20 +95,109 @@ class App < Roda
8395
end || []
8496

8597
items = rows.map do |row|
86-
{
87-
id: row['id'],
88-
name: row['name'],
89-
category: row['category'],
90-
price: row['price'],
91-
quantity: row['quantity'],
92-
active: row['active'] == 1,
93-
tags: JSON.parse(row['tags']),
94-
rating: { score: row['rating_score'], count: row['rating_count'] }
95-
}
98+
map_row(row)
9699
end
97100
render_json JSON.generate({ items: items, count: items.length })
98101
end
99102

103+
r.is 'crud/items' do
104+
r.get do
105+
category = request.params['category'] || 'electronics'
106+
page = (request.params['page'] || 1).to_i
107+
limit = (request.params['limit'] || 10).to_i
108+
offset = (page - 1) * limit
109+
110+
rows = self.class.get_async_db&.with do |connection|
111+
connection.exec_prepared('crud_list', [category, limit, offset])
112+
end || []
113+
114+
items = rows.map do |row|
115+
map_row(row)
116+
end
117+
render_json JSON.generate({ items: items, total: items.length, page: page, limit: limit })
118+
end
119+
120+
r.post do
121+
params = JSON.parse(request.body.read)
122+
id = params['id']
123+
name = params['name'] || 'New Product'
124+
category = params['category'] || 'electronics'
125+
price = (params['price'] || 0).to_i
126+
quantity = (params['quantity'] || 0).to_i
127+
128+
self.class.get_async_db&.with do |connection|
129+
connection.exec_prepared('crud_upsert', [id, name, category, price, quantity])
130+
end
131+
132+
self.class.redis&.with do |connection|
133+
connection.del(id.to_s)
134+
end
135+
136+
item = {
137+
'id' => id,
138+
'name' => name,
139+
'category' => category,
140+
'price' => price,
141+
'quantity' => quantity
142+
}
143+
144+
response.status = 201
145+
render_json JSON.generate(item)
146+
end
147+
end
148+
149+
r.is 'crud/items', Integer do |id|
150+
r.get do
151+
json = self.class.redis&.with do |connection|
152+
connection.get(id.to_s)
153+
end
154+
if json
155+
response['x-cache'] = 'HIT'
156+
return render_json json
157+
else
158+
response['x-cache'] = 'MISS'
159+
end
160+
161+
rows = self.class.get_async_db&.with do |connection|
162+
connection.exec_prepared('crud_get', [id])
163+
end || []
164+
165+
if row = rows.first
166+
item = map_row(row)
167+
json = JSON.generate(item)
168+
self.class.redis&.with do |connection|
169+
connection.set(id.to_s, json)
170+
end
171+
render_json json
172+
else
173+
r.halt 404, 'Not found'
174+
end
175+
end
176+
177+
r.put do
178+
params = JSON.parse(request.body.read)
179+
name = params['name'] || 'New Product'
180+
price = (params['price'] || 0).to_i
181+
quantity = (params['quantity'] || 0).to_i
182+
183+
row = self.class.get_async_db&.with do |connection|
184+
connection.exec_prepared('crud_update', [name, price, quantity, id])
185+
end || []
186+
187+
self.class.redis&.with do |connection|
188+
connection.del(id.to_s)
189+
end
190+
191+
item = {
192+
'id' => id,
193+
'name' => name,
194+
'price' => price,
195+
'quantity' => quantity
196+
}
197+
render_json JSON.generate(item)
198+
end
199+
end
200+
100201
r.public
101202
end
102203

@@ -112,15 +213,44 @@ def render_plain(plain)
112213
plain
113214
end
114215

216+
def map_row(row)
217+
mapped_row = {
218+
id: row['id'],
219+
name: row['name'],
220+
category: row['category'],
221+
price: row['price'],
222+
quantity: row['quantity'],
223+
active: row['active'] == 1,
224+
}
225+
mapped_row[:tags] = JSON.parse(row['tags']) if row['tags']
226+
mapped_row[:rating] = { score: row['rating_score'], count: row['rating_count'] } if row['rating_score'] && row['rating_count']
227+
mapped_row
228+
end
229+
115230
def self.get_async_db
116231
@async_db ||= begin
117232
return unless ENV['DATABASE_URL']
118233
max_connections = ENV.fetch('MAX_THREADS', 4).to_i
119234
ConnectionPool.new(size: max_connections, timeout: 5) do
120235
db = PG.connect(ENV['DATABASE_URL'])
121-
db.prepare('select', PG_QUERY)
236+
db.prepare('select', SELECT_QUERY)
237+
db.prepare('crud_get', CRUD_GET_SQL)
238+
db.prepare('crud_list', CRUD_LIST_SQL)
239+
db.prepare('crud_update', CRUD_UPDATE_SQL)
240+
db.prepare('crud_upsert', CRUD_UPSERT_SQL)
122241
db
123242
end
124243
end
125244
end
245+
246+
def self.redis
247+
@redis ||= begin
248+
return unless ENV['REDIS_URL']
249+
max_connections = ENV.fetch('MAX_THREADS', 4).to_i
250+
ConnectionPool::Wrapper.new(size: max_connections, timeout: 10) do
251+
Redis.new(url: ENV['REDIS_URL'])
252+
end
253+
end
254+
end
255+
126256
end

frameworks/roda/config.ru

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,5 @@
11
require_relative 'app'
22

3-
# Rack middleware to handle unknown HTTP methods before Puma/Sinatra
4-
class MethodGuard
5-
KNOWN = %w[GET POST PUT DELETE PATCH HEAD OPTIONS TRACE CONNECT].freeze
6-
7-
def initialize(app)
8-
@app = app
9-
end
10-
11-
def call(env)
12-
if KNOWN.include?(env['REQUEST_METHOD'])
13-
@app.call(env)
14-
else
15-
[405, { 'content-type' => 'text/plain' }, ['Method Not Allowed']]
16-
end
17-
end
18-
end
19-
203
# Threads marked as IO bound are allowed to go over Puma's max thread limit.
214
class MarkAsIOBoundThreads
225
def initialize(app)
@@ -32,6 +15,5 @@ class MarkAsIOBoundThreads
3215
end
3316

3417
use MarkAsIOBoundThreads
35-
use MethodGuard
3618
use Rack::Deflater # enable gzip
3719
run App

frameworks/roda/meta.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"api-4",
1818
"api-16",
1919
"async-db",
20+
"crud",
2021
"static"
2122
],
2223
"maintainers": ["p8"]

frameworks/roda/puma.rb

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@
66
bind "tcp://0.0.0.0:8080"
77
bind "ssl://0.0.0.0:8081?cert=#{tls_cert_path}&key=#{tls_key_path}"
88

9-
# Allow all HTTP methods so unknown ones reach Rack middleware (returned as 405)
10-
supported_http_methods :any
11-
129
preload_app!
1310

1411
before_fork do

frameworks/sinatra/config.ru

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,5 @@
11
require_relative 'app'
22

3-
# Rack middleware to handle unknown HTTP methods before Puma/Sinatra
4-
class MethodGuard
5-
KNOWN = %w[GET POST PUT DELETE PATCH HEAD OPTIONS TRACE CONNECT].freeze
6-
7-
def initialize(app)
8-
@app = app
9-
end
10-
11-
def call(env)
12-
if KNOWN.include?(env['REQUEST_METHOD'])
13-
@app.call(env)
14-
else
15-
[405, { 'content-type' => 'text/plain' }, ['Method Not Allowed']]
16-
end
17-
end
18-
end
19-
203
# Threads marked as IO bound are allowed to go over Puma's max thread limit.
214
class MarkAsIOBoundThreads
225
def initialize(app)
@@ -32,6 +15,5 @@ class MarkAsIOBoundThreads
3215
end
3316

3417
use MarkAsIOBoundThreads
35-
use MethodGuard
3618
use Rack::Deflater
3719
run App

frameworks/sinatra/puma.rb

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@
66
bind "tcp://0.0.0.0:8080"
77
bind "ssl://0.0.0.0:8081?cert=#{tls_cert_path}&key=#{tls_key_path}"
88

9-
# Allow all HTTP methods so unknown ones reach Rack middleware (returned as 405)
10-
supported_http_methods :any
11-
129
preload_app!
1310

1411
before_fork do

frameworks/symfony-spawn-tas/.env

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
APP_ENV=prod
2+
APP_DEBUG=0
3+
APP_SECRET=benchmark_httparena_secret
4+
DATABASE_URL=pgsql://bench:bench@localhost:5432/benchmark
5+
DEFAULT_URI=http://localhost
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/test/*
2+
/var/
3+
/vendor/
4+
/.env.local
5+
/.env.local.php
6+
/.env.*.local
7+
/public/bundles/
8+
/public/build/
9+
/node_modules/

0 commit comments

Comments
 (0)