rhoconnect 3.0.6 → 3.1.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +9 -0
- data/Gemfile +3 -3
- data/Gemfile.lock +38 -17
- data/Rakefile +0 -10
- data/bench/benchapp/Gemfile.lock +1 -0
- data/bench/distr_bench/distr_bench_main +94 -27
- data/bench/distr_bench/run_test_query_script.sh +22 -18
- data/bench/lib/bench/aws_utils.rb +326 -0
- data/bench/lib/bench/bench_result_processor.rb +268 -75
- data/bench/lib/bench/cli.rb +1 -0
- data/bench/lib/bench/distr_runner.rb +102 -0
- data/bench/lib/bench/utils.rb +127 -0
- data/bench/lib/bench.rb +16 -15
- data/bench/prepare_bench +3 -11
- data/bench/scripts/test_query_script.rb +6 -7
- data/bin/rhoconnect-benchmark +257 -5
- data/doc/benchmarks-running.txt +140 -0
- data/doc/client-java.txt +236 -0
- data/doc/client-objc.txt +41 -1
- data/doc/client.txt +12 -0
- data/doc/command-line.txt +12 -3
- data/doc/cud-conflicts.txt +68 -0
- data/doc/deploying.txt +1 -70
- data/doc/hosting-rhohub.txt +3 -0
- data/doc/install.txt +50 -13
- data/doc/java-plugin.txt +217 -177
- data/doc/net-plugin.txt +97 -64
- data/doc/plugin-intro.txt +4 -2
- data/doc/preparing-production.txt +63 -0
- data/doc/rhoconnect-redis-stack.txt +252 -0
- data/doc/source-adapters.txt +3 -1
- data/doc/tutorial.txt +111 -49
- data/examples/simple/dump.rdb +0 -0
- data/installer/unix-like/rho_connect_install_constants.rb +6 -5
- data/installer/unix-like/rho_connect_install_installers.rb +6 -2
- data/installer/utils/nix_install_test.rb +2 -0
- data/installer/utils/package_upload/auto-repo.rb +136 -0
- data/installer/utils/package_upload/repos.rake +6 -3
- data/installer/utils/package_upload/s3_upload.rb +11 -6
- data/installer/windows/rhosync.nsi +5 -5
- data/lib/rhoconnect/client_sync.rb +2 -2
- data/lib/rhoconnect/document.rb +12 -0
- data/lib/rhoconnect/jobs/source_job.rb +2 -2
- data/lib/rhoconnect/predefined_adapters/bench_adapter.rb +61 -0
- data/lib/rhoconnect/source.rb +5 -0
- data/lib/rhoconnect/source_adapter.rb +10 -1
- data/lib/rhoconnect/source_sync.rb +161 -88
- data/lib/rhoconnect/store.rb +48 -0
- data/lib/rhoconnect/test_methods.rb +6 -6
- data/lib/rhoconnect/version.rb +1 -1
- data/lib/rhoconnect.rb +25 -2
- data/spec/apps/rhotestapp/sources/sample_adapter.rb +29 -0
- data/spec/jobs/source_job_spec.rb +5 -5
- data/spec/source_adapter_spec.rb +10 -0
- data/spec/source_sync_spec.rb +114 -33
- data/spec/spec_helper.rb +21 -2
- data/spec/store_spec.rb +29 -0
- data/spec/support/shared_examples.rb +1 -1
- data/spec/test_methods_spec.rb +4 -4
- data/tasks/redis.rake +2 -2
- metadata +59 -59
- data/bench/benchapp/log/passenger.3000.log +0 -1
- data/bench/benchapp/log/passenger.9292.log +0 -59
- data/bench/benchapp/tmp/pids/passenger.3000.pid.lock +0 -0
- data/bench/benchapp/tmp/pids/passenger.9292.pid.lock +0 -0
- data/bench/lib/testdata/0-data.txt +0 -0
- data/bench/lib/testdata/1-data.txt +0 -0
- data/bench/lib/testdata/10-data.txt +0 -15
- data/bench/lib/testdata/2-data.txt +0 -3
- data/bench/lib/testdata/25-data.txt +0 -39
- data/bench/lib/testdata/250-data.txt +0 -353
- data/bench/lib/testdata/3-data.txt +0 -4
- data/bench/lib/testdata/50-data.txt +0 -70
- data/bench/lib/testdata/500-data.txt +0 -711
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'rest_client'
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
module Rhoconnect
|
6
|
+
# register the adapter as pre-defined
|
7
|
+
Rhoconnect.register_predefined_source('RhoInternalBenchmarkAdapter')
|
8
|
+
|
9
|
+
class RhoInternalBenchmarkAdapter < Rhoconnect::SourceAdapter
|
10
|
+
def initialize(source)
|
11
|
+
super(source)
|
12
|
+
end
|
13
|
+
|
14
|
+
def login
|
15
|
+
true
|
16
|
+
end
|
17
|
+
|
18
|
+
def query(params=nil)
|
19
|
+
Rhoconnect::Store.lock(lock_name,1) do
|
20
|
+
@result = Rhoconnect::Store.get_data(db_name)
|
21
|
+
end
|
22
|
+
@result
|
23
|
+
end
|
24
|
+
|
25
|
+
def create(create_hash)
|
26
|
+
id = create_hash['mock_id']
|
27
|
+
Rhoconnect::Store.lock(lock_name,1) do
|
28
|
+
Rhoconnect::Store.put_data(db_name,{id=>create_hash},true) if id
|
29
|
+
end
|
30
|
+
id
|
31
|
+
end
|
32
|
+
|
33
|
+
def update(update_hash)
|
34
|
+
id = update_hash.delete('id')
|
35
|
+
return unless id
|
36
|
+
Rhoconnect::Store.lock(lock_name,1) do
|
37
|
+
data = Rhoconnect::Store.get_data(db_name)
|
38
|
+
return unless data and data[id]
|
39
|
+
update_hash.each do |attrib,value|
|
40
|
+
data[id][attrib] = value
|
41
|
+
end
|
42
|
+
Rhoconnect::Store.put_data(db_name,data)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def delete(delete_hash)
|
47
|
+
id = delete_hash.delete('id')
|
48
|
+
Rhoconnect::Store.lock(lock_name,1) do
|
49
|
+
Rhoconnect::Store.delete_data(db_name,{id=>delete_hash}) if id
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def db_name
|
54
|
+
"test_db_storage:#{@source.app_id}:#{@source.user_id}"
|
55
|
+
end
|
56
|
+
|
57
|
+
def lock_name
|
58
|
+
"lock:#{db_name}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/rhoconnect/source.rb
CHANGED
@@ -127,6 +127,11 @@ module Rhoconnect
|
|
127
127
|
|
128
128
|
def self.load(obj_id,params)
|
129
129
|
validate_attributes(params)
|
130
|
+
|
131
|
+
# if source is pre-defined
|
132
|
+
# create it dynamically here
|
133
|
+
Rhoconnect.create_predefined_source(obj_id,params)
|
134
|
+
|
130
135
|
model_hash = @@model_data[obj_id.to_sym]
|
131
136
|
obj = new(model_hash) if model_hash
|
132
137
|
if obj
|
@@ -27,9 +27,13 @@ module Rhoconnect
|
|
27
27
|
source.name = source.name.dup if source.name.frozen?
|
28
28
|
source.name.strip!
|
29
29
|
end
|
30
|
+
# certain adapters are pre-defined and reserved
|
31
|
+
# load them and instantiate
|
32
|
+
if Rhoconnect.predefined_sources.has_key?(source.name)
|
33
|
+
adapter=(Object.const_get(source.name)).new(source)
|
34
|
+
elsif Object.const_defined?(source.name) && Object.const_get(source.name).to_s.split("::").first != 'Rhoconnect'
|
30
35
|
# fix until source adpaters are phased out, checking for Rhoconnect namespace
|
31
36
|
# so that backend models with same name as Rhoconnect models are instantiated correctly
|
32
|
-
if Object.const_defined?(source.name) && Object.const_get(source.name).to_s.split("::").first != 'Rhoconnect'
|
33
37
|
require under_score(source.name)
|
34
38
|
adapter=(Object.const_get(source.name)).new(source)
|
35
39
|
else
|
@@ -95,6 +99,11 @@ module Rhoconnect
|
|
95
99
|
Rhoconnect.expire_bulk_data(current_user.login,partition)
|
96
100
|
end
|
97
101
|
|
102
|
+
# do pre-processing before CUD operation
|
103
|
+
def validate(operation,operations_hashes,client_ids)
|
104
|
+
{}
|
105
|
+
end
|
106
|
+
|
98
107
|
def create(create_hash); end
|
99
108
|
|
100
109
|
def update(update_hash); end
|
@@ -10,16 +10,16 @@ module Rhoconnect
|
|
10
10
|
end
|
11
11
|
|
12
12
|
# CUD Operations
|
13
|
-
def create
|
14
|
-
_measure_and_process_cud('create'
|
13
|
+
def create
|
14
|
+
_measure_and_process_cud('create')
|
15
15
|
end
|
16
16
|
|
17
|
-
def update
|
18
|
-
_measure_and_process_cud('update'
|
17
|
+
def update
|
18
|
+
_measure_and_process_cud('update')
|
19
19
|
end
|
20
20
|
|
21
|
-
def delete
|
22
|
-
_measure_and_process_cud('delete'
|
21
|
+
def delete
|
22
|
+
_measure_and_process_cud('delete')
|
23
23
|
end
|
24
24
|
|
25
25
|
# Pass through CUD to adapter, no data stored
|
@@ -64,25 +64,25 @@ module Rhoconnect
|
|
64
64
|
res
|
65
65
|
end
|
66
66
|
|
67
|
-
def process_cud
|
67
|
+
def process_cud
|
68
68
|
if @source.cud_queue or @source.queue
|
69
|
-
async(:cud,@source.cud_queue || @source.queue
|
69
|
+
async(:cud,@source.cud_queue || @source.queue)
|
70
70
|
else
|
71
|
-
do_cud
|
71
|
+
do_cud
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
75
|
-
def do_cud
|
75
|
+
def do_cud
|
76
76
|
return if _auth_op('login') == false
|
77
|
-
self.create
|
78
|
-
self.update
|
79
|
-
self.delete
|
77
|
+
self.create
|
78
|
+
self.update
|
79
|
+
self.delete
|
80
80
|
_auth_op('logoff')
|
81
81
|
end
|
82
82
|
|
83
83
|
def process_query(params=nil)
|
84
84
|
if @source.query_queue or @source.queue
|
85
|
-
async(:query,@source.query_queue || @source.queue,
|
85
|
+
async(:query,@source.query_queue || @source.queue,params)
|
86
86
|
else
|
87
87
|
do_query(params)
|
88
88
|
end
|
@@ -105,10 +105,10 @@ module Rhoconnect
|
|
105
105
|
end
|
106
106
|
|
107
107
|
# Enqueue a job for the source based on job type
|
108
|
-
def async(job_type,queue_name,
|
108
|
+
def async(job_type,queue_name,params=nil)
|
109
109
|
SourceJob.queue = queue_name
|
110
110
|
Resque.enqueue(SourceJob,job_type,@source.id,
|
111
|
-
@source.app_id,@source.user_id,
|
111
|
+
@source.app_id,@source.user_id,params)
|
112
112
|
end
|
113
113
|
|
114
114
|
def push_objects(objects,timeout=10,raise_on_expire=false,rebuild_md=true)
|
@@ -180,108 +180,181 @@ module Rhoconnect
|
|
180
180
|
true
|
181
181
|
end
|
182
182
|
|
183
|
-
def _process_create(
|
184
|
-
# Perform operation
|
183
|
+
def _process_create(modified_recs,key,value)
|
185
184
|
link = @adapter.create value
|
186
185
|
# Store object-id link for the client
|
187
186
|
# If we have a link, store object in client document
|
188
187
|
# Otherwise, store object for delete on client
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
188
|
+
modified_recs.each do |modified_rec|
|
189
|
+
if link
|
190
|
+
modified_rec[:links] ||= {}
|
191
|
+
modified_rec[:links][modified_rec[:key]] = { 'l' => link.to_s }
|
192
|
+
modified_rec[:creates] ||= {}
|
193
|
+
modified_rec[:creates][link.to_s] = value
|
194
|
+
else
|
195
|
+
modified_rec[:deletes] ||= {}
|
196
|
+
modified_rec[:deletes][modified_rec[:key]] = value
|
197
|
+
end
|
197
198
|
end
|
198
199
|
end
|
199
200
|
|
200
|
-
def _process_update(
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
@adapter.update value
|
206
|
-
rescue Exception => e
|
207
|
-
# TODO: This will be slow!
|
208
|
-
cd = client.get_data(:cd)
|
209
|
-
client.put_data(:update_rollback,{key => cd[key]},true) if cd[key]
|
210
|
-
raise e
|
211
|
-
end
|
201
|
+
def _process_update(modified_recs,key,value)
|
202
|
+
# Add id to object hash to forward to backend call
|
203
|
+
value['id'] = key
|
204
|
+
# Perform operation
|
205
|
+
@adapter.update value
|
212
206
|
end
|
213
207
|
|
214
|
-
def _process_delete(
|
208
|
+
def _process_delete(modified_recs,key,value)
|
215
209
|
value['id'] = key
|
216
|
-
# Perform operation
|
217
210
|
@adapter.delete value
|
218
|
-
|
219
|
-
|
211
|
+
# Perform operation
|
212
|
+
modified_recs.each do |modified_rec|
|
213
|
+
modified_rec[:dels] ||= {}
|
214
|
+
modified_rec[:dels][modified_rec[:key]] = value
|
215
|
+
end
|
220
216
|
end
|
221
217
|
|
222
|
-
def _measure_and_process_cud(operation
|
218
|
+
def _measure_and_process_cud(operation)
|
223
219
|
Stats::Record.update("source:#{operation}:#{@source.name}") do
|
224
|
-
_process_cud(operation
|
220
|
+
_process_cud(operation)
|
225
221
|
end
|
226
222
|
end
|
227
223
|
|
228
|
-
def _process_cud(operation
|
224
|
+
def _process_cud(operation)
|
225
|
+
# take everything from the queue and erase it
|
226
|
+
# so that no other process will be able to process it again
|
227
|
+
operation_hashes = []
|
228
|
+
client_ids = []
|
229
|
+
@source.lock(operation) do |s|
|
230
|
+
operation_hashes,client_ids = s.get_zdata(operation)
|
231
|
+
s.flush_zdata(operation)
|
232
|
+
end
|
233
|
+
invalid_meta = @adapter.validate(operation,operation_hashes,client_ids)
|
234
|
+
|
229
235
|
errors,links,deletes,creates,dels = {},{},{},{},{}
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
236
|
+
operation_hashes.each_with_index do |client_operation_data,index|
|
237
|
+
client_id = client_ids[index]
|
238
|
+
|
239
|
+
current_invalid_meta = invalid_meta[index] || {}
|
240
|
+
client_operation_data.each do |key, value|
|
241
|
+
begin
|
242
|
+
continue_loop = true
|
243
|
+
modified_recs = [{:client_id => client_id, :key => key, :value => value }]
|
244
|
+
record_invalid_meta = current_invalid_meta[key] || {}
|
245
|
+
# Remove object from queue
|
246
|
+
client_operation_data.delete(key)
|
247
|
+
|
248
|
+
# skip the rec - if it is a duplicate of some other record
|
249
|
+
next if record_invalid_meta[:duplicate_of]
|
250
|
+
|
251
|
+
# prepare duplicate docs
|
252
|
+
duplicates = record_invalid_meta[:duplicates] || {}
|
253
|
+
duplicates.each do |duplicate|
|
254
|
+
modified_recs << duplicate
|
255
|
+
end
|
256
|
+
|
257
|
+
# raise conflict error if record is marked with one
|
258
|
+
raise SourceAdapterObjectConflictError.new(record_invalid_meta[:error]) if record_invalid_meta[:error]
|
259
|
+
|
260
|
+
# Call on source adapter to process individual object
|
261
|
+
case operation
|
262
|
+
when 'create'
|
263
|
+
_process_create(modified_recs,key,value)
|
264
|
+
when 'update'
|
265
|
+
_process_update(modified_recs,key,value)
|
266
|
+
when 'delete'
|
267
|
+
_process_delete(modified_recs,key,value)
|
268
|
+
end
|
269
|
+
rescue Exception => e
|
270
|
+
log "SourceAdapter raised #{operation} exception: #{e}"
|
271
|
+
log e.backtrace.join("\n")
|
272
|
+
continue_loop = false
|
273
|
+
modified_recs.each do |modified_rec|
|
274
|
+
modified_rec[:errors] ||= {}
|
275
|
+
modified_rec[:errors][modified_rec[:key]] = modified_rec[:value]
|
276
|
+
modified_rec[:errors]["#{modified_rec[:key]}-error"] = {'message'=>e.message}
|
277
|
+
end
|
245
278
|
end
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
279
|
+
|
280
|
+
{ :errors => errors,
|
281
|
+
:links => links,
|
282
|
+
:deletes => deletes,
|
283
|
+
:creates => creates,
|
284
|
+
:dels => dels }.each do |doc_name, doc|
|
285
|
+
modified_recs.each do |modified_rec|
|
286
|
+
doc[modified_rec[:client_id]] ||= {}
|
287
|
+
next unless modified_rec[doc_name]
|
288
|
+
doc[modified_rec[:client_id]].merge!(modified_rec[doc_name])
|
289
|
+
end
|
290
|
+
end
|
291
|
+
break unless continue_loop
|
292
|
+
end
|
293
|
+
|
294
|
+
# Record rest of queue (if something in the middle failed)
|
295
|
+
if not client_operation_data.empty?
|
296
|
+
@source.put_zdata(operation,client_id,client_operation_data,true)
|
253
297
|
end
|
254
298
|
end
|
255
|
-
|
299
|
+
|
300
|
+
# now, process all the modified docs
|
301
|
+
processed_clients = {}
|
302
|
+
client_ids.each do |client_id|
|
303
|
+
processed_clients[client_id] = Client.load(client_id,{:source_name => @source.name}) unless processed_clients[client_id]
|
304
|
+
end
|
256
305
|
{ "delete_page" => deletes,
|
257
306
|
"#{operation}_links" => links,
|
258
|
-
"#{operation}_errors" => errors }.each do |doctype,
|
259
|
-
|
307
|
+
"#{operation}_errors" => errors }.each do |doctype,client_docs|
|
308
|
+
client_docs.each do |client_id,data|
|
309
|
+
client = processed_clients[client_id]
|
310
|
+
client.put_data(doctype,data,true) unless data.empty?
|
311
|
+
end
|
260
312
|
end
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
313
|
+
|
314
|
+
if operation == 'create'
|
315
|
+
total_creates = {}
|
316
|
+
creates.each do |client_id,client_doc|
|
317
|
+
next if client_doc.empty?
|
318
|
+
client = processed_clients[client_id]
|
319
|
+
client.put_data(:cd,client_doc,true)
|
320
|
+
client.update_count(:cd_size,client_doc.size)
|
321
|
+
total_creates.merge!(client_doc)
|
322
|
+
end
|
323
|
+
unless total_creates.empty?
|
324
|
+
@source.lock(:md) do |s|
|
325
|
+
s.put_data(:md,total_creates,true)
|
326
|
+
s.update_count(:md_size,total_creates.size)
|
327
|
+
end
|
267
328
|
end
|
268
329
|
end
|
330
|
+
|
269
331
|
if operation == 'delete'
|
270
332
|
# Clean up deleted objects from master document and corresponding client document
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
333
|
+
total_dels = {}
|
334
|
+
dels.each do |client_id,client_doc|
|
335
|
+
next if client_doc.empty?
|
336
|
+
client = processed_clients[client_id]
|
337
|
+
client.delete_data(:cd,client_doc)
|
338
|
+
client.update_count(:cd_size,-client_doc.size)
|
339
|
+
total_dels.merge!(client_doc)
|
340
|
+
end
|
341
|
+
unless total_dels.empty?
|
342
|
+
@source.lock(:md) do |s|
|
343
|
+
s.delete_data(:md,total_dels)
|
344
|
+
s.update_count(:md_size,-total_dels.size)
|
345
|
+
end
|
276
346
|
end
|
277
347
|
end
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
348
|
+
if operation == 'update'
|
349
|
+
errors.each do |client_id, error_doc|
|
350
|
+
next if error_doc.empty?
|
351
|
+
client = processed_clients[client_id]
|
352
|
+
cd = client.get_data(:cd)
|
353
|
+
error_doc.each do |key, value|
|
354
|
+
client.put_data(:update_rollback,{key => cd[key]},true) if cd[key]
|
355
|
+
end
|
356
|
+
end
|
357
|
+
end
|
285
358
|
end
|
286
359
|
|
287
360
|
# Metadata Operation; source adapter returns json
|
data/lib/rhoconnect/store.rb
CHANGED
@@ -277,6 +277,54 @@ module Rhoconnect
|
|
277
277
|
@@db.rename(srckey,dstkey) if @@db.exists(srckey)
|
278
278
|
end
|
279
279
|
|
280
|
+
def put_zdata(dockey,assoc_key,data={},append=false)
|
281
|
+
return true unless (dockey and assoc_key and data)
|
282
|
+
flush_zdata(dockey) unless append
|
283
|
+
current_score = 0
|
284
|
+
current_score_data = Store.db.zrevrange(dockey,0,0,:with_scores => true)
|
285
|
+
current_score = current_score_data[-1].to_i if current_score_data
|
286
|
+
current_score += 1
|
287
|
+
Store.db.pipelined do
|
288
|
+
data.each do |key,hash_value|
|
289
|
+
unique_record_key = setelement(current_score,assoc_key, key)
|
290
|
+
Store.db.zadd(dockey, current_score, unique_record_key)
|
291
|
+
Store.put_data(unique_record_key,{key => hash_value})
|
292
|
+
end
|
293
|
+
end
|
294
|
+
true
|
295
|
+
end
|
296
|
+
|
297
|
+
# Retrieves set for given dockey,associated key (client_id), obj_hashes
|
298
|
+
def get_zdata(dockey)
|
299
|
+
data = Store.db.zrange(dockey, 0, -1)
|
300
|
+
ret = []
|
301
|
+
keys = []
|
302
|
+
scores = []
|
303
|
+
data.each do |zsetkey|
|
304
|
+
obj_hash = Store.get_data zsetkey
|
305
|
+
score,key,objkey = getelement(zsetkey)
|
306
|
+
if scores[-1] != score
|
307
|
+
ret << obj_hash
|
308
|
+
keys << key
|
309
|
+
scores << score
|
310
|
+
else
|
311
|
+
ret[-1].merge!(obj_hash)
|
312
|
+
end
|
313
|
+
end
|
314
|
+
[ret, keys]
|
315
|
+
end
|
316
|
+
|
317
|
+
# Deletes all keys and their hashes from the Redis DB
|
318
|
+
def flush_zdata(dockey)
|
319
|
+
data = Store.db.zrange(dockey, 0, -1)
|
320
|
+
Store.db.pipelined do
|
321
|
+
data.each do |hash_key|
|
322
|
+
Store.db.del(hash_key)
|
323
|
+
end
|
324
|
+
end
|
325
|
+
Store.db.zremrangebyrank(dockey, 0, -1)
|
326
|
+
end
|
327
|
+
|
280
328
|
alias_method :set_value, :put_value
|
281
329
|
alias_method :set_data, :put_data
|
282
330
|
|
@@ -132,8 +132,8 @@ module Rhoconnect
|
|
132
132
|
if @s.is_pass_through?
|
133
133
|
@ss.pass_through_cud({'create'=> {'temp-id' => record}},nil)
|
134
134
|
else
|
135
|
-
@
|
136
|
-
@ss.process_cud
|
135
|
+
@s.put_zdata(:create,@c.id,{'temp-id' => record},true)
|
136
|
+
@ss.process_cud
|
137
137
|
links = @c.get_data(:create_links)['temp-id']
|
138
138
|
links ? links['l'] : nil
|
139
139
|
end
|
@@ -161,8 +161,8 @@ module Rhoconnect
|
|
161
161
|
if @s.is_pass_through?
|
162
162
|
@ss.pass_through_cud({'update'=> record },nil)
|
163
163
|
else
|
164
|
-
@
|
165
|
-
@ss.process_cud
|
164
|
+
@s.put_zdata(:update,@c.id,record,true)
|
165
|
+
@ss.process_cud
|
166
166
|
end
|
167
167
|
end
|
168
168
|
|
@@ -194,8 +194,8 @@ module Rhoconnect
|
|
194
194
|
if @s.is_pass_through?
|
195
195
|
@ss.pass_through_cud({'delete'=> record },nil)
|
196
196
|
else
|
197
|
-
@
|
198
|
-
@ss.process_cud
|
197
|
+
@s.put_zdata(:delete,@c.id,record,true)
|
198
|
+
@ss.process_cud
|
199
199
|
end
|
200
200
|
end
|
201
201
|
|
data/lib/rhoconnect/version.rb
CHANGED
data/lib/rhoconnect.rb
CHANGED
@@ -46,7 +46,7 @@ module Rhoconnect
|
|
46
46
|
attr_accessor :base_directory, :app_directory, :data_directory,
|
47
47
|
:vendor_directory, :blackberry_bulk_sync, :redis, :environment,
|
48
48
|
:log_disabled, :license, :bulk_sync_poll_interval, :stats, :appserver, :api_token,
|
49
|
-
:raise_on_expired_lock, :lock_duration, :cookie_expire
|
49
|
+
:raise_on_expired_lock, :lock_duration, :cookie_expire, :predefined_sources
|
50
50
|
end
|
51
51
|
|
52
52
|
### Begin Rhoconnect setup methods
|
@@ -70,6 +70,7 @@ module Rhoconnect
|
|
70
70
|
Rhoconnect.lock_duration = get_setting(config,environment,:lock_duration)
|
71
71
|
Rhoconnect.environment = environment
|
72
72
|
Rhoconnect.cookie_expire = get_setting(config,environment,:cookie_expire) || 31536000
|
73
|
+
Rhoconnect.predefined_sources = {}
|
73
74
|
yield self if block_given?
|
74
75
|
Store.create(Rhoconnect.redis)
|
75
76
|
Resque.redis = Store.db
|
@@ -107,6 +108,10 @@ module Rhoconnect
|
|
107
108
|
# load ruby file for source adapter to re-load class
|
108
109
|
load under_score(source_name+'.rb')
|
109
110
|
end
|
111
|
+
|
112
|
+
# load all pre-defined adapters files
|
113
|
+
Dir[File.join(File.dirname(__FILE__),'rhoconnect','predefined_adapters','*.rb')].each { |adapter| load adapter }
|
114
|
+
|
110
115
|
# Create associations for all sources
|
111
116
|
Source.update_associations(app.sources)
|
112
117
|
end
|
@@ -149,7 +154,25 @@ module Rhoconnect
|
|
149
154
|
source_config
|
150
155
|
end
|
151
156
|
|
152
|
-
### End Rhoconnect setup methods
|
157
|
+
### End Rhoconnect setup methods
|
158
|
+
def register_predefined_source(source_name)
|
159
|
+
return if Rhoconnect.predefined_sources.has_key?(source_name)
|
160
|
+
|
161
|
+
Rhoconnect.predefined_sources[source_name] = {:source_loaded => false}
|
162
|
+
end
|
163
|
+
|
164
|
+
def create_predefined_source(source_name,params)
|
165
|
+
source_data = Rhoconnect.predefined_sources[source_name]
|
166
|
+
return unless source_data
|
167
|
+
if source_data[:source_loaded] != true
|
168
|
+
source_config = Rhoconnect.source_config(source_name)
|
169
|
+
source_config[:name] = source_name
|
170
|
+
Source.create(source_config,params)
|
171
|
+
app = App.load(Rhoconnect::APP_NAME)
|
172
|
+
app.sources << source_name
|
173
|
+
source_data[:source_loaded] = true
|
174
|
+
end
|
175
|
+
end
|
153
176
|
|
154
177
|
def check_default_secret!(secret)
|
155
178
|
if secret == '<changeme>'
|
@@ -19,6 +19,35 @@ class SampleAdapter < SourceAdapter
|
|
19
19
|
def sync
|
20
20
|
super
|
21
21
|
end
|
22
|
+
|
23
|
+
def validate(operation,operation_hashes,client_ids)
|
24
|
+
invalid_meta = {}
|
25
|
+
used_ids = {}
|
26
|
+
operation_hashes.each_with_index do |operation_hash,index|
|
27
|
+
client_id = client_ids[index]
|
28
|
+
operation_hash.each do |key,value|
|
29
|
+
if value['force_duplicate_error'] == '1'
|
30
|
+
invalid_meta[index] ||= {}
|
31
|
+
invalid_meta[index][key] ||= {}
|
32
|
+
invalid_meta[index][key][:error] = "Error during #{operation}: object confict detected"
|
33
|
+
end
|
34
|
+
if value['duplicate_of_cid']
|
35
|
+
invalid_meta[index] ||= {}
|
36
|
+
invalid_meta[index][key] ||= {}
|
37
|
+
invalid_meta[index][key][:duplicate_of] = true
|
38
|
+
master_client_id = value['duplicate_of_cid']
|
39
|
+
master_key = value['duplicate_of_key']
|
40
|
+
master_index = value['duplicate_of_queue_index'].to_i
|
41
|
+
|
42
|
+
invalid_meta[master_index] ||= {}
|
43
|
+
invalid_meta[master_index][master_key] ||= {}
|
44
|
+
invalid_meta[master_index][master_key][:duplicates] ||= []
|
45
|
+
invalid_meta[master_index][master_key][:duplicates] << {:client_id => client_id, :key => key, :value => value}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
invalid_meta
|
50
|
+
end
|
22
51
|
|
23
52
|
def create(create_hash)
|
24
53
|
Store.put_data('test_create_storage',{create_hash['_id']=>create_hash},true)
|
@@ -9,7 +9,7 @@ describe "SourceJob" do
|
|
9
9
|
|
10
10
|
it "should perform process_query" do
|
11
11
|
set_state('test_db_storage' => @data)
|
12
|
-
SourceJob.perform('query',@s.id,@s.app_id,@s.user_id,nil
|
12
|
+
SourceJob.perform('query',@s.id,@s.app_id,@s.user_id,nil)
|
13
13
|
verify_result(@s.docname(:md) => @data,
|
14
14
|
@s.docname(:md_size) => @data.size.to_s)
|
15
15
|
end
|
@@ -17,13 +17,13 @@ describe "SourceJob" do
|
|
17
17
|
it "should perform process_cud" do
|
18
18
|
expected = {'backend_id'=>@product1}
|
19
19
|
@product1['link'] = 'abc'
|
20
|
-
|
21
|
-
SourceJob.perform('cud',@s.id,@s.app_id,@s.user_id
|
20
|
+
set_zstate({@s.docname(:create) => {'1'=>@product1}}, @c.id, true)
|
21
|
+
SourceJob.perform('cud',@s.id,@s.app_id,@s.user_id,nil)
|
22
|
+
verify_zresult(@s.docname(:create) => [])
|
22
23
|
verify_result(@s.docname(:md) => expected,
|
23
24
|
@s.docname(:md_size) => expected.size.to_s,
|
24
25
|
@c.docname(:cd) => expected,
|
25
|
-
@c.docname(:cd_size) => expected.size.to_s
|
26
|
-
@c.docname(:create) => {})
|
26
|
+
@c.docname(:cd_size) => expected.size.to_s)
|
27
27
|
end
|
28
28
|
end
|
29
29
|
end
|
data/spec/source_adapter_spec.rb
CHANGED
@@ -33,6 +33,16 @@ describe "SourceAdapter" do
|
|
33
33
|
@sa.class.name.should == @s.name
|
34
34
|
end
|
35
35
|
|
36
|
+
it "should create all existing pre-defined adapters" do
|
37
|
+
Rhoconnect.predefined_sources.keys.sort.should == ['RhoInternalBenchmarkAdapter'].sort
|
38
|
+
Rhoconnect.predefined_sources.each do |adapter_name, filename|
|
39
|
+
pas = Source.load(adapter_name, @s_params)
|
40
|
+
pas.should_not == nil
|
41
|
+
pa = SourceAdapter.create(pas)
|
42
|
+
pa.class.name.should == "Rhoconnect::#{adapter_name}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
36
46
|
it "should create DynamicAdapter" do
|
37
47
|
@sa1 = SourceAdapter.create(@s2)
|
38
48
|
@sa1.class.name.should == 'Rhoconnect::DynamicAdapter'
|