rubyrep 1.0.5 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,80 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ include RR
4
+
5
+ describe NoisyConnection do
6
+ before(:each) do
7
+ Initializer.configuration = proxied_config
8
+ @connection = ProxyConnection.new Initializer.configuration.left
9
+ @connection.send(:extend, NoisyConnection)
10
+ @connection.sweeper = TaskSweeper.new(1)
11
+ end
12
+
13
+ it "select_cursor should return correct results" do
14
+ @connection.sweeper.should_receive(:ping).exactly(4).times
15
+ fetcher = @connection.select_cursor(:table => 'scanner_records')
16
+ row = fetcher.next_row
17
+ row.should == {
18
+ 'id' => '1',
19
+ 'name' => 'Alice - exists in both databases'
20
+ }
21
+ end
22
+
23
+ it "insert_record should write nil values correctly" do
24
+ @connection.sweeper.should_receive(:ping).exactly(2).times
25
+ @connection.begin_db_transaction
26
+ begin
27
+ @connection.insert_record('extender_combined_key', 'first_id' => 8, 'second_id' => '9', 'name' => nil)
28
+ @connection.select_one(
29
+ "select first_id, second_id, name
30
+ from extender_combined_key where (first_id, second_id) = (8, 9)") \
31
+ .should == {'first_id' => '8', 'second_id' => '9', "name" => nil}
32
+ ensure
33
+ @connection.rollback_db_transaction
34
+ end
35
+ end
36
+
37
+ it "update_record should update the specified record" do
38
+ @connection.sweeper.should_receive(:ping).exactly(2).times
39
+ @connection.begin_db_transaction
40
+ begin
41
+ @connection.update_record('scanner_records', 'id' => 1, 'name' => 'update_test')
42
+ @connection.select_one("select * from scanner_records where id = 1") \
43
+ .should == {'id' => '1', 'name' => 'update_test'}
44
+ ensure
45
+ @connection.rollback_db_transaction
46
+ end
47
+ end
48
+
49
+ it "delete_record should delete the specified record" do
50
+ @connection.sweeper.should_receive(:ping).exactly(2).times
51
+ @connection.begin_db_transaction
52
+ begin
53
+ @connection.delete_record('extender_combined_key', 'first_id' => 1, 'second_id' => '1', 'name' => 'xy')
54
+ @connection.select_one(
55
+ "select first_id, second_id, name
56
+ from extender_combined_key where (first_id, second_id) = (1, 1)") \
57
+ .should be_nil
58
+ ensure
59
+ @connection.rollback_db_transaction
60
+ end
61
+ end
62
+
63
+ it "commit_db_transaction should update TaskSweeper" do
64
+ @connection.begin_db_transaction
65
+ initializer = ReplicationInitializer.new Session.new(standard_config)
66
+ begin
67
+ @connection.execute "insert into scanner_records(id,name) values(99, 'bla')"
68
+ @connection.sweeper.should_receive(:ping).exactly(2).times
69
+ @connection.commit_db_transaction
70
+ initializer.silence_ddl_notices(:left) do # avoid PostgreSQL warning that no transaction is open
71
+ @connection.rollback_db_transaction
72
+ end
73
+ @connection.select_one("select name from scanner_records where id = 99")['name'].
74
+ should == 'bla'
75
+ ensure
76
+ @connection.execute "delete from scanner_records where id = 99"
77
+ end
78
+ end
79
+
80
+ end
@@ -22,17 +22,6 @@ describe ProxyConnection do
22
22
  @connection.connection.active?.should == false
23
23
  end
24
24
 
25
- it "refresh should not do anything if the connection is still active" do
26
- ConnectionExtenders.should_not_receive(:db_connect)
27
- @connection.refresh
28
- end
29
-
30
- it "refresh should reestablish the connection if it is no more active" do
31
- @connection.destroy
32
- @connection.refresh
33
- @connection.connection.should be_active
34
- end
35
-
36
25
  it "cursors should return the current cursor hash or an empty hash if nil" do
37
26
  @connection.cursors.should == {}
38
27
  @connection.cursors[:dummy_cursor] = :dummy_cursor
@@ -7,21 +7,22 @@ describe ReplicationDifference do
7
7
  Initializer.configuration = standard_config
8
8
  end
9
9
 
10
- it "initialize should store the session" do
10
+ it "initialize should store the loaders" do
11
11
  session = Session.new
12
- diff = ReplicationDifference.new session
13
- diff.session.should == session
12
+ loaders = LoggedChangeLoaders.new session
13
+ diff = ReplicationDifference.new loaders
14
+ diff.loaders.should == loaders
14
15
  end
15
16
 
16
17
  it "loaded? should return true if a difference was loaded" do
17
- diff = ReplicationDifference.new Session.new
18
+ diff = ReplicationDifference.new LoggedChangeLoaders.new(Session.new)
18
19
  diff.should_not be_loaded
19
20
  diff.loaded = true
20
21
  diff.should be_loaded
21
22
  end
22
23
 
23
24
  it "load should leave the instance unloaded if no changes are available" do
24
- diff = ReplicationDifference.new Session.new
25
+ diff = ReplicationDifference.new LoggedChangeLoaders.new(Session.new)
25
26
  diff.load
26
27
  diff.should_not be_loaded
27
28
  end
@@ -36,7 +37,7 @@ describe ReplicationDifference do
36
37
  'change_type' => 'I',
37
38
  'change_time' => Time.now
38
39
  }
39
- diff = ReplicationDifference.new session
40
+ diff = ReplicationDifference.new LoggedChangeLoaders.new(session)
40
41
  diff.load
41
42
 
42
43
  diff.should be_loaded
@@ -57,7 +58,7 @@ describe ReplicationDifference do
57
58
  'change_type' => 'D',
58
59
  'change_time' => Time.now
59
60
  }
