| 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:
script/ferret_server -e production start
script/ferret_server -e production stop
# 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
disconnects the db connection for the class specified by class_name used only in unit tests to check the automatic reconnection feature
# 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
# 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.
# 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
# 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
# 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
# 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
# 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
# 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