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}"