60
- diff = ReplicationDifference.new session
61
+ diff = ReplicationDifference.new LoggedChangeLoaders.new(session)
61
62
  diff.load
62
63
 
63
64
  diff.should be_loaded
@@ -96,7 +97,7 @@ describe ReplicationDifference do
96
97
  'change_type' => 'D',
97
98
  'change_time' => 5.seconds.ago
98
99
  }
99
- diff = ReplicationDifference.new session
100
+ diff = ReplicationDifference.new LoggedChangeLoaders.new(session)
100
101
  diff.load
101
102
 
102
103
  diff.should be_loaded
@@ -124,7 +125,7 @@ describe ReplicationDifference do
124
125
  'change_type' => 'I',
125
126
  'change_time' => Time.now
126
127
  }
127
- diff = ReplicationDifference.new session
128
+ diff = ReplicationDifference.new LoggedChangeLoaders.new(session)
128
129
  diff.load
129
130
 
130
131
  diff.should be_loaded
@@ -9,7 +9,7 @@ describe ReplicationHelper do
9
9
 
10
10
  it "initialize should initialize the correct committer" do
11
11
  session = Session.new
12
- rep_run = ReplicationRun.new(session)
12
+ rep_run = ReplicationRun.new(session, TaskSweeper.new(1))
13
13
  helper = ReplicationHelper.new(rep_run)
14
14
  c = helper.instance_eval {@committer}
15
15
  c.should be_an_instance_of(Committers::DefaultCommitter)
@@ -17,26 +17,26 @@ describe ReplicationHelper do
17
17
  end
18
18
 
19
19
  it "session should return the session" do
20
- rep_run = ReplicationRun.new(Session.new)
20
+ rep_run = ReplicationRun.new(Session.new, TaskSweeper.new(1))
21
21
  helper = ReplicationHelper.new(rep_run)
22
22
  helper.session.should == rep_run.session
23
23
  end
24
24
 
25
25
  it "replication_run should return the current ReplicationRun instance" do
26
- rep_run = ReplicationRun.new(Session.new)
26
+ rep_run = ReplicationRun.new(Session.new, TaskSweeper.new(1))
27
27
  helper = ReplicationHelper.new(rep_run)
28
28
  helper.replication_run.should == rep_run
29
29
  end
30
30
 
31
31
  it "options should return the correct options" do
32
32
  session = Session.new
33
- rep_run = ReplicationRun.new(session)
33
+ rep_run = ReplicationRun.new(session, TaskSweeper.new(1))
34
34
  helper = ReplicationHelper.new(rep_run)
35
35
  helper.options.should == session.configuration.options
36
36
  end
37
37
 
38
38
  it "insert_record should insert the given record" do
39
- rep_run = ReplicationRun.new(Session.new)
39
+ rep_run = ReplicationRun.new(Session.new, TaskSweeper.new(1))
40
40
  helper = ReplicationHelper.new(rep_run)
41
41
  c = helper.instance_eval {committer}
42
42
  c.should_receive(:insert_record).with(:right, 'scanner_records', :dummy_record)
@@ -44,7 +44,7 @@ describe ReplicationHelper do
44
44
  end
