rhoconnect 3.3.6 → 3.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (152) hide show
  1. data/CHANGELOG.md +40 -4
  2. data/Gemfile +2 -2
  3. data/Gemfile.lock +27 -25
  4. data/bench/benchapp/Gemfile +7 -27
  5. data/bench/benchapp/config.ru +9 -31
  6. data/bench/blobapp/Gemfile +7 -27
  7. data/bench/blobapp/config.ru +9 -29
  8. data/bench/lib/bench.rb +8 -1
  9. data/bench/lib/bench/test_data.rb +4 -1
  10. data/bench/scripts/blob_cud_script.rb +4 -0
  11. data/bench/scripts/cud_script.rb +7 -1
  12. data/bench/scripts/helpers.rb +1 -1
  13. data/bench/scripts/test_query_script.rb +20 -7
  14. data/bench/spec/bench_spec_helper.rb +3 -1
  15. data/bin/rhoconnect +22 -12
  16. data/commands/{commands/dtach_commands → dtach}/dtach_about.rb +0 -0
  17. data/commands/{commands/dtach_commands → dtach}/dtach_install.rb +0 -0
  18. data/commands/{commands/redis_commands → dtach}/redis_attach.rb +0 -0
  19. data/commands/execute.rb +14 -15
  20. data/commands/generators/update.rb +26 -0
  21. data/commands/{commands/redis_commands → redis}/redis_about.rb +0 -0
  22. data/commands/redis/redis_download.rb +13 -0
  23. data/commands/redis/redis_install.rb +26 -0
  24. data/commands/{commands/redis_commands → redis}/redis_make.rb +0 -0
  25. data/commands/{commands/redis_commands → redis}/redis_restart.rb +3 -2
  26. data/commands/{commands/redis_commands → redis}/redis_start.rb +0 -0
  27. data/commands/{commands/redis_commands → redis}/redis_startbg.rb +0 -0
  28. data/commands/{commands/redis_commands → redis}/redis_stop.rb +3 -2
  29. data/commands/{commands/rhoconnect → rhoconnect}/clean_start.rb +0 -0
  30. data/commands/{commands/rhoconnect → rhoconnect}/config.rb +7 -2
  31. data/commands/{commands/rhoconnect → rhoconnect}/create_user.rb +0 -0
  32. data/commands/{commands/rhoconnect → rhoconnect}/delete_device.rb +0 -0
  33. data/commands/{commands/rhoconnect → rhoconnect}/delete_user.rb +0 -0
  34. data/commands/{commands/rhoconnect → rhoconnect}/flushdb.rb +4 -4
  35. data/commands/{commands/rhoconnect → rhoconnect}/get_token.rb +0 -0
  36. data/commands/{commands/rhoconnect → rhoconnect}/reset.rb +0 -0
  37. data/commands/{commands/rhoconnect → rhoconnect}/reset_refresh.rb +0 -0
  38. data/commands/{commands/rhoconnect → rhoconnect}/restart.rb +0 -0
  39. data/commands/{commands/rhoconnect → rhoconnect}/secret.rb +0 -0
  40. data/commands/{commands/rhoconnect → rhoconnect}/set_admin_password.rb +0 -0
  41. data/commands/{commands/rhoconnect → rhoconnect}/spec.rb +0 -0
  42. data/commands/rhoconnect/start.rb +27 -0
  43. data/commands/{commands/rhoconnect → rhoconnect}/startbg.rb +0 -0
  44. data/commands/{commands/rhoconnect → rhoconnect}/startdebug.rb +2 -2
  45. data/commands/{commands/rhoconnect → rhoconnect}/stop.rb +3 -4
  46. data/commands/{commands/rhoconnect → rhoconnect}/version.rb +0 -0
  47. data/commands/{commands/rhoconnect → rhoconnect}/web.rb +0 -0
  48. data/commands/rhoconnect_attach/attach.rb +10 -0
  49. data/commands/rhoconnect_console/console.rb +16 -0
  50. data/commands/{commands/rhoconnect → rhoconnect_console}/console_helper.rb +0 -0
  51. data/commands/{commands/rhoconnect → rhoconnect_war}/war.rb +0 -0
  52. data/commands/{commands/redis_commands → utilities}/redis_runner.rb +22 -19
  53. data/commands/utilities/utilities.rb +6 -0
  54. data/doc/benchmarks.txt +2 -2
  55. data/doc/bulk-sync.txt +12 -1
  56. data/doc/client-java.txt +3 -3
  57. data/doc/client-objc.txt +1 -1
  58. data/doc/client.txt +5 -5
  59. data/doc/command-line.txt +80 -135
  60. data/doc/deploying.txt +119 -12
  61. data/doc/extending-rhoconnect-server.txt +1 -1
  62. data/doc/heroku-addon.txt +119 -23
  63. data/doc/install.txt +101 -39
  64. data/doc/java-plugin.txt +2 -2
  65. data/doc/licensing.txt +1 -1
  66. data/doc/plugin-intro.txt +3 -1
  67. data/doc/preparing-production.txt +4 -4
  68. data/doc/public/cli.txt +2 -2
  69. data/doc/push-backend-setup.txt +11 -1
  70. data/doc/push-client-setup.txt +72 -2
  71. data/doc/push-server-setup.txt +129 -8
  72. data/doc/rails-plugin.txt +245 -40
  73. data/doc/rest-api.txt +10 -6
  74. data/doc/rhoconnect-calculator.txt +237 -0
  75. data/doc/rhoconnect-redis-stack.txt +35 -0
  76. data/doc/session-and-configuration.txt +24 -0
  77. data/doc/settings.txt +51 -41
  78. data/doc/source-adapters.txt +45 -45
  79. data/doc/stats-middleware.txt +2 -2
  80. data/doc/supported-platforms.txt +6 -6
  81. data/doc/testing.txt +2 -2
  82. data/doc/tutorial.txt +63 -63
  83. data/examples/simple/Gemfile +7 -35
  84. data/examples/simple/config.ru +8 -26
  85. data/examples/simple/sources/product.rb +6 -6
  86. data/generators/rhoconnect.rb +5 -0
  87. data/generators/templates/application/Gemfile +7 -37
  88. data/generators/templates/application/Rakefile +8 -0
  89. data/generators/templates/application/config.ru +12 -31
  90. data/generators/templates/application/rcgemfile +44 -0
  91. data/generators/templates/application/settings/settings.yml +7 -5
  92. data/install.sh +4 -4
  93. data/installer/unix-like/create_texts.rb +7 -2
  94. data/installer/unix-like/rho_connect_install_constants.rb +2 -2
  95. data/installer/unix-like/rho_connect_install_installers.rb +1 -16
  96. data/lib/rhoconnect.rb +51 -38
  97. data/lib/rhoconnect/api/app/query.rb +4 -1
  98. data/lib/rhoconnect/api/app/search.rb +4 -1
  99. data/lib/rhoconnect/api/client/list_client_docs.rb +3 -1
  100. data/lib/rhoconnect/api/user/ping.rb +1 -5
  101. data/lib/rhoconnect/application/init.rb +43 -0
  102. data/lib/rhoconnect/async.rb +11 -6
  103. data/lib/rhoconnect/client_sync.rb +30 -37
  104. data/lib/rhoconnect/document.rb +4 -0
  105. data/lib/rhoconnect/graph_helper.rb +74 -56
  106. data/lib/rhoconnect/middleware/helpers.rb +4 -0
  107. data/lib/rhoconnect/ping.rb +1 -0
  108. data/lib/rhoconnect/ping/gcm.rb +58 -0
  109. data/lib/rhoconnect/predefined_adapters/bench_adapter.rb +7 -1
  110. data/lib/rhoconnect/source.rb +70 -56
  111. data/lib/rhoconnect/source_sync.rb +33 -5
  112. data/lib/rhoconnect/store.rb +358 -110
  113. data/lib/rhoconnect/user.rb +8 -0
  114. data/lib/rhoconnect/utilities.rb +16 -14
  115. data/lib/rhoconnect/version.rb +1 -1
  116. data/lib/rhoconnect/web-console/models/client.js +1 -1
  117. data/lib/rhoconnect/web-console/public/UNVR67bold.ttf +0 -0
  118. data/lib/rhoconnect/web-console/public/bootstrap.css +6 -0
  119. data/lib/rhoconnect/web-console/public/logo.png +0 -0
  120. data/lib/rhoconnect/web-console/server.rb +13 -11
  121. data/lib/rhoconnect/web-console/templates/index.erb +5 -5
  122. data/lib/rhoconnect/web-console/templates/jqplot.erb +1 -0
  123. data/lib/rhoconnect/web-console/views/doc.js +0 -4
  124. data/lib/rhoconnect/web-console/views/home.js +2 -1
  125. data/lib/rhoconnect/web-console/views/new_ping.js +11 -6
  126. data/lib/rhoconnect/web-console/views/stats.js +9 -5
  127. data/rhoconnect.gemspec +6 -4
  128. data/spec/api/app/fast_update_spec.rb +2 -2
  129. data/spec/api/source/get_source_params_spec.rb +1 -0
  130. data/spec/apps/rhotestapp/settings/settings.yml +5 -5
  131. data/spec/client_sync_spec.rb +3 -14
  132. data/spec/perf/perf_spec_helper.rb +11 -7
  133. data/spec/perf/store_perf_spec.rb +88 -11
  134. data/spec/ping/gcm_spec.rb +99 -0
  135. data/spec/server/server_spec.rb +7 -0
  136. data/spec/server/stats_spec.rb +9 -2
  137. data/spec/source_sync_spec.rb +29 -0
  138. data/spec/spec_helper.rb +40 -38
  139. data/spec/stats/record_spec.rb +18 -9
  140. data/spec/store_spec.rb +128 -19
  141. data/spec/testdata/10000-data.txt +0 -0
  142. data/spec/testdata/5-data.txt +0 -0
  143. data/spec/testdata/5000-data.txt +0 -0
  144. data/tasks/jasmine.rake +1 -0
  145. data/tasks/redis.rake +16 -13
  146. metadata +71 -39
  147. data/commands/commands/redis_commands/redis_download.rb +0 -33
  148. data/commands/commands/redis_commands/redis_install.rb +0 -26
  149. data/commands/commands/rhoconnect/attach.rb +0 -8
  150. data/commands/commands/rhoconnect/console.rb +0 -15
  151. data/commands/commands/rhoconnect/start.rb +0 -18
  152. data/commands/utilities/dtach_installed.rb +0 -10
