rhosync 2.1.11 → 2.1.12
Sign up to get free protection for your applications and to get access to all the features.
- 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
|