diff --git a/frameworks/Ruby/rack-app/Gemfile b/frameworks/Ruby/rack-app/Gemfile
new file mode 100644
index 00000000000..2ad03adfb21
--- /dev/null
+++ b/frameworks/Ruby/rack-app/Gemfile
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+source 'https://rubygems.org'
+
+gem 'rack-app'
+gem 'rack-app-front_end'
+gem 'iodine', '~> 0.7', platforms: %i[ruby windows]
+gem 'irb' # for Ruby 3.5
+gem 'logger' # for Ruby 3.5
+gem 'json', '~> 2.10'
+gem 'pg', '~> 1.5'
+gem 'sequel', '~> 5.0'
+gem 'sequel_pg', '~> 1.6', require: false
diff --git a/frameworks/Ruby/rack-app/Gemfile.lock b/frameworks/Ruby/rack-app/Gemfile.lock
new file mode 100644
index 00000000000..7da89e051ce
--- /dev/null
+++ b/frameworks/Ruby/rack-app/Gemfile.lock
@@ -0,0 +1,51 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ concurrent-ruby (1.3.5)
+ date (3.5.0)
+ erb (5.1.3)
+ io-console (0.8.1)
+ irb (1.15.3)
+ pp (>= 0.6.0)
+ rdoc (>= 4.0.0)
+ reline (>= 0.4.2)
+ json (2.15.2)
+ logger (1.7.0)
+ nio4r (2.7.5)
+ pp (0.6.3)
+ prettyprint
+ prettyprint (0.2.0)
+ psych (5.2.6)
+ date
+ stringio
+ puma (7.1.0)
+ nio4r (~> 2.0)
+ rack (3.2.4)
+ rack-app (11.0.2)
+ rack (>= 3.0.0)
+ rackup
+ rackup (2.2.1)
+ rack (>= 3)
+ rdoc (6.15.1)
+ erb
+ psych (>= 4.0.0)
+ tsort
+ reline (0.6.2)
+ io-console (~> 0.5)
+ stringio (3.1.7)
+ tsort (0.2.0)
+
+PLATFORMS
+ arm64-darwin-24
+ ruby
+
+DEPENDENCIES
+ concurrent-ruby
+ irb
+ json (~> 2.10)
+ logger
+ puma (~> 7.1)
+ rack-app
+
+BUNDLED WITH
+ 2.7.2
diff --git a/frameworks/Ruby/rack-app/README.md b/frameworks/Ruby/rack-app/README.md
new file mode 100644
index 00000000000..585c970919e
--- /dev/null
+++ b/frameworks/Ruby/rack-app/README.md
@@ -0,0 +1,44 @@
+# Rack-app Benchmarking Test
+
+rack-app is a minimalist web framework that focuses on simplicity and
+maintainability. The framework is meant to be used by seasoned web developers.
+
+https://github.com/rack-app/rack-app
+
+### Test Type Implementation Source Code
+
+* [JSON Serialization](app.rb): "/json"
+* [Single Database Query](app.rb): "/db"
+* [Multiple Database Queries](app.rb): "/db?queries={#}"
+* [Fortunes](app.rb): "/fortune"
+* [Plaintext](app.rb): "/plaintext"
+
+## Important Libraries
+
+The tests were run with:
+
+* [Sequel](https://rubygems.org/gems/sequel)
+* [PG](https://rubygems.org/gems/pg)
+
+## Test URLs
+
+### JSON
+
+http://localhost:8080/json
+
+### PLAINTEXT
+
+http://localhost:8080/plaintext
+
+### DB
+
+http://localhost:8080/db
+
+### QUERY
+
+http://localhost:8080/queries?queries=
+
+### FORTUNES
+
+http://localhost:8080/fortunes
+
diff --git a/frameworks/Ruby/rack-app/app.rb b/frameworks/Ruby/rack-app/app.rb
new file mode 100644
index 00000000000..e5310fc012d
--- /dev/null
+++ b/frameworks/Ruby/rack-app/app.rb
@@ -0,0 +1,76 @@
+# frozen_string_literal: true
+
+require 'rack/app'
+require 'rack/app/front_end'
+require 'json'
+
+class App < Rack::App
+ MAX_PK = 10_000
+ ID_RANGE = (1..10_000).freeze
+ ALL_IDS = ID_RANGE.to_a
+ QUERIES_MIN = 1
+ QUERIES_MAX = 500
+ JSON_TYPE = 'application/json'
+ HTML_TYPE = 'text/html; charset=utf-8'
+ PLAINTEXT_TYPE = 'text/plain'
+
+ apply_extensions :front_end
+
+ helpers do
+ def fortunes
+ fortunes = Fortune.all
+ fortunes << Fortune.new(
+ id: 0,
+ message: "Additional fortune added at request time."
+ )
+ fortunes.sort_by!(&:message)
+ end
+ end
+
+ get '/json' do
+ set_headers(JSON_TYPE)
+ { message: 'Hello, World!' }.to_json
+ end
+
+ get '/db' do
+ set_headers(JSON_TYPE)
+ World.with_pk(rand1).values.to_json
+ end
+
+ get '/queries' do
+ set_headers(JSON_TYPE)
+ ids = ALL_IDS.sample(bounded_queries)
+ DB.synchronize do
+ ids.map do |id|
+ World.with_pk(id).values
+ end
+ end.to_json
+ end
+
+ get '/fortunes' do
+ set_headers(HTML_TYPE)
+ render 'fortunes.html.erb'
+ end
+
+ get '/plaintext' do
+ set_headers(PLAINTEXT_TYPE)
+ 'Hello, World!'
+ end
+
+ private
+
+ # Return a random number between 1 and MAX_PK
+ def rand1
+ rand(MAX_PK).succ
+ end
+
+ def bounded_queries
+ queries = params['queries'].to_i
+ queries.clamp(QUERIES_MIN, QUERIES_MAX)
+ end
+
+ def set_headers(content_type)
+ response.headers[::Rack::CONTENT_TYPE] = content_type
+ response.headers['Server'] = 'rack-app'
+ end
+end
diff --git a/frameworks/Ruby/rack-app/app/fortunes.html.erb b/frameworks/Ruby/rack-app/app/fortunes.html.erb
new file mode 100644
index 00000000000..56c5c540270
--- /dev/null
+++ b/frameworks/Ruby/rack-app/app/fortunes.html.erb
@@ -0,0 +1,12 @@
+
+
+
Fortunes
+
+
+ | id | message |
+ <% fortunes.each do |record| %>
+ | <%= record.id %> | <%= ERB::Escape.html_escape(record.message) %> |
+ <% end %>
+
+
+
diff --git a/frameworks/Ruby/rack-app/benchmark_config.json b/frameworks/Ruby/rack-app/benchmark_config.json
new file mode 100644
index 00000000000..d25acd9a641
--- /dev/null
+++ b/frameworks/Ruby/rack-app/benchmark_config.json
@@ -0,0 +1,27 @@
+{
+ "framework": "rack-app",
+ "tests": [
+ {
+ "default": {
+ "json_url": "/json",
+ "plaintext_url": "/plaintext",
+ "db_url": "/db",
+ "query_url": "/queries?queries=",
+ "fortune_url": "/fortunes",
+ "port": 8080,
+ "approach": "Realistic",
+ "classification": "Micro",
+ "orm": "Full",
+ "database": "Postgres",
+ "framework": "rack-app",
+ "language": "Ruby",
+ "platform": "Mri",
+ "webserver": "Iodine",
+ "os": "Linux",
+ "database_os": "Linux",
+ "display_name": "rack-app",
+ "notes": ""
+ }
+ }
+ ]
+}
diff --git a/frameworks/Ruby/rack-app/boot.rb b/frameworks/Ruby/rack-app/boot.rb
new file mode 100644
index 00000000000..7711f76a0a1
--- /dev/null
+++ b/frameworks/Ruby/rack-app/boot.rb
@@ -0,0 +1,68 @@
+# frozen_string_literal: true
+require 'bundler/setup'
+require 'time'
+
+MAX_PK = 10_000
+ID_RANGE = (1..MAX_PK).freeze
+ALL_IDS = ID_RANGE.to_a
+QUERIES_MIN = 1
+QUERIES_MAX = 500
+SEQUEL_NO_ASSOCIATIONS = true
+#SERVER_STRING = "Sinatra"
+
+Bundler.require(:default) # Load core modules
+
+def connect(dbtype)
+ Bundler.require(dbtype) # Load database-specific modules
+
+ opts = {}
+
+ adapter = 'postgresql'
+
+ # Determine threading/thread pool size and timeout
+ if defined?(Puma) && (threads = Puma.cli_config.options.fetch(:max_threads)) > 1
+ opts[:max_connections] = threads
+ opts[:pool_timeout] = 10
+ else
+ opts[:max_connections] = 512
+ end
+
+ Sequel.connect \
+ '%{adapter}://%{host}/%{database}?user=%{user}&password=%{password}' % {
+ adapter: adapter,
+ host: 'tfb-database',
+ database: 'hello_world',
+ user: 'benchmarkdbuser',
+ password: 'benchmarkdbpass'
+ }, opts
+end
+
+DB = connect 'postgres'
+
+# Define ORM models
+class World < Sequel::Model(:World)
+ def_column_alias(:randomnumber, :randomNumber) if DB.database_type == :mysql
+
+ def self.batch_update(worlds)
+ if DB.database_type == :mysql
+ worlds.map(&:save_changes)
+ else
+ ids = []
+ sql = String.new("UPDATE world SET randomnumber = CASE id ")
+ worlds.each do |world|
+ sql << "when #{world.id} then #{world.randomnumber} "
+ ids << world.id
+ end
+ sql << "ELSE randomnumber END WHERE id IN ( #{ids.join(',')})"
+ DB.run(sql)
+ end
+ end
+end
+
+class Fortune < Sequel::Model(:Fortune)
+ # Allow setting id to zero (0) per benchmark requirements
+ unrestrict_primary_key
+end
+
+[World, Fortune].each(&:freeze)
+DB.freeze
diff --git a/frameworks/Ruby/rack-app/config.ru b/frameworks/Ruby/rack-app/config.ru
new file mode 100644
index 00000000000..27540c2f4ee
--- /dev/null
+++ b/frameworks/Ruby/rack-app/config.ru
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+require_relative 'boot'
+require_relative 'app'
+
+run App
diff --git a/frameworks/Ruby/rack-app/config/auto_tune.rb b/frameworks/Ruby/rack-app/config/auto_tune.rb
new file mode 100644
index 00000000000..1e075f56911
--- /dev/null
+++ b/frameworks/Ruby/rack-app/config/auto_tune.rb
@@ -0,0 +1,43 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+
+# Instantiate about one process per X MiB of available memory, scaling up to as
+# close to MAX_THREADS as possible while observing an upper bound based on the
+# number of virtual/logical CPUs. If there are fewer processes than
+# MAX_THREADS, add threads per process to reach MAX_THREADS.
+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 = 1
+MAX_THREADS = Integer(ENV['MAX_CONCURRENCY'] || 256)
+
+def meminfo(arg)
+ File.open('/proc/meminfo') do |f|
+ f.each_line do |line|
+ key, value = line.split(/:\s+/)
+ return value.split(/\s+/).first.to_i if key == arg
+ end
+ end
+
+ raise "Unable to find `#{arg}' in /proc/meminfo!"
+end
+
+def auto_tune
+ avail_mem = meminfo('MemAvailable') * 0.8 - MAX_THREADS * 1_024
+
+ workers = [
+ [(1.0 * avail_mem / KB_PER_WORKER).floor, MIN_WORKERS].max,
+ [(Etc.nprocessors * MAX_WORKERS_PER_VCPU).ceil, MIN_WORKERS].max
+ ].min
+
+ threads_per_worker = [
+ workers < MAX_THREADS ? (1.0 * MAX_THREADS / workers).ceil : -Float::INFINITY,
+ MIN_THREADS_PER_WORKER
+ ].max
+
+ [workers, threads_per_worker]
+end
+
+p auto_tune if $PROGRAM_NAME == __FILE__
diff --git a/frameworks/Ruby/rack-app/config/puma.rb b/frameworks/Ruby/rack-app/config/puma.rb
new file mode 100644
index 00000000000..1b6d05d8ac0
--- /dev/null
+++ b/frameworks/Ruby/rack-app/config/puma.rb
@@ -0,0 +1,10 @@
+require_relative 'auto_tune'
+
+# FWBM only... use the puma_auto_tune gem in production!
+_num_workers, num_threads = auto_tune
+
+threads num_threads
+
+before_fork do
+ Sequel::DATABASES.each(&:disconnect)
+end
diff --git a/frameworks/Ruby/rack-app/rack-app.dockerfile b/frameworks/Ruby/rack-app/rack-app.dockerfile
new file mode 100644
index 00000000000..2b24fcab8bd
--- /dev/null
+++ b/frameworks/Ruby/rack-app/rack-app.dockerfile
@@ -0,0 +1,21 @@
+FROM ruby:3.5-rc
+
+ENV RUBY_YJIT_ENABLE=1
+
+# Use Jemalloc
+RUN apt-get update && \
+ apt-get install -y --no-install-recommends libjemalloc2
+ENV LD_PRELOAD=libjemalloc.so.2
+
+WORKDIR /rack-app
+
+COPY Gemfile* ./
+
+ENV BUNDLE_FORCE_RUBY_PLATFORM=true
+RUN bundle install --jobs=8
+
+COPY . .
+
+EXPOSE 8080
+
+CMD bundle exec iodine -p 8080 -w $(ruby config/auto_tune.rb | grep -Eo '[0-9]+' | head -n 1)