@@ -1,4 +1,6 @@
1
1
  require 'digest/sha1'
2
+ require 'set'
3
+ require 'connection_pool'
2
4
 
3
5
  module Rhoconnect
4
6
 
@@ -18,7 +20,7 @@ module Rhoconnect
18
20
  def create(server=nil)
19
21
  @@db ||= _get_redis(server)
20
22
  raise "Error connecting to Redis store." unless @@db and
21
- (@@db.is_a?(Redis) or @@db.is_a?(Redis::Client))
23
+ (@@db.is_a?(Redis) or @@db.is_a?(Redis::Client) or @@db.is_a?(ConnectionPool::Wrapper))
22
24
  end
23
25
 
24
26
  def start_transaction
@@ -53,7 +55,7 @@ module Rhoconnect
53
55
  end
54
56
 
55
57
  def put_object(dockey, key, data={})
56
- _put_object(dockey, key, data)
58
+ _put_objects(dockey, {key => data})
57
59
  end
58
60
 
59
61
  # Adds set with given data, replaces existing set
@@ -64,12 +66,7 @@ module Rhoconnect
64
66
  flash_data(dockey) unless append
65
67
  # Inserts a hash or array
66
68
  if data.is_a?Hash
67
- @@db.pipelined do
68
- data.each do |key,value|
69
- raise ArgumentError, "Invalid value object: #{value.inspect}. Hash is expected." unless value.is_a?(Hash)
70
- _put_object(dockey, key, value)
71
- end
72
- end
69
+ _put_objects(dockey, data)
73
70
  else
