rhosync 2.1.11 → 2.1.12
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.
- data/CHANGELOG +6 -1
- data/Rakefile +1 -1
- data/lib/rhosync/body_content_type_parser.rb +29 -0
- data/lib/rhosync/client_sync.rb +9 -3
- data/lib/rhosync/console/server.rb +7 -3
- data/lib/rhosync/read_state.rb +2 -0
- data/lib/rhosync/server.rb +21 -25
- data/lib/rhosync/source.rb +38 -28
- data/lib/rhosync/source_adapter.rb +9 -5
- data/lib/rhosync/source_sync.rb +10 -10
- data/lib/rhosync/store.rb +36 -15
- data/lib/rhosync/version.rb +1 -1
- data/spec/api/stats_spec.rb +7 -2
- data/spec/server/server_spec.rb +6 -7
- data/spec/source_sync_spec.rb +39 -5
- data/spec/store_spec.rb +23 -0
- metadata +6 -5
data/CHANGELOG
CHANGED
@@ -1,4 +1,9 @@
|
|
1
|
-
## 2.1.
|
1
|
+
## 2.1.12
|
2
|
+
* #19304885 - fixing race condition in get_lock (lock is released between setnx and get calls)
|
3
|
+
* #18508155 - on failed syncs allow the user to retry it up to pre-defined number of times (another approach)
|
4
|
+
* #19143845 - added sinatra 1.3.x support
|
5
|
+
|
6
|
+
## 2.1.11
|
2
7
|
* #17526603 - implement clientreset support for specified sources
|
3
8
|
* #18356697 - store lock is never released (support request #1466)
|
4
9
|
* use redis 2.2.12 by default
|
data/Rakefile
CHANGED
@@ -66,7 +66,7 @@ begin
|
|
66
66
|
gemspec.files = FileList["[A-Z]*", "{bench,bin,generators,lib,spec,tasks}/**/*"]
|
67
67
|
|
68
68
|
# TODO: Due to https://www.pivotaltracker.com/story/show/3417862, we can't use JSON 1.4.3
|
69
|
-
gemspec.add_dependency "sinatra", "
|
69
|
+
gemspec.add_dependency "sinatra", ">= 1.2.7"
|
70
70
|
gemspec.add_dependency "json", "~>1.4.2"
|
71
71
|
gemspec.add_dependency "sqlite3-ruby", "~>1.2.5"
|
72
72
|
gemspec.add_dependency "rubyzip", "~>0.9.4"
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Rhosync
|
2
|
+
class BodyContentTypeParser
|
3
|
+
|
4
|
+
# Constants
|
5
|
+
#
|
6
|
+
CONTENT_TYPE = 'CONTENT_TYPE'.freeze
|
7
|
+
POST_BODY = 'rack.input'.freeze
|
8
|
+
FORM_INPUT = 'rack.request.form_input'.freeze
|
9
|
+
FORM_HASH = 'rack.request.form_hash'.freeze
|
10
|
+
|
11
|
+
def initialize(app)
|
12
|
+
@app = app
|
13
|
+
end
|
14
|
+
|
15
|
+
def call(env)
|
16
|
+
if env['CONTENT_TYPE'] && env['CONTENT_TYPE'].match(/^application\/json/)
|
17
|
+
begin
|
18
|
+
if (body = env[POST_BODY].read).length != 0
|
19
|
+
env.update(FORM_HASH => JSON.parse(body), FORM_INPUT => env[POST_BODY])
|
20
|
+
end
|
21
|
+
rescue JSON::ParserError => jpe
|
22
|
+
log jpe.message + jpe.backtrace.join("\n")
|
23
|
+
return [500, {'Content-Type' => 'text/plain'}, ["Server error while processing client data"]]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
@app.call(env)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/rhosync/client_sync.rb
CHANGED
@@ -40,7 +40,7 @@ module Rhosync
|
|
40
40
|
return _resend_search_result if params[:token] and params[:resend]
|
41
41
|
if params[:token] and !_ack_search(params[:token])
|
42
42
|
formatted_result = _format_search_result
|
43
|
-
|
43
|
+
_delete_search
|
44
44
|
return formatted_result
|
45
45
|
end
|
46
46
|
end
|
@@ -279,7 +279,7 @@ module Rhosync
|
|
279
279
|
|
280
280
|
def _ack_search(search_token)
|
281
281
|
if @client.get_value(:search_token) != search_token
|
282
|
-
|
282
|
+
_delete_search
|
283
283
|
@client.put_data(:search_errors,
|
284
284
|
{'search-error'=>{'message'=>'Search error - invalid token'}}
|
285
285
|
)
|
@@ -294,7 +294,7 @@ module Rhosync
|
|
294
294
|
@source_sync.search(@client.id,search_params) if params.nil? or !params[:token]
|
295
295
|
res,diffsize = compute_search
|
296
296
|
formatted_res = _format_search_result(res,diffsize)
|
297
|
-
|
297
|
+
_delete_search if diffsize == 0
|
298
298
|
formatted_res
|
299
299
|
end
|
300
300
|
|
@@ -361,6 +361,12 @@ module Rhosync
|
|
361
361
|
end
|
362
362
|
@client.flash_data("update_rollback_page")
|
363
363
|
end
|
364
|
+
|
365
|
+
def _delete_search
|
366
|
+
[:search, :search_page, :search_token, :search_errors].each do |search_doc|
|
367
|
+
@client.flash_data(search_doc)
|
368
|
+
end
|
369
|
+
end
|
364
370
|
|
365
371
|
def _send_errors
|
366
372
|
res = {}
|
@@ -15,9 +15,13 @@ module RhosyncConsole
|
|
15
15
|
end
|
16
16
|
|
17
17
|
class Server < Sinatra::Base
|
18
|
-
set :views,
|
19
|
-
|
20
|
-
|
18
|
+
set :views, RhosyncConsole::root_path("app","views")
|
19
|
+
if Sinatra.const_defined?("VERSION") && Gem::Version.new(Sinatra::VERSION) >= Gem::Version.new("1.3.0")
|
20
|
+
set :public_folder, RhosyncConsole::root_path("app","public")
|
21
|
+
else
|
22
|
+
set :public, RhosyncConsole::root_path("app","public")
|
23
|
+
end
|
24
|
+
set :static, true
|
21
25
|
use Rack::Session::Cookie
|
22
26
|
before do
|
23
27
|
headers['Expires'] = 'Sun, 19 Nov 1978 05:00:00 GMT'
|
data/lib/rhosync/read_state.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
module Rhosync
|
2
2
|
class ReadState < Model
|
3
3
|
field :refresh_time, :integer
|
4
|
+
field :prev_refresh_time, :integer
|
4
5
|
field :retry_counter, :integer
|
5
6
|
|
6
7
|
def self.create(fields)
|
@@ -9,6 +10,7 @@ module Rhosync
|
|
9
10
|
fields.delete(:user_id)
|
10
11
|
fields.delete(:source_name)
|
11
12
|
fields[:refresh_time] ||= Time.now.to_i
|
13
|
+
fields[:prev_refresh_time] ||= Time.now.to_i
|
12
14
|
fields[:retry_counter] ||= 0
|
13
15
|
super(fields,{})
|
14
16
|
end
|
data/lib/rhosync/server.rb
CHANGED
@@ -4,6 +4,7 @@ require 'erb'
|
|
4
4
|
require 'json'
|
5
5
|
require 'fileutils'
|
6
6
|
require 'rhosync'
|
7
|
+
require 'rhosync/body_content_type_parser'
|
7
8
|
|
8
9
|
module Rhosync
|
9
10
|
|
@@ -17,15 +18,17 @@ module Rhosync
|
|
17
18
|
|
18
19
|
class Server < Sinatra::Base
|
19
20
|
libdir = File.dirname(File.expand_path(__FILE__))
|
20
|
-
set :views,
|
21
|
-
|
22
|
-
|
21
|
+
set :views, "#{libdir}/server/views"
|
22
|
+
if Sinatra.const_defined?("VERSION") && Gem::Version.new(Sinatra::VERSION) >= Gem::Version.new("1.3.0")
|
23
|
+
set :public_folder, "#{libdir}/server/public"
|
24
|
+
else
|
25
|
+
set :public, "#{libdir}/server/public"
|
26
|
+
end
|
27
|
+
set :static, true
|
28
|
+
set :stats, false
|
23
29
|
|
24
30
|
# default secret
|
25
31
|
@@secret = '<changeme>'
|
26
|
-
|
27
|
-
# stats middleware disabled by default
|
28
|
-
@@stats = false
|
29
32
|
|
30
33
|
# Setup route and mimetype for bulk data downloads
|
31
34
|
# TODO: Figure out why "mime :data, 'application/octet-stream'" doesn't work
|
@@ -60,7 +63,7 @@ module Rhosync
|
|
60
63
|
def login
|
61
64
|
if params[:login] == 'rhoadmin'
|
62
65
|
user = User.authenticate(params[:login], params[:password])
|
63
|
-
elsif current_app and current_app.can_authenticate?
|
66
|
+
elsif current_app and current_app.can_authenticate? and params[:login]
|
64
67
|
user = current_app.authenticate(params[:login], params[:password], session)
|
65
68
|
end
|
66
69
|
if user
|
@@ -142,26 +145,25 @@ module Rhosync
|
|
142
145
|
|
143
146
|
# hook into new so we can enable middleware
|
144
147
|
def self.new
|
145
|
-
|
148
|
+
use Rhosync::BodyContentTypeParser
|
149
|
+
if settings.respond_to?(:stats) and settings.send(:stats) == true
|
146
150
|
use Rhosync::Stats::Middleware
|
147
|
-
|
151
|
+
Rhosync.stats = true
|
152
|
+
else
|
153
|
+
Rhosync::Server.disable :stats
|
154
|
+
Rhosync.stats = false
|
148
155
|
end
|
156
|
+
Rhosync::Server.set :secret, @@secret unless settings.respond_to?(:secret)
|
149
157
|
use Rack::Session::Cookie,
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
super
|
154
|
-
end
|
155
|
-
|
156
|
-
def self.set(option, value=self, &block)
|
157
|
-
@@stats = value if option == :stats and (value.is_a?(TrueClass) or value.is_a?(FalseClass))
|
158
|
-
@@secret = value if option == :secret and value.is_a?(String)
|
158
|
+
:key => 'rhosync_session',
|
159
|
+
:expire_after => 31536000,
|
160
|
+
:secret => Rhosync::Server.secret
|
159
161
|
super
|
160
162
|
end
|
161
163
|
|
162
164
|
def initialize
|
163
165
|
# Whine about default session secret
|
164
|
-
check_default_secret!(
|
166
|
+
check_default_secret!(Rhosync::Server.secret)
|
165
167
|
super
|
166
168
|
end
|
167
169
|
|
@@ -173,11 +175,6 @@ module Rhosync
|
|
173
175
|
cud = JSON.parse(params["cud"])
|
174
176
|
params.delete("cud")
|
175
177
|
params.merge!(cud)
|
176
|
-
end
|
177
|
-
#application/json; charset=UTF-8
|
178
|
-
if request.env['CONTENT_TYPE'] && request.env['CONTENT_TYPE'].match(/^application\/json/)
|
179
|
-
params.merge!(JSON.parse(request.body.read))
|
180
|
-
request.body.rewind
|
181
178
|
end
|
182
179
|
rescue JSON::ParserError => jpe
|
183
180
|
log jpe.message + jpe.backtrace.join("\n")
|
@@ -189,7 +186,6 @@ module Rhosync
|
|
189
186
|
if params[:version] and params[:version].to_i < 3
|
190
187
|
throw :halt, [404, "Server supports version 3 or higher of the protocol."]
|
191
188
|
end
|
192
|
-
#log "request params: #{params.inspect}"
|
193
189
|
end
|
194
190
|
|
195
191
|
%w[get post].each do |verb|
|
data/lib/rhosync/source.rb
CHANGED
@@ -265,40 +265,50 @@ module Rhosync
|
|
265
265
|
end
|
266
266
|
|
267
267
|
def if_need_refresh(client_id=nil,params=nil)
|
268
|
-
need_refresh =
|
268
|
+
need_refresh = lock(:md) do |s|
|
269
|
+
check = check_refresh_time
|
270
|
+
self.read_state.prev_refresh_time = self.read_state.refresh_time if check
|
271
|
+
self.read_state.refresh_time = Time.now.to_i + self.poll_interval if check
|
272
|
+
check
|
273
|
+
end
|
269
274
|
yield client_id,params if need_refresh
|
270
275
|
end
|
271
276
|
|
272
|
-
def
|
273
|
-
if self.poll_interval == 0
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
277
|
+
def rewind_refresh_time(query_failure)
|
278
|
+
return if self.poll_interval == 0
|
279
|
+
lock(:md) do |s|
|
280
|
+
rewind_time = false
|
281
|
+
# reset number of retries
|
282
|
+
# and prev_refresh_time on succesfull query
|
283
|
+
# or if last refresh was more than 'poll_interval' time ago
|
284
|
+
if not query_failure or ((Time.now.to_i - self.read_state.prev_refresh_time) >= self.poll_interval)
|
285
|
+
# we need to reset the prev_refresh_time here
|
286
|
+
# otherwise in case of expired poll interval
|
287
|
+
# and repeating failures - it will reset the counter
|
288
|
+
# on every error
|
289
|
+
self.read_state.prev_refresh_time = Time.now.to_i
|
290
|
+
self.read_state.retry_counter = 0
|
291
|
+
end
|
284
292
|
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
293
|
+
# rewind the refresh time on failure
|
294
|
+
# if retry limit is not reached
|
295
|
+
if query_failure
|
296
|
+
if self.read_state.retry_counter < self.retry_limit
|
297
|
+
self.read_state.increment!(:retry_counter)
|
298
|
+
rewind_time = true
|
299
|
+
# we have reached the limit - do not rewind the refresh time
|
300
|
+
# and reset the counter
|
301
|
+
else
|
302
|
+
self.read_state.retry_counter = 0
|
303
|
+
end
|
295
304
|
end
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
305
|
+
|
306
|
+
if rewind_time
|
307
|
+
self.read_state.refresh_time = self.read_state.prev_refresh_time
|
308
|
+
end
|
309
|
+
end
|
300
310
|
end
|
301
|
-
|
311
|
+
|
302
312
|
private
|
303
313
|
def poll_interval_key
|
304
314
|
"source:#{self.name}:poll_interval"
|
@@ -41,16 +41,20 @@ module Rhosync
|
|
41
41
|
|
42
42
|
def sync
|
43
43
|
if @result and @result.empty?
|
44
|
-
@source.
|
45
|
-
|
44
|
+
@source.lock(:md) do |s|
|
45
|
+
s.flash_data(:md)
|
46
|
+
s.put_value(:md_size,0)
|
47
|
+
end
|
46
48
|
else
|
47
49
|
if @result
|
48
50
|
Store.put_data(@tmp_docname,@result)
|
49
51
|
@stash_size += @result.size
|
50
52
|
end
|
51
|
-
@source.
|
52
|
-
|
53
|
-
|
53
|
+
@source.lock(:md) do |s|
|
54
|
+
s.flash_data(:md)
|
55
|
+
Store.rename(@tmp_docname,s.docname(:md))
|
56
|
+
s.put_value(:md_size,@stash_size)
|
57
|
+
end
|
54
58
|
end
|
55
59
|
end
|
56
60
|
|
data/lib/rhosync/source_sync.rb
CHANGED
@@ -59,19 +59,19 @@ module Rhosync
|
|
59
59
|
end
|
60
60
|
|
61
61
|
def do_query(params=nil)
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
end
|
69
|
-
# update refresh time
|
70
|
-
query_failure = Store.get_keys(@source.docname(:errors)).size > 0
|
71
|
-
@source.update_refresh_time(query_failure)
|
62
|
+
result = nil
|
63
|
+
@source.if_need_refresh do
|
64
|
+
Stats::Record.update("source:query:#{@source.name}") do
|
65
|
+
if _auth_op('login')
|
66
|
+
result = self.read(nil,params)
|
67
|
+
_auth_op('logoff')
|
72
68
|
end
|
69
|
+
# re-wind refresh time in case of error
|
70
|
+
query_failure = Store.exists?(@source.docname(:errors))
|
71
|
+
@source.rewind_refresh_time(query_failure)
|
73
72
|
end
|
74
73
|
end
|
74
|
+
result
|
75
75
|
end
|
76
76
|
|
77
77
|
# Enqueue a job for the source based on job type
|
data/lib/rhosync/store.rb
CHANGED
@@ -180,10 +180,23 @@ module Rhosync
|
|
180
180
|
|
181
181
|
# Deletes all keys matching a given mask
|
182
182
|
def flash_data(keymask)
|
183
|
-
|
184
|
-
|
183
|
+
if keymask[/[*\[\]?]/]
|
184
|
+
# If the keymask contains any pattern matching characters
|
185
|
+
# Use keys command to find all keys matching pattern (this is extremely expensive)
|
186
|
+
# Then delete matches
|
187
|
+
@@db.keys(keymask).each do |key|
|
188
|
+
@@db.del(key)
|
189
|
+
end
|
190
|
+
else
|
191
|
+
# The keymask doesn't contain pattern matching characters
|
192
|
+
# A delete call is all that is needed
|
193
|
+
@@db.del(keymask)
|
185
194
|
end
|
186
195
|
end
|
196
|
+
|
197
|
+
def exists?(key)
|
198
|
+
@@db.exists(key)
|
199
|
+
end
|
187
200
|
|
188
201
|
# Returns array of keys matching a given keymask
|
189
202
|
def get_keys(keymask)
|
@@ -209,19 +222,27 @@ module Rhosync
|
|
209
222
|
ts = current_time+(Rhosync.lock_duration || timeout)+1
|
210
223
|
loop do
|
211
224
|
if not @@db.setnx(lock_key,ts)
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
+
current_lock = @@db.get(lock_key)
|
226
|
+
# ensure lock wasn't released between the setnx and get calls
|
227
|
+
if current_lock
|
228
|
+
current_lock_timeout = current_lock.to_i
|
229
|
+
if raise_on_expire or Rhosync.raise_on_expired_lock
|
230
|
+
if current_lock_timeout <= current_time
|
231
|
+
# lock expired before operation which set it up completed
|
232
|
+
# this process cannot continue without corrupting locked data
|
233
|
+
raise StoreLockException, "Lock \"#{lock_key}\" expired before it was released"
|
234
|
+
end
|
235
|
+
else
|
236
|
+
if current_lock_timeout <= current_time and
|
237
|
+
@@db.getset(lock_key,ts).to_i <= current_time
|
238
|
+
# previous lock expired and we replaced it with our own
|
239
|
+
break
|
240
|
+
end
|
241
|
+
end
|
242
|
+
# lock was released between setnx and get - try to acquire it again
|
243
|
+
elsif @@db.setnx(lock_key,ts)
|
244
|
+
break
|
245
|
+
end
|
225
246
|
sleep(1)
|
226
247
|
current_time = Time.now.to_i
|
227
248
|
else
|
data/lib/rhosync/version.rb
CHANGED
data/spec/api/stats_spec.rb
CHANGED
@@ -3,12 +3,16 @@ require File.join(File.dirname(__FILE__),'api_helper')
|
|
3
3
|
describe "RhosyncApiStats" do
|
4
4
|
it_should_behave_like "ApiHelper"
|
5
5
|
|
6
|
+
def app
|
7
|
+
@app = Rhosync::Server.new
|
8
|
+
end
|
9
|
+
|
6
10
|
before(:each) do
|
7
|
-
Rhosync.stats
|
11
|
+
Rhosync::Server.set :stats, true
|
8
12
|
end
|
9
13
|
|
10
14
|
after(:each) do
|
11
|
-
Rhosync.stats
|
15
|
+
Rhosync::Server.set :stats, false
|
12
16
|
end
|
13
17
|
|
14
18
|
it "should retrieve metric names" do
|
@@ -55,6 +59,7 @@ describe "RhosyncApiStats" do
|
|
55
59
|
end
|
56
60
|
|
57
61
|
it "should raise error if stats not enabled" do
|
62
|
+
Rhosync::Server.set :stats, false
|
58
63
|
Rhosync.stats = false
|
59
64
|
post "/api/stats", {
|
60
65
|
:api_token => @api_token,
|
data/spec/server/server_spec.rb
CHANGED
@@ -48,10 +48,10 @@ describe "Server" do
|
|
48
48
|
User.load('new_user').should_not be_nil
|
49
49
|
end
|
50
50
|
|
51
|
-
it "should
|
51
|
+
it "should fail to login if wrong content-type" do
|
52
52
|
User.load('unknown').should be_nil
|
53
53
|
post "/login", {"login" => 'unknown', "password" => 'testpass'}.to_json, {'CONTENT_TYPE'=>'application/x-www-form-urlencoded'}
|
54
|
-
last_response.
|
54
|
+
last_response.should_not be_ok
|
55
55
|
User.load('unknown').should be_nil
|
56
56
|
end
|
57
57
|
|
@@ -255,11 +255,10 @@ describe "Server" do
|
|
255
255
|
last_response.body.should == "Server error while processing client data"
|
256
256
|
end
|
257
257
|
|
258
|
-
it "should
|
259
|
-
|
260
|
-
|
261
|
-
last_response.
|
262
|
-
last_response.body.should == "Internal server error"
|
258
|
+
it "should not login if login is empty" do
|
259
|
+
post "/application/clientlogin", ''
|
260
|
+
last_response.status.should == 401
|
261
|
+
last_response.body.should == ""
|
263
262
|
end
|
264
263
|
|
265
264
|
it "should get inserts json" do
|
data/spec/source_sync_spec.rb
CHANGED
@@ -219,13 +219,13 @@ describe "SourceSync" do
|
|
219
219
|
# 1) if retry_limit is set to N - then, first N retries should not update refresh_time
|
220
220
|
@s.read_state.retry_counter.should == 1
|
221
221
|
@s.read_state.refresh_time.should <= Time.now.to_i
|
222
|
-
|
222
|
+
|
223
223
|
# try once more and fail again
|
224
224
|
set_test_data('test_db_storage',{},msg,"query error")
|
225
225
|
res = @ss.do_query
|
226
226
|
verify_result(@s.docname(:md) => {},
|
227
227
|
@s.docname(:errors) => {'query-error'=>{'message'=>msg}})
|
228
|
-
|
228
|
+
|
229
229
|
# 2) if retry_limit is set to N and number of retries exceeded it - update refresh_time
|
230
230
|
@s.read_state.retry_counter.should == 0
|
231
231
|
@s.read_state.refresh_time.should > Time.now.to_i
|
@@ -241,7 +241,7 @@ describe "SourceSync" do
|
|
241
241
|
# 1) if retry_limit is set to N - then, first N retries should not update refresh_time
|
242
242
|
@s.read_state.retry_counter.should == 1
|
243
243
|
@s.read_state.refresh_time.should <= Time.now.to_i
|
244
|
-
|
244
|
+
|
245
245
|
# try once more (with success)
|
246
246
|
expected = {'1'=>@product1,'2'=>@product2}
|
247
247
|
set_test_data('test_db_storage',expected)
|
@@ -251,7 +251,40 @@ describe "SourceSync" do
|
|
251
251
|
@s.read_state.retry_counter.should == 0
|
252
252
|
@s.read_state.refresh_time.should > Time.now.to_i
|
253
253
|
end
|
254
|
-
|
254
|
+
|
255
|
+
it "should reset the retry counter if prev_refresh_time was set more than poll_interval secs ago" do
|
256
|
+
@s.retry_limit = 3
|
257
|
+
@s.poll_interval = 2
|
258
|
+
msg = "Error during query"
|
259
|
+
set_test_data('test_db_storage',{},msg,"query error")
|
260
|
+
res = @ss.do_query
|
261
|
+
verify_result(@s.docname(:md) => {},
|
262
|
+
@s.docname(:errors) => {'query-error'=>{'message'=>msg}})
|
263
|
+
# 1) if retry_limit is set to N - then, first N retries should not update refresh_time
|
264
|
+
@s.read_state.retry_counter.should == 1
|
265
|
+
@s.read_state.refresh_time.should <= Time.now.to_i
|
266
|
+
|
267
|
+
# 2) Make another error - results are the same
|
268
|
+
set_test_data('test_db_storage',{},msg,"query error")
|
269
|
+
res = @ss.do_query
|
270
|
+
verify_result(@s.docname(:md) => {},
|
271
|
+
@s.docname(:errors) => {'query-error'=>{'message'=>msg}})
|
272
|
+
# 1) if retry_limit is set to N - then, first N retries should not update refresh_time
|
273
|
+
@s.read_state.retry_counter.should == 2
|
274
|
+
@s.read_state.refresh_time.should <= Time.now.to_i
|
275
|
+
|
276
|
+
# wait until time interval exprires and prev_refresh_time is too old -
|
277
|
+
# this should reset the counter on next request with error
|
278
|
+
# and do not update refresh_time
|
279
|
+
sleep(3)
|
280
|
+
set_test_data('test_db_storage',{},msg,"query error")
|
281
|
+
res = @ss.do_query
|
282
|
+
verify_result(@s.docname(:md) => {},
|
283
|
+
@s.docname(:errors) => {'query-error'=>{'message'=>msg}})
|
284
|
+
@s.read_state.retry_counter.should == 1
|
285
|
+
@s.read_state.refresh_time.should <= Time.now.to_i
|
286
|
+
end
|
287
|
+
|
255
288
|
it "should do query with exception raised and update refresh time if retry_limit is 0" do
|
256
289
|
@s.retry_limit = 0
|
257
290
|
msg = "Error during query"
|
@@ -263,7 +296,7 @@ describe "SourceSync" do
|
|
263
296
|
@s.read_state.retry_counter.should == 0
|
264
297
|
@s.read_state.refresh_time.should > Time.now.to_i
|
265
298
|
end
|
266
|
-
|
299
|
+
|
267
300
|
it "should do query with exception raised and update refresh time if poll_interval == 0" do
|
268
301
|
@s.retry_limit = 1
|
269
302
|
@s.poll_interval = 0
|
@@ -292,6 +325,7 @@ describe "SourceSync" do
|
|
292
325
|
verify_result("source:#{@test_app_name}:__shared__:#{@s_fields[:name]}:md" => expected)
|
293
326
|
Store.db.keys("read_state:#{@test_app_name}:__shared__*").sort.should ==
|
294
327
|
[ "read_state:#{@test_app_name}:__shared__:SampleAdapter:refresh_time",
|
328
|
+
"read_state:#{@test_app_name}:__shared__:SampleAdapter:prev_refresh_time",
|
295
329
|
"read_state:#{@test_app_name}:__shared__:SampleAdapter:retry_counter",
|
296
330
|
"read_state:#{@test_app_name}:__shared__:SampleAdapter:rho__id"].sort
|
297
331
|
end
|
data/spec/store_spec.rb
CHANGED
@@ -167,6 +167,29 @@ describe "Store" do
|
|
167
167
|
Store.get_data(@s.docname(:md)).should == {}
|
168
168
|
end
|
169
169
|
|
170
|
+
it "should flash_data for all keys matching pattern" do
|
171
|
+
keys = ['test_flash_data1','test_flash_data2']
|
172
|
+
keys.each {|key| Store.put_data(key,@data)}
|
173
|
+
Store.flash_data('test_flash_data*')
|
174
|
+
keys.each {|key| Store.get_data(key).should == {} }
|
175
|
+
end
|
176
|
+
|
177
|
+
it "should flash_data without calling KEYS when there aren't pattern matching characters in the provided keymask" do
|
178
|
+
key = 'test_flash_data'
|
179
|
+
Store.put_data(key,@data)
|
180
|
+
Store.db.should_not_receive(:keys)
|
181
|
+
Store.db.should_receive(:del).once.with(key).and_return(true)
|
182
|
+
Store.flash_data(key)
|
183
|
+
end
|
184
|
+
|
185
|
+
it "should flash_data and call KEYS when there are pattern matching characters in the provided keymask" do
|
186
|
+
keys = ['test_flash_data1','test_flash_data2']
|
187
|
+
keys.each {|key| Store.put_data(key,@data)}
|
188
|
+
Store.db.should_receive(:keys).exactly(1).times.with("test_flash_data*").and_return(keys)
|
189
|
+
Store.db.should_receive(:del).exactly(2).times.with(/^test_flash_data[12]$/).and_return(true)
|
190
|
+
Store.flash_data("test_flash_data*")
|
191
|
+
end
|
192
|
+
|
170
193
|
it "should get_keys" do
|
171
194
|
expected = ["doc1:1:1:1:source1", "doc1:1:1:1:source2"]
|
172
195
|
Store.put_data(expected[0],@data)
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rhosync
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 19
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 2
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 2.1.
|
9
|
+
- 12
|
10
|
+
version: 2.1.12
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Rhomobile
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-10-
|
18
|
+
date: 2011-10-14 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: sinatra
|
@@ -23,7 +23,7 @@ dependencies:
|
|
23
23
|
requirement: &id001 !ruby/object:Gem::Requirement
|
24
24
|
none: false
|
25
25
|
requirements:
|
26
|
-
- - "
|
26
|
+
- - ">="
|
27
27
|
- !ruby/object:Gem::Version
|
28
28
|
hash: 17
|
29
29
|
segments:
|
@@ -372,6 +372,7 @@ files:
|
|
372
372
|
- lib/rhosync/api/upload_file.rb
|
373
373
|
- lib/rhosync/api_token.rb
|
374
374
|
- lib/rhosync/app.rb
|
375
|
+
- lib/rhosync/body_content_type_parser.rb
|
375
376
|
- lib/rhosync/bulk_data.rb
|
376
377
|
- lib/rhosync/bulk_data/bulk_data.rb
|
377
378
|
- lib/rhosync/bulk_data/syncdb.index.schema
|