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.
Files changed (74) hide show
  1. data/CHANGELOG.md +9 -0
  2. data/Gemfile +3 -3
  3. data/Gemfile.lock +38 -17
  4. data/Rakefile +0 -10
  5. data/bench/benchapp/Gemfile.lock +1 -0
  6. data/bench/distr_bench/distr_bench_main +94 -27
  7. data/bench/distr_bench/run_test_query_script.sh +22 -18
  8. data/bench/lib/bench/aws_utils.rb +326 -0
  9. data/bench/lib/bench/bench_result_processor.rb +268 -75
  10. data/bench/lib/bench/cli.rb +1 -0
  11. data/bench/lib/bench/distr_runner.rb +102 -0
  12. data/bench/lib/bench/utils.rb +127 -0
  13. data/bench/lib/bench.rb +16 -15
  14. data/bench/prepare_bench +3 -11
  15. data/bench/scripts/test_query_script.rb +6 -7
  16. data/bin/rhoconnect-benchmark +257 -5
  17. data/doc/benchmarks-running.txt +140 -0
  18. data/doc/client-java.txt +236 -0
  19. data/doc/client-objc.txt +41 -1
  20. data/doc/client.txt +12 -0
  21. data/doc/command-line.txt +12 -3
  22. data/doc/cud-conflicts.txt +68 -0
  23. data/doc/deploying.txt +1 -70
  24. data/doc/hosting-rhohub.txt +3 -0
  25. data/doc/install.txt +50 -13
  26. data/doc/java-plugin.txt +217 -177
  27. data/doc/net-plugin.txt +97 -64
  28. data/doc/plugin-intro.txt +4 -2
  29. data/doc/preparing-production.txt +63 -0
  30. data/doc/rhoconnect-redis-stack.txt +252 -0
  31. data/doc/source-adapters.txt +3 -1
  32. data/doc/tutorial.txt +111 -49
  33. data/examples/simple/dump.rdb +0 -0
  34. data/installer/unix-like/rho_connect_install_constants.rb +6 -5
  35. data/installer/unix-like/rho_connect_install_installers.rb +6 -2
  36. data/installer/utils/nix_install_test.rb +2 -0
  37. data/installer/utils/package_upload/auto-repo.rb +136 -0
  38. data/installer/utils/package_upload/repos.rake +6 -3
  39. data/installer/utils/package_upload/s3_upload.rb +11 -6
  40. data/installer/windows/rhosync.nsi +5 -5
  41. data/lib/rhoconnect/client_sync.rb +2 -2
  42. data/lib/rhoconnect/document.rb +12 -0
  43. data/lib/rhoconnect/jobs/source_job.rb +2 -2
  44. data/lib/rhoconnect/predefined_adapters/bench_adapter.rb +61 -0
  45. data/lib/rhoconnect/source.rb +5 -0
  46. data/lib/rhoconnect/source_adapter.rb +10 -1
  47. data/lib/rhoconnect/source_sync.rb +161 -88
  48. data/lib/rhoconnect/store.rb +48 -0
  49. data/lib/rhoconnect/test_methods.rb +6 -6
  50. data/lib/rhoconnect/version.rb +1 -1
  51. data/lib/rhoconnect.rb +25 -2
  52. data/spec/apps/rhotestapp/sources/sample_adapter.rb +29 -0
  53. data/spec/jobs/source_job_spec.rb +5 -5
  54. data/spec/source_adapter_spec.rb +10 -0
  55. data/spec/source_sync_spec.rb +114 -33
  56. data/spec/spec_helper.rb +21 -2
  57. data/spec/store_spec.rb +29 -0
  58. data/spec/support/shared_examples.rb +1 -1
  59. data/spec/test_methods_spec.rb +4 -4
  60. data/tasks/redis.rake +2 -2
  61. metadata +59 -59
  62. data/bench/benchapp/log/passenger.3000.log +0 -1
  63. data/bench/benchapp/log/passenger.9292.log +0 -59
  64. data/bench/benchapp/tmp/pids/passenger.3000.pid.lock +0 -0
  65. data/bench/benchapp/tmp/pids/passenger.9292.pid.lock +0 -0
  66. data/bench/lib/testdata/0-data.txt +0 -0
  67. data/bench/lib/testdata/1-data.txt +0 -0
  68. data/bench/lib/testdata/10-data.txt +0 -15
  69. data/bench/lib/testdata/2-data.txt +0 -3
  70. data/bench/lib/testdata/25-data.txt +0 -39
  71. data/bench/lib/testdata/250-data.txt +0 -353
  72. data/bench/lib/testdata/3-data.txt +0 -4
  73. data/bench/lib/testdata/50-data.txt +0 -70
  74. 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
