Ruby is
blazing fast ...
make ruby great again
APPLY FOR:
Ruby on Rails
API on Rails
Benchmark
How to speed up
API on Rails
Benchmark
How to speed up
# config/application.rb
# require "rails"
# Pick the frameworks you want:
require "active_model/railtie"
# require "active_job/railtie"
require "active_record/railtie"
# require "action_controller/railtie"
# require "action_mailer/railtie"
# require "action_view/railtie"
# require "action_cable/engine"
# require "sprockets/railtie"
# require "rails/test_unit/railtie"
rails new awesome_ruby_app --api
class ApplicationController < JSONAPI::ResourceControllerMetal
include Knock::Authenticable
before_action :authenticate_user
def head(type, options = {}) end # ...
end
gem "jsonapi-resources"
Reduce AR models instances
use views in database
Started GET "/mail-logs" for ::1 at 2017-03-17 18:50:46 +0100
Processing by MailLogsController#index as API_JSON
MailLog Load (0.4ms)
SELECT "mail_logs".* FROM "mail_logs"
ORDER BY "mail_logs"."number"
DESC LIMIT $1 OFFSET $2 [["LIMIT", 20], ["OFFSET", 0]]
(0.3ms) SELECT COUNT(*) FROM "mail_logs"
Completed 200 OK in 7ms (Views: 0.0ms)
API on Rails
Benchmark
How to speed up
Ruby 2.4.0
Rails 5.0.2
Puma 3.8.1
(single process == one core)
Apache Benchmark 2.3
(10000 request burst on index action)
MBP 2015
Requests per second: 250.07 [#/sec] (mean)
Time per request: 3.999 [ms] (mean)
Time per request: 3.999 [ms] (mean, across all concurrent requests)
Percentage of the requests served within a certain time (ms)
50% 4
66% 4
75% 4
80% 4
90% 4
95% 5
98% 5
99% 6
100% 39 (longest request)
250 req / s
3.999 ms / req (median)
API on Rails
Benchmark
How to speed up
Lets rewrite it all with
xyz framework
let use reverse proxy
and rewrite only heavy loaded API end points
Sinja 1.2.5
Sinatra 2.0.0.rc.1
Sequel 4.44.0
(ActiveSupport 5.0.2 -> jsonapi-serializers)
Puma 3.8.1
(single process == one core)
Apache Benchmark 2.3
(10000 request burst on index action)
MBP 2015'
# frozen_string_literal: true
require 'logger'
require 'sinatra'
require 'sequel'
require 'sinatra/jsonapi'
require 'sinja/sequel/helpers'
DB = Sequel.connect('postgres://localhost/cl-api_development')
DB.extension(:freeze_datasets)
DB.extension(:pagination)
DB.optimize_model_load = true
DB.loggers << Logger.new($stderr) if Sinatra::Base.development?
Sequel::Model.plugin :tactical_eager_loading
class MailLog < Sequel::Model
many_to_one :category
many_to_one :company
end
class Category < Sequel::Model
one_to_many :mail_log
end
class Company < Sequel::Model
one_to_many :mail_log
end
Sequel::Model.finalize_associations
Sequel::Model.freeze
class BaseSerializer
include JSONAPI::Serializer
end
class MailLogSerializer < BaseSerializer
attributes :number, :in_or_out, :document_date_on, :received_on, :external_number, :comment, :value_net, :value_vat
has_one :category
has_one :company
end
class CategorySerializer < BaseSerializer
attributes :name
has_many :mail_logs
end
class CompanySerializer < BaseSerializer
attributes :name, :icon
has_many :mail_logs
end
class MailLogs < Sinatra::Application
register Sinatra::JSONAPI
resource 'mail-logs' do
index do
MailLog.dataset
end
end
end
freeze_jsonapi
70 LOC of Ruby
Requests per second: 746.18 [#/sec] (mean)
Time per request: 1.340 [ms] (mean)
Time per request: 1.340 [ms] (mean, across all concurrent requests)
Transfer rate: 2731.85 [Kbytes/sec] received
Percentage of the requests served within a certain time (ms)
50% 1
66% 1
75% 1
80% 1
90% 2
95% 2
98% 2
99% 2
100% 5 (longest request)
746 req / s
2.98x
1.340 ms / req (median)
2.98x
How it Compare to
Phoenix / Elixir
? ms / req (median)
tomorrow
3:00 PM
Ruby is
fast enough
make ruby great again
Michał Czyż
@cs3b
Thank You!
Ruby is
fast enough
part II
Ruby vs Erlang (Plug implementation - without Phoenix)
written by
@paweldude
Elixir 1.4.2 (no Phoenix)
Plug 1.3.4
Ecto 2.1.4
Poison 2.2.0
(limiting elixir to single core `--erl "+S 1"`)
Apache Benchmark 2.3
(10000 request burst index)
MBP 2015
defmodule PlugBench.Repo do
use Ecto.Repo, otp_app: :plug_bench
end
defmodule PlugBench.Category do
use Ecto.Schema
schema "categories" do
field :name, :string
has_many :mail_log, PlugBench.MailLog
end
end
defmodule PlugBench.CategorySerializer do
use JaSerializer
location "/categories/:id"
attributes [:name]
end
defmodule PlugBench.Company do
use Ecto.Schema
schema "companies" do
field :name, :string
field :icon, :string
has_many :mail_log, PlugBench.MailLog
end
end
defmodule PlugBench.CompanySerializer do
use JaSerializer
location "/companies/:id"
attributes [:name]
end
defmodule PlugBench.MailLog do
use Ecto.Schema
@derive {Poison.Encoder, only: [:number, :in_our_out, :document_date_on, :received_on, :external_number, :comment, :value_net, :value_vat, :lp, :inserted_at, :updated_at]}
schema "mail_logs" do
field :number, :string
field :in_or_out, :string
field :document_date_on, :date
field :received_on, :date
field :external_number, :string
field :comment, :string
field :value_net, :decimal
field :value_vat, :decimal
field :lp, :integer
belongs_to :category, PlugBench.Category
belongs_to :company, PlugBench.Company
end
end
defmodule PlugBench.MailLogSerializer do
use JaSerializer
location "/mail-logs/:id"
attributes [:name, :number, :in_or_out, :document_date_on, :received_on,
:external_number, :comment, :value_net, :value_vat, :lp]
has_one :company,
serializer: PlugBench.CompanySerializer,
include: true,
field: :company_id
has_one :category,
serializer: PlugBench.CategorySerializer,
include: true,
field: :category_id
end
defmodule PlugBench.Endpoint do
import Plug.Conn
use Plug.Router
plug :match
plug Plug.Logger
plug Plug.Parsers, parsers: [:urlencoded],
pass: ["*/*"],
accept: ["*/*"]
plug :dispatch
get "/mail-logs" do
# it's not jsonapi yet
send_resp(conn, 200, PlugBench.Repo.all(PlugBench.MailLog) |> Poison.encode!)
end
match _ do
conn
|> put_resp_content_type("application/json")
|> send_resp(410, ~s({"status": "bad request"}))
end
end
105 LOC of Elixir
Requests per second: 1306.28 [#/sec] (mean)
Time per request: 0.766 [ms] (mean)
Time per request: 0.766 [ms] (mean, across all concurrent requests)
Transfer rate: 1865.99 [Kbytes/sec] received
Percentage of the requests served within a certain time (ms)
50% 1
66% 1
75% 1
80% 1
90% 1
95% 1
98% 2
99% 2
100% 60 (longest request)
1306 req / seconds
0.766 ms / req (median)
Michał Czyż
@cs3b
Thank You!
Ruby is
fast enough
make ruby great again
Ruby is Fast Enough - serving JSONAPI at speed
By Michał Czyż
Ruby is Fast Enough - serving JSONAPI at speed
rails ruby jsonapi-resources puma apache benchmark ab sinja sinatra sequel single process wrocloverb
- 3,244