sunspot_index_queue 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/MIT_LICENSE +20 -0
- data/README.rdoc +133 -0
- data/Rakefile +52 -0
- data/VERSION +1 -0
- data/lib/sunspot/index_queue.rb +168 -0
- data/lib/sunspot/index_queue/batch.rb +120 -0
- data/lib/sunspot/index_queue/entry.rb +137 -0
- data/lib/sunspot/index_queue/entry/active_record_impl.rb +140 -0
- data/lib/sunspot/index_queue/entry/data_mapper_impl.rb +122 -0
- data/lib/sunspot/index_queue/entry/mongo_impl.rb +276 -0
- data/lib/sunspot/index_queue/session_proxy.rb +111 -0
- data/lib/sunspot_index_queue.rb +5 -0
- data/spec/active_record_impl_spec.rb +44 -0
- data/spec/batch_spec.rb +118 -0
- data/spec/data_mapper_impl_spec.rb +37 -0
- data/spec/entry_impl_examples.rb +184 -0
- data/spec/entry_spec.rb +148 -0
- data/spec/index_queue_spec.rb +150 -0
- data/spec/integration_spec.rb +110 -0
- data/spec/mongo_impl_spec.rb +35 -0
- data/spec/session_proxy_spec.rb +174 -0
- data/spec/spec_helper.rb +94 -0
- data/sunspot_index_queue.gemspec +99 -0
- metadata +237 -0
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'sunspot/session_proxy/abstract_session_proxy'
|
2
|
+
|
3
|
+
module Sunspot
|
4
|
+
class IndexQueue
|
5
|
+
# This is a Sunspot::SessionProxy that works with the IndexQueue class. Most update requests will
|
6
|
+
# be added to the queue and be processed asynchronously. The exceptions are the +remove+ method with
|
7
|
+
# a block and the +remove_all+ method. These will send their commands directly to Solr since the queue
|
8
|
+
# cannot handle delete by query. You should avoid calling these methods
|
9
|
+
class SessionProxy < Sunspot::SessionProxy::AbstractSessionProxy
|
10
|
+
attr_reader :queue, :session
|
11
|
+
|
12
|
+
delegate :new_search, :search, :new_more_like_this, :more_like_this, :config, :to => :session
|
13
|
+
|
14
|
+
# Create a new session proxy for a particular queue (default to a queue for all classes bound to the
|
15
|
+
# default session configuration). You can specify the session argument if the session used for queries should be
|
16
|
+
# different than the one the queue is bound to.
|
17
|
+
def initialize (queue = nil, session = nil)
|
18
|
+
@queue = queue || IndexQueue.new
|
19
|
+
@session = session || @queue.session
|
20
|
+
end
|
21
|
+
|
22
|
+
# Does nothing in this implementation.
|
23
|
+
def batch
|
24
|
+
yield if block_given?
|
25
|
+
end
|
26
|
+
|
27
|
+
# Does nothing in this implementation.
|
28
|
+
def commit
|
29
|
+
# no op
|
30
|
+
end
|
31
|
+
|
32
|
+
# Does nothing in this implementation.
|
33
|
+
def commit_if_delete_dirty
|
34
|
+
# no op
|
35
|
+
end
|
36
|
+
|
37
|
+
# Does nothing in this implementation.
|
38
|
+
def commit_if_dirty
|
39
|
+
# no op
|
40
|
+
end
|
41
|
+
|
42
|
+
# Always returns false in this implementation.
|
43
|
+
def delete_dirty?
|
44
|
+
false
|
45
|
+
end
|
46
|
+
|
47
|
+
# Always returns false in this implementation.
|
48
|
+
def dirty?
|
49
|
+
false
|
50
|
+
end
|
51
|
+
|
52
|
+
# Queues up the index operation for later.
|
53
|
+
def index (*objects)
|
54
|
+
objects.flatten.each do |object|
|
55
|
+
queue.index(object)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Queues up the index operation for later.
|
60
|
+
def index! (*objects)
|
61
|
+
index(*objects)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Queues up the remove operation for later unless a block is passed. In that case it will
|
65
|
+
# be performed immediately.
|
66
|
+
def remove (*objects, &block)
|
67
|
+
if block
|
68
|
+
# Delete by query not supported by queue, so send to server
|
69
|
+
queue.session.remove(*objects, &block)
|
70
|
+
else
|
71
|
+
objects.flatten.each do |object|
|
72
|
+
queue.remove(object)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Queues up the remove operation for later unless a block is passed. In that case it will
|
78
|
+
# be performed immediately.
|
79
|
+
def remove! (*objects, &block)
|
80
|
+
if block
|
81
|
+
# Delete by query not supported by queue, so send to server
|
82
|
+
queue.session.remove!(*objects, &block)
|
83
|
+
else
|
84
|
+
remove(*objects)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Proxies remove_all to the queue session.
|
89
|
+
def remove_all (*classes)
|
90
|
+
# Delete by query not supported by queue, so send to server
|
91
|
+
queue.session.remove_all(*classes)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Proxies remove_all! to the queue session.
|
95
|
+
def remove_all! (*classes)
|
96
|
+
# Delete by query not supported by queue, so send to server
|
97
|
+
queue.session.remove_all!(*classes)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Queues up the index operation for later.
|
101
|
+
def remove_by_id (clazz, id)
|
102
|
+
queue.remove(:class => clazz, :id => id)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Queues up the index operation for later.
|
106
|
+
def remove_by_id! (clazz, id)
|
107
|
+
remove_by_id(clazz, id)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
|
+
require 'sqlite3'
|
3
|
+
require File.expand_path('../entry_impl_examples', __FILE__)
|
4
|
+
|
5
|
+
describe Sunspot::IndexQueue::Entry::ActiveRecordImpl do
|
6
|
+
|
7
|
+
before :all do
|
8
|
+
db_dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'tmp'))
|
9
|
+
Dir.mkdir(db_dir) unless File.exist?(db_dir)
|
10
|
+
db = File.join(db_dir, 'sunspot_index_queue_test.sqlite3')
|
11
|
+
ActiveRecord::Base.establish_connection("adapter" => "sqlite3", "database" => db)
|
12
|
+
Sunspot::IndexQueue::Entry.implementation = :active_record
|
13
|
+
Sunspot::IndexQueue::Entry::ActiveRecordImpl.create_table
|
14
|
+
end
|
15
|
+
|
16
|
+
after :all do
|
17
|
+
db_dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'tmp'))
|
18
|
+
db = File.join(db_dir, 'sunspot_index_queue_test.sqlite3')
|
19
|
+
ActiveRecord::Base.connection.disconnect!
|
20
|
+
File.delete(db) if File.exist?(db)
|
21
|
+
Dir.delete(db_dir) if File.exist?(db_dir) and Dir.entries(db_dir).reject{|f| f.match(/^\.+$/)}.empty?
|
22
|
+
Sunspot::IndexQueue::Entry.implementation = nil
|
23
|
+
end
|
24
|
+
|
25
|
+
let(:factory) do
|
26
|
+
factory = Object.new
|
27
|
+
def factory.create (attributes)
|
28
|
+
Sunspot::IndexQueue::Entry::ActiveRecordImpl.create!(attributes)
|
29
|
+
end
|
30
|
+
|
31
|
+
def factory.delete_all
|
32
|
+
Sunspot::IndexQueue::Entry::ActiveRecordImpl.delete_all
|
33
|
+
end
|
34
|
+
|
35
|
+
def factory.find (id)
|
36
|
+
Sunspot::IndexQueue::Entry::ActiveRecordImpl.find_by_id(id)
|
37
|
+
end
|
38
|
+
|
39
|
+
factory
|
40
|
+
end
|
41
|
+
|
42
|
+
it_should_behave_like "Entry implementation"
|
43
|
+
|
44
|
+
end
|
data/spec/batch_spec.rb
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe Sunspot::IndexQueue::Batch do
|
4
|
+
|
5
|
+
before :all do
|
6
|
+
Sunspot::IndexQueue::Entry.implementation = :mock
|
7
|
+
end
|
8
|
+
|
9
|
+
after :all do
|
10
|
+
Sunspot::IndexQueue::Entry.implementation = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
subject { Sunspot::IndexQueue::Batch.new(queue, [entry_1, entry_2]) }
|
14
|
+
let(:entry_1) { Sunspot::IndexQueue::Entry::MockImpl.new(:record => record_1, :delete => false) }
|
15
|
+
let(:entry_2) { Sunspot::IndexQueue::Entry::MockImpl.new(:record => record_2, :delete => true) }
|
16
|
+
let(:record_1) { Sunspot::IndexQueue::Test::Searchable.new(1) }
|
17
|
+
let(:record_2) { Sunspot::IndexQueue::Test::Searchable.new(2) }
|
18
|
+
let(:queue) { Sunspot::IndexQueue.new(:session => session) }
|
19
|
+
let(:session) { Sunspot::Session.new }
|
20
|
+
|
21
|
+
it "should submit all entries in a batch to Solr and commit them" do
|
22
|
+
entry_1.stub!(:record).and_return(record_1)
|
23
|
+
session.should_receive(:batch).and_yield
|
24
|
+
session.should_receive(:index).with(record_1)
|
25
|
+
session.should_receive(:remove_by_id).with(entry_2.record_class_name, entry_2.record_id)
|
26
|
+
session.should_receive(:commit)
|
27
|
+
Sunspot::IndexQueue::Entry.implementation.should_receive(:delete_entries).with([entry_1, entry_2])
|
28
|
+
subject.submit!
|
29
|
+
entry_1.processed?.should == true
|
30
|
+
entry_2.processed?.should == true
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should submit all entries individually and commit them if the batch errors out" do
|
34
|
+
entry_1.stub!(:record).and_return(record_1)
|
35
|
+
session.should_receive(:batch).and_yield
|
36
|
+
session.should_receive(:index).with(record_1).twice
|
37
|
+
session.should_receive(:remove_by_id).with(entry_2.record_class_name, entry_2.record_id).twice
|
38
|
+
session.should_receive(:commit).and_raise("boom")
|
39
|
+
session.should_receive(:commit)
|
40
|
+
Sunspot::IndexQueue::Entry.implementation.should_receive(:delete_entries).with([entry_1, entry_2])
|
41
|
+
subject.submit!
|
42
|
+
entry_1.processed?.should == true
|
43
|
+
entry_2.processed?.should == true
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
it "should add error messages to each entry that errors out" do
|
48
|
+
entry_1.stub!(:record).and_return(record_1)
|
49
|
+
error = StandardError.new("boom")
|
50
|
+
session.should_receive(:batch).and_yield
|
51
|
+
session.should_receive(:index).and_raise(error)
|
52
|
+
session.should_receive(:remove_by_id).with(entry_2.record_class_name, entry_2.record_id)
|
53
|
+
session.should_receive(:commit)
|
54
|
+
entry_1.should_receive(:set_error!).with(error, queue.retry_interval)
|
55
|
+
Sunspot::IndexQueue::Entry.implementation.should_receive(:delete_entries).with([entry_2])
|
56
|
+
subject.submit!
|
57
|
+
entry_1.processed?.should == false
|
58
|
+
entry_2.processed?.should == true
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should add error messages to all entries when a commit fails" do
|
62
|
+
entry_1.stub!(:record).and_return(record_1)
|
63
|
+
error = StandardError.new("boom")
|
64
|
+
session.should_receive(:batch).and_yield
|
65
|
+
session.should_receive(:index).with(record_1).twice
|
66
|
+
session.should_receive(:remove_by_id).with(entry_2.record_class_name, entry_2.record_id).twice
|
67
|
+
session.should_receive(:commit).twice.and_raise(error)
|
68
|
+
Sunspot::IndexQueue::Entry.implementation.should_not_receive(:delete_entries)
|
69
|
+
entry_1.should_receive(:set_error!).with(error, queue.retry_interval)
|
70
|
+
entry_2.should_receive(:set_error!).with(error, queue.retry_interval)
|
71
|
+
subject.submit!
|
72
|
+
entry_1.processed?.should == false
|
73
|
+
entry_2.processed?.should == false
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should silently ignore entries that no longer have a record" do
|
77
|
+
entry_1.stub!(:record).and_return(nil)
|
78
|
+
session.should_receive(:batch).and_yield
|
79
|
+
session.should_not_receive(:index)
|
80
|
+
session.should_receive(:remove_by_id).with(entry_2.record_class_name, entry_2.record_id)
|
81
|
+
session.should_receive(:commit)
|
82
|
+
Sunspot::IndexQueue::Entry.implementation.should_receive(:delete_entries).with([entry_1, entry_2])
|
83
|
+
subject.submit!
|
84
|
+
entry_1.processed?.should == true
|
85
|
+
entry_2.processed?.should == true
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should raise an error and reset entries if the Solr server is not responding for an entry" do
|
89
|
+
entry_1.stub!(:record).and_return(record_1)
|
90
|
+
error = Errno::ECONNREFUSED.new
|
91
|
+
session.should_receive(:batch).and_yield
|
92
|
+
session.should_receive(:index).with(record_1)
|
93
|
+
session.should_receive(:remove_by_id).with(entry_2.record_class_name, entry_2.record_id).and_raise(error)
|
94
|
+
session.should_not_receive(:commit)
|
95
|
+
Sunspot::IndexQueue::Entry.implementation.should_not_receive(:delete_entries)
|
96
|
+
entry_1.should_receive(:reset!)
|
97
|
+
entry_2.should_receive(:reset!)
|
98
|
+
lambda{subject.submit!}.should raise_error(Sunspot::IndexQueue::SolrNotResponding)
|
99
|
+
entry_1.processed?.should == false
|
100
|
+
entry_2.processed?.should == false
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should raise an error and reset entries if the Solr server is not responding on a commit" do
|
104
|
+
entry_1.stub!(:record).and_return(record_1)
|
105
|
+
error = Errno::ECONNREFUSED.new
|
106
|
+
session.should_receive(:batch).and_yield
|
107
|
+
session.should_receive(:index).with(record_1)
|
108
|
+
session.should_receive(:remove_by_id).with(entry_2.record_class_name, entry_2.record_id)
|
109
|
+
session.should_receive(:commit).and_raise(error)
|
110
|
+
Sunspot::IndexQueue::Entry.implementation.should_not_receive(:delete_entries)
|
111
|
+
entry_1.should_receive(:reset!)
|
112
|
+
entry_2.should_receive(:reset!)
|
113
|
+
lambda{subject.submit!}.should raise_error(Sunspot::IndexQueue::SolrNotResponding)
|
114
|
+
entry_1.processed?.should == false
|
115
|
+
entry_2.processed?.should == false
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
|
+
require 'sqlite3'
|
3
|
+
require 'dm-migrations'
|
4
|
+
require File.expand_path('../entry_impl_examples', __FILE__)
|
5
|
+
|
6
|
+
describe Sunspot::IndexQueue::Entry::DataMapperImpl do
|
7
|
+
|
8
|
+
before :all do
|
9
|
+
DataMapper.setup(:default, 'sqlite::memory:')
|
10
|
+
Sunspot::IndexQueue::Entry.implementation = :data_mapper
|
11
|
+
Sunspot::IndexQueue::Entry::DataMapperImpl.auto_migrate!
|
12
|
+
end
|
13
|
+
|
14
|
+
after :all do
|
15
|
+
Sunspot::IndexQueue::Entry.implementation = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
let(:factory) do
|
19
|
+
factory = Object.new
|
20
|
+
def factory.create (attributes)
|
21
|
+
Sunspot::IndexQueue::Entry::DataMapperImpl.create!(attributes)
|
22
|
+
end
|
23
|
+
|
24
|
+
def factory.delete_all
|
25
|
+
Sunspot::IndexQueue::Entry::DataMapperImpl.all.destroy!
|
26
|
+
end
|
27
|
+
|
28
|
+
def factory.find (id)
|
29
|
+
Sunspot::IndexQueue::Entry::DataMapperImpl.get(id)
|
30
|
+
end
|
31
|
+
|
32
|
+
factory
|
33
|
+
end
|
34
|
+
|
35
|
+
it_should_behave_like "Entry implementation"
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,184 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# Shared examples for Entry implementations. In order to use these examples, the example group should define
|
4
|
+
# a block for :factory that will create an entry when yielded to with a hash of attributes..
|
5
|
+
shared_examples_for "Entry implementation" do
|
6
|
+
|
7
|
+
after :each do
|
8
|
+
factory.delete_all
|
9
|
+
end
|
10
|
+
|
11
|
+
context "class methods" do
|
12
|
+
before :each do
|
13
|
+
test_class = "Sunspot::IndexQueue::Test::Searchable"
|
14
|
+
@entry_1 = factory.create('record_class_name' => test_class, 'record_id' => '1', 'is_delete' => false, 'priority' => 0, 'run_at' => Time.now.utc)
|
15
|
+
@entry_2 = factory.create('record_class_name' => test_class, 'record_id' => '2', 'is_delete' => false, 'priority' => 10, 'run_at' => Time.now.utc, 'error' => "boom!", 'attempts' => 1)
|
16
|
+
@entry_3 = factory.create('record_class_name' => "Object", 'record_id' => '3', 'is_delete' => false, 'priority' => 5, 'run_at' => Time.now.utc, 'error' => "boom!", 'attempts' => 1)
|
17
|
+
@entry_4 = factory.create('record_class_name' => test_class, 'record_id' => '4', 'is_delete' => true, 'priority' => 0, 'run_at' => Time.now.utc + 60)
|
18
|
+
@entry_5 = factory.create('record_class_name' => test_class, 'record_id' => '5', 'is_delete' => false, 'priority' => -10, 'run_at' => Time.now.utc - 60)
|
19
|
+
@entry_6 = factory.create('record_class_name' => test_class, 'record_id' => '6', 'is_delete' => false, 'priority' => 0, 'run_at' => Time.now.utc - 3600)
|
20
|
+
@entry_7 = factory.create('record_class_name' => test_class, 'record_id' => '7', 'is_delete' => true, 'priority' => 10, 'run_at' => Time.now.utc - 60)
|
21
|
+
end
|
22
|
+
|
23
|
+
context "without class_names filter" do
|
24
|
+
let(:queue) { Sunspot::IndexQueue.new(:batch_size => 3, :retry_interval => 5)}
|
25
|
+
|
26
|
+
it "should get the total_count" do
|
27
|
+
Sunspot::IndexQueue::Entry.implementation.total_count(queue).should == 7
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should get the ready_count" do
|
31
|
+
Sunspot::IndexQueue::Entry.implementation.ready_count(queue).should == 6
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should get the error_count" do
|
35
|
+
Sunspot::IndexQueue::Entry.implementation.error_count(queue).should == 2
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should get the errors" do
|
39
|
+
errors = Sunspot::IndexQueue::Entry.implementation.errors(queue, 2, 0)
|
40
|
+
errors.collect{|e| e.record_id}.sort.should == [@entry_2.record_id, @entry_3.record_id]
|
41
|
+
|
42
|
+
errors = Sunspot::IndexQueue::Entry.implementation.errors(queue, 1, 1)
|
43
|
+
errors.collect{|e| e.record_id}.sort.should == [@entry_3.record_id]
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should reset all entries" do
|
47
|
+
Sunspot::IndexQueue::Entry.implementation.reset!(queue)
|
48
|
+
Sunspot::IndexQueue::Entry.implementation.error_count(queue).should == 0
|
49
|
+
@entry_2 = factory.find(@entry_2.id)
|
50
|
+
@entry_2.error.should == nil
|
51
|
+
@entry_2.attempts.should == 0
|
52
|
+
|
53
|
+
@entry_3 = factory.find(@entry_3.id)
|
54
|
+
@entry_3.error.should == nil
|
55
|
+
@entry_3.attempts.should == 0
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should get the next_batch! by index time and priority" do
|
59
|
+
batch = Sunspot::IndexQueue::Entry.implementation.next_batch!(queue)
|
60
|
+
batch.collect{|e| e.record_id}.sort.should == [@entry_2.record_id, @entry_3.record_id, @entry_7.record_id]
|
61
|
+
batch = Sunspot::IndexQueue::Entry.implementation.next_batch!(queue)
|
62
|
+
batch.collect{|e| e.record_id}.sort.should == [@entry_1.record_id, @entry_5.record_id, @entry_6.record_id]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context "with class_names filter" do
|
67
|
+
let(:queue) { Sunspot::IndexQueue.new(:batch_size => 3, :retry_interval => 5, :class_names => "Sunspot::IndexQueue::Test::Searchable")}
|
68
|
+
|
69
|
+
it "should get the total_count" do
|
70
|
+
Sunspot::IndexQueue::Entry.implementation.total_count(queue).should == 6
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should get the ready_count" do
|
74
|
+
Sunspot::IndexQueue::Entry.implementation.ready_count(queue).should == 5
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should get the error_count" do
|
78
|
+
Sunspot::IndexQueue::Entry.implementation.error_count(queue).should == 1
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should get the errors" do
|
82
|
+
errors = Sunspot::IndexQueue::Entry.implementation.errors(queue, 2, 0)
|
83
|
+
errors.collect{|e| e.record_id}.sort.should == [@entry_2.record_id]
|
84
|
+
errors = Sunspot::IndexQueue::Entry.implementation.errors(queue, 1, 1)
|
85
|
+
errors.collect{|e| e.record_id}.sort.should == []
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should reset all entries" do
|
89
|
+
Sunspot::IndexQueue::Entry.implementation.reset!(queue)
|
90
|
+
Sunspot::IndexQueue::Entry.implementation.error_count(queue).should == 0
|
91
|
+
@entry_2 = factory.find(@entry_2.id)
|
92
|
+
@entry_2.error.should == nil
|
93
|
+
@entry_2.attempts.should == 0
|
94
|
+
|
95
|
+
@entry_3 = factory.find(@entry_3.id)
|
96
|
+
@entry_3.error.should_not == nil
|
97
|
+
@entry_3.attempts.should == 1
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should get the next_batch! by index time and priority" do
|
101
|
+
batch = Sunspot::IndexQueue::Entry.implementation.next_batch!(queue)
|
102
|
+
batch.collect{|e| e.record_id}.sort.should == [@entry_2.record_id, @entry_6.record_id, @entry_7.record_id]
|
103
|
+
batch = Sunspot::IndexQueue::Entry.implementation.next_batch!(queue)
|
104
|
+
batch.collect{|e| e.record_id}.sort.should == [@entry_1.record_id, @entry_5.record_id]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
context "add and remove" do
|
109
|
+
it "should add an entry" do
|
110
|
+
Sunspot::IndexQueue::Entry.implementation.add(Sunspot::IndexQueue::Test::Searchable, "10", false, 100)
|
111
|
+
entry = Sunspot::IndexQueue::Entry.implementation.next_batch!(Sunspot::IndexQueue.new).detect{|e| e.priority == 100}
|
112
|
+
entry.record_class_name.should == "Sunspot::IndexQueue::Test::Searchable"
|
113
|
+
entry.record_id.should == "10"
|
114
|
+
entry.is_delete?.should == false
|
115
|
+
entry.priority.should == 100
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should delete a list of entry ids" do
|
119
|
+
Sunspot::IndexQueue::Entry.implementation.delete_entries([@entry_1.id, @entry_2.id])
|
120
|
+
factory.find(@entry_1.id).should == nil
|
121
|
+
factory.find(@entry_2.id).should == nil
|
122
|
+
factory.find(@entry_4.id).id.should == @entry_4.id
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should not add multiple entries unless a row is being processed" do
|
126
|
+
Sunspot::IndexQueue::Entry.implementation.add(Sunspot::IndexQueue::Test::Searchable, "10", false, 80)
|
127
|
+
Sunspot::IndexQueue::Entry.implementation.next_batch!(Sunspot::IndexQueue.new)
|
128
|
+
Sunspot::IndexQueue::Entry.implementation.add(Sunspot::IndexQueue::Test::Searchable, "10", false, 100)
|
129
|
+
Sunspot::IndexQueue::Entry.implementation.add(Sunspot::IndexQueue::Test::Searchable, "10", false, 110)
|
130
|
+
Sunspot::IndexQueue::Entry.implementation.add(Sunspot::IndexQueue::Test::Searchable, "10", true, 90)
|
131
|
+
Sunspot::IndexQueue::Entry.implementation.reset!(Sunspot::IndexQueue.new)
|
132
|
+
entries = Sunspot::IndexQueue::Entry.implementation.next_batch!(Sunspot::IndexQueue.new)
|
133
|
+
entries.detect{|e| e.priority == 80}.record_id.should == "10"
|
134
|
+
entries.detect{|e| e.priority == 100}.should == nil
|
135
|
+
entries.detect{|e| e.priority == 90}.should == nil
|
136
|
+
entry = entries.detect{|e| e.priority == 110}
|
137
|
+
entry.is_delete?.should == true
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
context "instance methods" do
|
143
|
+
|
144
|
+
it "should get the record_class_name" do
|
145
|
+
entry = Sunspot::IndexQueue::Entry.implementation.new('record_class_name' => "Test")
|
146
|
+
entry.record_class_name.should == "Test"
|
147
|
+
end
|
148
|
+
|
149
|
+
it "should get the record_id" do
|
150
|
+
entry = Sunspot::IndexQueue::Entry.implementation.new('record_id' => "1")
|
151
|
+
entry.record_id.should == "1"
|
152
|
+
end
|
153
|
+
|
154
|
+
it "should determine if the entry is an delete" do
|
155
|
+
entry = Sunspot::IndexQueue::Entry.implementation.new('is_delete' => false)
|
156
|
+
entry.is_delete?.should == false
|
157
|
+
entry = Sunspot::IndexQueue::Entry.implementation.new('is_delete' => true)
|
158
|
+
entry.is_delete?.should == true
|
159
|
+
end
|
160
|
+
|
161
|
+
it "should reset an entry to be indexed immediately" do
|
162
|
+
entry = factory.create('record_class_name' => "Test", 'record_id' => 1, 'is_delete' => false, 'priority' => 10, 'run_at' => Time.now.utc + 600, 'error' => "boom!", 'attempts' => 2)
|
163
|
+
queue = Sunspot::IndexQueue.new
|
164
|
+
queue.error_count.should == 1
|
165
|
+
queue.ready_count.should == 0
|
166
|
+
entry.reset!
|
167
|
+
queue.error_count.should == 0
|
168
|
+
queue.ready_count.should == 1
|
169
|
+
factory.find(entry.id).attempts.should == 0
|
170
|
+
end
|
171
|
+
|
172
|
+
it "should set the error on an entry" do
|
173
|
+
entry = factory.create('record_class_name' => "Test", 'record_id' => 1, 'is_delete' => false, 'priority' => 10, 'run_at' => Time.now.utc + 600, 'attempts' => 1)
|
174
|
+
error = ArgumentError.new("boom")
|
175
|
+
error.stub!(:backtrace).and_return(["line 1", "line 2"])
|
176
|
+
entry.set_error!(error)
|
177
|
+
entry = factory.find(entry.id)
|
178
|
+
entry.error.should include("ArgumentError")
|
179
|
+
entry.error.should include("boom")
|
180
|
+
entry.error.should include("line 1")
|
181
|
+
entry.error.should include("line 2")
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|