1 # -*- encoding: binary -*-
4 # This is highly experimental!
6 # A self-contained Rack application for aggregating in the
7 # +tcpi_last_data_recv+ field in +struct+ +tcp_info+ defined in
8 # +/usr/include/linux/tcp.h+. This is only useful for \Linux 2.6 and later.
9 # This primarily supports Unicorn and derived servers, but may also be
10 # used with any Ruby web server using the core TCPServer class in Ruby.
12 # Hitting the Rack endpoint configured for this application will return
13 # a an ASCII histogram response body with the following headers:
15 # - X-Count - number of requests received
17 # The following headers are only present if X-Count is greater than one.
19 # - X-Min - lowest last_data_recv time recorded (in milliseconds)
20 # - X-Max - highest last_data_recv time recorded (in milliseconds)
21 # - X-Mean - mean last_data_recv time recorded (rounded, in milliseconds)
22 # - X-Std-Dev - standard deviation of last_data_recv times
23 # - X-Outliers-Low - number of low outliers (hopefully many!)
24 # - X-Outliers-High - number of high outliers (hopefully zero!)
26 # == To use with Unicorn and derived servers (preload_app=false):
28 # Put the following in our Unicorn config file (not config.ru):
30 # require "raindrops/last_data_recv"
32 # Then follow the instructions below for config.ru:
34 # == To use with any Rack server using TCPServer
36 # Setup a route for Raindrops::LastDataRecv in your Rackup config file
37 # (typically config.ru):
40 # map "/raindrops/last_data_recv" do
41 # run Raindrops::LastDataRecv.new
50 # == To use with any other Ruby web server that uses TCPServer
52 # Put the following in any piece of Ruby code loaded after the server has
53 # bound its TCP listeners:
55 # ObjectSpace.each_object(TCPServer) do |s|
56 # s.extend Raindrops::Aggregate::LastDataRecv
60 # Raindrops::Aggregate::LastDataRecv.default_aggregate.master_loop
63 # Then follow the above instructions for config.ru
65 class Raindrops::LastDataRecv
70 agg = Raindrops::Aggregate::LastDataRecv.default_aggregate
71 AGGREGATE_THREAD = Thread.new { agg.master_loop }
75 def initialize(opts = {})
76 if defined?(Unicorn::HttpServer::LISTENERS)
77 Raindrops::Aggregate::LastDataRecv.cornify!
80 opts[:aggregate] || Raindrops::Aggregate::LastDataRecv.default_aggregate
87 "Content-Type" => "text/plain",
88 "X-Count" => count.to_s,
91 headers["X-Min"] = a.min.to_s
92 headers["X-Max"] = a.max.to_s
93 headers["X-Mean"] = a.mean.round.to_s
94 headers["X-Std-Dev"] = a.std_dev.round.to_s
95 headers["X-Outliers-Low"] = a.outliers_low.to_s
96 headers["X-Outliers-High"] = a.outliers_high.to_s
99 headers["Content-Length"] = body.size.to_s
100 [ 200, headers, [ body ] ]