rubyrep 1.0.5 → 1.0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +6 -0
- data/Manifest.txt +6 -0
- data/lib/rubyrep/configuration.rb +14 -0
- data/lib/rubyrep/logged_change.rb +16 -195
- data/lib/rubyrep/logged_change_loader.rb +196 -0
- data/lib/rubyrep/noisy_connection.rb +80 -0
- data/lib/rubyrep/proxy_connection.rb +2 -9
- data/lib/rubyrep/replication_difference.rb +14 -9
- data/lib/rubyrep/replication_initializer.rb +9 -0
- data/lib/rubyrep/replication_run.rb +29 -6
- data/lib/rubyrep/replication_runner.rb +6 -2
- data/lib/rubyrep/session.rb +85 -43
- data/lib/rubyrep/task_sweeper.rb +77 -0
- data/lib/rubyrep/version.rb +1 -1
- data/lib/rubyrep.rb +3 -0
- data/spec/logged_change_loader_spec.rb +68 -0
- data/spec/logged_change_spec.rb +45 -55
- data/spec/noisy_connection_spec.rb +80 -0
- data/spec/proxy_connection_spec.rb +0 -11
- data/spec/replication_difference_spec.rb +10 -9
- data/spec/replication_helper_spec.rb +17 -14
- data/spec/replication_initializer_spec.rb +22 -0
- data/spec/replication_run_spec.rb +20 -9
- data/spec/session_spec.rb +23 -1
- data/spec/spec_helper.rb +1 -0
- data/spec/task_sweeper_spec.rb +47 -0
- data/spec/two_way_replicator_spec.rb +50 -40
- data.tar.gz.sig +0 -0
- metadata +8 -2
- metadata.gz.sig +0 -0
@@ -8,6 +8,9 @@ module RR
|
|
8
8
|
# The current Session object
|
9
9
|
attr_accessor :session
|
10
10
|
|
11
|
+
# The current TaskSweeper
|
12
|
+
attr_accessor :sweeper
|
13
|
+
|
11
14
|
# Returns the current ReplicationHelper; creates it if necessary
|
12
15
|
def helper
|
13
16
|
@helper ||= ReplicationHelper.new(self)
|
@@ -22,22 +25,29 @@ module RR
|
|
22
25
|
# Executes the replication run.
|
23
26
|
def run
|
24
27
|
return unless [:left, :right].any? do |database|
|
25
|
-
|
26
|
-
|
28
|
+
changes_pending = false
|
29
|
+
t = Thread.new do
|
30
|
+
changes_pending = session.send(database).select_one(
|
27
31
|
"select id from #{session.configuration.options[:rep_prefix]}_pending_changes"
|
28
32
|
) != nil
|
29
33
|
end
|
34
|
+
t.join session.configuration.options[:database_connection_timeout]
|
35
|
+
changes_pending
|
30
36
|
end
|
37
|
+
|
38
|
+
loaders = LoggedChangeLoaders.new(session)
|
39
|
+
|
31
40
|
begin
|
32
41
|
success = false
|
33
42
|
replicator # ensure that replicator is created and has chance to validate settings
|
34
43
|
|
35
44
|
loop do
|
36
45
|
begin
|
37
|
-
|
38
|
-
diff = ReplicationDifference.new
|
46
|
+
loaders.update # ensure the cache of change log records is up-to-date
|
47
|
+
diff = ReplicationDifference.new loaders
|
39
48
|
diff.load
|
40
49
|
break unless diff.loaded?
|
50
|
+
raise "Replication run timed out" if sweeper.terminated?
|
41
51
|
replicator.replicate_difference diff if diff.type != :no_diff
|
42
52
|
rescue Exception => e
|
43
53
|
helper.log_replication_outcome diff, e.message,
|
@@ -50,10 +60,23 @@ module RR
|
|
50
60
|
end
|
51
61
|
end
|
52
62
|
|
63
|
+
# Installs the current sweeper into the database connections
|
64
|
+
def install_sweeper
|
65
|
+
[:left, :right].each do |database|
|
66
|
+
unless session.send(database).respond_to?(:sweeper)
|
67
|
+
session.send(database).send(:extend, NoisyConnection)
|
68
|
+
end
|
69
|
+
session.send(database).sweeper = sweeper
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
53
73
|
# Creates a new ReplicationRun instance.
|
54
74
|
# * +session+: the current Session
|
55
|
-
|
75
|
+
# * +sweeper+: the current TaskSweeper
|
76
|
+
def initialize(session, sweeper)
|
56
77
|
self.session = session
|
78
|
+
self.sweeper = sweeper
|
79
|
+
install_sweeper
|
57
80
|
end
|
58
81
|
end
|
59
|
-
end
|
82
|
+
end
|
@@ -108,8 +108,12 @@ EOS
|
|
108
108
|
until termination_requested do
|
109
109
|
begin
|
110
110
|
session.refresh
|
111
|
-
|
112
|
-
|
111
|
+
timeout = session.configuration.options[:database_connection_timeout]
|
112
|
+
terminated = TaskSweeper.timeout(timeout) do |sweeper|
|
113
|
+
run = ReplicationRun.new session, sweeper
|
114
|
+
run.run
|
115
|
+
end.terminated?
|
116
|
+
raise "replication run timed out" if terminated
|
113
117
|
rescue Exception => e
|
114
118
|
now = Time.now.iso8601
|
115
119
|
$stderr.puts "#{now} Exception caught: #{e}"
|
data/lib/rubyrep/session.rb
CHANGED
@@ -83,32 +83,7 @@ module RR
|
|
83
83
|
@table_map[db_arm][table] || table
|
84
84
|
end
|
85
85
|
|
86
|
-
#
|
87
|
-
# db_arm:: should be either :left or :right
|
88
|
-
# config:: the rubyrep Configuration
|
89
|
-
def db_connect(db_arm, config)
|
90
|
-
arm_config = config.send db_arm
|
91
|
-
@proxies[db_arm] = DatabaseProxy.new
|
92
|
-
@connections[db_arm] = @proxies[db_arm].create_session arm_config
|
93
|
-
end
|
94
|
-
|
95
|
-
# Does the actual work of establishing a proxy connection
|
96
|
-
# db_arm:: should be either :left or :right
|
97
|
-
# config:: the rubyrep Configuration
|
98
|
-
def proxy_connect(db_arm, config)
|
99
|
-
arm_config = config.send db_arm
|
100
|
-
if arm_config.include? :proxy_host
|
101
|
-
drb_url = "druby://#{arm_config[:proxy_host]}:#{arm_config[:proxy_port]}"
|
102
|
-
@proxies[db_arm] = DRbObject.new nil, drb_url
|
103
|
-
else
|
104
|
-
# If one connection goes through a proxy, so has the other one.
|
105
|
-
# So if necessary, create a "fake" proxy
|
106
|
-
@proxies[db_arm] = DatabaseProxy.new
|
107
|
-
end
|
108
|
-
@connections[db_arm] = @proxies[db_arm].create_session arm_config
|
109
|
-
end
|
110
|
-
|
111
|
-
# True if proxy connections are used
|
86
|
+
# Returns +true+ if proxy connections are used
|
112
87
|
def proxied?
|
113
88
|
[configuration.left, configuration.right].any? \
|
114
89
|
{|arm_config| arm_config.include? :proxy_host}
|
@@ -144,9 +119,90 @@ module RR
|
|
144
119
|
end
|
145
120
|
end
|
146
121
|
|
147
|
-
#
|
122
|
+
# Returns +true+ if the specified database connection is not alive.
|
123
|
+
# * +database+: target database (either +:left+ or :+right+)
|
124
|
+
def database_unreachable?(database)
|
125
|
+
unreachable = true
|
126
|
+
Thread.new do
|
127
|
+
begin
|
128
|
+
if send(database) && send(database).select_one("select 1+1 as x")['x'].to_i == 2
|
129
|
+
unreachable = false # database is actually reachable
|
130
|
+
end
|
131
|
+
end rescue nil
|
132
|
+
end.join configuration.options[:database_connection_timeout]
|
133
|
+
unreachable
|
134
|
+
end
|
135
|
+
|
136
|
+
# Disconnnects the specified database
|
137
|
+
# * +database+: the target database (either :+left+ or :+right+)
|
138
|
+
def disconnect_database(database)
|
139
|
+
proxy, connection = @proxies[database], @connection[database]
|
140
|
+
@proxies[database] = nil
|
141
|
+
@connections[database] = nil
|
142
|
+
if proxy
|
143
|
+
proxy.destroy_session(connection)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Refreshes both database connections
|
148
148
|
def refresh
|
149
|
-
[:left, :right].each {|database|
|
149
|
+
[:left, :right].each {|database| refresh_database_connection database}
|
150
|
+
end
|
151
|
+
|
152
|
+
# Refreshes the specified database connection.
|
153
|
+
# (I. e. reestablish if not active anymore.)
|
154
|
+
# * +database+: target database (either :+left+ or :+right+)
|
155
|
+
def refresh_database_connection(database)
|
156
|
+
if database_unreachable?(database)
|
157
|
+
# step 1: disconnect both database connection (if still possible)
|
158
|
+
begin
|
159
|
+
Thread.new do
|
160
|
+
disconnect_database database rescue nil
|
161
|
+
end.join configuration.options[:database_connection_timeout]
|
162
|
+
end
|
163
|
+
|
164
|
+
connect_exception = nil
|
165
|
+
# step 2: try to reconnect the database
|
166
|
+
Thread.new do
|
167
|
+
begin
|
168
|
+
connect_database database
|
169
|
+
rescue Exception => e
|
170
|
+
# save exception so it can be rethrown outside of the thread
|
171
|
+
connect_exception = e
|
172
|
+
end
|
173
|
+
end.join configuration.options[:database_connection_timeout]
|
174
|
+
raise connect_exception if connect_exception
|
175
|
+
|
176
|
+
# step 3: verify if database connections actually work (to detect silent connection failures)
|
177
|
+
if database_unreachable?(database)
|
178
|
+
raise "no connection to '#{database}' database"
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# Set up the (proxied or direct) database connections to the specified
|
184
|
+
# database.
|
185
|
+
# * +database+: the target database (either :+left+ or :+right+)
|
186
|
+
def connect_database(database)
|
187
|
+
if configuration.left == configuration.right and database == :right
|
188
|
+
# If both database configurations point to the same database
|
189
|
+
# then don't create the database connection twice.
|
190
|
+
# Assumes that the left database is always connected before the right one.
|
191
|
+
self.right = self.left
|
192
|
+
else
|
193
|
+
# Connect the database / proxy
|
194
|
+
arm_config = configuration.send database
|
195
|
+
if arm_config.include? :proxy_host
|
196
|
+
drb_url = "druby://#{arm_config[:proxy_host]}:#{arm_config[:proxy_port]}"
|
197
|
+
@proxies[database] = DRbObject.new nil, drb_url
|
198
|
+
else
|
199
|
+
# Create fake proxy
|
200
|
+
@proxies[database] = DatabaseProxy.new
|
201
|
+
end
|
202
|
+
@connections[database] = @proxies[database].create_session arm_config
|
203
|
+
|
204
|
+
send(database).manual_primary_keys = manual_primary_keys(database)
|
205
|
+
end
|
150
206
|
end
|
151
207
|
|
152
208
|
# Creates a new rubyrep session with the provided Configuration
|
@@ -157,21 +213,7 @@ module RR
|
|
157
213
|
# Keep the database configuration for future reference
|
158
214
|
self.configuration = config
|
159
215
|
|
160
|
-
|
161
|
-
connection_method = proxied? ? :proxy_connect : :db_connect
|
162
|
-
|
163
|
-
# Connect the left database / proxy
|
164
|
-
self.send connection_method, :left, configuration
|
165
|
-
left.manual_primary_keys = manual_primary_keys(:left)
|
166
|
-
|
167
|
-
# If both database configurations point to the same database
|
168
|
-
# then don't create the database connection twice
|
169
|
-
if configuration.left == configuration.right
|
170
|
-
self.right = self.left
|
171
|
-
else
|
172
|
-
self.send connection_method, :right, configuration
|
173
|
-
right.manual_primary_keys = manual_primary_keys(:right)
|
174
|
-
end
|
216
|
+
refresh
|
175
217
|
end
|
176
218
|
end
|
177
219
|
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module RR
|
2
|
+
|
3
|
+
# Monitors and cancels stalled tasks
|
4
|
+
class TaskSweeper
|
5
|
+
|
6
|
+
# Executes the give block in a separate Thread.
|
7
|
+
# Returns if block is finished or stalled.
|
8
|
+
# The block must call regular #ping to announce it is not stalled.
|
9
|
+
# * +timeout_period+:
|
10
|
+
# Maximum time (in seonds) without ping, after which a task is considered stalled.
|
11
|
+
# Returns the created sweeper (allows checking if task was terminated).
|
12
|
+
def self.timeout(timeout_period)
|
13
|
+
sweeper = TaskSweeper.new(timeout_period)
|
14
|
+
sweeper.send(:timeout) {yield sweeper}
|
15
|
+
sweeper
|
16
|
+
end
|
17
|
+
|
18
|
+
# Time in seconds after which a task is considered stalled. Timer is reset
|
19
|
+
# by calling #ping.
|
20
|
+
attr_accessor :timeout_period
|
21
|
+
|
22
|
+
# Must be called by the executed task to announce it is still alive
|
23
|
+
def ping
|
24
|
+
self.last_ping = Time.now
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns +true+ if the task was timed out.
|
28
|
+
# The terminated task is expected to free all resources and exit.
|
29
|
+
def terminated?
|
30
|
+
terminated
|
31
|
+
end
|
32
|
+
|
33
|
+
# Waits without timeout till the task executing thread is finished
|
34
|
+
def join
|
35
|
+
thread && thread.join
|
36
|
+
end
|
37
|
+
|
38
|
+
# Creates a new TaskSweeper
|
39
|
+
# * +timeout_period+: timeout value in seconds
|
40
|
+
def initialize(timeout_period)
|
41
|
+
self.timeout_period = timeout_period
|
42
|
+
self.terminated = false
|
43
|
+
self.last_ping = Time.now
|
44
|
+
end
|
45
|
+
|
46
|
+
protected
|
47
|
+
|
48
|
+
# Time of last ping
|
49
|
+
attr_accessor :last_ping
|
50
|
+
|
51
|
+
# Set to +true+ if the executed task has timed out
|
52
|
+
attr_accessor :terminated
|
53
|
+
|
54
|
+
# The task executing thread
|
55
|
+
attr_accessor :thread
|
56
|
+
|
57
|
+
# Executes the given block and times it out if stalled.
|
58
|
+
def timeout
|
59
|
+
exception = nil
|
60
|
+
self.thread = Thread.new do
|
61
|
+
begin
|
62
|
+
yield
|
63
|
+
rescue Exception => e
|
64
|
+
# save exception so it can be rethrown outside of the thread
|
65
|
+
exception = e
|
66
|
+
end
|
67
|
+
end
|
68
|
+
while self.thread.join(self.timeout_period) == nil do
|
69
|
+
if self.last_ping < Time.now - self.timeout_period
|
70
|
+
self.terminated = true
|
71
|
+
break
|
72
|
+
end
|
73
|
+
end
|
74
|
+
raise exception if exception
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/lib/rubyrep/version.rb
CHANGED
data/lib/rubyrep.rb
CHANGED
@@ -42,15 +42,18 @@ require 'syncers/syncers'
|
|
42
42
|
require 'syncers/two_way_syncer'
|
43
43
|
require 'sync_runner'
|
44
44
|
require 'trigger_mode_switcher'
|
45
|
+
require 'logged_change_loader'
|
45
46
|
require 'logged_change'
|
46
47
|
require 'replication_difference'
|
47
48
|
require 'replication_helper'
|
48
49
|
require 'replicators/replicators'
|
49
50
|
require 'replicators/two_way_replicator'
|
51
|
+
require 'task_sweeper'
|
50
52
|
require 'replication_run'
|
51
53
|
require 'replication_runner'
|
52
54
|
require 'uninstall_runner'
|
53
55
|
require 'generate_runner'
|
56
|
+
require 'noisy_connection'
|
54
57
|
|
55
58
|
Dir["#{File.dirname(__FILE__)}/rubyrep/connection_extenders/*.rb"].each do |extender|
|
56
59
|
# jdbc_extender.rb is only loaded if we are running on jruby
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
include RR
|
4
|
+
|
5
|
+
describe LoggedChangeLoaders do
|
6
|
+
before(:each) do
|
7
|
+
Initializer.configuration = standard_config
|
8
|
+
end
|
9
|
+
|
10
|
+
it "initializers should create both logged change loaders" do
|
11
|
+
session = Session.new
|
12
|
+
loaders = LoggedChangeLoaders.new(session)
|
13
|
+
loaders[:left].session.should == session
|
14
|
+
loaders[:left].database.should == :left
|
15
|
+
loaders[:right].database.should == :right
|
16
|
+
end
|
17
|
+
|
18
|
+
it "update should execute a forced update of both logged change loaders" do
|
19
|
+
session = Session.new
|
20
|
+
loaders = LoggedChangeLoaders.new(session)
|
21
|
+
loaders[:left].should_receive(:update).with(:forced => true)
|
22
|
+
loaders[:right].should_receive(:update).with(:forced => true)
|
23
|
+
loaders.update
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
describe LoggedChangeLoader do
|
29
|
+
before(:each) do
|
30
|
+
Initializer.configuration = standard_config
|
31
|
+
end
|
32
|
+
|
33
|
+
# Note:
|
34
|
+
# LoggedChangeLoader is a helper for LoggedChange.
|
35
|
+
# It is tested through the specs for LoggedChange.
|
36
|
+
|
37
|
+
it "oldest_change_time should return nil if there are no changes" do
|
38
|
+
session = Session.new
|
39
|
+
session.left.execute "delete from rr_pending_changes"
|
40
|
+
loader = LoggedChangeLoader.new session, :left
|
41
|
+
loader.oldest_change_time.should be_nil
|
42
|
+
end
|
43
|
+
|
44
|
+
it "oldest_change_time should return the time of the oldest change" do
|
45
|
+
session = Session.new
|
46
|
+
session.left.begin_db_transaction
|
47
|
+
begin
|
48
|
+
time = Time.now
|
49
|
+
session.left.insert_record 'rr_pending_changes', {
|
50
|
+
'change_table' => 'left_table',
|
51
|
+
'change_key' => 'id|1',
|
52
|
+
'change_type' => 'I',
|
53
|
+
'change_time' => time
|
54
|
+
}
|
55
|
+
session.left.insert_record 'rr_pending_changes', {
|
56
|
+
'change_table' => 'left_table',
|
57
|
+
'change_key' => 'id|2',
|
58
|
+
'change_type' => 'I',
|
59
|
+
'change_time' => 100.seconds.from_now
|
60
|
+
}
|
61
|
+
loader = LoggedChangeLoader.new session, :left
|
62
|
+
loader.oldest_change_time.should.to_s == time.to_s
|
63
|
+
ensure
|
64
|
+
session.left.rollback_db_transaction
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
data/spec/logged_change_spec.rb
CHANGED
@@ -9,7 +9,8 @@ describe LoggedChange do
|
|
9
9
|
|
10
10
|
it "initialize should store session and database" do
|
11
11
|
session = Session.new
|
12
|
-
|
12
|
+
loader = LoggedChangeLoader.new session, :left
|
13
|
+
change = LoggedChange.new loader
|
13
14
|
change.session.should == session
|
14
15
|
change.database.should == :left
|
15
16
|
end
|
@@ -37,7 +38,8 @@ describe LoggedChange do
|
|
37
38
|
'change_type' => 'I',
|
38
39
|
'change_time' => Time.now
|
39
40
|
}
|
40
|
-
|
41
|
+
loader = LoggedChangeLoader.new session, :left
|
42
|
+
change = LoggedChange.new loader
|
41
43
|
change.load_specified 'left_table', {'id' => '2'}
|
42
44
|
|
43
45
|
change.table.should == 'left_table'
|
@@ -62,7 +64,8 @@ describe LoggedChange do
|
|
62
64
|
'change_type' => 'I',
|
63
65
|
'change_time' => Time.now
|
64
66
|
}
|
65
|
-
|
67
|
+
loader = LoggedChangeLoader.new session, :left
|
68
|
+
change = LoggedChange.new loader
|
66
69
|
change.load_specified 'scanner_records', {'id1' => 1, 'id2' => 2}
|
67
70
|
|
68
71
|
change.table.should == 'scanner_records'
|
@@ -83,7 +86,8 @@ describe LoggedChange do
|
|
83
86
|
'change_type' => 'I',
|
84
87
|
'change_time' => Time.now
|
85
88
|
}
|
86
|
-
|
89
|
+
loader = LoggedChangeLoader.new session, :left
|
90
|
+
change = LoggedChange.new loader
|
87
91
|
change.load_specified 'left_table', {'id' => 1}
|
88
92
|
|
89
93
|
session.left.
|
@@ -113,7 +117,8 @@ describe LoggedChange do
|
|
113
117
|
'change_type' => 'U',
|
114
118
|
'change_time' => t2
|
115
119
|
}
|
116
|
-
|
120
|
+
loader = LoggedChangeLoader.new session, :left
|
121
|
+
change = LoggedChange.new loader
|
117
122
|
change.load_specified 'left_table', {'id' => 1}
|
118
123
|
|
119
124
|
change.first_changed_at.to_s.should == t1.to_s
|
@@ -141,7 +146,8 @@ describe LoggedChange do
|
|
141
146
|
'change_type' => 'U',
|
142
147
|
'change_time' => Time.now
|
143
148
|
}
|
144
|
-
|
149
|
+
loader = LoggedChangeLoader.new session, :left
|
150
|
+
change = LoggedChange.new loader
|
145
151
|
change.load_specified 'left_table', {'id' => 1}
|
146
152
|
|
147
153
|
change.type.should == :update
|
@@ -175,7 +181,8 @@ describe LoggedChange do
|
|
175
181
|
'change_type' => 'D',
|
176
182
|
'change_time' => Time.now
|
177
183
|
}
|
178
|
-
|
184
|
+
loader = LoggedChangeLoader.new session, :left
|
185
|
+
change = LoggedChange.new loader
|
179
186
|
change.load_specified 'left_table', {'id' => '1'}
|
180
187
|
|
181
188
|
change.type.should == :no_change
|
@@ -215,7 +222,8 @@ describe LoggedChange do
|
|
215
222
|
'change_type' => 'U',
|
216
223
|
'change_time' => Time.now
|
217
224
|
}
|
218
|
-
|
225
|
+
loader = LoggedChangeLoader.new session, :left
|
226
|
+
change = LoggedChange.new loader
|
219
227
|
change.load_specified 'left_table', {'id' => '1'}
|
220
228
|
change.type.should == :insert
|
221
229
|
change.key.should == {'id' => '2'}
|
@@ -233,8 +241,8 @@ describe LoggedChange do
|
|
233
241
|
'change_type' => 'I',
|
234
242
|
'change_time' => Time.now
|
235
243
|
}
|
236
|
-
|
237
|
-
change = LoggedChange.new
|
244
|
+
loader.update :forced => true
|
245
|
+
change = LoggedChange.new loader
|
238
246
|
change.load_specified 'left_table', {'id' => '5'}
|
239
247
|
change.type.should == :update
|
240
248
|
change.key.should == {'id' => '5'}
|
@@ -254,7 +262,8 @@ describe LoggedChange do
|
|
254
262
|
'change_type' => 'I',
|
255
263
|
'change_time' => Time.now
|
256
264
|
}
|
257
|
-
|
265
|
+
loader = LoggedChangeLoader.new session, :left
|
266
|
+
change = LoggedChange.new loader
|
258
267
|
change.load_specified 'scanner_records', {'id' => '1'}
|
259
268
|
|
260
269
|
change.table.should == 'scanner_records'
|
@@ -275,7 +284,8 @@ describe LoggedChange do
|
|
275
284
|
session = Session.new
|
276
285
|
session.left.begin_db_transaction
|
277
286
|
begin
|
278
|
-
|
287
|
+
loader = LoggedChangeLoader.new session, :left
|
288
|
+
change = LoggedChange.new loader
|
279
289
|
change.load_specified 'scanner_records', {'id' => '1'}
|
280
290
|
|
281
291
|
change.table.should == 'scanner_records'
|
@@ -307,7 +317,8 @@ describe LoggedChange do
|
|
307
317
|
'change_type' => 'U',
|
308
318
|
'change_time' => Time.now
|
309
319
|
}
|
310
|
-
|
320
|
+
loader = LoggedChangeLoader.new session, :left
|
321
|
+
change = LoggedChange.new loader
|
311
322
|
change.load_specified 'left_table', {'id' => '1'}
|
312
323
|
session.left.insert_record 'rr_pending_changes', {
|
313
324
|
'change_table' => 'left_table',
|
@@ -315,7 +326,7 @@ describe LoggedChange do
|
|
315
326
|
'change_type' => 'D',
|
316
327
|
'change_time' => Time.now
|
317
328
|
}
|
318
|
-
|
329
|
+
loader.update :forced => true
|
319
330
|
change.load
|
320
331
|
|
321
332
|
change.table.should == 'left_table'
|
@@ -341,7 +352,8 @@ describe LoggedChange do
|
|
341
352
|
'change_type' => 'U',
|
342
353
|
'change_time' => Time.now
|
343
354
|
}
|
344
|
-
|
355
|
+
loader = LoggedChangeLoader.new session, :left
|
356
|
+
change = LoggedChange.new loader
|
345
357
|
change.load_specified 'left_table', {'id' => '1'}
|
346
358
|
session.left.insert_record 'rr_pending_changes', {
|
347
359
|
'change_table' => 'left_table',
|
@@ -350,7 +362,7 @@ describe LoggedChange do
|
|
350
362
|
'change_type' => 'U',
|
351
363
|
'change_time' => Time.now
|
352
364
|
}
|
353
|
-
|
365
|
+
loader.update :forced => true
|
354
366
|
change.load
|
355
367
|
|
356
368
|
change.table.should == 'left_table'
|
@@ -362,39 +374,9 @@ describe LoggedChange do
|
|
362
374
|
end
|
363
375
|
end
|
364
376
|
|
365
|
-
it "oldest_change_time should return nil if there are no changes" do
|
366
|
-
session = Session.new
|
367
|
-
session.left.execute "delete from rr_pending_changes"
|
368
|
-
change = LoggedChange.new session, :left
|
369
|
-
change.oldest_change_time.should be_nil
|
370
|
-
end
|
371
|
-
|
372
|
-
it "oldest_change_time should return the time of the oldest change" do
|
373
|
-
session = Session.new
|
374
|
-
session.left.begin_db_transaction
|
375
|
-
begin
|
376
|
-
time = Time.now
|
377
|
-
session.left.insert_record 'rr_pending_changes', {
|
378
|
-
'change_table' => 'left_table',
|
379
|
-
'change_key' => 'id|1',
|
380
|
-
'change_type' => 'I',
|
381
|
-
'change_time' => time
|
382
|
-
}
|
383
|
-
session.left.insert_record 'rr_pending_changes', {
|
384
|
-
'change_table' => 'left_table',
|
385
|
-
'change_key' => 'id|2',
|
386
|
-
'change_type' => 'I',
|
387
|
-
'change_time' => 100.seconds.from_now
|
388
|
-
}
|
389
|
-
change = LoggedChange.new session, :left
|
390
|
-
change.oldest_change_time.should.to_s == time.to_s
|
391
|
-
ensure
|
392
|
-
session.left.rollback_db_transaction
|
393
|
-
end
|
394
|
-
end
|
395
|
-
|
396
377
|
it "key_from_raw_key should return the correct column_name => value hash for the given key" do
|
397
|
-
|
378
|
+
loader = LoggedChangeLoader.new Session.new, :left
|
379
|
+
change = LoggedChange.new loader
|
398
380
|
change.key_to_hash("a|1|b|2").should == {
|
399
381
|
'a' => '1',
|
400
382
|
'b' => '2'
|
@@ -402,7 +384,8 @@ describe LoggedChange do
|
|
402
384
|
end
|
403
385
|
|
404
386
|
it "key_from_raw_key should work with multi character key_sep strings" do
|
405
|
-
|
387
|
+
loader = LoggedChangeLoader.new Session.new, :left
|
388
|
+
change = LoggedChange.new loader
|
406
389
|
change.stub!(:key_sep).and_return('BLA')
|
407
390
|
change.key_to_hash("aBLA1BLAbBLA2").should == {
|
408
391
|
'a' => '1',
|
@@ -411,7 +394,8 @@ describe LoggedChange do
|
|
411
394
|
end
|
412
395
|
|
413
396
|
it "load_oldest should not load a change if none available" do
|
414
|
-
|
397
|
+
loader = LoggedChangeLoader.new Session.new, :left
|
398
|
+
change = LoggedChange.new loader
|
415
399
|
change.should_not_receive :load_specified
|
416
400
|
change.load_oldest
|
417
401
|
end
|
@@ -432,7 +416,8 @@ describe LoggedChange do
|
|
432
416
|
'change_type' => 'I',
|
433
417
|
'change_time' => Time.now
|
434
418
|
}
|
435
|
-
|
419
|
+
loader = LoggedChangeLoader.new session, :left
|
420
|
+
change = LoggedChange.new loader
|
436
421
|
change.load_oldest
|
437
422
|
|
438
423
|
change.key.should == {'id' => '1'}
|
@@ -463,7 +448,8 @@ describe LoggedChange do
|
|
463
448
|
'change_type' => 'I',
|
464
449
|
'change_time' => Time.now
|
465
450
|
}
|
466
|
-
|
451
|
+
loader = LoggedChangeLoader.new session, :left
|
452
|
+
change = LoggedChange.new loader
|
467
453
|
change.load_oldest
|
468
454
|
|
469
455
|
change.type.should == :insert
|
@@ -473,8 +459,12 @@ describe LoggedChange do
|
|
473
459
|
end
|
474
460
|
end
|
475
461
|
|
476
|
-
it "to_yaml should blank out session" do
|
477
|
-
|
478
|
-
|
462
|
+
it "to_yaml should blank out session and loader" do
|
463
|
+
session = Session.new
|
464
|
+
loader = LoggedChangeLoader.new session, :left
|
465
|
+
change = LoggedChange.new loader
|
466
|
+
yaml = change.to_yaml
|
467
|
+
yaml.should_not =~ /session/
|
468
|
+
yaml.should_not =~ /loader/
|
479
469
|
end
|
480
470
|
end
|