74
71
  put_list(dockey,data,append)
75
72
  end
@@ -81,8 +78,8 @@ module Rhoconnect
81
78
  if dockey and data
82
79
  flash_data(dockey) unless append
83
80
  @@db.pipelined do
84
- data.each do |value|
85
- @@db.rpush(dockey, value)
81
+ data.each do |element|
82
+ @@db.rpush(dockey, element)
86
83
  end
87
84
  end
88
85
  end
@@ -95,29 +92,45 @@ module Rhoconnect
95
92
  return 0 unless dockey and data
96
93
 
97
94
  new_object_count = 0
98
- objs = get_objects(dockey, data.keys)
95
+ objs = get_objects(dockey, data.keys) || {}
96
+
97
+ collected_adds = {}
98
+ collected_rems = {}
99
+ my_bucket = nil
99
100
  @@db.pipelined do
100
- data.each do |key,value|
101
+ data.each do |key,obj|
101
102
  is_create = objs[key].nil?
102
103
  new_object_count += 1 if is_create
103
104
  obj_bucket = _add_bucket_index(dockey, "#{_create_obj_index(key)}")
104
- value.each do |attrib,value|
105
- next if _is_reserved?(attrib, value)
106
-
107
- new_element = setelement(key,attrib,value)
108
- element_exists = is_create ? false : objs[key].has_key?(attrib)
109
- if element_exists
110
- existing_element = setelement(key,attrib,objs[key][attrib])
111
- if existing_element != new_element
112
- @@db.srem(obj_bucket, existing_element)
113
- @@db.sadd(obj_bucket, new_element)
114
- end
115
- else
116
- @@db.sadd(obj_bucket, new_element)
117
- end
105
+
106
+ # collect SREM (if object exists in DB)
107
+ unless is_create
108
+ old_element = set_obj_element(key,objs[key])
109
+ collected_rems[obj_bucket] ||= []
110
+ collected_rems[obj_bucket] << old_element
118
111
  end
