How To Write Middleware In Ruby

SATOSHI TAGOMORI
SATOSHI TAGOMORISoftware Engineer at Treasure Data, Inc.
How To Write
Middleware in Ruby
2016/12/02 RubyConf Taiwan Day 1
Satoshi Tagomori (@tagomoris)
Satoshi "Moris" Tagomori
(@tagomoris)
Fluentd, MessagePack-Ruby,
Norikra, ...
Treasure Data, Inc.
http://www.fluentd.org/
open source data collector for unified logging layer.
LOG
script to
parse data
cron job for
loading
filtering
script
syslog
script
Tweet-
fetching
script
aggregation
script
aggregation
script
script to
parse data
rsync
server
FILE
LOG
FILE
✓ Parse/Format data
✓ Buffering & Retries
✓ Load balancing
✓ Failover
Before
After
How To Write Middleware In Ruby
Middleware? : Fluentd
• Long running daemon process
• Compatibility for API, behavior and configuration files
• Multi platform / environment support
• Linux, Mac and Windows(!)
• Baremetal servers, Virtual machines, Containers
• Many use cases
• Various data, Various data formats, Unexpected errors
• Various traffic - small to huge
• Long running daemon process
• Compatibility for API, behavior and configuration files
• Multi platform / environment support
• Linux, Mac and Windows(!)
• Ruby, JRuby?, Rubinius?
• Baremetal servers, Virtual machines, Containers
• Many use cases
• Various data, Various data formats, Unexpected errors
• Various traffic - small to huge
Middleware? Batches:
Minutes - Hours
• Long running daemon process
• Compatibility for API, behavior and configuration files
• Multi platform / environment support
• Linux, Mac and Windows(!)
• Ruby, JRuby?, Rubinius?
• Baremetal servers, Virtual machines, Containers
• Many use cases
• Various data, Various data formats, Unexpected errors
• Various traffic - small to huge
Middleware? Providing APIs
and/or Client Libraries
• Long running daemon process
• Compatibility for API, behavior and configuration files
• Multi platform / environment support
• Linux, Mac and Windows(!)
• Ruby, JRuby?, Rubinius?
• Baremetal servers, Virtual machines, Containers
• Many use cases
• Various data, Various data formats, Unexpected errors
• Various traffic - small to huge
Middleware?
Daily Development
& Deployment
Providing Client Tools
• Long running daemon process
• Compatibility for API, behavior and configuration files
• Multi platform / environment support
• Linux, Mac and Windows(!)
• Ruby, JRuby?, Rubinius?
• Baremetal servers, Virtual machines, Containers
• Many use cases
• Various data, Various data formats, Unexpected errors
• Various traffic - small to huge
Middleware?
Make Your Application
Stable
• Long running daemon process
• Compatibility for API, behavior and configuration files
• Multi platform / environment support
• Linux, Mac and Windows(!)
• Ruby, JRuby?, Rubinius?
• Baremetal servers, Virtual machines, Containers
• Many use cases
• Various data, Various data formats, Unexpected errors
• Various traffic - small to huge
Middleware?
Make Your Application
Fast and Scalable
Case studies from
development of Fluentd
• Platform: Linux, Mac and Windows
• Resource: Memory usage and malloc
• Resource and Stability: Handling JSON
• Stability: Threads and exceptions
Platforms:
Linux, Mac and Windows
Linux and Mac:
Thread/process scheduling
• Both are UNIX-like systems...
• Mac (development), Linux (production)
• Test code must run on both!
• CI services provide multi-environment support
• Fluentd uses Travis CI :D
• Travis CI provides "os" option: "linux" & "osx"
• Important tests to be written: Threading
class MyTest < ::Test::Unit::TestCase
test 'yay 1' do
data = []
thr = Thread.new do
data << "line 1"
end
data << "line 2"
assert_equal ["line 1", "line 2"], data
end
end
class MyTest < ::Test::Unit::TestCase
test 'client sends 2 data' do
list = []
thr = Thread.new do # Mock server
TCPServer.open("127.0.0.1", 2048) do |server|
while sock = server.accept
list << sock.read.chomp
end
end
end
2.times do |i|
TCPSocket.open("127.0.0.1", 2048) do |client|
client.write "data #{i}"
end
end
assert_equal(["data 0", "data 1"], list)
end
end
Loaded suite example
Started
F
===========================================================================================
Failure: test: client sends 2 data(MyTest)
example.rb:22:in `block in <class:MyTest>'
19: end
20: end
21:
=> 22: assert_equal(["data 0", "data 1"], list)
23: end
24: end
<["data 0", "data 1"]> expected but was
<["data 0"]>
diff:
["data 0", "data 1"]
===========================================================================================
Finished in 0.007253 seconds.
-------------------------------------------------------------------------------------------
1 tests, 1 assertions, 1 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
0% passed
-------------------------------------------------------------------------------------------
137.87 tests/s, 137.87 assertions/s
Mac OS X (10.11.16)
class MyTest < ::Test::Unit::TestCase
test 'yay 1' do
data = []
thr = Thread.new do
data << "line 1"
end
data << "line 2"
assert_equal ["line 1", "line 2"], data
end
end
class MyTest < ::Test::Unit::TestCase
test 'client sends 2 data' do
list = []
thr = Thread.new do # Mock server
TCPServer.open("127.0.0.1", 2048) do |server|
while sock = server.accept
list << sock.read.chomp
end
end
end
2.times do |i|
TCPSocket.open("127.0.0.1", 2048) do |client|
client.write "data #{i}"
end
end
assert_equal(["data 0", "data 1"], list)
end
end
class MyTest < ::Test::Unit::TestCase
test 'yay 1' do
data = []
thr = Thread.new do
data << "line 1"
end
data << "line 2"
assert_equal ["line 1", "line 2"], data
end
end
class MyTest < ::Test::Unit::TestCase
test 'client sends 2 data' do
list = []
thr = Thread.new do # Mock server
TCPServer.open("127.0.0.1", 2048) do |server|
listening = true
while sock = server.accept
list << sock.read.chomp
end
end
end
2.times do |i|
TCPSocket.open("127.0.0.1", 2048) do |client|
client.write "data #{i}"
end
end
sleep 1
assert_equal(["data 0", "data 1"], list)
end
end
Loaded suite example
Started
.
Finished in 1.002745 seconds.
--------------------------------------------------------------------------------------------
1 tests, 1 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
100% passed
--------------------------------------------------------------------------------------------
1.00 tests/s, 1.00 assertions/s
Mac OS X (10.11.16)
Loaded suite example
Started
E
=================================================================================================
Error: test: client sends 2 data(MyTest): Errno::ECONNREFUSED: Connection refused - connect(2)
for "127.0.0.1" port 2048
example.rb:16:in `initialize'
example.rb:16:in `open'
example.rb:16:in `block (2 levels) in <class:MyTest>'
example.rb:15:in `times'
example.rb:15:in `block in <class:MyTest>'
=================================================================================================
Finished in 0.005918197 seconds.
-------------------------------------------------------------------------------------------------
1 tests, 0 assertions, 0 failures, 1 errors, 0 pendings, 0 omissions, 0 notifications
0% passed
-------------------------------------------------------------------------------------------------
168.97 tests/s, 0.00 assertions/s
Linux (Ubuntu 16.04)
class MyTest < ::Test::Unit::TestCase
test 'yay 1' do
data = []
thr = Thread.new do
data << "line 1"
end
data << "line 2"
assert_equal ["line 1", "line 2"], data
end
end
class MyTest < ::Test::Unit::TestCase
test 'client sends 2 data' do
list = []
thr = Thread.new do # Mock server
TCPServer.open("127.0.0.1", 2048) do |server|
listening = true
while sock = server.accept
list << sock.read.chomp
end
end
end
2.times do |i|
TCPSocket.open("127.0.0.1", 2048) do |client|
client.write "data #{i}"
end
end
sleep 1
assert_equal(["data 0", "data 1"], list)
end
end
class MyTest < ::Test::Unit::TestCase
test 'yay 1' do
data = []
thr = Thread.new do
data << "line 1"
end
data << "line 2"
assert_equal ["line 1", "line 2"], data
end
end
class MyTest < ::Test::Unit::TestCase
test 'client sends 2 data' do
list = []
thr = Thread.new do # Mock server
TCPServer.open("127.0.0.1", 2048) do |server|
listening = true
while sock = server.accept
list << sock.read.chomp
end
end
end
2.times do |i|
TCPSocket.open("127.0.0.1", 2048) do |client|
client.write "data #{i}"
end
end
sleep 1
assert_equal(["data 0", "data 1"], list)
end
end
class MyTest < ::Test::Unit::TestCase
test 'yay 1' do
data = []
thr = Thread.new do
data << "line 1"
end
data << "line 2"
assert_equal ["line 1", "line 2"], data
end
end
class MyTest < ::Test::Unit::TestCase
test 'client sends 2 data' do
list = []
thr = Thread.new do # Mock server
TCPServer.open("127.0.0.1", 2048) do |server|
listening = true
while sock = server.accept
list << sock.read.chomp
end
end
end
2.times do |i|
TCPSocket.open("127.0.0.1", 2048) do |client|
client.write "data #{i}"
end
end
sleep 1
assert_equal(["data 0", "data 1"], list)
end
end
class MyTest < ::Test::Unit::TestCase
test 'yay 1' do
data = []
thr = Thread.new do
data << "line 1"
end
data << "line 2"
assert_equal ["line 1", "line 2"], data
end
end
require 'socket'
class MyTest < ::Test::Unit::TestCase
test 'client sends 2 data' do
list = []
listening = false
thr = Thread.new do # Mock server
TCPServer.open("127.0.0.1", 2048) do |server|
listening = true
while sock = server.accept
list << sock.read.chomp
end
end
end
sleep 0.1 until listening
2.times do |i|
TCPSocket.open("127.0.0.1", 2048) do |client|
client.write "data #{i}"
end
end
require 'timeout'
Timeout.timeout(3){ sleep 0.1 until list.size >= 2 }
assert_equal(["data 0", "data 1"], list)
end
end
*NIX and Windows:
fork-exec and spawn
• Windows: another thread scheduling :(
• daemonize:
• double fork (or Process.daemon) on *nix
• spawn on Windows
• Execute one another process:
• fork & exec on *nix
• spawn on Windows
• CI on Windows: AppVeyor
Lesson 1:
Run Tests
on All Platforms Supported
Resource:
Memory usage and malloc
Memory Usage:
Object leak
• Temp values must leak in
long running process
• 1,000 objects / hour

=> 8,760,000 objects / year
• Some solutions:
• In-process GC
• Storage with TTL
• (External storages: Redis, ...)
module MyDaemon
class Process
def hour_key
Time.now.to_i / 3600
end
def hourly_store
@map[hour_key] ||= {}
end
def put(key, value)
hourly_store[key] = value
end
def get(key)
hourly_store[key]
end
# add # of data per hour
def read_data(table_name, data)
key = "records_of_#{table_name}"
put(key, get(key) + data.size)
end
end
Lesson 2:
Make Sure to Collect Garbages
Resource and Stability:
Handling JSON
Formatting Data Into JSON
• Fluentd handles JSON in many use cases
• both of parsing and generating
• it consumes much CPU time...
• JSON, Yajl and Oj
• JSON: ruby standard library
• Yajl (yajl-ruby): ruby binding of YAJL (SAX-based)
• Oj (oj): Optimized JSON
class MyTest < ::Test::Unit::TestCase
test 'yay 1' do
data = []
thr = Thread.new do
data << "line 1"
end
data << "line 2"
assert_equal ["line 1", "line 2"], data
end
end
require 'json'; require 'yajl'; require 'oj'
Oj.default_options = {bigdecimal_load: :float, mode: :compat, use_to_json: true}
module MyDaemon
class Json
def initialize(mode)
klass = case mode
when :json then JSON
when :yajl then Yajl
when :oj then Oj
end
@proc = klass.method(:dump)
end
def dump(data); @proc.call(data); end
end
end
require 'benchmark'
N = 500_000
obj = {"message" => "a"*100, "100" => 100, "pi" => 3.14159, "true" => true}
Benchmark.bm{|x|
x.report("json") {
formatter = MyDaemon::Json.new(:json)
N.times{ formatter.dump(obj) }
}
x.report("yajl") {
formatter = MyDaemon::Json.new(:yajl)
N.times{ formatter.dump(obj) }
}
x.report("oj") {
formatter = MyDaemon::Json.new(:oj)
N.times{ formatter.dump(obj) }
}
}
$ ruby example2.rb
user system total real
json 3.870000 0.050000 3.920000 ( 4.005429)
yajl 2.940000 0.030000 2.970000 ( 2.998924)
oj 1.130000 0.020000 1.150000 ( 1.152596)
# for 500_000 objects
Mac OS X (10.11.16)
Ruby 2.3.1
yajl-ruby 1.3.0
oj 2.18.0
Speed is not only thing:
APIs for unstable I/O
• JSON and Oj have only ".load"
• it raises parse error for:
• incomplete JSON string
• additional bytes after JSON string
• Yajl has stream parser: very useful for servers
• method to feed input data
• callback for parsed objects
class MyTest < ::Test::Unit::TestCase
test 'yay 1' do
data = []
thr = Thread.new do
data << "line 1"
end
data << "line 2"
assert_equal ["line 1", "line 2"], data
end
end
require 'oj'
Oj.load('{"message":"this is ') # Oj::ParseError
Oj.load('{"message":"this is a pen."}') # => Hash
Oj.load('{"message":"this is a pen."}{"messa"') # Oj::ParseError
Speed is not only thing:
APIs for unstable I/O
• JSON and Oj have only ".load"
• it raises parse error for:
• incomplete JSON string
• additional bytes after JSON string
• Yajl has stream parser: very useful for servers
• method to feed input data
• callback for parsed objects
class MyTest < ::Test::Unit::TestCase
test 'yay 1' do
data = []
thr = Thread.new do
data << "line 1"
end
data << "line 2"
assert_equal ["line 1", "line 2"], data
end
end
require 'yajl'
parsed_objs = []
parser = Yajl::Parser.new
parser.on_parse_complete = ->(obj){ parsed_objs << obj }
parse << '{"message":"aaaaaaaaaaaaaaa'
parse << 'aaaaaaaaa"}{"message"' # on_parse_complete is called
parse << ':"bbbbbbbbb"'
parse << '}' # on_parse_complete is called again
class MyTest < ::Test::Unit::TestCase
test 'yay 1' do
data = []
thr = Thread.new do
data << "line 1"
end
data << "line 2"
assert_equal ["line 1", "line 2"], data
end
end
require 'socket'
require 'oj'
TCPServer.open(port) do |server|
while sock = server.accept
begin
buf = ""
while input = sock.readpartial(1024)
buf << input
# can we feed this value to Oj.load ?
begin
obj = Oj.load(buf) # never succeeds if buf has 2 objects
call_method(obj)
buf = ""
rescue Oj::ParseError
# try with next input ...
end
end
rescue EOFError
sock.close rescue nil
end
end
end
class MyTest < ::Test::Unit::TestCase
test 'yay 1' do
data = []
thr = Thread.new do
data << "line 1"
end
data << "line 2"
assert_equal ["line 1", "line 2"], data
end
end
require 'socket'
require 'yajl'
TCPServer.open(port) do |server|
while sock = server.accept
begin
parser = Yajl::Parser.new
parser.on_parse_complete = ->(obj){ call_method(obj) }
while input = sock.readpartial(1024)
parser << input
end
rescue EOFError
sock.close rescue nil
end
end
end
Lesson 3:
Choose
Fast/Well-Designed(/Stable)
Libraries
Stability:
Threads and Exceptions
Thread in Ruby
• GVL(GIL): Giant VM Lock (Global Interpreter Lock)
• Just one thread in many threads can run at a time
• Ruby VM can use only 1 CPU core
• Thread in I/O is *not* running
• I/O threads can run in parallel
threads in I/O running threads
• We can write network servers in Ruby!
class MyTest < ::Test::Unit::TestCase
test 'yay 1' do
data = []
thr = Thread.new do
data << "line 1"
end
data << "line 2"
assert_equal ["line 1", "line 2"], data
end
end
class MyTestCase < ::Test::Unit::TestCase
test 'sent data should be received' do
received = []
sent = []
listening = false
th1 = Thread.new do
TCPServer.open("127.0.0.1", 2048) do |server|
listening = true
while sock = server.accepto
received << sock.read
end
end
end
sleep 0.1 until listening
["foo", "bar"].each do |str|
begin
TCPSocket.open("127.0.0.1", 2048) do |client|
client.write "data #{i}"
end
sent << str
rescue => e
# ignore
end
end
assert_equal sent, received
end
end
Loaded suite example7
Started
.
Finished in 0.104729 seconds.
-------------------------------------------------------------------------------------------
1 tests, 1 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
100% passed
-------------------------------------------------------------------------------------------
9.55 tests/s, 9.55 assertions/s
class MyTestCase < ::Test::Unit::TestCase
test 'sent data should be received' do
received = []
sent = []
listening = false
th1 = Thread.new do
TCPServer.open("127.0.0.1", 2048) do |server|
listening = true
while sock = server.accepto
received << sock.read
end
end
end
sleep 0.1 until listening
["foo", "bar"].each do |str|
begin
TCPSocket.open("127.0.0.1", 2048) do |client|
client.write "data #{i}"
end
sent << str
rescue => e
# ignore
end
end
assert_equal sent, received
end
end
class MyTestCase < ::Test::Unit::TestCase
test 'sent data should be received' do
received = []
sent = []
listening = false
th1 = Thread.new do
TCPServer.open("127.0.0.1", 2048) do |server|
listening = true
while sock = server.accepto
received << sock.read
end
end
end
sleep 0.1 until listening
["foo", "bar"].each do |str|
begin
TCPSocket.open("127.0.0.1", 2048) do |client|
client.write "data #{i}"
end
sent << str
rescue => e
# ignore
end
end
assert_equal sent, received
end
end
class MyTestCase < ::Test::Unit::TestCase
test 'sent data should be received' do
received = []
sent = []
listening = false
th1 = Thread.new do
TCPServer.open("127.0.0.1", 2048) do |server|
listening = true
while sock = server.accepto
received << sock.read
end
end
end
sleep 0.1 until listening
["foo", "bar"].each do |str|
begin
TCPSocket.open("127.0.0.1", 2048) do |client|
client.write "data #{i}"
end
sent << str
rescue => e
# ignore
end
end
assert_equal sent, received
end
end
class MyTestCase < ::Test::Unit::TestCase
test 'sent data should be received' do
received = []
sent = []
listening = false
th1 = Thread.new do
TCPServer.open("127.0.0.1", 2048) do |server|
listening = true
while sock = server.accepto
received << sock.read
end
end
end
sleep 0.1 until listening
["foo", "bar"].each do |str|
begin
TCPSocket.open("127.0.0.1", 2048) do |client|
client.write "data #{i}"
end
sent << str
rescue => e
# ignore
end
end
assert_equal sent, received
end
end
class MyTestCase < ::Test::Unit::TestCase
test 'sent data should be received' do
received = []
sent = []
listening = false
th1 = Thread.new do
TCPServer.open("127.0.0.1", 2048) do |server|
listening = true
while sock = server.accepto
received << sock.read
end
end
end
sleep 0.1 until listening
["foo", "bar"].each do |str|
begin
TCPSocket.open("127.0.0.1", 2048) do |client|
client.write "data #{i}"
end
sent << str
rescue => e
# ignore
end
end
assert_equal sent, received
end
end
class MyTestCase < ::Test::Unit::TestCase
test 'sent data should be received' do
received = []
sent = []
listening = false
th1 = Thread.new do
TCPServer.open("127.0.0.1", 2048) do |server|
listening = true
while sock = server.accepto
received << sock.read
end
end
end
sleep 0.1 until listening
["foo", "bar"].each do |str|
begin
TCPSocket.open("127.0.0.1", 2048) do |client|
client.write "data #{i}"
end
sent << str
rescue => e
# ignore
end
end
assert_equal sent, received # [] == []
end
end
Thread in Ruby:
Methods for errors
• Threads will die silently if any errors are raised
• abort_on_exception
• raise error in threads on main thread if true
• required to make sure not to create false success
(silent crash)
• report_on_exception
• warn errors in threads if true (2.4 feature)
class MyTestCase < ::Test::Unit::TestCase
test 'sent data should be received' do
received = []
sent = []
listening = false
th1 = Thread.new do
Thread.current.abort_on_exception = true
TCPServer.open("127.0.0.1", 2048) do |server|
listening = true
while sock = server.accepto
received << sock.read
end
end
end
sleep 0.1 until listening
["foo", "bar"].each do |str|
begin
TCPSocket.open("127.0.0.1", 2048) do |client|
client.write "data #{i}"
end
sent << str
rescue => e
# ignore
end
end
assert_equal sent, received # [] == []
end
end
Loaded suite example7
Started
E
===========================================================================================
Error: test: sent data should be received(MyTestCase):
NoMethodError: undefined method `accepto' for #<TCPServer:(closed)>
Did you mean? accept
example7.rb:14:in `block (3 levels) in <class:MyTestCase>'
example7.rb:12:in `open'
example7.rb:12:in `block (2 levels) in <class:MyTestCase>'
===========================================================================================
Finished in 0.0046 seconds.
-------------------------------------------------------------------------------------------
1 tests, 0 assertions, 0 failures, 1 errors, 0 pendings, 0 omissions, 0 notifications
0% passed
-------------------------------------------------------------------------------------------
217.39 tests/s, 0.00 assertions/s
sleeping = false
Thread.abort_on_exception = true
Thread.new{ sleep 0.1 until sleeping ; raise "yay" }
begin
sleeping = true
sleep 5
rescue => e
p(here: "rescue in main thread", error: e)
end
p "foo!"
Thread in Ruby:
Process crash from errors in threads
• Middleware SHOULD NOT crash as far as possible :)
• An error from a TCP connection MUST NOT crash the
whole process
• Many points to raise errors...
• Socket I/O, Executing commands
• Parsing HTTP requests, Parsing JSON (or other formats)
• Process
• should crash in tests, but
• should not in production
Thread in Ruby:
What needed in your code about threads
• Set Thread#abort_on_exception = true
• for almost all threads...
• "rescue" all errors in threads
• to log these errors, and not to crash whole process
• "raise" rescued errors again only in testing
• to make tests failed for bugs
Lesson 4:
Handle Exceptions
in Right Way
Wrap-up:
Writing Middleware is ...
Writing Middleware:
• Taking care about:
• various platforms and environment
• Resource usage and stability
• Requiring to know about:
• Ruby's features
• Ruby VM's behavior
• Library implementation
• In different viewpoint from writing applications!
Write your code,
like middleware :D
Make it efficient & stable!
Thank you!
@tagomoris
Loaded suite example
Started
F
===========================================================================================
Failure: test: client sends 2 data(MyTest)
example.rb:22:in `block in <class:MyTest>'
19: end
20: end
21:
=> 22: assert_equal(["data 0", "data 1"], list)
23: end
24: end
<["data 0", "data 1"]> expected but was
<["data 0", "data 1"]>
diff:
["data 0", "data 1"]
===========================================================================================
Finished in 0.009425 seconds.
-------------------------------------------------------------------------------------------
1 tests, 1 assertions, 1 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
0% passed
-------------------------------------------------------------------------------------------
106.10 tests/s, 106.10 assertions/s
Mac OS X (10.11.16)
Memory Usage:
Memory fragmentation
• High memory usage, low # of objects
• memory fragmentation?
• glibc malloc: weak for fine-grained memory allocation
and multi threading
• Switching to jemalloc by LD_PRELOAD
• FreeBSD standard malloc (available on Linux)
• fluentd's rpm/deb package uses jemalloc in default
abort_on_exception in detail
• It doesn't abort the whole process, actually
• it just re-raise errors in main thread
sleeping = false
Thread.abort_on_exception = true
Thread.new{ sleep 0.1 until sleeping ; raise "yay" }
begin
sleeping = true
sleep 5
rescue => e
p(here: "rescue in main thread", error: e)
end
p "foo!"
$ ruby example.rb
{:here=>"rescue in main thread", :error=>#<RuntimeError: yay>}
"foo!"
1 of 62

Recommended

The Patterns of Distributed Logging and Containers by
The Patterns of Distributed Logging and ContainersThe Patterns of Distributed Logging and Containers
The Patterns of Distributed Logging and ContainersSATOSHI TAGOMORI
24.9K views43 slides
Perfect Norikra 2nd Season by
Perfect Norikra 2nd SeasonPerfect Norikra 2nd Season
Perfect Norikra 2nd SeasonSATOSHI TAGOMORI
6.2K views37 slides
How to Make Norikra Perfect by
How to Make Norikra PerfectHow to Make Norikra Perfect
How to Make Norikra PerfectSATOSHI TAGOMORI
4.7K views29 slides
Open Source Software, Distributed Systems, Database as a Cloud Service by
Open Source Software, Distributed Systems, Database as a Cloud ServiceOpen Source Software, Distributed Systems, Database as a Cloud Service
Open Source Software, Distributed Systems, Database as a Cloud ServiceSATOSHI TAGOMORI
5.4K views39 slides
Fluentd Overview, Now and Then by
Fluentd Overview, Now and ThenFluentd Overview, Now and Then
Fluentd Overview, Now and ThenSATOSHI TAGOMORI
7.7K views69 slides
Distributed Stream Processing on Fluentd / #fluentd by
Distributed Stream Processing on Fluentd / #fluentdDistributed Stream Processing on Fluentd / #fluentd
Distributed Stream Processing on Fluentd / #fluentdSATOSHI TAGOMORI
6.3K views62 slides

More Related Content

What's hot

Stream Processing using Apache Spark and Apache Kafka by
Stream Processing using Apache Spark and Apache KafkaStream Processing using Apache Spark and Apache Kafka
Stream Processing using Apache Spark and Apache KafkaAbhinav Singh
3.8K views29 slides
Open Source Logging and Monitoring Tools by
Open Source Logging and Monitoring ToolsOpen Source Logging and Monitoring Tools
Open Source Logging and Monitoring ToolsPhase2
12.4K views86 slides
Introducing HerdDB - a distributed JVM embeddable database built upon Apache ... by
Introducing HerdDB - a distributed JVM embeddable database built upon Apache ...Introducing HerdDB - a distributed JVM embeddable database built upon Apache ...
Introducing HerdDB - a distributed JVM embeddable database built upon Apache ...StreamNative
464 views22 slides
Apache Kafka: New Features That You Might Not Know About by
Apache Kafka: New Features That You Might Not Know AboutApache Kafka: New Features That You Might Not Know About
Apache Kafka: New Features That You Might Not Know AboutYaroslav Tkachenko
4.9K views25 slides
Kafka Streams: the easiest way to start with stream processing by
Kafka Streams: the easiest way to start with stream processingKafka Streams: the easiest way to start with stream processing
Kafka Streams: the easiest way to start with stream processingYaroslav Tkachenko
6.6K views40 slides
Kafka Summit SF 2017 - Exactly-once Stream Processing with Kafka Streams by
Kafka Summit SF 2017 - Exactly-once Stream Processing with Kafka StreamsKafka Summit SF 2017 - Exactly-once Stream Processing with Kafka Streams
Kafka Summit SF 2017 - Exactly-once Stream Processing with Kafka Streamsconfluent
3.2K views68 slides

What's hot(20)

Stream Processing using Apache Spark and Apache Kafka by Abhinav Singh
Stream Processing using Apache Spark and Apache KafkaStream Processing using Apache Spark and Apache Kafka
Stream Processing using Apache Spark and Apache Kafka
Abhinav Singh3.8K views
Open Source Logging and Monitoring Tools by Phase2
Open Source Logging and Monitoring ToolsOpen Source Logging and Monitoring Tools
Open Source Logging and Monitoring Tools
Phase212.4K views
Introducing HerdDB - a distributed JVM embeddable database built upon Apache ... by StreamNative
Introducing HerdDB - a distributed JVM embeddable database built upon Apache ...Introducing HerdDB - a distributed JVM embeddable database built upon Apache ...
Introducing HerdDB - a distributed JVM embeddable database built upon Apache ...
StreamNative464 views
Apache Kafka: New Features That You Might Not Know About by Yaroslav Tkachenko
Apache Kafka: New Features That You Might Not Know AboutApache Kafka: New Features That You Might Not Know About
Apache Kafka: New Features That You Might Not Know About
Yaroslav Tkachenko4.9K views
Kafka Streams: the easiest way to start with stream processing by Yaroslav Tkachenko
Kafka Streams: the easiest way to start with stream processingKafka Streams: the easiest way to start with stream processing
Kafka Streams: the easiest way to start with stream processing
Yaroslav Tkachenko6.6K views
Kafka Summit SF 2017 - Exactly-once Stream Processing with Kafka Streams by confluent
Kafka Summit SF 2017 - Exactly-once Stream Processing with Kafka StreamsKafka Summit SF 2017 - Exactly-once Stream Processing with Kafka Streams
Kafka Summit SF 2017 - Exactly-once Stream Processing with Kafka Streams
confluent3.2K views
Power of the Log: LSM & Append Only Data Structures by confluent
Power of the Log: LSM & Append Only Data StructuresPower of the Log: LSM & Append Only Data Structures
Power of the Log: LSM & Append Only Data Structures
confluent6.8K views
LINE's messaging service architecture underlying more than 200 million monthl... by kawamuray
LINE's messaging service architecture underlying more than 200 million monthl...LINE's messaging service architecture underlying more than 200 million monthl...
LINE's messaging service architecture underlying more than 200 million monthl...
kawamuray2.1K views
A Unified Platform for Real-time Storage and Processing by StreamNative
A Unified Platform for Real-time Storage and ProcessingA Unified Platform for Real-time Storage and Processing
A Unified Platform for Real-time Storage and Processing
StreamNative3.4K views
Architecture of a Kafka camus infrastructure by mattlieber
Architecture of a Kafka camus infrastructureArchitecture of a Kafka camus infrastructure
Architecture of a Kafka camus infrastructure
mattlieber12.6K views
Fluentd v1.0 in a nutshell by N Masahiro
Fluentd v1.0 in a nutshellFluentd v1.0 in a nutshell
Fluentd v1.0 in a nutshell
N Masahiro16K views
Building event streaming pipelines using Apache Pulsar by StreamNative
Building event streaming pipelines using Apache PulsarBuilding event streaming pipelines using Apache Pulsar
Building event streaming pipelines using Apache Pulsar
StreamNative291 views
From Message to Cluster: A Realworld Introduction to Kafka Capacity Planning by confluent
From Message to Cluster: A Realworld Introduction to Kafka Capacity PlanningFrom Message to Cluster: A Realworld Introduction to Kafka Capacity Planning
From Message to Cluster: A Realworld Introduction to Kafka Capacity Planning
confluent558 views
Bullet: A Real Time Data Query Engine by DataWorks Summit
Bullet: A Real Time Data Query EngineBullet: A Real Time Data Query Engine
Bullet: A Real Time Data Query Engine
DataWorks Summit599 views
Multitenancy: Kafka clusters for everyone at LINE by kawamuray
Multitenancy: Kafka clusters for everyone at LINEMultitenancy: Kafka clusters for everyone at LINE
Multitenancy: Kafka clusters for everyone at LINE
kawamuray2K views
Apache Kafka, and the Rise of Stream Processing by Guozhang Wang
Apache Kafka, and the Rise of Stream ProcessingApache Kafka, and the Rise of Stream Processing
Apache Kafka, and the Rise of Stream Processing
Guozhang Wang1.7K views
Real-time streaming and data pipelines with Apache Kafka by Joe Stein
Real-time streaming and data pipelines with Apache KafkaReal-time streaming and data pipelines with Apache Kafka
Real-time streaming and data pipelines with Apache Kafka
Joe Stein48.1K views
When apache pulsar meets apache flink by StreamNative
When apache pulsar meets apache flinkWhen apache pulsar meets apache flink
When apache pulsar meets apache flink
StreamNative2.3K views

Viewers also liked

Modern Black Mages Fighting in the Real World by
Modern Black Mages Fighting in the Real WorldModern Black Mages Fighting in the Real World
Modern Black Mages Fighting in the Real WorldSATOSHI TAGOMORI
9.4K views81 slides
Fighting API Compatibility On Fluentd Using "Black Magic" by
Fighting API Compatibility On Fluentd Using "Black Magic"Fighting API Compatibility On Fluentd Using "Black Magic"
Fighting API Compatibility On Fluentd Using "Black Magic"SATOSHI TAGOMORI
7.4K views71 slides
20160730 fluentd meetup in matsue slide by
20160730 fluentd meetup in matsue slide20160730 fluentd meetup in matsue slide
20160730 fluentd meetup in matsue slidecosmo0920
4.9K views51 slides
AWSにおけるバッチ処理の ベストプラクティス - Developers.IO Meetup 05 by
AWSにおけるバッチ処理の ベストプラクティス - Developers.IO Meetup 05AWSにおけるバッチ処理の ベストプラクティス - Developers.IO Meetup 05
AWSにおけるバッチ処理の ベストプラクティス - Developers.IO Meetup 05都元ダイスケ Miyamoto
54.2K views21 slides
To Have Own Data Analytics Platform, Or NOT To by
To Have Own Data Analytics Platform, Or NOT ToTo Have Own Data Analytics Platform, Or NOT To
To Have Own Data Analytics Platform, Or NOT ToSATOSHI TAGOMORI
8.3K views30 slides
Ruby and Distributed Storage Systems by
Ruby and Distributed Storage SystemsRuby and Distributed Storage Systems
Ruby and Distributed Storage SystemsSATOSHI TAGOMORI
13.3K views43 slides

Viewers also liked(8)

Modern Black Mages Fighting in the Real World by SATOSHI TAGOMORI
Modern Black Mages Fighting in the Real WorldModern Black Mages Fighting in the Real World
Modern Black Mages Fighting in the Real World
SATOSHI TAGOMORI9.4K views
Fighting API Compatibility On Fluentd Using "Black Magic" by SATOSHI TAGOMORI
Fighting API Compatibility On Fluentd Using "Black Magic"Fighting API Compatibility On Fluentd Using "Black Magic"
Fighting API Compatibility On Fluentd Using "Black Magic"
SATOSHI TAGOMORI7.4K views
20160730 fluentd meetup in matsue slide by cosmo0920
20160730 fluentd meetup in matsue slide20160730 fluentd meetup in matsue slide
20160730 fluentd meetup in matsue slide
cosmo09204.9K views
AWSにおけるバッチ処理の ベストプラクティス - Developers.IO Meetup 05 by 都元ダイスケ Miyamoto
AWSにおけるバッチ処理の ベストプラクティス - Developers.IO Meetup 05AWSにおけるバッチ処理の ベストプラクティス - Developers.IO Meetup 05
AWSにおけるバッチ処理の ベストプラクティス - Developers.IO Meetup 05
To Have Own Data Analytics Platform, Or NOT To by SATOSHI TAGOMORI
To Have Own Data Analytics Platform, Or NOT ToTo Have Own Data Analytics Platform, Or NOT To
To Have Own Data Analytics Platform, Or NOT To
SATOSHI TAGOMORI8.3K views
Ruby and Distributed Storage Systems by SATOSHI TAGOMORI
Ruby and Distributed Storage SystemsRuby and Distributed Storage Systems
Ruby and Distributed Storage Systems
SATOSHI TAGOMORI13.3K views
Fluentd v0.14 Plugin API Details by SATOSHI TAGOMORI
Fluentd v0.14 Plugin API DetailsFluentd v0.14 Plugin API Details
Fluentd v0.14 Plugin API Details
SATOSHI TAGOMORI21.6K views
Distributed Logging Architecture in Container Era by SATOSHI TAGOMORI
Distributed Logging Architecture in Container EraDistributed Logging Architecture in Container Era
Distributed Logging Architecture in Container Era
SATOSHI TAGOMORI6.9K views

Similar to How To Write Middleware In Ruby

Node.js: The What, The How and The When by
Node.js: The What, The How and The WhenNode.js: The What, The How and The When
Node.js: The What, The How and The WhenFITC
4.6K views40 slides
Metarhia: Node.js Macht Frei by
Metarhia: Node.js Macht FreiMetarhia: Node.js Macht Frei
Metarhia: Node.js Macht FreiTimur Shemsedinov
1.3K views41 slides
Debugging the Web with Fiddler by
Debugging the Web with FiddlerDebugging the Web with Fiddler
Debugging the Web with FiddlerIdo Flatow
976 views36 slides
Scaling asp.net websites to millions of users by
Scaling asp.net websites to millions of usersScaling asp.net websites to millions of users
Scaling asp.net websites to millions of usersoazabir
65.2K views35 slides
Monitoring and Scaling Redis at DataDog - Ilan Rabinovitch, DataDog by
 Monitoring and Scaling Redis at DataDog - Ilan Rabinovitch, DataDog Monitoring and Scaling Redis at DataDog - Ilan Rabinovitch, DataDog
Monitoring and Scaling Redis at DataDog - Ilan Rabinovitch, DataDogRedis Labs
2K views76 slides

Similar to How To Write Middleware In Ruby(20)

Node.js: The What, The How and The When by FITC
Node.js: The What, The How and The WhenNode.js: The What, The How and The When
Node.js: The What, The How and The When
FITC4.6K views
Debugging the Web with Fiddler by Ido Flatow
Debugging the Web with FiddlerDebugging the Web with Fiddler
Debugging the Web with Fiddler
Ido Flatow976 views
Scaling asp.net websites to millions of users by oazabir
Scaling asp.net websites to millions of usersScaling asp.net websites to millions of users
Scaling asp.net websites to millions of users
oazabir65.2K views
Monitoring and Scaling Redis at DataDog - Ilan Rabinovitch, DataDog by Redis Labs
 Monitoring and Scaling Redis at DataDog - Ilan Rabinovitch, DataDog Monitoring and Scaling Redis at DataDog - Ilan Rabinovitch, DataDog
Monitoring and Scaling Redis at DataDog - Ilan Rabinovitch, DataDog
Redis Labs2K views
Building and Scaling Node.js Applications by Ohad Kravchick
Building and Scaling Node.js ApplicationsBuilding and Scaling Node.js Applications
Building and Scaling Node.js Applications
Ohad Kravchick1.7K views
The server side story: Parallel and Asynchronous programming in .NET - ITPro... by Panagiotis Kanavos
The server side story:  Parallel and Asynchronous programming in .NET - ITPro...The server side story:  Parallel and Asynchronous programming in .NET - ITPro...
The server side story: Parallel and Asynchronous programming in .NET - ITPro...
Become a Performance Diagnostics Hero by TechWell
Become a Performance Diagnostics HeroBecome a Performance Diagnostics Hero
Become a Performance Diagnostics Hero
TechWell146 views
Cascading introduction by Alex Su
Cascading introductionCascading introduction
Cascading introduction
Alex Su1.3K views
introduction to node.js by orkaplan
introduction to node.jsintroduction to node.js
introduction to node.js
orkaplan3.7K views
Brk3288 sql server v.next with support on linux, windows and containers was... by Bob Ward
Brk3288 sql server v.next with support on linux, windows and containers   was...Brk3288 sql server v.next with support on linux, windows and containers   was...
Brk3288 sql server v.next with support on linux, windows and containers was...
Bob Ward752 views
Top Java Performance Problems and Metrics To Check in Your Pipeline by Andreas Grabner
Top Java Performance Problems and Metrics To Check in Your PipelineTop Java Performance Problems and Metrics To Check in Your Pipeline
Top Java Performance Problems and Metrics To Check in Your Pipeline
Andreas Grabner2K views
OSDC 2016 - Unifying Logs and Metrics Data with Elastic Beats by Monica Sarbu by NETWAYS
OSDC 2016 - Unifying Logs and Metrics Data with Elastic Beats by Monica SarbuOSDC 2016 - Unifying Logs and Metrics Data with Elastic Beats by Monica Sarbu
OSDC 2016 - Unifying Logs and Metrics Data with Elastic Beats by Monica Sarbu
NETWAYS148 views
How bol.com makes sense of its logs, using the Elastic technology stack. by Renzo Tomà
How bol.com makes sense of its logs, using the Elastic technology stack.How bol.com makes sense of its logs, using the Elastic technology stack.
How bol.com makes sense of its logs, using the Elastic technology stack.
Renzo Tomà1.1K views
Introduction to Node.js by Richard Lee
Introduction to Node.jsIntroduction to Node.js
Introduction to Node.js
Richard Lee2K views
GemStone/S @ Vienna University by ESUG
GemStone/S @ Vienna University GemStone/S @ Vienna University
GemStone/S @ Vienna University
ESUG536 views

More from SATOSHI TAGOMORI

Ractor's speed is not light-speed by
Ractor's speed is not light-speedRactor's speed is not light-speed
Ractor's speed is not light-speedSATOSHI TAGOMORI
4.6K views26 slides
Good Things and Hard Things of SaaS Development/Operations by
Good Things and Hard Things of SaaS Development/OperationsGood Things and Hard Things of SaaS Development/Operations
Good Things and Hard Things of SaaS Development/OperationsSATOSHI TAGOMORI
830 views23 slides
Maccro Strikes Back by
Maccro Strikes BackMaccro Strikes Back
Maccro Strikes BackSATOSHI TAGOMORI
7.8K views33 slides
Invitation to the dark side of Ruby by
Invitation to the dark side of RubyInvitation to the dark side of Ruby
Invitation to the dark side of RubySATOSHI TAGOMORI
15.4K views31 slides
Hijacking Ruby Syntax in Ruby (RubyConf 2018) by
Hijacking Ruby Syntax in Ruby (RubyConf 2018)Hijacking Ruby Syntax in Ruby (RubyConf 2018)
Hijacking Ruby Syntax in Ruby (RubyConf 2018)SATOSHI TAGOMORI
11.1K views53 slides
Make Your Ruby Script Confusing by
Make Your Ruby Script ConfusingMake Your Ruby Script Confusing
Make Your Ruby Script ConfusingSATOSHI TAGOMORI
1.4K views18 slides

More from SATOSHI TAGOMORI(19)

Ractor's speed is not light-speed by SATOSHI TAGOMORI
Ractor's speed is not light-speedRactor's speed is not light-speed
Ractor's speed is not light-speed
SATOSHI TAGOMORI4.6K views
Good Things and Hard Things of SaaS Development/Operations by SATOSHI TAGOMORI
Good Things and Hard Things of SaaS Development/OperationsGood Things and Hard Things of SaaS Development/Operations
Good Things and Hard Things of SaaS Development/Operations
SATOSHI TAGOMORI830 views
Invitation to the dark side of Ruby by SATOSHI TAGOMORI
Invitation to the dark side of RubyInvitation to the dark side of Ruby
Invitation to the dark side of Ruby
SATOSHI TAGOMORI15.4K views
Hijacking Ruby Syntax in Ruby (RubyConf 2018) by SATOSHI TAGOMORI
Hijacking Ruby Syntax in Ruby (RubyConf 2018)Hijacking Ruby Syntax in Ruby (RubyConf 2018)
Hijacking Ruby Syntax in Ruby (RubyConf 2018)
SATOSHI TAGOMORI11.1K views
Lock, Concurrency and Throughput of Exclusive Operations by SATOSHI TAGOMORI
Lock, Concurrency and Throughput of Exclusive OperationsLock, Concurrency and Throughput of Exclusive Operations
Lock, Concurrency and Throughput of Exclusive Operations
SATOSHI TAGOMORI2K views
Data Processing and Ruby in the World by SATOSHI TAGOMORI
Data Processing and Ruby in the WorldData Processing and Ruby in the World
Data Processing and Ruby in the World
SATOSHI TAGOMORI6.5K views
Planet-scale Data Ingestion Pipeline: Bigdam by SATOSHI TAGOMORI
Planet-scale Data Ingestion Pipeline: BigdamPlanet-scale Data Ingestion Pipeline: Bigdam
Planet-scale Data Ingestion Pipeline: Bigdam
SATOSHI TAGOMORI6.3K views
Technologies, Data Analytics Service and Enterprise Business by SATOSHI TAGOMORI
Technologies, Data Analytics Service and Enterprise BusinessTechnologies, Data Analytics Service and Enterprise Business
Technologies, Data Analytics Service and Enterprise Business
SATOSHI TAGOMORI5K views
Overview of data analytics service: Treasure Data Service by SATOSHI TAGOMORI
Overview of data analytics service: Treasure Data ServiceOverview of data analytics service: Treasure Data Service
Overview of data analytics service: Treasure Data Service
SATOSHI TAGOMORI2.6K views
Data Analytics Service Company and Its Ruby Usage by SATOSHI TAGOMORI
Data Analytics Service Company and Its Ruby UsageData Analytics Service Company and Its Ruby Usage
Data Analytics Service Company and Its Ruby Usage
SATOSHI TAGOMORI21K views
Tale of ISUCON and Its Bench Tools by SATOSHI TAGOMORI
Tale of ISUCON and Its Bench ToolsTale of ISUCON and Its Bench Tools
Tale of ISUCON and Its Bench Tools
SATOSHI TAGOMORI5.7K views
Data Analytics Service Company and Its Ruby Usage by SATOSHI TAGOMORI
Data Analytics Service Company and Its Ruby UsageData Analytics Service Company and Its Ruby Usage
Data Analytics Service Company and Its Ruby Usage
SATOSHI TAGOMORI8.9K views
Data-Driven Development Era and Its Technologies by SATOSHI TAGOMORI
Data-Driven Development Era and Its TechnologiesData-Driven Development Era and Its Technologies
Data-Driven Development Era and Its Technologies
SATOSHI TAGOMORI8.6K views

Recently uploaded

Sprint 226 by
Sprint 226Sprint 226
Sprint 226ManageIQ
11 views18 slides
Understanding HTML terminology by
Understanding HTML terminologyUnderstanding HTML terminology
Understanding HTML terminologyartembondar5
7 views8 slides
20231129 - Platform @ localhost 2023 - Application-driven infrastructure with... by
20231129 - Platform @ localhost 2023 - Application-driven infrastructure with...20231129 - Platform @ localhost 2023 - Application-driven infrastructure with...
20231129 - Platform @ localhost 2023 - Application-driven infrastructure with...sparkfabrik
8 views46 slides
Programming Field by
Programming FieldProgramming Field
Programming Fieldthehardtechnology
6 views9 slides
EV Charging App Case by
EV Charging App Case EV Charging App Case
EV Charging App Case iCoderz Solutions
9 views1 slide
Airline Booking Software by
Airline Booking SoftwareAirline Booking Software
Airline Booking SoftwareSharmiMehta
9 views26 slides

Recently uploaded(20)

Sprint 226 by ManageIQ
Sprint 226Sprint 226
Sprint 226
ManageIQ11 views
Understanding HTML terminology by artembondar5
Understanding HTML terminologyUnderstanding HTML terminology
Understanding HTML terminology
artembondar57 views
20231129 - Platform @ localhost 2023 - Application-driven infrastructure with... by sparkfabrik
20231129 - Platform @ localhost 2023 - Application-driven infrastructure with...20231129 - Platform @ localhost 2023 - Application-driven infrastructure with...
20231129 - Platform @ localhost 2023 - Application-driven infrastructure with...
sparkfabrik8 views
Airline Booking Software by SharmiMehta
Airline Booking SoftwareAirline Booking Software
Airline Booking Software
SharmiMehta9 views
Top-5-production-devconMunich-2023-v2.pptx by Tier1 app
Top-5-production-devconMunich-2023-v2.pptxTop-5-production-devconMunich-2023-v2.pptx
Top-5-production-devconMunich-2023-v2.pptx
Tier1 app6 views
Navigating container technology for enhanced security by Niklas Saari by Metosin Oy
Navigating container technology for enhanced security by Niklas SaariNavigating container technology for enhanced security by Niklas Saari
Navigating container technology for enhanced security by Niklas Saari
Metosin Oy14 views
Quality Engineer: A Day in the Life by John Valentino
Quality Engineer: A Day in the LifeQuality Engineer: A Day in the Life
Quality Engineer: A Day in the Life
John Valentino7 views
Dapr Unleashed: Accelerating Microservice Development by Miroslav Janeski
Dapr Unleashed: Accelerating Microservice DevelopmentDapr Unleashed: Accelerating Microservice Development
Dapr Unleashed: Accelerating Microservice Development
Miroslav Janeski13 views
AI and Ml presentation .pptx by FayazAli87
AI and Ml presentation .pptxAI and Ml presentation .pptx
AI and Ml presentation .pptx
FayazAli8714 views
Ports-and-Adapters Architecture for Embedded HMI by Burkhard Stubert
Ports-and-Adapters Architecture for Embedded HMIPorts-and-Adapters Architecture for Embedded HMI
Ports-and-Adapters Architecture for Embedded HMI
Burkhard Stubert29 views
Dev-HRE-Ops - Addressing the _Last Mile DevOps Challenge_ in Highly Regulated... by TomHalpin9
Dev-HRE-Ops - Addressing the _Last Mile DevOps Challenge_ in Highly Regulated...Dev-HRE-Ops - Addressing the _Last Mile DevOps Challenge_ in Highly Regulated...
Dev-HRE-Ops - Addressing the _Last Mile DevOps Challenge_ in Highly Regulated...
TomHalpin96 views
DRYiCE™ iAutomate: AI-enhanced Intelligent Runbook Automation by HCLSoftware
DRYiCE™ iAutomate: AI-enhanced Intelligent Runbook AutomationDRYiCE™ iAutomate: AI-enhanced Intelligent Runbook Automation
DRYiCE™ iAutomate: AI-enhanced Intelligent Runbook Automation
HCLSoftware6 views
Gen Apps on Google Cloud PaLM2 and Codey APIs in Action by Márton Kodok
Gen Apps on Google Cloud PaLM2 and Codey APIs in ActionGen Apps on Google Cloud PaLM2 and Codey APIs in Action
Gen Apps on Google Cloud PaLM2 and Codey APIs in Action
Márton Kodok16 views

How To Write Middleware In Ruby

  • 1. How To Write Middleware in Ruby 2016/12/02 RubyConf Taiwan Day 1 Satoshi Tagomori (@tagomoris)
  • 2. Satoshi "Moris" Tagomori (@tagomoris) Fluentd, MessagePack-Ruby, Norikra, ... Treasure Data, Inc.
  • 3. http://www.fluentd.org/ open source data collector for unified logging layer.
  • 4. LOG script to parse data cron job for loading filtering script syslog script Tweet- fetching script aggregation script aggregation script script to parse data rsync server FILE LOG FILE ✓ Parse/Format data ✓ Buffering & Retries ✓ Load balancing ✓ Failover Before After
  • 6. Middleware? : Fluentd • Long running daemon process • Compatibility for API, behavior and configuration files • Multi platform / environment support • Linux, Mac and Windows(!) • Baremetal servers, Virtual machines, Containers • Many use cases • Various data, Various data formats, Unexpected errors • Various traffic - small to huge
  • 7. • Long running daemon process • Compatibility for API, behavior and configuration files • Multi platform / environment support • Linux, Mac and Windows(!) • Ruby, JRuby?, Rubinius? • Baremetal servers, Virtual machines, Containers • Many use cases • Various data, Various data formats, Unexpected errors • Various traffic - small to huge Middleware? Batches: Minutes - Hours
  • 8. • Long running daemon process • Compatibility for API, behavior and configuration files • Multi platform / environment support • Linux, Mac and Windows(!) • Ruby, JRuby?, Rubinius? • Baremetal servers, Virtual machines, Containers • Many use cases • Various data, Various data formats, Unexpected errors • Various traffic - small to huge Middleware? Providing APIs and/or Client Libraries
  • 9. • Long running daemon process • Compatibility for API, behavior and configuration files • Multi platform / environment support • Linux, Mac and Windows(!) • Ruby, JRuby?, Rubinius? • Baremetal servers, Virtual machines, Containers • Many use cases • Various data, Various data formats, Unexpected errors • Various traffic - small to huge Middleware? Daily Development & Deployment Providing Client Tools
  • 10. • Long running daemon process • Compatibility for API, behavior and configuration files • Multi platform / environment support • Linux, Mac and Windows(!) • Ruby, JRuby?, Rubinius? • Baremetal servers, Virtual machines, Containers • Many use cases • Various data, Various data formats, Unexpected errors • Various traffic - small to huge Middleware? Make Your Application Stable
  • 11. • Long running daemon process • Compatibility for API, behavior and configuration files • Multi platform / environment support • Linux, Mac and Windows(!) • Ruby, JRuby?, Rubinius? • Baremetal servers, Virtual machines, Containers • Many use cases • Various data, Various data formats, Unexpected errors • Various traffic - small to huge Middleware? Make Your Application Fast and Scalable
  • 12. Case studies from development of Fluentd • Platform: Linux, Mac and Windows • Resource: Memory usage and malloc • Resource and Stability: Handling JSON • Stability: Threads and exceptions
  • 14. Linux and Mac: Thread/process scheduling • Both are UNIX-like systems... • Mac (development), Linux (production) • Test code must run on both! • CI services provide multi-environment support • Fluentd uses Travis CI :D • Travis CI provides "os" option: "linux" & "osx" • Important tests to be written: Threading
  • 15. class MyTest < ::Test::Unit::TestCase test 'yay 1' do data = [] thr = Thread.new do data << "line 1" end data << "line 2" assert_equal ["line 1", "line 2"], data end end class MyTest < ::Test::Unit::TestCase test 'client sends 2 data' do list = [] thr = Thread.new do # Mock server TCPServer.open("127.0.0.1", 2048) do |server| while sock = server.accept list << sock.read.chomp end end end 2.times do |i| TCPSocket.open("127.0.0.1", 2048) do |client| client.write "data #{i}" end end assert_equal(["data 0", "data 1"], list) end end
  • 16. Loaded suite example Started F =========================================================================================== Failure: test: client sends 2 data(MyTest) example.rb:22:in `block in <class:MyTest>' 19: end 20: end 21: => 22: assert_equal(["data 0", "data 1"], list) 23: end 24: end <["data 0", "data 1"]> expected but was <["data 0"]> diff: ["data 0", "data 1"] =========================================================================================== Finished in 0.007253 seconds. ------------------------------------------------------------------------------------------- 1 tests, 1 assertions, 1 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications 0% passed ------------------------------------------------------------------------------------------- 137.87 tests/s, 137.87 assertions/s Mac OS X (10.11.16)
  • 17. class MyTest < ::Test::Unit::TestCase test 'yay 1' do data = [] thr = Thread.new do data << "line 1" end data << "line 2" assert_equal ["line 1", "line 2"], data end end class MyTest < ::Test::Unit::TestCase test 'client sends 2 data' do list = [] thr = Thread.new do # Mock server TCPServer.open("127.0.0.1", 2048) do |server| while sock = server.accept list << sock.read.chomp end end end 2.times do |i| TCPSocket.open("127.0.0.1", 2048) do |client| client.write "data #{i}" end end assert_equal(["data 0", "data 1"], list) end end
  • 18. class MyTest < ::Test::Unit::TestCase test 'yay 1' do data = [] thr = Thread.new do data << "line 1" end data << "line 2" assert_equal ["line 1", "line 2"], data end end class MyTest < ::Test::Unit::TestCase test 'client sends 2 data' do list = [] thr = Thread.new do # Mock server TCPServer.open("127.0.0.1", 2048) do |server| listening = true while sock = server.accept list << sock.read.chomp end end end 2.times do |i| TCPSocket.open("127.0.0.1", 2048) do |client| client.write "data #{i}" end end sleep 1 assert_equal(["data 0", "data 1"], list) end end
  • 19. Loaded suite example Started . Finished in 1.002745 seconds. -------------------------------------------------------------------------------------------- 1 tests, 1 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications 100% passed -------------------------------------------------------------------------------------------- 1.00 tests/s, 1.00 assertions/s Mac OS X (10.11.16)
  • 20. Loaded suite example Started E ================================================================================================= Error: test: client sends 2 data(MyTest): Errno::ECONNREFUSED: Connection refused - connect(2) for "127.0.0.1" port 2048 example.rb:16:in `initialize' example.rb:16:in `open' example.rb:16:in `block (2 levels) in <class:MyTest>' example.rb:15:in `times' example.rb:15:in `block in <class:MyTest>' ================================================================================================= Finished in 0.005918197 seconds. ------------------------------------------------------------------------------------------------- 1 tests, 0 assertions, 0 failures, 1 errors, 0 pendings, 0 omissions, 0 notifications 0% passed ------------------------------------------------------------------------------------------------- 168.97 tests/s, 0.00 assertions/s Linux (Ubuntu 16.04)
  • 21. class MyTest < ::Test::Unit::TestCase test 'yay 1' do data = [] thr = Thread.new do data << "line 1" end data << "line 2" assert_equal ["line 1", "line 2"], data end end class MyTest < ::Test::Unit::TestCase test 'client sends 2 data' do list = [] thr = Thread.new do # Mock server TCPServer.open("127.0.0.1", 2048) do |server| listening = true while sock = server.accept list << sock.read.chomp end end end 2.times do |i| TCPSocket.open("127.0.0.1", 2048) do |client| client.write "data #{i}" end end sleep 1 assert_equal(["data 0", "data 1"], list) end end
  • 22. class MyTest < ::Test::Unit::TestCase test 'yay 1' do data = [] thr = Thread.new do data << "line 1" end data << "line 2" assert_equal ["line 1", "line 2"], data end end class MyTest < ::Test::Unit::TestCase test 'client sends 2 data' do list = [] thr = Thread.new do # Mock server TCPServer.open("127.0.0.1", 2048) do |server| listening = true while sock = server.accept list << sock.read.chomp end end end 2.times do |i| TCPSocket.open("127.0.0.1", 2048) do |client| client.write "data #{i}" end end sleep 1 assert_equal(["data 0", "data 1"], list) end end
  • 23. class MyTest < ::Test::Unit::TestCase test 'yay 1' do data = [] thr = Thread.new do data << "line 1" end data << "line 2" assert_equal ["line 1", "line 2"], data end end class MyTest < ::Test::Unit::TestCase test 'client sends 2 data' do list = [] thr = Thread.new do # Mock server TCPServer.open("127.0.0.1", 2048) do |server| listening = true while sock = server.accept list << sock.read.chomp end end end 2.times do |i| TCPSocket.open("127.0.0.1", 2048) do |client| client.write "data #{i}" end end sleep 1 assert_equal(["data 0", "data 1"], list) end end
  • 24. class MyTest < ::Test::Unit::TestCase test 'yay 1' do data = [] thr = Thread.new do data << "line 1" end data << "line 2" assert_equal ["line 1", "line 2"], data end end require 'socket' class MyTest < ::Test::Unit::TestCase test 'client sends 2 data' do list = [] listening = false thr = Thread.new do # Mock server TCPServer.open("127.0.0.1", 2048) do |server| listening = true while sock = server.accept list << sock.read.chomp end end end sleep 0.1 until listening 2.times do |i| TCPSocket.open("127.0.0.1", 2048) do |client| client.write "data #{i}" end end require 'timeout' Timeout.timeout(3){ sleep 0.1 until list.size >= 2 } assert_equal(["data 0", "data 1"], list) end end
  • 25. *NIX and Windows: fork-exec and spawn • Windows: another thread scheduling :( • daemonize: • double fork (or Process.daemon) on *nix • spawn on Windows • Execute one another process: • fork & exec on *nix • spawn on Windows • CI on Windows: AppVeyor
  • 26. Lesson 1: Run Tests on All Platforms Supported
  • 28. Memory Usage: Object leak • Temp values must leak in long running process • 1,000 objects / hour
 => 8,760,000 objects / year • Some solutions: • In-process GC • Storage with TTL • (External storages: Redis, ...) module MyDaemon class Process def hour_key Time.now.to_i / 3600 end def hourly_store @map[hour_key] ||= {} end def put(key, value) hourly_store[key] = value end def get(key) hourly_store[key] end # add # of data per hour def read_data(table_name, data) key = "records_of_#{table_name}" put(key, get(key) + data.size) end end
  • 29. Lesson 2: Make Sure to Collect Garbages
  • 31. Formatting Data Into JSON • Fluentd handles JSON in many use cases • both of parsing and generating • it consumes much CPU time... • JSON, Yajl and Oj • JSON: ruby standard library • Yajl (yajl-ruby): ruby binding of YAJL (SAX-based) • Oj (oj): Optimized JSON
  • 32. class MyTest < ::Test::Unit::TestCase test 'yay 1' do data = [] thr = Thread.new do data << "line 1" end data << "line 2" assert_equal ["line 1", "line 2"], data end end require 'json'; require 'yajl'; require 'oj' Oj.default_options = {bigdecimal_load: :float, mode: :compat, use_to_json: true} module MyDaemon class Json def initialize(mode) klass = case mode when :json then JSON when :yajl then Yajl when :oj then Oj end @proc = klass.method(:dump) end def dump(data); @proc.call(data); end end end require 'benchmark' N = 500_000 obj = {"message" => "a"*100, "100" => 100, "pi" => 3.14159, "true" => true} Benchmark.bm{|x| x.report("json") { formatter = MyDaemon::Json.new(:json) N.times{ formatter.dump(obj) } } x.report("yajl") { formatter = MyDaemon::Json.new(:yajl) N.times{ formatter.dump(obj) } } x.report("oj") { formatter = MyDaemon::Json.new(:oj) N.times{ formatter.dump(obj) } } }
  • 33. $ ruby example2.rb user system total real json 3.870000 0.050000 3.920000 ( 4.005429) yajl 2.940000 0.030000 2.970000 ( 2.998924) oj 1.130000 0.020000 1.150000 ( 1.152596) # for 500_000 objects Mac OS X (10.11.16) Ruby 2.3.1 yajl-ruby 1.3.0 oj 2.18.0
  • 34. Speed is not only thing: APIs for unstable I/O • JSON and Oj have only ".load" • it raises parse error for: • incomplete JSON string • additional bytes after JSON string • Yajl has stream parser: very useful for servers • method to feed input data • callback for parsed objects
  • 35. class MyTest < ::Test::Unit::TestCase test 'yay 1' do data = [] thr = Thread.new do data << "line 1" end data << "line 2" assert_equal ["line 1", "line 2"], data end end require 'oj' Oj.load('{"message":"this is ') # Oj::ParseError Oj.load('{"message":"this is a pen."}') # => Hash Oj.load('{"message":"this is a pen."}{"messa"') # Oj::ParseError
  • 36. Speed is not only thing: APIs for unstable I/O • JSON and Oj have only ".load" • it raises parse error for: • incomplete JSON string • additional bytes after JSON string • Yajl has stream parser: very useful for servers • method to feed input data • callback for parsed objects
  • 37. class MyTest < ::Test::Unit::TestCase test 'yay 1' do data = [] thr = Thread.new do data << "line 1" end data << "line 2" assert_equal ["line 1", "line 2"], data end end require 'yajl' parsed_objs = [] parser = Yajl::Parser.new parser.on_parse_complete = ->(obj){ parsed_objs << obj } parse << '{"message":"aaaaaaaaaaaaaaa' parse << 'aaaaaaaaa"}{"message"' # on_parse_complete is called parse << ':"bbbbbbbbb"' parse << '}' # on_parse_complete is called again
  • 38. class MyTest < ::Test::Unit::TestCase test 'yay 1' do data = [] thr = Thread.new do data << "line 1" end data << "line 2" assert_equal ["line 1", "line 2"], data end end require 'socket' require 'oj' TCPServer.open(port) do |server| while sock = server.accept begin buf = "" while input = sock.readpartial(1024) buf << input # can we feed this value to Oj.load ? begin obj = Oj.load(buf) # never succeeds if buf has 2 objects call_method(obj) buf = "" rescue Oj::ParseError # try with next input ... end end rescue EOFError sock.close rescue nil end end end
  • 39. class MyTest < ::Test::Unit::TestCase test 'yay 1' do data = [] thr = Thread.new do data << "line 1" end data << "line 2" assert_equal ["line 1", "line 2"], data end end require 'socket' require 'yajl' TCPServer.open(port) do |server| while sock = server.accept begin parser = Yajl::Parser.new parser.on_parse_complete = ->(obj){ call_method(obj) } while input = sock.readpartial(1024) parser << input end rescue EOFError sock.close rescue nil end end end
  • 42. Thread in Ruby • GVL(GIL): Giant VM Lock (Global Interpreter Lock) • Just one thread in many threads can run at a time • Ruby VM can use only 1 CPU core • Thread in I/O is *not* running • I/O threads can run in parallel threads in I/O running threads • We can write network servers in Ruby!
  • 43. class MyTest < ::Test::Unit::TestCase test 'yay 1' do data = [] thr = Thread.new do data << "line 1" end data << "line 2" assert_equal ["line 1", "line 2"], data end end class MyTestCase < ::Test::Unit::TestCase test 'sent data should be received' do received = [] sent = [] listening = false th1 = Thread.new do TCPServer.open("127.0.0.1", 2048) do |server| listening = true while sock = server.accepto received << sock.read end end end sleep 0.1 until listening ["foo", "bar"].each do |str| begin TCPSocket.open("127.0.0.1", 2048) do |client| client.write "data #{i}" end sent << str rescue => e # ignore end end assert_equal sent, received end end
  • 44. Loaded suite example7 Started . Finished in 0.104729 seconds. ------------------------------------------------------------------------------------------- 1 tests, 1 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications 100% passed ------------------------------------------------------------------------------------------- 9.55 tests/s, 9.55 assertions/s
  • 45. class MyTestCase < ::Test::Unit::TestCase test 'sent data should be received' do received = [] sent = [] listening = false th1 = Thread.new do TCPServer.open("127.0.0.1", 2048) do |server| listening = true while sock = server.accepto received << sock.read end end end sleep 0.1 until listening ["foo", "bar"].each do |str| begin TCPSocket.open("127.0.0.1", 2048) do |client| client.write "data #{i}" end sent << str rescue => e # ignore end end assert_equal sent, received end end
  • 46. class MyTestCase < ::Test::Unit::TestCase test 'sent data should be received' do received = [] sent = [] listening = false th1 = Thread.new do TCPServer.open("127.0.0.1", 2048) do |server| listening = true while sock = server.accepto received << sock.read end end end sleep 0.1 until listening ["foo", "bar"].each do |str| begin TCPSocket.open("127.0.0.1", 2048) do |client| client.write "data #{i}" end sent << str rescue => e # ignore end end assert_equal sent, received end end
  • 47. class MyTestCase < ::Test::Unit::TestCase test 'sent data should be received' do received = [] sent = [] listening = false th1 = Thread.new do TCPServer.open("127.0.0.1", 2048) do |server| listening = true while sock = server.accepto received << sock.read end end end sleep 0.1 until listening ["foo", "bar"].each do |str| begin TCPSocket.open("127.0.0.1", 2048) do |client| client.write "data #{i}" end sent << str rescue => e # ignore end end assert_equal sent, received end end
  • 48. class MyTestCase < ::Test::Unit::TestCase test 'sent data should be received' do received = [] sent = [] listening = false th1 = Thread.new do TCPServer.open("127.0.0.1", 2048) do |server| listening = true while sock = server.accepto received << sock.read end end end sleep 0.1 until listening ["foo", "bar"].each do |str| begin TCPSocket.open("127.0.0.1", 2048) do |client| client.write "data #{i}" end sent << str rescue => e # ignore end end assert_equal sent, received end end
  • 49. class MyTestCase < ::Test::Unit::TestCase test 'sent data should be received' do received = [] sent = [] listening = false th1 = Thread.new do TCPServer.open("127.0.0.1", 2048) do |server| listening = true while sock = server.accepto received << sock.read end end end sleep 0.1 until listening ["foo", "bar"].each do |str| begin TCPSocket.open("127.0.0.1", 2048) do |client| client.write "data #{i}" end sent << str rescue => e # ignore end end assert_equal sent, received end end
  • 50. class MyTestCase < ::Test::Unit::TestCase test 'sent data should be received' do received = [] sent = [] listening = false th1 = Thread.new do TCPServer.open("127.0.0.1", 2048) do |server| listening = true while sock = server.accepto received << sock.read end end end sleep 0.1 until listening ["foo", "bar"].each do |str| begin TCPSocket.open("127.0.0.1", 2048) do |client| client.write "data #{i}" end sent << str rescue => e # ignore end end assert_equal sent, received # [] == [] end end
  • 51. Thread in Ruby: Methods for errors • Threads will die silently if any errors are raised • abort_on_exception • raise error in threads on main thread if true • required to make sure not to create false success (silent crash) • report_on_exception • warn errors in threads if true (2.4 feature)
  • 52. class MyTestCase < ::Test::Unit::TestCase test 'sent data should be received' do received = [] sent = [] listening = false th1 = Thread.new do Thread.current.abort_on_exception = true TCPServer.open("127.0.0.1", 2048) do |server| listening = true while sock = server.accepto received << sock.read end end end sleep 0.1 until listening ["foo", "bar"].each do |str| begin TCPSocket.open("127.0.0.1", 2048) do |client| client.write "data #{i}" end sent << str rescue => e # ignore end end assert_equal sent, received # [] == [] end end
  • 53. Loaded suite example7 Started E =========================================================================================== Error: test: sent data should be received(MyTestCase): NoMethodError: undefined method `accepto' for #<TCPServer:(closed)> Did you mean? accept example7.rb:14:in `block (3 levels) in <class:MyTestCase>' example7.rb:12:in `open' example7.rb:12:in `block (2 levels) in <class:MyTestCase>' =========================================================================================== Finished in 0.0046 seconds. ------------------------------------------------------------------------------------------- 1 tests, 0 assertions, 0 failures, 1 errors, 0 pendings, 0 omissions, 0 notifications 0% passed ------------------------------------------------------------------------------------------- 217.39 tests/s, 0.00 assertions/s sleeping = false Thread.abort_on_exception = true Thread.new{ sleep 0.1 until sleeping ; raise "yay" } begin sleeping = true sleep 5 rescue => e p(here: "rescue in main thread", error: e) end p "foo!"
  • 54. Thread in Ruby: Process crash from errors in threads • Middleware SHOULD NOT crash as far as possible :) • An error from a TCP connection MUST NOT crash the whole process • Many points to raise errors... • Socket I/O, Executing commands • Parsing HTTP requests, Parsing JSON (or other formats) • Process • should crash in tests, but • should not in production
  • 55. Thread in Ruby: What needed in your code about threads • Set Thread#abort_on_exception = true • for almost all threads... • "rescue" all errors in threads • to log these errors, and not to crash whole process • "raise" rescued errors again only in testing • to make tests failed for bugs
  • 58. Writing Middleware: • Taking care about: • various platforms and environment • Resource usage and stability • Requiring to know about: • Ruby's features • Ruby VM's behavior • Library implementation • In different viewpoint from writing applications!
  • 59. Write your code, like middleware :D Make it efficient & stable! Thank you! @tagomoris
  • 60. Loaded suite example Started F =========================================================================================== Failure: test: client sends 2 data(MyTest) example.rb:22:in `block in <class:MyTest>' 19: end 20: end 21: => 22: assert_equal(["data 0", "data 1"], list) 23: end 24: end <["data 0", "data 1"]> expected but was <["data 0", "data 1"]> diff: ["data 0", "data 1"] =========================================================================================== Finished in 0.009425 seconds. ------------------------------------------------------------------------------------------- 1 tests, 1 assertions, 1 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications 0% passed ------------------------------------------------------------------------------------------- 106.10 tests/s, 106.10 assertions/s Mac OS X (10.11.16)
  • 61. Memory Usage: Memory fragmentation • High memory usage, low # of objects • memory fragmentation? • glibc malloc: weak for fine-grained memory allocation and multi threading • Switching to jemalloc by LD_PRELOAD • FreeBSD standard malloc (available on Linux) • fluentd's rpm/deb package uses jemalloc in default
  • 62. abort_on_exception in detail • It doesn't abort the whole process, actually • it just re-raise errors in main thread sleeping = false Thread.abort_on_exception = true Thread.new{ sleep 0.1 until sleeping ; raise "yay" } begin sleeping = true sleep 5 rescue => e p(here: "rescue in main thread", error: e) end p "foo!" $ ruby example.rb {:here=>"rescue in main thread", :error=>#<RuntimeError: yay>} "foo!"