Class ActsAsFerret::Remote::Server
In: lib/ferret_server.rb
Parent: Object

This class acts as a drb server listening for indexing and search requests from models declared to ‘acts_as_ferret :remote => true‘

Usage:

  • modify RAILS_ROOT/config/ferret_server.yml to suit your needs.
  • environments for which no section in the config file exists will use the index locally (good for unit tests/development mode)
  • run script/ferret_server to start the server:

script/ferret_server -e production start

  • to stop the server run

script/ferret_server -e production stop

Methods

Included Modules

ActsAsFerret::Remote::UnixDaemon

Public Class methods

[Source]

    # File lib/ferret_server.rb, line 69
69:       def initialize
70:         ActiveRecord::Base.allow_concurrency = true
71:         require 'ar_mysql_auto_reconnect_patch'
72:         @cfg = ActsAsFerret::Remote::Config.new
73:         ActiveRecord::Base.logger = @logger = Logger.new(@cfg.log_file)
74:         ActiveRecord::Base.logger.level = Logger.const_get(@cfg.log_level.upcase) rescue Logger::DEBUG
75:         if @cfg.script
76:           path = File.join(RAILS_ROOT, @cfg.script) 
77:           load path
78:           @logger.info "loaded custom startup script from #{path}"
79:         end
80:       end

Public Instance methods

disconnects the db connection for the class specified by class_name used only in unit tests to check the automatic reconnection feature

[Source]

     # File lib/ferret_server.rb, line 171
171:       def db_disconnect!(class_name)
172:         with_class class_name do |clazz|
173:           clazz.connection.disconnect!
174:         end
175:       end

make sure we have a versioned index in place, building one if necessary

[Source]

     # File lib/ferret_server.rb, line 160
160:       def ensure_index_exists(index_name)
161:         @logger.debug "DRb server: ensure_index_exists for index #{index_name}"
162:         definition = ActsAsFerret::get_index(index_name).index_definition
163:         dir = definition[:index_dir]
164:         unless File.directory?(dir) && File.file?(File.join(dir, 'segments')) && dir =~ %r{/\d+(_\d+)?$}
165:           rebuild_index(index_name)
166:         end
167:       end

handles all incoming method calls, and sends them on to the correct local index instance.

Calls are not queued, so this will block until the call returned.

[Source]

     # File lib/ferret_server.rb, line 112
112:       def method_missing(name, *args)
113:         @logger.debug "\#method_missing(#{name.inspect}, #{args.inspect})"
114: 
115: 
116:         index_name = args.shift
117:         index = if name.to_s =~ /^multi_(.+)/
118:           name = $1
119:           ActsAsFerret::multi_index(index_name)
120:         else
121:           ActsAsFerret::get_index(index_name)
122:         end
123: 
124:         if index.nil?
125:           @logger.error "\#index with name #{index_name} not found in call to #{name} with args #{args.inspect}"
126:           raise ActsAsFerret::IndexNotDefined.new(index_name)
127:         end
128: 
129: 
130:         # TODO find another way to implement the reconnection logic (maybe in
131:         # local_index or class_methods)
132:         #  reconnect_when_needed(clazz) do
133:         
134:         # using respond_to? here so we not have to catch NoMethodError
135:         # which would silently catch those from deep inside the indexing
136:         # code, too...
137: 
138:         if index.respond_to?(name)
139:           index.send name, *args
140:         # TODO check where we need this:
141:         #elsif clazz.respond_to?(name)
142:         #      @logger.debug "no luck, trying to call class method instead"
143:         #      clazz.send name, *args
144:         else
145:           raise NoMethodError.new("method #{name} not supported by DRb server")
146:         end
147:       rescue => e
148:         @logger.error "ferret server error #{$!}\n#{$!.backtrace.join "\n"}"
149:         raise e
150:       end

hides LocalIndex#rebuild_index to implement index versioning

[Source]

     # File lib/ferret_server.rb, line 178
178:       def rebuild_index(index_name)
179:         definition = ActsAsFerret::get_index(index_name).index_definition.dup
180:         models = definition[:registered_models]
181:         index = new_index_for(definition)
182:         # TODO fix reconnection stuff
183:         #  reconnect_when_needed(clazz) do
184:         #    @logger.debug "DRb server: rebuild index for class(es) #{models.inspect} in #{index.options[:path]}"
185:         index.index_models models
186:         #  end
187:         new_version = File.join definition[:index_base_dir], Time.now.utc.strftime('%Y%m%d%H%M%S')
188:         # create a unique directory name (needed for unit tests where 
189:         # multiple rebuilds per second may occur)
190:         if File.exists?(new_version)
191:           i = 0
192:           i+=1 while File.exists?("#{new_version}_#{i}")
193:           new_version << "_#{i}"
194:         end
195:           
196:         File.rename index.options[:path], new_version
197:         ActsAsFerret::change_index_dir index_name, new_version 
198:       end

[Source]

     # File lib/ferret_server.rb, line 152
152:       def register_class(class_name)
153:         @logger.debug "############ registerclass #{class_name}"
154:         class_name.constantize
155:         @logger.debug "index for class #{class_name}: #{ActsAsFerret::ferret_indexes[class_name.underscore.to_sym]}"
156: 
157:       end

run the server and block until it exits

[Source]

    # File lib/ferret_server.rb, line 91
91:       def run
92:         raise "ferret_server not configured for #{RAILS_ENV}" unless (@cfg.uri rescue nil)
93:         run_drb_service
94:       end

[Source]

     # File lib/ferret_server.rb, line 96
 96:       def run_drb_service
 97:         $stdout.puts("starting ferret server...")
 98:         self.class.running = true
 99:         DRb.start_service(@cfg.uri, self)
100:         DRb.thread.join
101:       rescue Exception => e
102:         @logger.error(e.to_s)
103:         raise
104:       end

start the server as a daemon process

[Source]

    # File lib/ferret_server.rb, line 84
84:       def start
85:         raise "ferret_server not configured for #{RAILS_ENV}" unless (@cfg.uri rescue nil)
86:         platform_daemon { run_drb_service }
87:       end

Protected Instance methods

[Source]

     # File lib/ferret_server.rb, line 224
224:         def new_index_for(index_definition)
225:           ferret_cfg = index_definition[:ferret].dup
226:           ferret_cfg.update :auto_flush  => false, 
227:                             :create      => true,
228:                             :field_infos => ActsAsFerret::field_infos(index_definition),
229:                             :path        => File.join(index_definition[:index_base_dir], 'rebuild')
230:           returning Ferret::Index::Index.new(ferret_cfg) do |i|
231:             i.batch_size = index_definition[:reindex_batch_size]
232:             i.logger = @logger
233:           end
234:         end

[Source]

     # File lib/ferret_server.rb, line 203
203:         def reconnect_when_needed(clazz)
204:           retried = false
205:           begin
206:             yield
207:           rescue ActiveRecord::StatementInvalid => e
208:             if e.message =~ /MySQL server has gone away/
209:               if retried
210:                 raise e
211:               else
212:                 @logger.info "StatementInvalid caught, trying to reconnect..."
213:                 clazz.connection.reconnect!
214:                 retried = true
215:                 retry
216:               end
217:             else
218:               @logger.error "StatementInvalid caught, but unsure what to do with it: #{e}"
219:               raise e
220:             end
221:           end
222:         end

[Validate]