112
+ # update the object and collect SADD
113
+ objs[key] ||= {}
114
+ objs[key].merge!(obj)
115
+
116
+ new_element = set_obj_element(key,objs[key])
117
+ collected_adds[obj_bucket] ||= []
118
+ collected_adds[obj_bucket] << new_element
119
+ end
120
+ # process all SADD and SREM commands as one
121
+ # SREM must go first
122
+ collected_rems.each do |bucket, bucket_data|
123
+ @@db.srem(bucket, bucket_data)
124
+ end
125
+ collected_adds.each do |bucket, bucket_data|
126
+ @@db.sadd(bucket, bucket_data)
119
127
  end
120
128
  end
129
+
130
+
131
+ #data1 = @@db.smembers(my_bucket)
132
+ #puts "data1 is #{data1.inspect}"
133
+
121
134
  new_object_count
122
135
  end
123
136
 
@@ -125,28 +138,14 @@ module Rhoconnect
125
138
  def delete_objects(dockey,data=[])
126
139
  return 0 unless dockey and data
127
140
 
128
- deleted_object_count = 0
129
141
  objs = get_objects(dockey, data)
130
- @@db.pipelined do
131
- data.each do |id|
132
- if _delete_object(dockey, id, objs[id])
133
- deleted_object_count += 1
134
- end
135
- end
136
- end
137
- _cleanup_buckets(dockey, objs.keys)
138
- deleted_object_count
142
+ _delete_objects(dockey, objs)
139
143
  end
140
144
 
141
145
  # Deletes data from a given doctype,source,user
142
146
  def delete_data(dockey,data={})
143
147
  if dockey and data
144
- @@db.pipelined do
145
- data.each do |key,value|
146
- _delete_object(dockey, key, value)
147
- end
148
- end
149
- _cleanup_buckets(dockey, data.keys)
148
+ _delete_objects(dockey, data)
150
149
  end
151
150
  true
152
151
  end
@@ -180,15 +179,12 @@ module Rhoconnect
180
179
  end
181
180
 
182
181
  def get_object(dockey, key)
183
- _get_object(dockey, key)
182
+ res = _get_objects(dockey, [key])
183
+ (res and res.size > 0) ? res.values[0] : nil
184
184
  end
185
185
 
186
186
  def get_objects(dockey, keys)
187
- res = {}
188
- keys.each do |key|
189
- res[key] = _get_object(dockey, key)
190
- end if keys
191
- res
187
+ _get_objects(dockey, keys)
192
188
  end
193
189
 
194
190
  # Retrieves set for given dockey,source,user
@@ -197,14 +193,18 @@ module Rhoconnect
197
193
  if dockey
198
194
  if type == Hash
199
195
  buckets = _get_buckets(dockey)
200
- buckets.each do |bucket|
201
- members = @@db.smembers(bucket)
202
- members.each do |element|
203
- key,attrib,value = getelement(element)
204
- res[key] = {} unless res[key]
205
- res[key].merge!({attrib => value})
206
- end if members
207
- end if buckets
196
+ members = @@db.pipelined do
197
+ buckets.each do |bucket|
198
+ @@db.smembers(bucket)
199
+ end if buckets
200
+ end
201
+ members.each do |elements|
202
+ elements.each do |element|
203
+ key,obj = get_obj_element(element)
204
+ res[key] = obj
205
+ #res[key].merge!({attrib => value})
206
+ end if elements
207
+ end if members
208
208
  else
209
209
  res = get_list(dockey)
210
210
  end
@@ -221,35 +221,116 @@ module Rhoconnect
221
221
  end
222
222
 
223
223
  # Retrieves diff data hash between two sets
224
+ # each entry is in the form of DIFF_OBJ_ELEMENT => [OBJ_KEY, OBJ_DATA_PAIRS]
224
225
  def get_diff_data(src_dockey,dst_dockey,p_size=nil)
225
226
  res = {}
226
227
  if src_dockey and dst_dockey
227
228
  # obtain combined indices
228
229
  indices = @@db.hgetall("#{dst_dockey}:indices")
229
- indices.merge!(@@db.hgetall("#{dst_dockey}:indices"))
230
230
  indices.keys.each do |index|
231
231
  dst_bucket_name = "#{dst_dockey}:#{index}"
232
232
  src_bucket_name = "#{src_dockey}:#{index}"
