null+****@clear*****
null+****@clear*****
2010年 6月 23日 (水) 11:43:52 JST
Kouhei Sutou 2010-06-23 02:43:52 +0000 (Wed, 23 Jun 2010)
New Revision: 7676d3b64780128a03cd501fa727a1105beb8cef
Log:
* add a munin plugin for query performance.
Added files:
data/munin/groonga_query_performance
Modified files:
data/munin/Makefile.am
Modified: data/munin/Makefile.am (+2 -1)
===================================================================
--- data/munin/Makefile.am 2010-06-22 13:38:30 +0000 (fbb58c7)
+++ data/munin/Makefile.am 2010-06-23 02:43:52 +0000 (6bfa8e3)
@@ -4,4 +4,5 @@ dist_munin_plugins_SCRIPTS = \
groonga_cpu_time \
groonga_status \
groonga_memory \
- groonga_n_records
+ groonga_n_records \
+ groonga_query_performance
Added: data/munin/groonga_query_performance (+133 -0) 100755
===================================================================
--- /dev/null
+++ data/munin/groonga_query_performance 2010-06-23 02:43:52 +0000 (8081d02)
@@ -0,0 +1,133 @@
+#!/usr/bin/env ruby
+
+#%# family=auto
+#%# capabilities=autoconf
+
+require 'English'
+require 'strscan'
+
+label = ENV["label"]
+
+ @ log_path = ENV["log_path"]
+
+command = ARGV.shift
+
+case command
+when "autoconf", "detect"
+ if @log_path.nil?
+ puts "no (query log file path isn't specified by env.log_path)"
+ exit(false)
+ end
+ unless File.readable?(@log_path)
+ puts "no (query log file isn't readable: <#{@log_path}>)"
+ exit(false)
+ end
+ puts "yes"
+ exit(true)
+when "config"
+ if label.nil?
+ title = "groonga: query performance"
+ else
+ title = "groonga: #{label}: query performance"
+ end
+ puts <<EOF
+graph_title #{title}
+graph_vlabel seconds
+graph_category groonga
+graph_info groonga query performance
+
+longest.label Longest
+average.label Average
+median.label Median
+EOF
+ exit(true)
+end
+
+class ReverseLineReader
+ def initialize(io)
+ @io = io
+ @io.seek(0, IO::SEEK_END)
+ @buffer = ""
+ @data = ""
+ end
+
+ def each
+ separator = $/
+ separator_length = separator.length
+ while read_to_buffer
+ loop do
+ index =****@buffe*****(separator, @buffer.length - 1 - separator_length)
+ break if index.nil? or index.zero?
+ last_line =****@buffe*****!((index + separator_length)..-1)
+ yield(last_line)
+ end
+ end
+ yield(@buffer) unles****@buffe*****?
+ end
+
+ private
+ BYTES_PER_READ = 4096
+ def read
+ position =****@io*****
+ if position < BYTES_PER_READ
+ bytes_per_read = position
+ else
+ bytes_per_read = BYTES_PER_READ
+ end
+
+ if bytes_per_read.zero?
+ @data.replace("")
+ else
+ @io.seek(-bytes_per_read, IO::SEEK_CUR)
+ @io.read(bytes_per_read, @data)
+ @io.seek(-bytes_per_read, IO::SEEK_CUR)
+ end
+
+ @data
+ end
+
+ def read_to_buffer
+ data = read
+ if data.empty?
+ false
+ else
+ @buffer.insert(0, data)
+ true
+ end
+ end
+end
+
+span = 60 * 5 # 5min
+mega = 1_000_000.0
+now = Time.now
+elapsed_times = []
+File.open(@log_path) do |log_file|
+ ReverseLineReader.new(log_file).each do |line|
+ case line
+ when /\A(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)\.(\d+)\|([\da-f])+\|<(\d+) rc=0$/
+ _, year, month, day, hour, minutes, seconds, milliseconds,
+ context, elapsed = $LAST_MATCH_INFO.to_a
+ time_stamp = Time.local(year, month, day,
+ hour, minutes, seconds, milliseconds)
+ difference = now - time_stamp
+ break if difference > span
+ elapsed_in_micro_seconds = elapsed.to_i / mega
+ elapsed_times << elapsed_in_micro_seconds
+ end
+ end
+end
+
+sorted_elapsed_times = elapsed_times.sort
+if sorted_elapsed_times.empty?
+ longest = 0
+ average = 0
+ median = 0
+else
+ longest = sorted_elapsed_times.last
+ average = sorted_elapsed_times.inject(&:+) / sorted_elapsed_times.size.to_f
+ median = sorted_elapsed_times[sorted_elapsed_times.size / 2]
+end
+
+puts "longest.value #{longest}"
+puts "average.value #{average}"
+puts "median.value #{median}"