rhoconnect 3.0.6 → 3.1.0.beta1

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.
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'