233
- @@db.sdiff(dst_bucket_name,src_bucket_name).each do |element|
234
- key,attrib,value = getelement(element)
235
- res[key] = {} unless res[key]
236
- res[key].merge!({attrib => value})
233
+ diff_elements = @@db.sdiff(dst_bucket_name,src_bucket_name)
234
+ diff_elements.each do |element|
235
+ keypairs = get_obj_key_and_pairs(element)
236
+ next unless keypairs
237
+ res[element] = keypairs
238
+ return res if p_size and (res.size >= p_size)
237
239
  end
238
- break if p_size and (res.size >= p_size)
239
240
  end
240
241
  end
241
- if p_size
242
- diff = {}
243
- page_size = p_size
244
- res.each do |key,item|
245
- diff[key] = item
246
- page_size -= 1
247
- break if page_size <= 0
242
+ res
243
+ end
244
+
245
+ def get_inserts_deletes(inserts_elements_map, deletes_elements_map)
246
+ inserts_obj_hash = {}
247
+ inserts_elements_map.each do |element,keypairs|
248
+ key,obj_pairs = keypairs[0],keypairs[1]
249
+ next unless (key and obj_pairs)
250
+ inserts_obj_hash[key] = Set.new(obj_pairs)
251
+ end
252
+
253
+ deletes_obj_hash = {}
254
+ deletes_elements_map.each do |element,keypairs|
255
+ key,obj_pairs = keypairs[0],keypairs[1]
256
+ next unless (key and obj_pairs)
257
+ deletes_obj_hash[key] = Set.new(obj_pairs)
258
+ end
259
+ # modified attributes
260
+ inserts = {}
261
+ deletes = {}
262
+
263
+ inserts_obj_hash.each do |key, obj_set|
264
+ deletes_pairs = nil
265
+ inserts_pairs = nil
266
+ if deletes_obj_hash.has_key?(key)
267
+ deletes_pairs = deletes_obj_hash[key].dup.subtract(obj_set).to_a
268
+ inserts_pairs = obj_set.dup.subtract(deletes_obj_hash[key]).to_a
269
+ # remove the key from the deletes set - we already processed it
270
+ deletes_obj_hash.delete(key)
271
+ else
272
+ # if object is not in the deletes set - then, it's all inserts
273
+ inserts_pairs = obj_set.to_a
274
+ end
275
+ # split resulting pairs
276
+ if inserts_pairs and inserts_pairs.size > 0
277
+ inserts[key] = split_obj_pairs(inserts_pairs)
278
+ end
279
+ if deletes_pairs and deletes_pairs.size > 0
280
+ deletes[key] = split_obj_pairs(deletes_pairs)
248
281
  end
249
- diff
250
- else
251
- res
252
282
  end
283
+ # after we analyzed the inserts__obj_hash
284
+ # => deletes_obj_hash should contain only the unmatched deletes
285
+ deletes_obj_hash.each do |key, obj_set|
286
+ if obj_set.size > 0
287
+ deletes[key] = split_obj_pairs(obj_set.to_a)
288
+ end
289
+ end
290
+
291
+ [inserts, deletes]
292
+ end
293
+
294
+ def update_elements(dockey, inserts_elements_map, deletes_elements_map)
295
+ indices_to_cleanup = Set.new
296
+ @@db.pipelined do
297
+ collected_adds = {}
298
+ collected_rems = {}
299
+
300
+ inserts_elements_map.each do |element,keypairs|
301
+ key = keypairs[0]
302
+ next if not key or not element or element.size == 0
303
+
304
+ obj_bucket_index = _create_obj_index(key)
305
+ bucket_name = "#{dockey}:#{obj_bucket_index}"
306
+ _add_bucket_index(dockey, obj_bucket_index)
307
+
308
+ collected_adds[bucket_name] ||= []
309
+ collected_adds[bucket_name] << element
310
+ end
311
+
312
+ deletes_elements_map.each do |element,keypairs|
313
+ key = keypairs[0]
314
+ next if not key or not element or element.size == 0
315
+
316
+ obj_bucket_index = _create_obj_index(key)
317
+ bucket_name = "#{dockey}:#{obj_bucket_index}"
318
+ indices_to_cleanup << bucket_name
319
+
320
+ collected_rems[bucket_name] ||= []
321
+ collected_rems[bucket_name] << element
322
+ end
323
+
324
+ # now, perform SREM first, then SADD
325
+ collected_rems.each do |bucket, bucket_data|
326
+ @@db.srem(bucket, bucket_data)
327
+ end
328
+ collected_adds.each do |bucket,bucket_data|
329
+ @@db.sadd(bucket, bucket_data)
330
+ end
331
+ end
332
+ # now, cleanup buckets if necessary
333
+ _cleanup_buckets(dockey, indices_to_cleanup.to_a)
253
334
  end