45
45
 
46
46
  it "update_record should update the given record" do
47
- rep_run = ReplicationRun.new(Session.new)
47
+ rep_run = ReplicationRun.new(Session.new, TaskSweeper.new(1))
48
48
  helper = ReplicationHelper.new(rep_run)
49
49
  c = helper.instance_eval {committer}
50
50
  c.should_receive(:update_record).with(:right, 'scanner_records', :dummy_record, nil)
@@ -52,7 +52,7 @@ describe ReplicationHelper do
52
52
  end
53
53
 
54
54
  it "update_record should update the given record with the provided old key" do
55
- rep_run = ReplicationRun.new(Session.new)
55
+ rep_run = ReplicationRun.new(Session.new, TaskSweeper.new(1))
56
56
  helper = ReplicationHelper.new(rep_run)
57
57
  c = helper.instance_eval {committer}
58
58
  c.should_receive(:update_record).with(:right, 'scanner_records', :dummy_record, :old_key)
@@ -60,7 +60,7 @@ describe ReplicationHelper do
60
60
  end
61
61
 
62
62
  it "delete_record should delete the given record" do
63
- rep_run = ReplicationRun.new(Session.new)
63
+ rep_run = ReplicationRun.new(Session.new, TaskSweeper.new(1))
64
64
  helper = ReplicationHelper.new(rep_run)
65
65
  c = helper.instance_eval {committer}
66
66
  c.should_receive(:delete_record).with(:right, 'scanner_records', :dummy_record)
@@ -68,7 +68,7 @@ describe ReplicationHelper do
68
68
  end
69
69
 
70
70
  it "load_record should load the specified record (values converted to original data types)" do
71
- rep_run = ReplicationRun.new(Session.new)
71
+ rep_run = ReplicationRun.new(Session.new, TaskSweeper.new(1))
72
72
  helper = ReplicationHelper.new(rep_run)
73
73
  helper.load_record(:right, 'scanner_records', 'id' => '2').should == {
74
74
  'id' => 2, # Note: it's a number, not a string...
@@ -80,11 +80,14 @@ describe ReplicationHelper do
80
80
  session = Session.new
81
81
  session.left.begin_db_transaction
82
82
  begin
83
- rep_run = ReplicationRun.new(session)
83
+ rep_run = ReplicationRun.new(session, TaskSweeper.new(1))
84
84
  helper = ReplicationHelper.new(rep_run)
85
- left_change = LoggedChange.new session, :left
86
- right_change = LoggedChange.new session, :right
87
- diff = ReplicationDifference.new session
85
+
86
+ loaders = LoggedChangeLoaders.new(session)
87
+
88
+ left_change = LoggedChange.new loaders[:left]
89
+ right_change = LoggedChange.new loaders[:right]
90
+ diff = ReplicationDifference.new loaders
88
91
  diff.changes.replace :left => left_change, :right => right_change
89
92
  diff.type = :conflict
90
93
 
@@ -116,7 +119,7 @@ describe ReplicationHelper do
116
119
  end
117
120
 
118
121
  it "finalize should be delegated to the committer" do
119
- rep_run = ReplicationRun.new(Session.new)
122
+ rep_run = ReplicationRun.new(Session.new, TaskSweeper.new(1))
120
123
  helper = ReplicationHelper.new(rep_run)
121
124
 
122
125
  c = helper.instance_eval {@committer}
@@ -363,6 +363,19 @@ describe ReplicationInitializer do
363
363
  initializer.ensure_infrastructure
364
364
  end
365
365
 
366
+ it "call_after_init_handler should call the according handler" do
367
+ config = deep_copy(standard_config)
368
+ received_session = nil
369
+ config.options[:after_infrastructure_setup] = lambda do |session|
370
+ received_session = session
371
+ end
372
+ session = Session.new config
373
+ initializer = ReplicationInitializer.new session
374
+ initializer.call_after_infrastructure_setup_handler
375
+
376
+ received_session.should == session
377
+ end
378
+
366
379
  it "exclude_ruby_rep_tables should exclude the correct system tables" do
367
380
  config = deep_copy(standard_config)
368
381
  initializer = ReplicationInitializer.new(Session.new(config))
@@ -416,6 +429,12 @@ describe ReplicationInitializer do
416
429
  config = deep_copy(standard_config)
417
430
  config.options[:committer] = :buffered_commit
418
431
  config.options[:use_ansi] = false
432
+
433
+ received_session = nil
434
+ config.options[:after_infrastructure_setup] = lambda do |session|
435
+ received_session = session
436
+ end
437
+
419
438
  config.include_tables 'rr_pending_changes' # added to verify that it is ignored
420
439
 
421
440
  session = Session.new(config)
@@ -426,6 +445,9 @@ describe ReplicationInitializer do
426
445
  initializer.should_receive(:ensure_infrastructure).any_number_of_times
427
446
  initializer.should_receive(:restore_unconfigured_tables).any_number_of_times
428
447
  initializer.prepare_replication
448
+
449
+ received_session.should == session
450
+
429
451
  # verify sequences have been setup
430
452
  session.left.sequence_values('rr','scanner_left_records_only').values[0][:increment].should == 2
431
453
  session.right.sequence_values('rr','scanner_left_records_only').values[0][:increment].should == 2
@@ -9,12 +9,23 @@ describe ReplicationRun do
9
9
 
10
10
  it "initialize should store the provided session" do
11
11
  session = Session.new
12
- run = ReplicationRun.new session
12
+ sweeper = TaskSweeper.new 1
13
+ run = ReplicationRun.new session, sweeper
13
14
  run.session.should == session
14
15
  end
15
16
 
17
+ it "install_sweeper should install a task sweeper into the database connections" do
18
+ session = Session.new
19
+ sweeper = TaskSweeper.new 1
20
+ run = ReplicationRun.new session, sweeper
21
+ run.install_sweeper
22
+
23
+ session.left.sweeper.should == sweeper
24
+ session.right.sweeper.should == sweeper
25
+ end
26
+
16
27
  it "helper should return the correctly initialized replication helper" do
17
- run = ReplicationRun.new Session.new
28
+ run = ReplicationRun.new Session.new, TaskSweeper.new(1)
18
29
  run.helper.should be_an_instance_of(ReplicationHelper)
19
30
  run.helper.replication_run.should == run
20
31
  run.helper.should == run.helper # ensure the helper is created only once
@@ -22,7 +33,7 @@ describe ReplicationRun do
22
33
 
23
34
  it "replicator should return the configured replicator" do
24
35
  session = Session.new
25
- run = ReplicationRun.new session
36
+ run = ReplicationRun.new session, TaskSweeper.new(1)
26
37
  run.replicator.
27
38
  should be_an_instance_of(Replicators.replicators[session.configuration.options[:replicator]])
28
39
  run.replicator.should == run.replicator # should only create the replicator once
@@ -47,7 +58,7 @@ describe ReplicationRun do
47
58
  'change_time' => Time.now
48
59
  }
