Skip to content

Commit

Permalink
Merge pull request brynary#10 from oggy/sphinx
Browse files Browse the repository at this point in the history
Sphinx panel, with AJAX support
  • Loading branch information
pboling committed Sep 23, 2012
2 parents 5f009c9 + be89aa4 commit 07765b3
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 51 deletions.
1 change: 1 addition & 0 deletions lib/rack/insight/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class << self
'Dalli::Client' => [:instance, :perform] } },
:active_record => {:probes => {'ActiveRecord' => [:class, :allocate]}},
# :log_panel => The log panel configures its probes in its initializer
:sphinx => {:probes => {'Riddle::Client' => [:instance, :request]}},
:sql => {:probes => Hash[%w{ PostgreSQLAdapter MysqlAdapter SQLiteAdapter
Mysql2Adapter OracleEnhancedAdapter }.map do |adapter|
["ActiveRecord::ConnectionAdapters::#{adapter}", [:instance, :execute]]
Expand Down
36 changes: 12 additions & 24 deletions lib/rack/insight/panels/sphinx_panel.rb
Original file line number Diff line number Diff line change
@@ -1,39 +1,27 @@
module Rack::Insight

class SphinxPanel < Panel
require "rack/insight/panels/sphinx_panel/sphinx_extension"
require "rack/insight/panels/sphinx_panel/stats"

self.has_table = false

def self.record(*sphinx_command_args, &block)
return block.call unless Rack::Insight.enabled?

start_time = Time.now
result = block.call
total_time = Time.now - start_time
stats.record_call(total_time * 1_000, sphinx_command_args)
return result
def request_start(env, start)
@stats = Stats.new
end

def self.reset
Thread.current["rack-insight.sphinx"] = Stats.new
def request_finish(env, status, headers, body, timing)
store(env, @stats)
@stats = nil
end

def self.stats
Thread.current["rack-insight.sphinx"] ||= Stats.new
def after_detect(method_call, timing, args, message)
@stats.record_call(timing.duration, args, method_call)
end

def heading
"Sphinx: %.2fms (#{self.class.stats.queries.size} calls)" % self.class.stats.time
def heading_for_request(number)
stats = retrieve(number).first
"Sphinx: %.2fms (#{stats.queries.size} calls)" % stats.time
end

def content
result = render_template "panels/sphinx", :stats => self.class.stats
self.class.reset
return result
def content_for_request(number)
render_template "panels/sphinx", :stats => retrieve(number).first
end

end

end
158 changes: 131 additions & 27 deletions lib/rack/insight/panels/sphinx_panel/stats.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,28 @@ class SphinxPanel

class Stats
class Query
include Rack::Insight::FilteredBacktrace

require 'riddle/client'
MatchModes = Riddle::Client::MatchModes.invert
RankModes = Riddle::Client::RankModes.invert
SortModes = Riddle::Client::SortModes.invert
AttributeTypes = Riddle::Client::AttributeTypes.invert
GroupFunctions = Riddle::Client::GroupFunctions.invert
FilterTypes = Riddle::Client::FilterTypes.invert

attr_reader :time
attr_reader :command

def initialize(time, *command_args)
def initialize(time, command_args, method_call)
riddle_command, messages = *command_args
@time = time
if command_args.flatten.first == :search
@command = "search: " + decode_message(command_args.first.flatten.last).collect{|k,v| "#{k} => #{v}"}.join(", ")
if riddle_command == :search
@command = "search: " + decode_message(messages.first).inspect
else
@command = command_args.flatten.first.to_s + ": No more info is available for this Sphinx request type"
@command = command_args.inspect + ": No more info is available for this Sphinx request type"
end
@backtrace = method_call.backtrace
end

def display_time
Expand All @@ -23,43 +35,136 @@ def decode_message(m)
@m = m.clone
params = ActiveSupport::OrderedHash.new

# Mode, Limits
params[:offset] = consume_int
params[:limit] = consume_int
params[:match_mode] = consume_int
params[:rank_mode] = consume_int
params[:sort_mode] = consume_int
params[:sort_by] = consume_string
params[:match_mode] = MatchModes[consume_int]

# Ranking
params[:rank_mode] = RankModes[consume_int]
if params[:rank_mode] == :expr
params[:rank_expr] = consume_string
end

# Sort Mode
params[:sorting] = {
mode: SortModes[consume_int],
by: consume_string,
}

# Query
params[:query] = consume_string
wl = consume_int
weights = []
wl.times do weights << consume_int end
params[:weights] = weights

# Weights
params[:weights] = (1..consume_int).map { consume_int }

# Index
params[:index] = consume_string

consume_string
# ID Range
consume_int
params[:id_range] = consume_64int..consume_64int

# Filters
params[:filters] = (1..consume_int).map do
attribute = consume_string
type = FilterTypes[consume_int]
values =
case type
when :values
(1..consume_int).map { consume_64int }
when :range
consume_64int..consume_64int
when :float_range
consume_float..consume_float
end
exclude = consume_int
{attribute: attribute, values: values, exclude: exclude}
end

# Grouping
params[:group] = {
function: GroupFunctions[consume_int],
by: consume_string,
max_matches: consume_int,
clause: consume_string,
retry: {cutoff: consume_int, count: consume_int, delay: consume_int},
distinct: consume_string,
}

# Anchor Point
if consume_int == 0
params[:anchor] = nil
else
params[:anchor] = {
attributes: [consume_string, consume_string],
values: [consume_int, consume_int],
}
end

# Per Index Weights
per_index_weights = params[:per_index_weights] = {}
(1..consume_int).each do |key, value|
key = consume_string
value = consume_int
per_index_weights[key] = value
end

# Max Query Time
params[:max_query_time] = consume_int

# Per Field Weights
per_field_weights = params[:per_field_weights] = {}
(1..consume_int).each do |key, value|
key = consume_string
value = consume_int
per_field_weights[key] = value
end

params[:comments] = consume_string

return params if Riddle::Client::Versions[:search] < 0x116

# Overrides
overrides = params[:overrides] = {}
(1..consume_int).each do
key = consume_string
type = AttributeTypes[consume_int]
method =
case type
when :float
:consume_float
when :bigint
:consume_64int
else
:consume_int
end
values = (1..consume_int).map { send(method) }
overrides[key] = values
end

params[:select] = consume_string

@m.empty? or
params[:unknown] = @m

params[:id_range] = [consume_64int, consume_64int]
params
end

def consume_int
i = @m.unpack("N").first
@m = @m.slice(4, @m.length - 4)
i
@m.slice!(0, 4).unpack("N").first
end

def consume_64int
i = @m.unpack("NN").first
@m = @m.slice(8, @m.length - 8)
i
@m.slice!(0, 8).unpack("NN").first
end

def consume_float
@m.slice!(0, 4).unpack('N').pack('L*').unpack('f').first
end

def consume_string
len = consume_int
s = @m.slice(0, len)
@m = @m.slice(len, @m.length - len)
s
@m.slice!(0, consume_int)
end
end

Expand All @@ -72,9 +177,8 @@ def initialize
@time = 0.0
end

def record_call(time, *command_args)

@queries << Query.new(time, command_args)
def record_call(time, command_args, method_call)
@queries << Query.new(time, command_args, method_call)
@calls += 1
@time += time
end
Expand Down
14 changes: 14 additions & 0 deletions lib/rack/insight/views/panels/sphinx.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<tr>
<th>Time&nbsp;(ms)</th>
<th>Command</th>
<th class="backtrace"></th>
</tr>
</thead>
<tbody>
Expand All @@ -24,7 +25,20 @@
<tr class="<%= i % 2 == 0 ? "even" : "odd" %>">
<td><%= query.display_time %></td>
<td><%= query.command %></td>
<td><%= "<a href='#' class='reveal_backtrace'>Show Backtrace</a>" if query.has_backtrace? %></td>
</tr>
<% if query.has_backtrace? %>
<tr style="display:none">
<td></td>
<td colspan="2">
<ul>
<% query.filtered_backtrace.each do |line| %>
<li><%=h line %></li>
<% end %>
</ul>
</td>
</tr>
<% end %>
<% i += 1 %>
<% end %>
</tbody>
Expand Down

0 comments on commit 07765b3

Please sign in to comment.