254
335
 
255
336
  # Deletes all keys matching a given mask
@@ -367,7 +448,7 @@ module Rhoconnect
367
448
  data.each do |key,hash_value|
368
449
  unique_record_key = setelement(current_score,assoc_key, key)
369
450
  Store.db.zadd(dockey, current_score, unique_record_key)
370
- Store.put_data("#{dockey}:#{unique_record_key}",{key => hash_value})
451
+ Store.put_data("#{dockey}:#{unique_record_key}",{key => hash_value})
371
452
  end
372
453
  true
373
454
  end
@@ -403,6 +484,62 @@ module Rhoconnect
403
484
  Store.db.zremrangebyrank(dockey, 0, -1)
404
485
  end
405
486
 
487
+ # these methods should be used for transactional, multi-source CUD queue requests
488
+ def put_multi_zdata(dockey,assoc_key,sources=[],data={},append=false)
489
+ return true unless (dockey and assoc_key and sources and data)
490
+ flush_multi_zdata(dockey) unless append
491
+ current_score = 0
492
+ current_score_data = Store.db.zrevrange(dockey,0,0,:with_scores => true)
493
+ current_score = current_score_data[-1][1].to_i if current_score_data and current_score_data[-1]
494
+ current_score += 1
495
+ Store.put_data("#{dockey}:#{current_score.to_i}:#{assoc_key}:sources", sources)
496
+ data.each do |source_key,source_hashes|
497
+ source_hashes.each do |operation,obj_hashes|
498
+ unique_zrecord_key = setelement("#{current_score.to_i},#{assoc_key}",source_key,operation)
499
+ Store.db.zadd(dockey, current_score.to_i, unique_zrecord_key)
500
+ Store.put_data("#{dockey}:#{unique_zrecord_key}", obj_hashes)
501
+ end
502
+ end
503
+ true
504
+ end
505
+
506
+ def get_multi_zdata(dockey)
507
+ data = Store.db.zrange(dockey, 0, -1)
508
+ ret = []
509
+ sources = []
510
+ keys = []
511
+ unless data.nil?
512
+ scores = []
513
+ data.each do |zsetkey|
514
+ scored_assoc_key,source_key,operation = getelement(zsetkey)
515
+ score,assoc_key = scored_assoc_key.split(',')
516
+ obj_hash = Store.get_data "#{dockey}:#{zsetkey}"
517
+ if scores[-1] != score.to_i
518
+ sources << Store.get_data("#{dockey}:#{score}:#{assoc_key}:sources", Array)
519
+ ret << {source_key => {operation => obj_hash}}
520
+ scores << score.to_i
521
+ keys << assoc_key
522
+ else
523
+ ret[-1][source_key] ||= {}
524
+ ret[-1][source_key].merge!({operation => obj_hash})
525
+ end
526
+ end
527
+ end
528
+ [ret, sources, keys]
529
+ end
530
+
531
+ # Deletes all keys and their hashes from the Redis DB
532
+ def flush_multi_zdata(dockey)
533
+ data = Store.db.zrange(dockey, 0, -1)
534
+ data.each do |zsetkey|
535
+ scored_assoc_key,source_key,operation = getelement(zsetkey)
536
+ score,assoc_key = scored_assoc_key.split(',')
537
+ _delete_doc("#{dockey}:#{zsetkey}")
538
+ _delete_doc("#{dockey}:#{score}:#{assoc_key}:sources")
539
+ end
540
+ Store.db.zremrangebyrank(dockey, 0, -1)
541
+ end
542
+
406
543
  def exists?(key)
407
544
  @@db.exists(key) || @@db.exists("#{key}:indices")
408
545
  end
@@ -411,20 +548,46 @@ module Rhoconnect
411
548
  alias_method :set_data, :put_data
412
549
 
413
550
  private
551
+ if RUBY_VERSION =~ /1.9/
414
552
  def _get_redis(server=nil)
415
553
  url = ENV[REDIS_URL] || ENV[REDISTOGO_URL] || nil
416
554
  if url
417
- Redis.connect(:url => url)
555
+ ConnectionPool::Wrapper.new(:size => Rhoconnect.connection_pool_size,
556
+ :timeout => Rhoconnect.connection_pool_timeout) do
557
+ Redis.connect(:url => url, :timeout => Rhoconnect.redis_timeout, :thread_safe => true)
558
+ end
418
559
  elsif server and server.is_a?(String)
