How do I capture a rsolr Sunspot exception raised on a different thread from a model callback?

99封情书 提交于 2019-12-11 02:59:28

问题


Even though I can capture the exception raised from @post.save and log the error and print the threads

begin
  if @post.save

rescue Exception => ex
# rescue Errno::ECONNREFUSED => ex
  Thread.list.each {|t| p t}
  Rails.logger.error "ERROR: Could not save blog post! Is the Solr server up and running? Exception: #{ex}"

it still errors out on the web page and doesn't show any of my code in the stack trace. The solr Sunspot model callback is running on a separate thread.

rsolr (1.0.9) lib/rsolr/connection.rb:19:in `rescue in execute'
rsolr (1.0.9) lib/rsolr/connection.rb:14:in `execute'
...
sunspot_rails (2.1.0) lib/sunspot/rails/solr_instrumentation.rb:15:in `send_and_receive_with_as_instrumentation'
(eval):2:in `post'
rsolr (1.0.9) lib/rsolr/client.rb:67:in `update'
...
/usr/local/rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/webrick/httpserver.rb:138:in `service'
/usr/local/rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/webrick/httpserver.rb:94:in `run'
/usr/local/rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/webrick/server.rb:295:in `block in start_thread'

Notice block in start_thread?

So how can I capture this exception and display an error to the user? I don't think the whole app should crash just because Solr isn't running.

I found this, http://makandracards.com/makandra/5273-using-solr-with-sunspot, but it only describes how to capture search exceptions, not index/update exceptions.


回答1:


Wow. After what seems like 20 hours... create lib/rsolr/connection.rb:

require 'net/http'
require 'net/https'

# The default/Net::Http adapter for RSolr.
class RSolr::Connection

  # using the request_context hash,
  # send a request,
  # then return the standard rsolr response hash {:status, :body, :headers}
  def execute client, request_context
    h = http request_context[:uri], request_context[:proxy], request_context[:read_timeout], request_context[:open_timeout]
    request = setup_raw_request request_context
    request.body = request_context[:data] if request_context[:method] == :post and request_context[:data]
    begin
      response = h.request request
      charset = response.type_params["charset"]
      {:status => response.code.to_i, :headers => response.to_hash, :body => force_charset(response.body, charset)}
    rescue Errno::ECONNREFUSED => e
      Rails.logger.error "ERROR: #execute: Could not connect to Solr: #{e.message}"
      # How to display an error message to the user? 
      # ActionController::Base.flash.now.alert "Could not connect to search indexer."
      # Maybe http://stackoverflow.com/questions/393395/how-to-call-expire-fragment-from-rails-observer-model/608700#608700 ?
      return nil
      # raise(Errno::ECONNREFUSED.new(request_context.inspect))
    # catch the undefined closed? exception -- this is a confirmed ruby bug
    rescue NoMethodError
      Rails.logger.error "ERROR: #execute: NoMethodError: Could not connect to Solr: #{e.message}"
      return nil
      # $!.message == "undefined method `closed?' for nil:NilClass" ?
        # raise(Errno::ECONNREFUSED.new) :
        # raise($!)
    end
  end

  protected

  # This returns a singleton of a Net::HTTP or Net::HTTP.Proxy request object.
  def http uri, proxy = nil, read_timeout = nil, open_timeout = nil
    @http ||= (
      http = if proxy
        proxy_user, proxy_pass = proxy.userinfo.split(/:/) if proxy.userinfo
        Net::HTTP.Proxy(proxy.host, proxy.port, proxy_user, proxy_pass).new uri.host, uri.port
      else
        Net::HTTP.new uri.host, uri.port
      end
      http.use_ssl = uri.port == 443 || uri.instance_of?(URI::HTTPS)
      http.read_timeout = read_timeout if read_timeout
      http.open_timeout = open_timeout if open_timeout
      http
    )
  end

  #
  def setup_raw_request request_context
    http_method = case request_context[:method]
    when :get
      Net::HTTP::Get
    when :post
      Net::HTTP::Post
    when :head
      Net::HTTP::Head
    else
      raise "Only :get, :post and :head http method types are allowed."
    end
    headers = request_context[:headers] || {}
    raw_request = http_method.new request_context[:uri].request_uri
    raw_request.initialize_http_header headers
    raw_request.basic_auth(request_context[:uri].user, request_context[:uri].password) if request_context[:uri].user && request_context[:uri].password
    raw_request
  end

  private

  def force_charset body, charset
    return body unless charset and body.respond_to?(:force_encoding)
    body.force_encoding(charset)
  end

end


来源:https://stackoverflow.com/questions/22704241/how-do-i-capture-a-rsolr-sunspot-exception-raised-on-a-different-thread-from-a-m

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!