49
60
 
50
- run = ReplicationRun.new session
61
+ run = ReplicationRun.new session, TaskSweeper.new(1)
51
62
  run.run
52
63
 
53
64
  session.right.select_one("select * from extender_no_record").should == {
@@ -66,7 +77,7 @@ describe ReplicationRun do
66
77
 
67
78
  it "run should not create the replicator if there are no pending changes" do
68
79
  session = Session.new
69
- run = ReplicationRun.new session
80
+ run = ReplicationRun.new session, TaskSweeper.new(1)
70
81
  run.should_not_receive(:replicator)
71
82
  run.run
72
83
  end
@@ -90,7 +101,7 @@ describe ReplicationRun do
90
101
  'change_time' => Time.now
91
102
  }
92
103
 
93
- run = ReplicationRun.new session
104
+ run = ReplicationRun.new session, TaskSweeper.new(1)
94
105
  run.replicator.should_not_receive(:replicate)
95
106
  run.run
96
107
 
@@ -113,7 +124,7 @@ describe ReplicationRun do
113
124
  'change_type' => 'D',
114
125
  'change_time' => Time.now
115
126
  }
116
- run = ReplicationRun.new session
127
+ run = ReplicationRun.new session, TaskSweeper.new(1)
117
128
  run.replicator.stub!(:replicate_difference).and_return {raise Exception, 'dummy message'}
118
129
  run.run
119
130
 
@@ -140,7 +151,7 @@ describe ReplicationRun do
140
151
  'change_time' => Time.now
141
152
  }
142
153
 
143
- run = ReplicationRun.new session
154
+ run = ReplicationRun.new session, TaskSweeper.new(1)
144
155
  lambda {run.run}.should raise_error(ArgumentError)
145
156
  ensure
146
157
  session.left.rollback_db_transaction
@@ -163,7 +174,7 @@ describe ReplicationRun do
163
174
  'name' => 'bla'
164
175
  }