@@ -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(client_id)
14
- _measure_and_process_cud('create',client_id)
13
+ def create
14
+ _measure_and_process_cud('create')
15
15
  end
16
16
 
17
- def update(client_id)
18
- _measure_and_process_cud('update',client_id)
17
+ def update
18
+ _measure_and_process_cud('update')
19
19
  end
20
20
 
21
- def delete(client_id)
22
- _measure_and_process_cud('delete',client_id)
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(client_id)
67
+ def process_cud
68
68
  if @source.cud_queue or @source.queue
69
- async(:cud,@source.cud_queue || @source.queue,client_id)
69
+ async(:cud,@source.cud_queue || @source.queue)
70
70
  else
71
- do_cud(client_id)
71
+ do_cud
72
72
  end
73
73
  end
74
74
 
75
- def do_cud(client_id)
75
+ def do_cud
76
76
  return if _auth_op('login') == false
77
- self.create(client_id)
78
- self.update(client_id)
79
- self.delete(client_id)
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,nil,params)
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,client_id=nil,params=nil)
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,client_id,params)
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(client,key,value,links,creates,deletes)
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
- if link
190
- links ||= {}
191
- links[key] = { 'l' => link.to_s }
192
- creates ||= {}
193
- creates[link.to_s] = value
194
- else
195
- deletes ||= {}
196
- deletes[key] = value
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(client,key,value)
201
- begin
202
- # Add id to object hash to forward to backend call
203
- value['id'] = key
204
- # Perform operation
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(client,key,value,dels)
208
+ def _process_delete(modified_recs,key,value)
215
209
  value['id'] = key
216
- # Perform operation
217
210
  @adapter.delete value
218
- dels ||= {}
219
- dels[key] = value
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,client_id)
218
+ def _measure_and_process_cud(operation)
223
219
  Stats::Record.update("source:#{operation}:#{@source.name}") do
224
- _process_cud(operation,client_id)
220
+ _process_cud(operation)
225
221
  end
226
222
  end
227
223
 
228
- def _process_cud(operation,client_id)
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
- client = Client.load(client_id,{:source_name => @source.name})
231
- modified = client.get_data(operation)
232
- # Process operation queue, one object at a time
233
- modified.each do |key,value|
234
- begin
235
- # Remove object from queue
236
- modified.delete(key)
237
- # Call on source adapter to process individual object
238
- case operation
239
- when 'create'
240
- _process_create(client,key,value,links,creates,deletes)
241
- when 'update'
242
- _process_update(client,key,value)
243
- when 'delete'
244
- _process_delete(client,key,value,dels)
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
- rescue Exception => e
247
- log "SourceAdapter raised #{operation} exception: #{e}"
248
- log e.backtrace.join("\n")
249
- errors ||= {}
250
- errors[key] = value
251
- errors["#{key}-error"] = {'message'=>e.message}
252
- break
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
- # Record operation results
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,value|
259
- client.put_data(doctype,value,true) unless value.empty?
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
- unless operation != 'create' and creates.empty?
262
- client.put_data(:cd,creates,true)
263
- client.update_count(:cd_size,creates.size)
264
- @source.lock(:md) do |s|
265
- s.put_data(:md,creates,true)
266
- s.update_count(:md_size,creates.size)
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
- client.delete_data(:cd,dels)
272
- client.update_count(:cd_size,-dels.size)
273
- @source.lock(:md) do |s|
274
- s.delete_data(:md,dels)
275
- s.update_count(:md_size,-dels.size)
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
- # Record rest of queue (if something in the middle failed)
279
- if modified.empty?
280
- client.flash_data(operation)
281
- else
282
- client.put_data(operation,modified)
283
- end
284
- modified.size
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
@@ -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
- @c.put_data(:create,{'temp-id' => record})
136
- @ss.process_cud(@c.id)
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
- @c.put_data(:update,record)
165
- @ss.process_cud(@c.id)
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
- @c.put_data(:delete,record)
198
- @ss.process_cud(@c.id)
197
+ @s.put_zdata(:delete,@c.id,record,true)
198
+ @ss.process_cud
199
199
  end
200
200
  end
201
201
 
@@ -1,3 +1,3 @@
1
1
  module Rhoconnect
2
- VERSION = '3.0.6'
2
+ VERSION = '3.1.0.beta1'
3
3
  end
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,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
- set_state(@c.docname(:create) => {'1'=>@product1})
21
- SourceJob.perform('cud',@s.id,@s.app_id,@s.user_id,@c.id,nil)
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
@@ -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'