419
560
  host,port,db,password = server.split(':')
420
- Redis.new(:thread_safe => true, :host => host,
421
- :port => port, :db => db, :password => password)
422
- elsif server and server.is_a?(Redis)
561
+ ConnectionPool::Wrapper.new(:size => Rhoconnect.connection_pool_size,
562
+ :timeout => Rhoconnect.connection_pool_timeout) do
563
+ Redis.connect(:thread_safe => true, :host => host,
564
+ :port => port, :db => db, :password => password, :timeout => Rhoconnect.redis_timeout)
565
+ end
566
+ elsif server and (server.is_a?(Redis) or server.is_a?(ConnectionPool::Wrapper))
423
567
  server
424
568
  else
425
- Redis.new(:thread_safe => true)
569
+ ConnectionPool::Wrapper.new(:size => 5, :timeout => 30) do
570
+ Redis.connect(:timeout => 30, :thread_safe => true)
571
+ end
426
572
  end
427
573
  end
574
+ else # Ruby 1.8 does not support Connnection Pools
575
+ def _get_redis(server=nil)
576
+ url = ENV[REDIS_URL] || ENV[REDISTOGO_URL] || nil
577
+ if url
578
+ Redis.connect(:url => url, :timeout => Rhoconnect.redis_timeout, :thread_safe => true)
579
+ elsif server and server.is_a?(String)
580
+ host,port,db,password = server.split(':')
581
+ Redis.connect(:thread_safe => true, :host => host,
582
+ :port => port, :db => db, :password => password, :timeout => Rhoconnect.redis_timeout)
583
+ elsif server and (server.is_a?(Redis) or server.is_a?(ConnectionPool::Wrapper))
584
+ server
585
+ else
586
+ Redis.connect(:timeout => 30, :thread_safe => true)
587
+ end
588
+ end
589
+ end # end of if RUBY_VERSION
590
+
428
591
 
429
592
  def _lock_key(dockey)
430
593
  "lock:#{dockey}"
@@ -476,57 +639,142 @@ module Rhoconnect
476
639
  @@db.hvals("#{dockey}:indices")
477
640
  end
478
641
 
479
- def _cleanup_buckets(dockey, keys)
480
- keys.each do |key|
481
- obj_index = _create_obj_index(key)
482
- bucket_name = "#{dockey}:#{obj_index}"
483
- _remove_bucket_index(dockey, obj_index) unless @@db.exists(bucket_name)
484
- end if keys
642
+ def _cleanup_buckets(dockey, indices_to_cleanup)
643
+ indices_to_cleanup.each do |index|
644
+ bucket_name = "#{dockey}:#{index}"
645
+ _remove_bucket_index(dockey, index) unless @@db.exists(bucket_name)
646
+ end if indices_to_cleanup
485
647
  end
486
648
 
487
- def _put_object(dockey, key, obj={})
488
- return if obj.empty? or not dockey or not key
649
+ def _put_objects(dockey, data={})
650
+ return if data.empty? or not dockey
489
651
 
490
- obj_bucket_index = _create_obj_index(key)
491
- bucket_name = "#{dockey}:#{obj_bucket_index}"
492
- _add_bucket_index(dockey, obj_bucket_index)
652
+ collected_adds = {}
493
653
  @@db.pipelined do
494
- obj.each do |attrib, value|
495
- unless _is_reserved?(attrib,value)
496
- @@db.sadd(bucket_name,setelement(key,attrib,value))
497
- end
654
+ data.each do |key,obj|
655
+ raise ArgumentError, "Invalid value object: #{obj.inspect}. Hash is expected." unless obj.is_a?(Hash)
656
+ next if obj.empty? or not key
657
+
658
+ obj_bucket_index = _create_obj_index(key)
659
+ bucket_name = "#{dockey}:#{obj_bucket_index}"
660
+ _add_bucket_index(dockey, obj_bucket_index)
661
+ collected_adds[bucket_name] ||= []
662
+ collected_adds[bucket_name] << set_obj_element(key, obj)
663
+
664
+ end
665
+ # all SADD operations on a bucket key
666
+ # are combined into one - it proves to perform faster
667
+ collected_adds.each do |bucket,bucket_data|
668
+ @@db.sadd(bucket, bucket_data)
498
669
  end
499
670
  end
500
671
  end
501
672
 
502
- def _delete_object(dockey, key, obj)
503
- return false if not key or not dockey or not obj
673
+ def _delete_objects(dockey, data={})
674
+ return 0 if data.empty? or not dockey
504
675
 
