rhoconnect 3.3.6 → 3.4.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +40 -4
- data/Gemfile +2 -2
- data/Gemfile.lock +27 -25
- data/bench/benchapp/Gemfile +7 -27
- data/bench/benchapp/config.ru +9 -31
- data/bench/blobapp/Gemfile +7 -27
- data/bench/blobapp/config.ru +9 -29
- data/bench/lib/bench.rb +8 -1
- data/bench/lib/bench/test_data.rb +4 -1
- data/bench/scripts/blob_cud_script.rb +4 -0
- data/bench/scripts/cud_script.rb +7 -1
- data/bench/scripts/helpers.rb +1 -1
- data/bench/scripts/test_query_script.rb +20 -7
- data/bench/spec/bench_spec_helper.rb +3 -1
- data/bin/rhoconnect +22 -12
- data/commands/{commands/dtach_commands → dtach}/dtach_about.rb +0 -0
- data/commands/{commands/dtach_commands → dtach}/dtach_install.rb +0 -0
- data/commands/{commands/redis_commands → dtach}/redis_attach.rb +0 -0
- data/commands/execute.rb +14 -15
- data/commands/generators/update.rb +26 -0
- data/commands/{commands/redis_commands → redis}/redis_about.rb +0 -0
- data/commands/redis/redis_download.rb +13 -0
- data/commands/redis/redis_install.rb +26 -0
- data/commands/{commands/redis_commands → redis}/redis_make.rb +0 -0
- data/commands/{commands/redis_commands → redis}/redis_restart.rb +3 -2
- data/commands/{commands/redis_commands → redis}/redis_start.rb +0 -0
- data/commands/{commands/redis_commands → redis}/redis_startbg.rb +0 -0
- data/commands/{commands/redis_commands → redis}/redis_stop.rb +3 -2
- data/commands/{commands/rhoconnect → rhoconnect}/clean_start.rb +0 -0
- data/commands/{commands/rhoconnect → rhoconnect}/config.rb +7 -2
- data/commands/{commands/rhoconnect → rhoconnect}/create_user.rb +0 -0
- data/commands/{commands/rhoconnect → rhoconnect}/delete_device.rb +0 -0
- data/commands/{commands/rhoconnect → rhoconnect}/delete_user.rb +0 -0
- data/commands/{commands/rhoconnect → rhoconnect}/flushdb.rb +4 -4
- data/commands/{commands/rhoconnect → rhoconnect}/get_token.rb +0 -0
- data/commands/{commands/rhoconnect → rhoconnect}/reset.rb +0 -0
- data/commands/{commands/rhoconnect → rhoconnect}/reset_refresh.rb +0 -0
- data/commands/{commands/rhoconnect → rhoconnect}/restart.rb +0 -0
- data/commands/{commands/rhoconnect → rhoconnect}/secret.rb +0 -0
- data/commands/{commands/rhoconnect → rhoconnect}/set_admin_password.rb +0 -0
- data/commands/{commands/rhoconnect → rhoconnect}/spec.rb +0 -0
- data/commands/rhoconnect/start.rb +27 -0
- data/commands/{commands/rhoconnect → rhoconnect}/startbg.rb +0 -0
- data/commands/{commands/rhoconnect → rhoconnect}/startdebug.rb +2 -2
- data/commands/{commands/rhoconnect → rhoconnect}/stop.rb +3 -4
- data/commands/{commands/rhoconnect → rhoconnect}/version.rb +0 -0
- data/commands/{commands/rhoconnect → rhoconnect}/web.rb +0 -0
- data/commands/rhoconnect_attach/attach.rb +10 -0
- data/commands/rhoconnect_console/console.rb +16 -0
- data/commands/{commands/rhoconnect → rhoconnect_console}/console_helper.rb +0 -0
- data/commands/{commands/rhoconnect → rhoconnect_war}/war.rb +0 -0
- data/commands/{commands/redis_commands → utilities}/redis_runner.rb +22 -19
- data/commands/utilities/utilities.rb +6 -0
- data/doc/benchmarks.txt +2 -2
- data/doc/bulk-sync.txt +12 -1
- data/doc/client-java.txt +3 -3
- data/doc/client-objc.txt +1 -1
- data/doc/client.txt +5 -5
- data/doc/command-line.txt +80 -135
- data/doc/deploying.txt +119 -12
- data/doc/extending-rhoconnect-server.txt +1 -1
- data/doc/heroku-addon.txt +119 -23
- data/doc/install.txt +101 -39
- data/doc/java-plugin.txt +2 -2
- data/doc/licensing.txt +1 -1
- data/doc/plugin-intro.txt +3 -1
- data/doc/preparing-production.txt +4 -4
- data/doc/public/cli.txt +2 -2
- data/doc/push-backend-setup.txt +11 -1
- data/doc/push-client-setup.txt +72 -2
- data/doc/push-server-setup.txt +129 -8
- data/doc/rails-plugin.txt +245 -40
- data/doc/rest-api.txt +10 -6
- data/doc/rhoconnect-calculator.txt +237 -0
- data/doc/rhoconnect-redis-stack.txt +35 -0
- data/doc/session-and-configuration.txt +24 -0
- data/doc/settings.txt +51 -41
- data/doc/source-adapters.txt +45 -45
- data/doc/stats-middleware.txt +2 -2
- data/doc/supported-platforms.txt +6 -6
- data/doc/testing.txt +2 -2
- data/doc/tutorial.txt +63 -63
- data/examples/simple/Gemfile +7 -35
- data/examples/simple/config.ru +8 -26
- data/examples/simple/sources/product.rb +6 -6
- data/generators/rhoconnect.rb +5 -0
- data/generators/templates/application/Gemfile +7 -37
- data/generators/templates/application/Rakefile +8 -0
- data/generators/templates/application/config.ru +12 -31
- data/generators/templates/application/rcgemfile +44 -0
- data/generators/templates/application/settings/settings.yml +7 -5
- data/install.sh +4 -4
- data/installer/unix-like/create_texts.rb +7 -2
- data/installer/unix-like/rho_connect_install_constants.rb +2 -2
- data/installer/unix-like/rho_connect_install_installers.rb +1 -16
- data/lib/rhoconnect.rb +51 -38
- data/lib/rhoconnect/api/app/query.rb +4 -1
- data/lib/rhoconnect/api/app/search.rb +4 -1
- data/lib/rhoconnect/api/client/list_client_docs.rb +3 -1
- data/lib/rhoconnect/api/user/ping.rb +1 -5
- data/lib/rhoconnect/application/init.rb +43 -0
- data/lib/rhoconnect/async.rb +11 -6
- data/lib/rhoconnect/client_sync.rb +30 -37
- data/lib/rhoconnect/document.rb +4 -0
- data/lib/rhoconnect/graph_helper.rb +74 -56
- data/lib/rhoconnect/middleware/helpers.rb +4 -0
- data/lib/rhoconnect/ping.rb +1 -0
- data/lib/rhoconnect/ping/gcm.rb +58 -0
- data/lib/rhoconnect/predefined_adapters/bench_adapter.rb +7 -1
- data/lib/rhoconnect/source.rb +70 -56
- data/lib/rhoconnect/source_sync.rb +33 -5
- data/lib/rhoconnect/store.rb +358 -110
- data/lib/rhoconnect/user.rb +8 -0
- data/lib/rhoconnect/utilities.rb +16 -14
- data/lib/rhoconnect/version.rb +1 -1
- data/lib/rhoconnect/web-console/models/client.js +1 -1
- data/lib/rhoconnect/web-console/public/UNVR67bold.ttf +0 -0
- data/lib/rhoconnect/web-console/public/bootstrap.css +6 -0
- data/lib/rhoconnect/web-console/public/logo.png +0 -0
- data/lib/rhoconnect/web-console/server.rb +13 -11
- data/lib/rhoconnect/web-console/templates/index.erb +5 -5
- data/lib/rhoconnect/web-console/templates/jqplot.erb +1 -0
- data/lib/rhoconnect/web-console/views/doc.js +0 -4
- data/lib/rhoconnect/web-console/views/home.js +2 -1
- data/lib/rhoconnect/web-console/views/new_ping.js +11 -6
- data/lib/rhoconnect/web-console/views/stats.js +9 -5
- data/rhoconnect.gemspec +6 -4
- data/spec/api/app/fast_update_spec.rb +2 -2
- data/spec/api/source/get_source_params_spec.rb +1 -0
- data/spec/apps/rhotestapp/settings/settings.yml +5 -5
- data/spec/client_sync_spec.rb +3 -14
- data/spec/perf/perf_spec_helper.rb +11 -7
- data/spec/perf/store_perf_spec.rb +88 -11
- data/spec/ping/gcm_spec.rb +99 -0
- data/spec/server/server_spec.rb +7 -0
- data/spec/server/stats_spec.rb +9 -2
- data/spec/source_sync_spec.rb +29 -0
- data/spec/spec_helper.rb +40 -38
- data/spec/stats/record_spec.rb +18 -9
- data/spec/store_spec.rb +128 -19
- data/spec/testdata/10000-data.txt +0 -0
- data/spec/testdata/5-data.txt +0 -0
- data/spec/testdata/5000-data.txt +0 -0
- data/tasks/jasmine.rake +1 -0
- data/tasks/redis.rake +16 -13
- metadata +71 -39
- data/commands/commands/redis_commands/redis_download.rb +0 -33
- data/commands/commands/redis_commands/redis_install.rb +0 -26
- data/commands/commands/rhoconnect/attach.rb +0 -8
- data/commands/commands/rhoconnect/console.rb +0 -15
- data/commands/commands/rhoconnect/start.rb +0 -18
- data/commands/utilities/dtach_installed.rb +0 -10
data/lib/rhoconnect/store.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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 |
|
85
|
-
@@db.rpush(dockey,
|
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,
|
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
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
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)
|
234
|
-
|
235
|
-
|
236
|
-
|
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
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
421
|
-
|
422
|
-
|
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
|
-
|
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,
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
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
|
488
|
-
return if
|
649
|
+
def _put_objects(dockey, data={})
|
650
|
+
return if data.empty? or not dockey
|
489
651
|
|
490
|
-
|
491
|
-
bucket_name = "#{dockey}:#{obj_bucket_index}"
|
492
|
-
_add_bucket_index(dockey, obj_bucket_index)
|
652
|
+
collected_adds = {}
|
493
653
|
@@db.pipelined do
|
494
|
-
|
495
|
-
unless
|
496
|
-
|
497
|
-
|
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
|
503
|
-
return
|
673
|
+
def _delete_objects(dockey, data={})
|
674
|
+
return 0 if data.empty? or not dockey
|
504
675
|
|
505
|
-
|
506
|
-
|
676
|
+
deleted_object_count = 0
|
677
|
+
indices_to_cleanup = Set.new
|
678
|
+
collected_rems = {}
|
507
679
|
@@db.pipelined do
|
508
|
-
|
509
|
-
|
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
|
-
|
697
|
+
_cleanup_buckets(dockey, indices_to_cleanup.to_a)
|
698
|
+
deleted_object_count
|
513
699
|
end
|
514
700
|
|
515
|
-
def
|
701
|
+
def _get_objects(dockey, keys)
|
516
702
|
return nil unless dockey
|
517
703
|
res = nil
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
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
|