165
176
 
166
- run = ReplicationRun.new session
177
+ run = ReplicationRun.new session, TaskSweeper.new(1)
167
178
  run.run
168
179
 
169
180
  session.right.select_one("select * from extender_no_record").should == {
data/spec/session_spec.rb CHANGED
@@ -67,6 +67,7 @@ describe Session do # here database connection caching is _not_ disabled
67
67
  dummy_connection = mock("dummy connection")
68
68
  dummy_connection.stub!(:tables).and_return([])
69
69
  dummy_connection.stub!(:manual_primary_keys=)
70
+ dummy_connection.stub!(:select_one).and_return({'x' => '2'})
70
71
  dummy_proxy.should_receive(:create_session).and_return(dummy_connection)
71
72
  DRbObject.should_receive(:new).with(nil,"druby://localhost:9876").and_return(dummy_proxy)
72
73
 
@@ -88,12 +89,33 @@ describe Session do # here database connection caching is _not_ disabled
88
89
  session.right.manual_primary_keys.should == {'extender_without_key'=>['id']}
89
90
  end
90
91
 
91
- it "refresh should reestablsih the database connections if no more active" do
92
+ it "refresh should reestablish the database connections if not active anymore" do
92
93
  session = Session.new
93
94
  session.right.destroy
94
95
  session.right.connection.should_not be_active
96
+ lambda {session.right.select_one("select 1+1 as x")}.should raise_error
95
97
  session.refresh
96
98
  session.right.connection.should be_active
99
+ session.right.select_one("select 1+1 as x")['x'].to_i.should == 2
100
+ end
101
+
102
+ it "refresh should raise error even if database connect fails silently" do
103
+ session = Session.new
104
+ session.right.destroy
105
+ session.right.connection.should_not be_active
106
+ session.should_receive(:connect_database)
107
+ lambda {session.refresh}.should raise_error(/no connection to.*right.*database/)
108
+ end
109
+
110
+ it "refresh should work with proxied database connections" do
111
+ ensure_proxy
112
+ session = Session.new(proxied_config)
113
+ session.right.destroy
114
+ session.right.connection.should_not be_active
115
+ lambda {session.right.select_one("select 1+1 as x")}.should raise_error
116
+ session.refresh
117
+ session.right.connection.should be_active
118
+ session.right.select_one("select 1+1 as x")['x'].to_i.should == 2
97
119
  end
98
120
 
99
121
  it "refresh should not do anyting if the connection is still active" do
data/spec/spec_helper.rb CHANGED
@@ -94,6 +94,7 @@ def mock_active_record(number_of_calls)
94
94
  dummy_connection.stub!(:extend)
95
95
  dummy_connection.stub!(:tables).and_return([])
96
96
  dummy_connection.stub!(:initialize_search_path)
97
+ dummy_connection.stub!(:select_one).and_return({'x' => '2'})
97
98
 
98
99
  ConnectionExtenders::DummyActiveRecord.should_receive(:connection).send(number_of_calls) \
99
100
  .and_return {dummy_connection}
@@ -0,0 +1,47 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ include RR
4
+
5
+ describe TaskSweeper do
6
+ before(:each) do
7
+ end
8
+
9
+ it "should execute the given task" do
10
+ x = nil
11
+ TaskSweeper.timeout(1) {|sweeper| x = 1}
12
+ x.should == 1
13
+ end
14
+
15
+ it "should raise exceptions thrown by the task" do
16
+ lambda {
17
+ TaskSweeper.timeout(1) {raise "bla"}
18
+ }.should raise_error("bla")
19
+ end
20
+
21
+ it "should return if task stalls" do
22
+ start = Time.now
23
+ TaskSweeper.timeout(0.01) {sleep 10}.should be_terminated
24
+ (Time.now - start < 5).should be_true
25
+ end
26
+
27
+ it "should not return if task is active" do
28
+ start = Time.now
29
+ TaskSweeper.timeout(0.1) do |sweeper|
30
+ 10.times do
31
+ sleep 0.05
32
+ sweeper.ping
33
+ end
34
+ end.should_not be_terminated
35
+ (Time.now - start > 0.4).should be_true
36
+
37
+ end
38
+
39
+ it "should notify a stalled task about it's termination" do
40
+ terminated = false
41
+ TaskSweeper.timeout(0.01) do |sweeper|
42
+ sleep 0.05
43
+ terminated = sweeper.terminated?
44
+ end.join
45
+ terminated.should be_true
46
+ end
47
+ end