505
- obj_bucket_index = _create_obj_index(key)
506
- bucket_name = "#{dockey}:#{obj_bucket_index}"
676
+ deleted_object_count = 0
677
+ indices_to_cleanup = Set.new
678
+ collected_rems = {}
507
679
  @@db.pipelined do
508
- obj.each do |attrib,value|
509
- @@db.srem(bucket_name, setelement(key,attrib,value))
680
+ data.each do |key, obj|
681
+ next if obj.empty? or not key
682
+ obj_bucket_index = _create_obj_index(key)
683
+ bucket_name = "#{dockey}:#{obj_bucket_index}"
684
+ indices_to_cleanup << obj_bucket_index
685
+
686
+ collected_rems[bucket_name] ||= []
687
+ collected_rems[bucket_name] << set_obj_element(key, obj)
688
+ deleted_object_count += 1
689
+ end
690
+
691
+ # all SREM operations on a bucket
692
+ # are combined into one
693
+ collected_rems.each do |bucket,bucket_data|
694
+ @@db.srem(bucket, bucket_data)
510
695
  end
511
696
  end
512
- true
697
+ _cleanup_buckets(dockey, indices_to_cleanup.to_a)
698
+ deleted_object_count
513
699
  end
514
700
 
515
- def _get_object(dockey, key)
701
+ def _get_objects(dockey, keys)
516
702
  return nil unless dockey
517
703
  res = nil
518
- obj_bucket_index = _create_obj_index(key)
519
- bucket_name = "#{dockey}:#{obj_bucket_index}"
520
- key_str_size = "#{key}".size
521
- members = @@db.smembers(bucket_name)
522
- members.each do |element|
523
- next unless element[0..key_str_size - 1] == key
524
- obj_id,attrib,value = getelement(element)
525
- res ||= {}
526
- res[attrib] = value
704
+ keys_map = Set.new
705
+ buckets = Set.new
706
+ keys.each do |key|
707
+ obj_bucket_index = _create_obj_index(key)
708
+ bucket_name = "#{dockey}:#{obj_bucket_index}"
709
+ keys_map << key
710
+ buckets << bucket_name
711
+ end
712
+ members = @@db.pipelined do
713
+ buckets.to_a.each do |bucket_name|
714
+ @@db.smembers(bucket_name)
715
+ end
716
+ end
717
+ members.each do |bucket_data|
718
+ bucket_data.each do |element|
719
+ key,pairs = get_obj_key_and_pairs(element)
720
+ next unless keys_map.include?(key)
721
+ obj = split_obj_pairs(pairs)
722
+ next if obj.empty?
723
+ res ||= {}
724
+ res[key] = obj
725
+ end if bucket_data
527
726
  end if members
528
727
  res
529
728
  end
729
+
730
+ # operations on object elements
731
+ def get_obj_element(elem)
732
+ key,pairs = get_obj_key_and_pairs(elem)
733
+ return unless (key and pairs)
734
+ [key,split_obj_pairs(pairs)]
735
+ end
736
+ def get_obj_key_and_pairs(elem)
737
+ pairs = elem.split(":^rho&:")
738
+ return unless pairs
739
+ [pairs[0], pairs[1..-1]]
740
+ end
741
+ def split_obj_pairs(pairs)
742
+ obj = {}
743
+ pairs.each do |pair|
744
+ attrib,value = pair.split(':',2)
745
+ obj[attrib] = value
746
+ end
747
+ obj
748
+ end
749
+
750
+ # Set Obj Element MUST ensure the order of attribs
751
+ # In 1.8.7 Hash keys are not-sorted - therefore
752
+ # to ensure same order - we sort them before storing
753
+ if RUBY_VERSION =~ /1.8/
754
+ def set_obj_element(key, obj)
755
+ return unless (key and key.size > 0 and obj and obj.size > 0)
756
+ elem = "#{key}"
757
+ obj.sort.each do |attrib, value|
758
+ unless _is_reserved?(attrib,value)
759
+ elem += ":^rho&:#{attrib}:#{value}"
760
+ end
761
+ end
762
+ elem
763
+ end
764
+ # in Ruby 1.9.x Hash keys are sorted (always in the same order), so
765
+ # we do not need to do redundant sorting
766
+ else
767
+ def set_obj_element(key, obj)
768
+ return unless (key and key.size > 0 and obj and obj.size > 0)
769
+ elem = "#{key}"
770
+ obj.each do |attrib, value|
771
+ unless _is_reserved?(attrib,value)
772
+ elem += ":^rho&:#{attrib}:#{value}"
773
+ end
774
+ end
775
+ elem
776
+ end
777
+ end # if Ruby 1.8
530
778
  end
531
779
  end
532
780
  end