rhoconnect 3.4.5 → 4.0.0.beta.10
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.
- data/CHANGELOG.md +57 -3
- data/Gemfile +9 -7
- data/Gemfile.lock +37 -37
- data/Rakefile +18 -7
- data/bench/benchapp/Gemfile +1 -1
- data/bench/benchapp/config.ru +0 -3
- data/bench/benchapp/controllers/ruby/application.rb +17 -0
- data/bench/benchapp/controllers/ruby/application_controller.rb +17 -0
- data/bench/benchapp/controllers/ruby/mock_adapter_controller.rb +8 -0
- data/bench/benchapp/controllers/ruby/queue_mock_adapter_controller.rb +8 -0
- data/bench/benchapp/{sources → models/ruby}/mock_adapter.rb +1 -1
- data/bench/benchapp/{sources → models/ruby}/queue_mock_adapter.rb +0 -0
- data/bench/benchapp/spec/{sources → models/ruby}/mock_adapter_spec.rb +1 -1
- data/bench/benchapp/spec/{sources → models/ruby}/queue_mock_adapter_spec.rb +1 -1
- data/bench/benchapp/spec/spec_helper.rb +2 -2
- data/bench/blobapp/Gemfile +1 -1
- data/bench/blobapp/config.ru +0 -3
- data/bench/blobapp/controllers/ruby/application_controller.rb +17 -0
- data/bench/blobapp/controllers/ruby/blob_adapter_controller.rb +8 -0
- data/bench/blobapp/{sources → models/ruby}/blob_adapter.rb +9 -2
- data/bench/blobapp/spec/{sources → models/ruby}/blob_adapter_spec.rb +1 -1
- data/bench/blobapp/spec/spec_helper.rb +1 -1
- data/bench/lib/bench/cli.rb +1 -1
- data/bench/scripts/blob_cud_script.rb +1 -1
- data/bench/scripts/query_md_script.rb +1 -1
- data/bench/scripts/query_only_script.rb +1 -1
- data/bench/scripts/query_script.rb +1 -1
- data/bench/scripts/test_query_script.rb +7 -1
- data/bench/spec/mock_adapter_spec.rb +1 -1
- data/bench/spec/result_spec.rb +3 -3
- data/bin/rhoconnect +5 -3
- data/commands/dtach/dtach_install.rb +2 -2
- data/commands/execute.rb +8 -3
- data/commands/generators/app.rb +3 -3
- data/commands/generators/controller.rb +6 -0
- data/commands/generators/model.rb +6 -0
- data/commands/generators/source.rb +3 -3
- data/commands/generators/update.rb +1 -1
- data/commands/redis/redis_about.rb +2 -2
- data/commands/redis/redis_download.rb +1 -1
- data/commands/redis/redis_install.rb +4 -3
- data/commands/redis/redis_restart.rb +4 -4
- data/commands/redis/redis_start.rb +5 -4
- data/commands/redis/redis_startbg.rb +5 -4
- data/commands/redis/redis_status.rb +13 -0
- data/commands/redis/redis_stop.rb +3 -3
- data/commands/rhoconnect/config.rb +28 -16
- data/commands/rhoconnect/flushdb.rb +1 -2
- data/commands/rhoconnect/get_token.rb +15 -11
- data/commands/rhoconnect/restart.rb +13 -5
- data/commands/rhoconnect/set_admin_password.rb +8 -8
- data/commands/rhoconnect/start.rb +74 -16
- data/commands/rhoconnect/startbg.rb +1 -1
- data/commands/rhoconnect/startdebug.rb +1 -1
- data/commands/rhoconnect/stop.rb +13 -1
- data/commands/rhoconnect/web.rb +5 -5
- data/commands/rhoconnect_console/console.rb +7 -5
- data/commands/{rhoconnect → rhoconnect_spec}/spec.rb +0 -0
- data/commands/rhoconnect_war/war.rb +9 -9
- data/commands/utilities/blank_app.ru +56 -0
- data/commands/utilities/redis_runner.rb +54 -19
- data/doc/authentication.txt +80 -6
- data/doc/blob-sync.txt +104 -97
- data/doc/bulk-sync.txt +1 -1
- data/doc/client-java.txt +3 -3
- data/doc/client-objc.txt +2 -2
- data/doc/client.txt +4 -4
- data/doc/command-line.txt +105 -200
- data/doc/data-partitioning.txt +40 -0
- data/doc/deploying.txt +249 -77
- data/doc/extending-rhoconnect-server.txt +40 -57
- data/doc/heroku-addon.txt +2 -0
- data/doc/install.txt +45 -95
- data/doc/introduction.txt +1 -1
- data/doc/java-plugin.txt +365 -190
- data/doc/metadata.txt +1 -1
- data/doc/migration.txt +108 -142
- data/doc/preparing-production.txt +1 -1
- data/doc/push-backend-setup.txt +2 -0
- data/doc/push-client-setup-android.txt +78 -0
- data/doc/push-client-setup-bb.txt +81 -0
- data/doc/push-client-setup-ios.txt +70 -0
- data/doc/push-client-setup-rps.txt +200 -0
- data/doc/push-client-setup.txt +63 -66
- data/doc/push-server-setup.txt +67 -40
- data/doc/push-testing.txt +29 -0
- data/doc/push.txt +21 -6
- data/doc/rest-api.txt +128 -55
- data/doc/rhoconnect-redis-stack.txt +120 -0
- data/doc/settings.txt +4 -12
- data/doc/source-adapters-intro.txt +28 -0
- data/doc/source-adapters.txt +235 -272
- data/doc/stats-middleware.txt +9 -29
- data/doc/supported-platforms.txt +21 -30
- data/doc/testing.txt +40 -42
- data/doc/tutorial.txt +72 -57
- data/examples/simple/Gemfile +1 -1
- data/examples/simple/application.rb +4 -5
- data/examples/simple/my_server.rb +2 -2
- data/examples/simple/settings/settings.yml +1 -1
- data/generators/rhoconnect.rb +151 -50
- data/generators/templates/application/Gemfile +1 -1
- data/generators/templates/application/Rakefile +3 -3
- data/generators/templates/application/config.ru +1 -4
- data/generators/templates/application/controllers/application_controller.rb +17 -0
- data/generators/templates/application/controllers/js/application_controller.js +14 -0
- data/generators/templates/application/controllers/ruby/application_controller.rb +17 -0
- data/generators/templates/application/package.json +8 -0
- data/generators/templates/application/rcgemfile +2 -5
- data/generators/templates/application/settings/settings.yml +3 -3
- data/generators/templates/application/spec/application_controller_spec.rb +23 -0
- data/generators/templates/application/spec/js_spec.rb +25 -0
- data/generators/templates/application/spec/spec_helper.rb +21 -7
- data/generators/templates/source/controllers/js/controller.js +7 -0
- data/generators/templates/source/controllers/ruby/controller.rb +8 -0
- data/generators/templates/source/controllers/ruby/controller_spec.rb +27 -0
- data/generators/templates/source/models/js/model.js +46 -0
- data/generators/templates/source/{source_adapter.rb → models/ruby/model.rb} +15 -10
- data/generators/templates/source/{source_spec.rb → models/ruby/model_spec.rb} +1 -1
- data/install.sh +5 -5
- data/installer/unix-like/create_texts.rb +2 -2
- data/installer/unix-like/rho_connect_install_constants.rb +2 -2
- data/installer/unix-like/rho_connect_install_utilities.rb +1 -1
- data/installer/utils/constants.rb +4 -4
- data/js-adapters/ballroom.js +216 -0
- data/js-adapters/node.rb +52 -0
- data/js-adapters/node_channel.rb +181 -0
- data/js-adapters/request.js +27 -0
- data/js-adapters/response.js +57 -0
- data/js-adapters/rhoconnect_helpers.js +60 -0
- data/js-adapters/router.js +60 -0
- data/js-adapters/server.js +5 -0
- data/lib/rhoconnect/api/app/ans_login.rb +3 -3
- data/lib/rhoconnect/api/app/bulk_data.rb +10 -10
- data/lib/rhoconnect/api/app/fast_delete.rb +11 -10
- data/lib/rhoconnect/api/app/fast_insert.rb +11 -10
- data/lib/rhoconnect/api/app/fast_update.rb +11 -10
- data/lib/rhoconnect/api/app/login.rb +5 -5
- data/lib/rhoconnect/api/app/push_deletes.rb +12 -11
- data/lib/rhoconnect/api/app/push_objects.rb +12 -11
- data/lib/rhoconnect/api/app/query.rb +8 -7
- data/lib/rhoconnect/api/app/queue_updates.rb +98 -94
- data/lib/rhoconnect/api/app/search.rb +8 -7
- data/lib/rhoconnect/api/client/client_get_db_doc.rb +5 -5
- data/lib/rhoconnect/api/client/client_set_db_doc.rb +8 -8
- data/lib/rhoconnect/api/client/create.rb +7 -7
- data/lib/rhoconnect/api/client/get_client_params.rb +4 -4
- data/lib/rhoconnect/api/client/list_client_docs.rb +17 -17
- data/lib/rhoconnect/api/client/register.rb +12 -12
- data/lib/rhoconnect/api/client/reset.rb +5 -5
- data/lib/rhoconnect/api/readstate/set_refresh_time.rb +9 -9
- data/lib/rhoconnect/api/source/get_source_params.rb +4 -4
- data/lib/rhoconnect/api/source/list_sources.rb +16 -16
- data/lib/rhoconnect/api/source/update_source_params.rb +6 -6
- data/lib/rhoconnect/api/store/get_db_doc.rb +4 -4
- data/lib/rhoconnect/api/store/set_db_doc.rb +7 -7
- data/lib/rhoconnect/api/system/get_adapter.rb +4 -4
- data/lib/rhoconnect/api/system/get_license_info.rb +8 -8
- data/lib/rhoconnect/api/system/login.rb +15 -15
- data/lib/rhoconnect/api/system/reset.rb +11 -11
- data/lib/rhoconnect/api/system/save_adapter.rb +4 -4
- data/lib/rhoconnect/api/system/stats.rb +22 -22
- data/lib/rhoconnect/api/user/create_user.rb +7 -7
- data/lib/rhoconnect/api/user/delete_client.rb +6 -6
- data/lib/rhoconnect/api/user/delete_user.rb +11 -10
- data/lib/rhoconnect/api/user/list_clients.rb +4 -4
- data/lib/rhoconnect/api/user/list_source_docs.rb +10 -10
- data/lib/rhoconnect/api/user/list_users.rb +3 -3
- data/lib/rhoconnect/api/user/ping.rb +3 -3
- data/lib/rhoconnect/api/user/show_user.rb +3 -3
- data/lib/rhoconnect/api/user/update_user.rb +5 -5
- data/lib/rhoconnect/api/user/user_get_db_doc.rb +5 -5
- data/lib/rhoconnect/api/user/user_set_db_doc.rb +10 -10
- data/lib/rhoconnect/api_token.rb +5 -6
- data/lib/rhoconnect/app.rb +6 -46
- data/lib/rhoconnect/application/init.rb +5 -2
- data/lib/rhoconnect/async.rb +76 -39
- data/lib/rhoconnect/bulk_data/bulk_data.rb +6 -4
- data/lib/rhoconnect/client.rb +59 -9
- data/lib/rhoconnect/condition/admin_required.rb +27 -0
- data/lib/rhoconnect/condition/client_required.rb +50 -0
- data/lib/rhoconnect/condition/login_required.rb +22 -0
- data/lib/rhoconnect/condition/source_required.rb +49 -0
- data/lib/rhoconnect/condition/verbs.rb +17 -0
- data/lib/rhoconnect/condition/verify_success.rb +19 -0
- data/lib/rhoconnect/controller/app_base.rb +74 -0
- data/lib/rhoconnect/controller/base.rb +68 -0
- data/lib/rhoconnect/controller/clients_controller.rb +79 -0
- data/lib/rhoconnect/controller/dynamic_adapter_controller.rb +93 -0
- data/lib/rhoconnect/controller/js_base.rb +124 -0
- data/lib/rhoconnect/controller/read_state_controller.rb +22 -0
- data/lib/rhoconnect/controller/source_adapter_base.rb +14 -0
- data/lib/rhoconnect/controller/sources_controller.rb +44 -0
- data/lib/rhoconnect/controller/store_controller.rb +25 -0
- data/lib/rhoconnect/controller/system_controller.rb +67 -0
- data/lib/rhoconnect/controller/users_controller.rb +99 -0
- data/lib/rhoconnect/db_adapter.rb +1 -3
- data/lib/rhoconnect/document.rb +159 -50
- data/lib/rhoconnect/handler/authenticate/execute_methods.rb +77 -0
- data/lib/rhoconnect/handler/authenticate/runner.rb +49 -0
- data/lib/rhoconnect/handler/authenticate.rb +3 -0
- data/lib/rhoconnect/handler/bulk_data.rb +28 -0
- data/lib/rhoconnect/handler/changes/engine.rb +271 -0
- data/lib/rhoconnect/handler/changes/execute_methods.rb +88 -0
- data/lib/rhoconnect/handler/changes/pass_through_runner.rb +11 -0
- data/lib/rhoconnect/handler/changes/runner.rb +53 -0
- data/lib/rhoconnect/handler/changes.rb +31 -0
- data/lib/rhoconnect/handler/helpers/auth_method.rb +29 -0
- data/lib/rhoconnect/handler/helpers/binding.rb +18 -0
- data/lib/rhoconnect/handler/helpers/bulk_data.rb +53 -0
- data/lib/rhoconnect/handler/helpers/source_job.rb +14 -0
- data/lib/rhoconnect/handler/helpers.rb +4 -0
- data/lib/rhoconnect/handler/plugin_callbacks/execute_methods.rb +99 -0
- data/lib/rhoconnect/handler/plugin_callbacks/runner.rb +28 -0
- data/lib/rhoconnect/handler/plugin_callbacks.rb +67 -0
- data/lib/rhoconnect/handler/query/engine.rb +93 -0
- data/lib/rhoconnect/handler/query/execute_methods.rb +21 -0
- data/lib/rhoconnect/handler/query/pass_through_runner.rb +35 -0
- data/lib/rhoconnect/handler/query/runner.rb +270 -0
- data/lib/rhoconnect/handler/query.rb +19 -0
- data/lib/rhoconnect/handler/search/engine.rb +60 -0
- data/lib/rhoconnect/handler/search/execute_methods.rb +32 -0
- data/lib/rhoconnect/handler/search/pass_through_runner.rb +18 -0
- data/lib/rhoconnect/handler/search/runner.rb +104 -0
- data/lib/rhoconnect/handler/search.rb +26 -0
- data/lib/rhoconnect/handler/sync.rb +29 -0
- data/lib/rhoconnect/jobs/source_job.rb +13 -4
- data/lib/rhoconnect/js_adapter.rb +79 -0
- data/lib/rhoconnect/license.rb +10 -2
- data/lib/rhoconnect/middleware/current_user.rb +14 -1
- data/lib/rhoconnect/middleware/helpers.rb +10 -93
- data/lib/rhoconnect/middleware/x_domain_session_wrapper.rb +1 -1
- data/lib/rhoconnect/model/base.rb +229 -0
- data/lib/rhoconnect/model/dynamic_adapter_model.rb +90 -0
- data/lib/rhoconnect/model/js_base.rb +121 -0
- data/lib/rhoconnect/ping/android.rb +1 -1
- data/lib/rhoconnect/predefined_adapters/bench_adapter.rb +7 -4
- data/lib/rhoconnect/read_state.rb +3 -3
- data/lib/rhoconnect/server.rb +159 -190
- data/lib/rhoconnect/source.rb +100 -11
- data/lib/rhoconnect/stats/record.rb +10 -10
- data/lib/rhoconnect/store.rb +905 -591
- data/lib/rhoconnect/{model.rb → store_orm.rb} +53 -115
- data/lib/rhoconnect/tasks.rb +18 -4
- data/lib/rhoconnect/test_methods.rb +30 -17
- data/lib/rhoconnect/user.rb +35 -17
- data/lib/rhoconnect/utilities.rb +1 -1
- data/lib/rhoconnect/version.rb +2 -2
- data/lib/rhoconnect/web-console/server.rb +29 -14
- data/lib/rhoconnect/web-console/views/home.js +10 -10
- data/lib/rhoconnect/web-console/views/new_ping.js +1 -1
- data/lib/rhoconnect.rb +120 -51
- data/rhoconnect.gemspec +4 -3
- data/spec/api/api_helper.rb +1 -6
- data/spec/api/app/fast_delete_spec.rb +4 -4
- data/spec/api/app/fast_insert_spec.rb +4 -4
- data/spec/api/app/fast_update_spec.rb +8 -8
- data/spec/api/app/push_deletes_spec.rb +2 -2
- data/spec/api/app/push_objects_spec.rb +5 -5
- data/spec/api/client/client_get_db_doc_spec.rb +6 -4
- data/spec/api/client/client_set_db_doc_spec.rb +3 -2
- data/spec/api/client/get_client_params_spec.rb +14 -0
- data/spec/api/client/list_client_docs_spec.rb +30 -20
- data/spec/api/client/reset_spec.rb +36 -0
- data/spec/api/source/get_source_params_spec.rb +23 -17
- data/spec/api/system/get_license_info_spec.rb +0 -20
- data/spec/api/system/login_spec.rb +8 -0
- data/spec/api/system/reset_spec.rb +0 -1
- data/spec/api/system/stats_spec.rb +5 -5
- data/spec/api/user/create_user_spec.rb +14 -6
- data/spec/api/user/delete_user_spec.rb +20 -18
- data/spec/api/user/list_users_spec.rb +5 -6
- data/spec/api/user/update_user_spec.rb +5 -4
- data/spec/apps/rhotestapp/config.ru +16 -1
- data/spec/apps/rhotestapp/controllers/js/js_sample_controller.js +23 -0
- data/spec/apps/rhotestapp/controllers/js/sample2_controller.js +32 -0
- data/spec/apps/rhotestapp/controllers/ruby/application_controller.rb +21 -0
- data/spec/apps/rhotestapp/controllers/ruby/sample_adapter_controller.rb +8 -0
- data/spec/apps/rhotestapp/models/js/js_sample.js +55 -0
- data/spec/apps/rhotestapp/models/js/sample2.js +25 -0
- data/spec/apps/rhotestapp/{sources → models/ruby}/base_adapter.rb +0 -0
- data/spec/apps/rhotestapp/{sources → models/ruby}/fixed_schema_adapter.rb +0 -0
- data/spec/apps/rhotestapp/{sources → models/ruby}/other_adapter.rb +0 -0
- data/spec/apps/rhotestapp/{sources → models/ruby}/sample_adapter.rb +0 -0
- data/spec/apps/rhotestapp/{sources → models/ruby}/simple_adapter.rb +2 -2
- data/spec/apps/rhotestapp/{sources → models/ruby}/sub_adapter.rb +0 -0
- data/spec/apps/rhotestapp/settings/settings.yml +0 -1
- data/spec/bulk_data/bulk_data_spec.rb +20 -5
- data/spec/cli/cli_spec.rb +83 -0
- data/spec/client_spec.rb +20 -17
- data/spec/client_sync_spec.rb +244 -406
- data/spec/controllers/js_base_spec.rb +89 -0
- data/spec/doc/doc_spec.rb +18 -18
- data/spec/document_spec.rb +29 -13
- data/spec/dynamic_adapter_spec.rb +6 -6
- data/spec/generator/generator_spec.rb +7 -4
- data/spec/jobs/bulk_data_job_spec.rb +14 -10
- data/spec/jobs/source_job_spec.rb +8 -8
- data/spec/license_spec.rb +5 -2
- data/spec/models/js_model_spec.rb +39 -0
- data/spec/node_spec.rb +42 -0
- data/spec/perf/store_perf_spec.rb +67 -12
- data/spec/ping/android_spec.rb +1 -1
- data/spec/read_state_spec.rb +1 -1
- data/spec/rhoconnect_spec.rb +1 -1
- data/spec/server/cors_spec.rb +14 -18
- data/spec/server/server_spec.rb +265 -88
- data/spec/server/stats_spec.rb +1 -1
- data/spec/source_adapter_spec.rb +54 -27
- data/spec/source_spec.rb +8 -3
- data/spec/source_sync_spec.rb +538 -468
- data/spec/spec_helper.rb +35 -4
- data/spec/stats/record_spec.rb +10 -10
- data/spec/{model_spec.rb → store_orm_spec.rb} +56 -54
- data/spec/store_spec.rb +159 -179
- data/spec/support/shared_examples.rb +36 -27
- data/spec/sync_states_spec.rb +40 -33
- data/spec/test_methods_spec.rb +18 -14
- data/spec/user_spec.rb +17 -30
- metadata +156 -52
- data/bench/benchapp/application.rb +0 -39
- data/bench/blobapp/application.rb +0 -44
- data/commands/rhoconnect/clean_start.rb +0 -9
- data/commands/rhoconnect/create_user.rb +0 -18
- data/commands/rhoconnect/delete_device.rb +0 -9
- data/commands/rhoconnect/delete_user.rb +0 -8
- data/commands/rhoconnect/reset.rb +0 -16
- data/commands/rhoconnect/reset_refresh.rb +0 -11
- data/generators/templates/application/application.rb +0 -43
- data/lib/rhoconnect/client_sync.rb +0 -434
- data/lib/rhoconnect/dynamic_adapter.rb +0 -91
- data/lib/rhoconnect/middleware/admin_user.rb +0 -23
- data/lib/rhoconnect/middleware/current_request.rb +0 -16
- data/lib/rhoconnect/middleware/login_required.rb +0 -22
- data/lib/rhoconnect/source_adapter.rb +0 -132
- data/lib/rhoconnect/source_sync.rb +0 -464
- data/spec/apps/rhotestapp/application.rb +0 -23
data/lib/rhoconnect/store.rb
CHANGED
|
@@ -5,762 +5,1077 @@ require 'connection_pool'
|
|
|
5
5
|
module Rhoconnect
|
|
6
6
|
|
|
7
7
|
class StoreLockException < RuntimeError; end
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
class Store
|
|
10
|
-
|
|
11
|
-
@@db = nil
|
|
10
|
+
@@dbs = nil
|
|
12
11
|
|
|
13
12
|
class << self
|
|
14
|
-
def
|
|
15
|
-
|
|
16
|
-
def db=(server=nil)
|
|
17
|
-
@@db = _get_redis(server)
|
|
13
|
+
def nullify
|
|
14
|
+
@@dbs = nil
|
|
18
15
|
end
|
|
19
|
-
|
|
16
|
+
|
|
20
17
|
def create(server=nil)
|
|
21
|
-
@@
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
return @@dbs if @@dbs
|
|
19
|
+
|
|
20
|
+
if server.is_a?Array
|
|
21
|
+
server.each do |server_string|
|
|
22
|
+
db_inst = RedisImpl.new
|
|
23
|
+
db_inst.create(server_string)
|
|
24
|
+
@@dbs ||= []
|
|
25
|
+
@@dbs << db_inst
|
|
26
|
+
end
|
|
27
|
+
else
|
|
28
|
+
db_inst = RedisImpl.new
|
|
29
|
+
db_inst.create(server)
|
|
30
|
+
@@dbs ||= []
|
|
31
|
+
@@dbs << db_inst
|
|
32
|
+
end
|
|
33
|
+
@@dbs
|
|
24
34
|
end
|
|
25
|
-
|
|
26
|
-
def
|
|
27
|
-
@@
|
|
35
|
+
|
|
36
|
+
def reconnect
|
|
37
|
+
@@dbs.each do |db_inst|
|
|
38
|
+
db_inst.reconnect
|
|
39
|
+
end
|
|
28
40
|
end
|
|
29
|
-
|
|
30
|
-
def
|
|
31
|
-
@@
|
|
41
|
+
|
|
42
|
+
def num_stores
|
|
43
|
+
@@dbs.nil? ? 0 : @@dbs.size
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def get_store(index = 0)
|
|
47
|
+
@@dbs[index]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def flush_all
|
|
51
|
+
@@dbs.each do |store_instance|
|
|
52
|
+
store_instance.flush_all
|
|
53
|
+
end
|
|
32
54
|
end
|
|
55
|
+
|
|
56
|
+
# Deletes all keys matching a given mask
|
|
57
|
+
def flush_data(keymask)
|
|
58
|
+
@@dbs.each do |store|
|
|
59
|
+
store.flush_data(keymask)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
alias_method :flash_data, :flush_data
|
|
33
63
|
|
|
34
64
|
def doc_type(dockey)
|
|
35
|
-
|
|
65
|
+
get_store(0).db.type(dockey) if dockey
|
|
36
66
|
end
|
|
37
67
|
|
|
38
68
|
def set_db_doc(dockey, data, append=false)
|
|
39
|
-
|
|
40
|
-
put_value(dockey, data)
|
|
41
|
-
else
|
|
42
|
-
put_data(dockey, data, append)
|
|
43
|
-
end
|
|
69
|
+
get_store(0).set_db_doc(dockey, data, append)
|
|
44
70
|
end
|
|
45
71
|
|
|
46
72
|
def get_db_doc(dockey)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
Store.get_data(dockey).to_json
|
|
73
|
+
doc = ""
|
|
74
|
+
@@dbs.each do |store|
|
|
75
|
+
if store.exists?(dockey)
|
|
76
|
+
doc = store.get_db_doc(dockey)
|
|
77
|
+
break
|
|
78
|
+
end
|
|
54
79
|
end
|
|
80
|
+
doc
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def keys(pattern)
|
|
84
|
+
get_store(0).keys(pattern)
|
|
55
85
|
end
|
|
56
86
|
|
|
57
87
|
def put_object(dockey, key, data={})
|
|
58
|
-
|
|
88
|
+
get_store(0).put_object(dockey, key, data)
|
|
59
89
|
end
|
|
60
90
|
|
|
61
91
|
# Adds set with given data, replaces existing set
|
|
62
92
|
# if it exists or appends data to the existing set
|
|
63
93
|
# if append flag set to true
|
|
64
94
|
def put_data(dockey,data={},append=false)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
put_list(dockey,data,append)
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
true
|
|
95
|
+
get_store(0).put_data(dockey, data, append)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Same as above, but sets TTL on every key
|
|
99
|
+
def put_tmp_data(dockey,data={},append=false)
|
|
100
|
+
get_store(0).put_tmp_data(dockey, data, append)
|
|
75
101
|
end
|
|
76
102
|
|
|
77
103
|
def put_list(dockey, data=[], append=false)
|
|
78
|
-
|
|
79
|
-
flash_data(dockey) unless append
|
|
80
|
-
@@db.pipelined do
|
|
81
|
-
data.each do |element|
|
|
82
|
-
@@db.rpush(dockey, element)
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
true
|
|
104
|
+
get_store(0).put_list(dockey, data, append)
|
|
87
105
|
end
|
|
88
106
|
|
|
89
107
|
# updates objects for a given doctype, source, user
|
|
90
108
|
# create new objects if necessary
|
|
91
109
|
def update_objects(dockey, data={})
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
new_object_count = 0
|
|
95
|
-
objs = get_objects(dockey, data.keys) || {}
|
|
96
|
-
|
|
97
|
-
collected_adds = {}
|
|
98
|
-
collected_rems = {}
|
|
99
|
-
my_bucket = nil
|
|
100
|
-
@@db.pipelined do
|
|
101
|
-
data.each do |key,obj|
|
|
102
|
-
is_create = objs[key].nil?
|
|
103
|
-
new_object_count += 1 if is_create
|
|
104
|
-
obj_bucket = _add_bucket_index(dockey, "#{_create_obj_index(key)}")
|
|
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
|
|
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)
|
|
127
|
-
end
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
#data1 = @@db.smembers(my_bucket)
|
|
132
|
-
#puts "data1 is #{data1.inspect}"
|
|
133
|
-
|
|
134
|
-
new_object_count
|
|
110
|
+
get_store(0).update_objects(dockey, data)
|
|
135
111
|
end
|
|
136
112
|
|
|
137
113
|
# Removes objects from a given doctype,source,user
|
|
138
114
|
def delete_objects(dockey,data=[])
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
objs = get_objects(dockey, data)
|
|
142
|
-
_delete_objects(dockey, objs)
|
|
115
|
+
get_store(0).delete_objects(dockey, data)
|
|
143
116
|
end
|
|
144
117
|
|
|
145
118
|
# Deletes data from a given doctype,source,user
|
|
146
119
|
def delete_data(dockey,data={})
|
|
147
|
-
|
|
148
|
-
_delete_objects(dockey, data)
|
|
149
|
-
end
|
|
150
|
-
true
|
|
120
|
+
get_store(0).delete_data(dockey, data)
|
|
151
121
|
end
|
|
152
122
|
|
|
153
123
|
# Adds a simple key/value pair
|
|
154
124
|
def put_value(dockey,value)
|
|
155
|
-
|
|
156
|
-
if value
|
|
157
|
-
@@db.set(dockey,value.to_s)
|
|
158
|
-
else
|
|
159
|
-
@@db.del(dockey)
|
|
160
|
-
end
|
|
161
|
-
end
|
|
125
|
+
get_store(0).put_value(dockey, value)
|
|
162
126
|
end
|
|
163
127
|
|
|
164
128
|
# Retrieves value for a given key
|
|
165
129
|
def get_value(dockey)
|
|
166
|
-
|
|
130
|
+
get_store(0).get_value(dockey)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def delete_value(dockey)
|
|
134
|
+
get_store(0).delete_value(dockey)
|
|
167
135
|
end
|
|
168
136
|
|
|
169
137
|
def incr(dockey)
|
|
170
|
-
|
|
138
|
+
get_store(0).incr(dockey)
|
|
171
139
|
end
|
|
172
140
|
|
|
173
141
|
def decr(dockey)
|
|
174
|
-
|
|
142
|
+
get_store(0).decr(dockey)
|
|
175
143
|
end
|
|
176
144
|
|
|
177
145
|
def update_count(dockey, count)
|
|
178
|
-
|
|
146
|
+
get_store(0).update_count(dockey, count)
|
|
179
147
|
end
|
|
180
148
|
|
|
181
149
|
def get_object(dockey, key)
|
|
182
|
-
|
|
183
|
-
(res and res.size > 0) ? res.values[0] : nil
|
|
150
|
+
get_store(0).get_object(dockey, key)
|
|
184
151
|
end
|
|
185
152
|
|
|
186
153
|
def get_objects(dockey, keys)
|
|
187
|
-
|
|
154
|
+
get_store(0).get_objects(dockey, keys)
|
|
188
155
|
end
|
|
189
156
|
|
|
190
157
|
# Retrieves set for given dockey,source,user
|
|
191
158
|
def get_data(dockey,type=Hash)
|
|
192
|
-
|
|
193
|
-
if dockey
|
|
194
|
-
if type == Hash
|
|
195
|
-
buckets = _get_buckets(dockey)
|
|
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
|
-
else
|
|
209
|
-
res = get_list(dockey)
|
|
210
|
-
end
|
|
211
|
-
end
|
|
212
|
-
res
|
|
159
|
+
get_store(0).get_data(dockey, type)
|
|
213
160
|
end
|
|
214
161
|
|
|
215
162
|
def get_list(dockey)
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
163
|
+
get_store(0).get_list(dockey)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# low-level operations with sorted sets
|
|
167
|
+
def zadd(dockey, score, value)
|
|
168
|
+
get_store(0).zadd(dockey, score, value)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def zrem(dockey, value)
|
|
172
|
+
get_store(0).zrem(dockey, value)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def zremrangebyscore(dockey, min_elem, max_elem)
|
|
176
|
+
get_store(0).zremrangebyscore(dockey, min_elem, max_elem)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def zscore(dockey, value)
|
|
180
|
+
get_store(0).zscore(dockey, value)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def zrevrange(dockey, start, stop)
|
|
184
|
+
get_store(0).zrevrange(dockey, start, stop)
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def zrange(dockey, start, stop)
|
|
188
|
+
get_store(0).zrange(dockey, start, stop)
|
|
221
189
|
end
|
|
222
190
|
|
|
223
191
|
# Retrieves diff data hash between two sets
|
|
224
192
|
# each entry is in the form of DIFF_OBJ_ELEMENT => [OBJ_KEY, OBJ_DATA_PAIRS]
|
|
225
193
|
def get_diff_data(src_dockey,dst_dockey,p_size=nil)
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
next unless keypairs
|
|
237
|
-
res[element] = keypairs
|
|
238
|
-
return res if p_size and (res.size >= p_size)
|
|
239
|
-
end
|
|
240
|
-
end
|
|
241
|
-
end
|
|
242
|
-
res
|
|
194
|
+
get_store(0).get_diff_data(src_dockey, dst_dockey, p_size)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Retrieves diff data hash between two sets by using BruteForce approach
|
|
198
|
+
# => download both sets from Redis and compute diffs inside of Ruby
|
|
199
|
+
# worst-cast scenario - it is much slower than doing Redis sdiff
|
|
200
|
+
# but : it allows Redis clustering
|
|
201
|
+
# each entry is in the form of DIFF_OBJ_ELEMENT => [OBJ_KEY, OBJ_DATA_PAIRS]
|
|
202
|
+
def get_diff_data_bruteforce(src_dockey,dst_dockey,p_size=nil)
|
|
203
|
+
get_store(0).get_diff_data_bruteforce(src_dockey, dst_dockey, p_size)
|
|
243
204
|
end
|
|
244
205
|
|
|
245
206
|
def get_inserts_deletes(inserts_elements_map, deletes_elements_map)
|
|
246
|
-
|
|
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)
|
|
281
|
-
end
|
|
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]
|
|
207
|
+
get_store(0).get_inserts_deletes(inserts_elements_map, deletes_elements_map)
|
|
292
208
|
end
|
|
293
209
|
|
|
294
210
|
def update_elements(dockey, inserts_elements_map, deletes_elements_map)
|
|
295
|
-
|
|
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)
|
|
334
|
-
end
|
|
335
|
-
|
|
336
|
-
# Deletes all keys matching a given mask
|
|
337
|
-
def flash_data(keymask)
|
|
338
|
-
if keymask.to_s[/[*\[\]?]/]
|
|
339
|
-
# If the keymask contains any pattern matching characters
|
|
340
|
-
# Use keys command to find all keys matching pattern (this is extremely expensive)
|
|
341
|
-
# Then delete matches
|
|
342
|
-
@@db.keys(keymask).each do |key|
|
|
343
|
-
_delete_doc(key)
|
|
344
|
-
end
|
|
345
|
-
else
|
|
346
|
-
# The keymask doesn't contain pattern matching characters
|
|
347
|
-
# A delete call is all that is needed
|
|
348
|
-
_delete_doc(keymask)
|
|
349
|
-
end
|
|
211
|
+
get_store(0).update_elements(dockey, inserts_elements_map, deletes_elements_map)
|
|
350
212
|
end
|
|
351
213
|
|
|
352
214
|
# Lock a given key and release when provided block is finished
|
|
353
|
-
def lock(dockey,timeout=0,raise_on_expire=false)
|
|
354
|
-
|
|
355
|
-
res = yield
|
|
356
|
-
release_lock(dockey,m_lock)
|
|
357
|
-
res
|
|
215
|
+
def lock(dockey,timeout=0,raise_on_expire=false, &block)
|
|
216
|
+
get_store(0).lock(dockey, timeout, raise_on_expire, &block)
|
|
358
217
|
end
|
|
359
218
|
|
|
360
219
|
def get_lock(dockey,timeout=0,raise_on_expire=false)
|
|
361
|
-
|
|
362
|
-
current_time = Time.now.to_i
|
|
363
|
-
ts = current_time+(Rhoconnect.lock_duration || timeout)+1
|
|
364
|
-
loop do
|
|
365
|
-
if not @@db.setnx(lock_key,ts)
|
|
366
|
-
current_lock = @@db.get(lock_key)
|
|
367
|
-
# ensure lock wasn't released between the setnx and get calls
|
|
368
|
-
if current_lock
|
|
369
|
-
current_lock_timeout = current_lock.to_i
|
|
370
|
-
if raise_on_expire or Rhoconnect.raise_on_expired_lock
|
|
371
|
-
if current_lock_timeout <= current_time
|
|
372
|
-
# lock expired before operation which set it up completed
|
|
373
|
-
# this process cannot continue without corrupting locked data
|
|
374
|
-
raise StoreLockException, "Lock \"#{lock_key}\" expired before it was released"
|
|
375
|
-
end
|
|
376
|
-
else
|
|
377
|
-
if current_lock_timeout <= current_time and
|
|
378
|
-
@@db.getset(lock_key,ts).to_i <= current_time
|
|
379
|
-
# previous lock expired and we replaced it with our own
|
|
380
|
-
break
|
|
381
|
-
end
|
|
382
|
-
end
|
|
383
|
-
# lock was released between setnx and get - try to acquire it again
|
|
384
|
-
elsif @@db.setnx(lock_key,ts)
|
|
385
|
-
break
|
|
386
|
-
end
|
|
387
|
-
sleep(1)
|
|
388
|
-
current_time = Time.now.to_i
|
|
389
|
-
else
|
|
390
|
-
break #no lock was set, so we set ours and leaving
|
|
391
|
-
end
|
|
392
|
-
end
|
|
393
|
-
return ts
|
|
220
|
+
get_store(0).get_lock(dockey, timeout, raise_on_expire)
|
|
394
221
|
end
|
|
395
222
|
|
|
396
|
-
# Due to redis bug #140, setnx always returns true so this doesn't work
|
|
397
|
-
# def get_lock(dockey,timeout=0)
|
|
398
|
-
# lock_key = _lock_key(dockey)
|
|
399
|
-
# until @@db.setnx(lock_key,1) do
|
|
400
|
-
# sleep(1)
|
|
401
|
-
# end
|
|
402
|
-
# @@db.expire(lock_key,timeout+1)
|
|
403
|
-
# Time.now.to_i+timeout+1
|
|
404
|
-
# end
|
|
405
|
-
|
|
406
223
|
def release_lock(dockey,lock,raise_on_expire=false)
|
|
407
|
-
|
|
224
|
+
get_store(0).release_lock(dockey, lock, raise_on_expire)
|
|
408
225
|
end
|
|
409
226
|
|
|
410
227
|
# Create a copy of srckey in dstkey
|
|
411
228
|
def clone(srckey,dstkey)
|
|
412
|
-
|
|
413
|
-
if buckets.size
|
|
414
|
-
@@db.pipelined do
|
|
415
|
-
buckets.each do |bucket_index|
|
|
416
|
-
_add_bucket_index(dstkey, bucket_index)
|
|
417
|
-
@@db.sdiffstore("#{dstkey}:#{bucket_index}", "#{srckey}:#{bucket_index}", '')
|
|
418
|
-
end
|
|
419
|
-
end
|
|
420
|
-
else
|
|
421
|
-
@@db.sdiffstore(dstkey,srckey,'')
|
|
422
|
-
end
|
|
229
|
+
get_store(0).clone(srckey, dstkey)
|
|
423
230
|
end
|
|
424
231
|
|
|
425
232
|
# Rename srckey to dstkey
|
|
426
233
|
def rename(srckey,dstkey)
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
@@db.rename("#{srckey}:#{bucket_index}", "#{dstkey}:#{bucket_index}")
|
|
434
|
-
end
|
|
435
|
-
end
|
|
436
|
-
else
|
|
437
|
-
@@db.rename(srckey,dstkey) if @@db.exists(srckey)
|
|
438
|
-
end
|
|
234
|
+
get_store(0).rename(srckey, dstkey)
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# Rename srckey to dstkey
|
|
238
|
+
def rename_tmp_data(srckey,dstkey)
|
|
239
|
+
get_store(0).rename_tmp_data(srckey, dstkey)
|
|
439
240
|
end
|
|
440
241
|
|
|
242
|
+
|
|
441
243
|
def put_zdata(dockey,assoc_key,data={},append=false)
|
|
442
|
-
|
|
443
|
-
flush_zdata(dockey) unless append
|
|
444
|
-
current_score = 0
|
|
445
|
-
current_score_data = Store.db.zrevrange(dockey,0,0,:with_scores => true)
|
|
446
|
-
current_score = current_score_data[-1][1].to_i if current_score_data and current_score_data[-1]
|
|
447
|
-
current_score += 1
|
|
448
|
-
data.each do |key,hash_value|
|
|
449
|
-
unique_record_key = setelement(current_score,assoc_key, key)
|
|
450
|
-
Store.db.zadd(dockey, current_score, unique_record_key)
|
|
451
|
-
Store.put_data("#{dockey}:#{unique_record_key}",{key => hash_value})
|
|
452
|
-
end
|
|
453
|
-
true
|
|
244
|
+
get_store(0).put_zdata(dockey, assoc_key, data, append)
|
|
454
245
|
end
|
|
455
246
|
|
|
456
247
|
# Retrieves set for given dockey,associated key (client_id), obj_hashes
|
|
457
248
|
def get_zdata(dockey)
|
|
458
|
-
|
|
459
|
-
ret = []
|
|
460
|
-
keys = []
|
|
461
|
-
unless data.nil?
|
|
462
|
-
scores = []
|
|
463
|
-
data.each do |zsetkey|
|
|
464
|
-
obj_hash = Store.get_data "#{dockey}:#{zsetkey}"
|
|
465
|
-
score,key,objkey = getelement(zsetkey)
|
|
466
|
-
if scores[-1] != score
|
|
467
|
-
ret << obj_hash
|
|
468
|
-
keys << key
|
|
469
|
-
scores << score
|
|
470
|
-
else
|
|
471
|
-
ret[-1].merge!(obj_hash)
|
|
472
|
-
end
|
|
473
|
-
end
|
|
474
|
-
end
|
|
475
|
-
[ret, keys]
|
|
249
|
+
get_store(0).get_zdata(dockey)
|
|
476
250
|
end
|
|
477
251
|
|
|
478
252
|
# Deletes all keys and their hashes from the Redis DB
|
|
479
253
|
def flush_zdata(dockey)
|
|
480
|
-
|
|
481
|
-
data.each do |hash_key|
|
|
482
|
-
_delete_doc("#{dockey}:#{hash_key}")
|
|
483
|
-
end
|
|
484
|
-
Store.db.zremrangebyrank(dockey, 0, -1)
|
|
485
|
-
end
|
|
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)
|
|
254
|
+
get_store(0).flush_zdata(dockey)
|
|
541
255
|
end
|
|
542
256
|
|
|
543
257
|
def exists?(key)
|
|
544
|
-
|
|
258
|
+
get_store(0).exists?(key)
|
|
545
259
|
end
|
|
546
260
|
|
|
547
261
|
alias_method :set_value, :put_value
|
|
548
262
|
alias_method :set_data, :put_data
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
class RedisImpl
|
|
267
|
+
RESERVED_ATTRIB_NAMES = ["attrib_type", "id"] unless defined? RESERVED_ATTRIB_NAMES
|
|
268
|
+
@db = nil
|
|
269
|
+
|
|
270
|
+
def create(server=nil)
|
|
271
|
+
@db ||= _get_redis(server)
|
|
272
|
+
raise "Error connecting to Redis store." unless @db and
|
|
273
|
+
(@db.is_a?(Redis) or @db.is_a?(Redis::Client) or @db.is_a?(ConnectionPool::Wrapper))
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def reconnect
|
|
277
|
+
@db.client.reconnect
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def flush_all
|
|
281
|
+
@db.flushdb
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def start_transaction
|
|
285
|
+
@db.multi
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def execute_transaction
|
|
289
|
+
@db.exec
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def doc_type(dockey)
|
|
293
|
+
@db.type(dockey) if dockey
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def set_db_doc(dockey, data, append=false)
|
|
297
|
+
if data.is_a?(String)
|
|
298
|
+
put_value(dockey, data)
|
|
299
|
+
else
|
|
300
|
+
put_data(dockey, data, append)
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
def get_db_doc(dockey)
|
|
305
|
+
doctype = doc_type(dockey)
|
|
306
|
+
if doctype == 'string'
|
|
307
|
+
get_value(dockey)
|
|
308
|
+
elsif doctype == 'list'
|
|
309
|
+
get_data(dockey, Array).to_json
|
|
310
|
+
else
|
|
311
|
+
get_data(dockey).to_json
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
def put_object(dockey, key, data={})
|
|
316
|
+
_put_objects(dockey, {key => data})
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
# Same as above, but sets TTL on every key
|
|
320
|
+
def put_tmp_data(dockey,data={},append=false)
|
|
321
|
+
put_data(dockey, data, append, Rhoconnect.store_key_ttl)
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
# Adds set with given data, replaces existing set
|
|
325
|
+
# if it exists or appends data to the existing set
|
|
326
|
+
# if append flag set to true
|
|
327
|
+
# if ttl > 0 - sets expriration time on the keys
|
|
328
|
+
def put_data(dockey,data={},append=false, ttl=0)
|
|
329
|
+
if dockey and data
|
|
330
|
+
flush_data(dockey) unless append
|
|
331
|
+
# Inserts a hash or array
|
|
332
|
+
if data.is_a?Hash
|
|
333
|
+
_put_objects(dockey, data, ttl)
|
|
568
334
|
else
|
|
569
|
-
|
|
570
|
-
Redis.connect(:timeout => 30, :thread_safe => true)
|
|
571
|
-
end
|
|
335
|
+
put_list(dockey,data,append, ttl)
|
|
572
336
|
end
|
|
573
337
|
end
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
else
|
|
586
|
-
Redis.connect(:timeout => 30, :thread_safe => true)
|
|
338
|
+
true
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
def put_list(dockey, data=[], append=false, ttl=0)
|
|
342
|
+
if dockey and data
|
|
343
|
+
flush_data(dockey) unless append
|
|
344
|
+
@db.pipelined do
|
|
345
|
+
data.each do |element|
|
|
346
|
+
@db.rpush(dockey, element)
|
|
347
|
+
end
|
|
348
|
+
@db.expire(dockey, ttl) if ttl > 0
|
|
587
349
|
end
|
|
588
350
|
end
|
|
589
|
-
|
|
590
|
-
|
|
351
|
+
true
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
# updates objects for a given doctype, source, user
|
|
355
|
+
# create new objects if necessary
|
|
356
|
+
def update_objects(dockey, data={})
|
|
357
|
+
return 0 unless dockey and data
|
|
591
358
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
359
|
+
new_object_count = 0
|
|
360
|
+
objs = get_objects(dockey, data.keys) || {}
|
|
361
|
+
|
|
362
|
+
collected_adds = {}
|
|
363
|
+
collected_rems = {}
|
|
364
|
+
my_bucket = nil
|
|
365
|
+
@db.pipelined do
|
|
366
|
+
data.each do |key,obj|
|
|
367
|
+
is_create = objs[key].nil?
|
|
368
|
+
new_object_count += 1 if is_create
|
|
369
|
+
obj_bucket = _add_bucket_index(dockey, "#{_create_obj_index(key)}")
|
|
595
370
|
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
def _delete_doc(dockey)
|
|
602
|
-
# check if this doc has buckets
|
|
603
|
-
if(@@db.exists("#{dockey}:indices"))
|
|
604
|
-
buckets_list = _get_buckets(dockey)
|
|
605
|
-
# delete all buckets
|
|
606
|
-
@@db.pipelined do
|
|
607
|
-
@@db.del("#{dockey}:indices")
|
|
608
|
-
buckets_list.each do |bucket|
|
|
609
|
-
@@db.del(bucket)
|
|
610
|
-
end
|
|
371
|
+
# collect SREM (if object exists in DB)
|
|
372
|
+
unless is_create
|
|
373
|
+
old_element = set_obj_element(key,objs[key])
|
|
374
|
+
collected_rems[obj_bucket] ||= []
|
|
375
|
+
collected_rems[obj_bucket] << old_element
|
|
611
376
|
end
|
|
377
|
+
# update the object and collect SADD
|
|
378
|
+
objs[key] ||= {}
|
|
379
|
+
objs[key].merge!(obj)
|
|
380
|
+
|
|
381
|
+
new_element = set_obj_element(key,objs[key])
|
|
382
|
+
collected_adds[obj_bucket] ||= []
|
|
383
|
+
collected_adds[obj_bucket] << new_element
|
|
384
|
+
end
|
|
385
|
+
# process all SADD and SREM commands as one
|
|
386
|
+
# SREM must go first
|
|
387
|
+
collected_rems.each do |bucket, bucket_data|
|
|
388
|
+
@db.srem(bucket, bucket_data)
|
|
389
|
+
end
|
|
390
|
+
collected_adds.each do |bucket, bucket_data|
|
|
391
|
+
@db.sadd(bucket, bucket_data)
|
|
612
392
|
end
|
|
613
|
-
|
|
614
|
-
# delete main doc
|
|
615
|
-
@@db.del(dockey)
|
|
616
393
|
end
|
|
617
394
|
|
|
618
|
-
# create object's bucket index
|
|
619
|
-
# using SHA1 hashing
|
|
620
|
-
def _create_obj_index(key)
|
|
621
|
-
Digest::SHA1.hexdigest(key)[0..1]
|
|
622
|
-
end
|
|
623
395
|
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
@@db.hsetnx("#{dockey}:indices", bucket_index, bucket_name)
|
|
627
|
-
bucket_name
|
|
628
|
-
end
|
|
396
|
+
#data1 = @db.smembers(my_bucket)
|
|
397
|
+
#puts "data1 is #{data1.inspect}"
|
|
629
398
|
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
399
|
+
new_object_count
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
# Removes objects from a given doctype,source,user
|
|
403
|
+
def delete_objects(dockey,data=[])
|
|
404
|
+
return 0 unless dockey and data
|
|
633
405
|
|
|
634
|
-
|
|
635
|
-
|
|
406
|
+
objs = get_objects(dockey, data)
|
|
407
|
+
_delete_objects(dockey, objs)
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
# Deletes data from a given doctype,source,user
|
|
411
|
+
def delete_data(dockey,data={})
|
|
412
|
+
if dockey and data
|
|
413
|
+
_delete_objects(dockey, data)
|
|
414
|
+
end
|
|
415
|
+
true
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
# Adds a simple key/value pair
|
|
419
|
+
def put_value(dockey,value)
|
|
420
|
+
if dockey
|
|
421
|
+
if value
|
|
422
|
+
@db.set(dockey,value.to_s)
|
|
423
|
+
else
|
|
424
|
+
@db.del(dockey)
|
|
425
|
+
end
|
|
426
|
+
end
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
# Retrieves value for a given key
|
|
430
|
+
def get_value(dockey)
|
|
431
|
+
@db.get(dockey) if dockey
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
def delete_value(dockey)
|
|
435
|
+
@db.del(dockey)
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
def incr(dockey)
|
|
439
|
+
@db.incr(dockey)
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
def decr(dockey)
|
|
443
|
+
@db.decr(dockey)
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
def update_count(dockey, count)
|
|
447
|
+
@db.incrby(dockey, count)
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
def get_object(dockey, key)
|
|
451
|
+
res = _get_objects(dockey, [key])
|
|
452
|
+
(res and res.size > 0) ? res.values[0] : nil
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
def get_objects(dockey, keys)
|
|
456
|
+
_get_objects(dockey, keys)
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
# Retrieves set for given dockey,source,user
|
|
460
|
+
def get_data(dockey,type=Hash)
|
|
461
|
+
res = type == Hash ? {} : []
|
|
462
|
+
if dockey
|
|
463
|
+
if type == Hash
|
|
464
|
+
buckets = _get_buckets(dockey)
|
|
465
|
+
members = @db.pipelined do
|
|
466
|
+
buckets.each do |bucket|
|
|
467
|
+
@db.smembers(bucket)
|
|
468
|
+
end if buckets
|
|
469
|
+
end
|
|
470
|
+
members.each do |elements|
|
|
471
|
+
elements.each do |element|
|
|
472
|
+
key,obj = get_obj_element(element)
|
|
473
|
+
res[key] = obj
|
|
474
|
+
#res[key].merge!({attrib => value})
|
|
475
|
+
end if elements
|
|
476
|
+
end if members
|
|
477
|
+
else
|
|
478
|
+
res = get_list(dockey)
|
|
479
|
+
end
|
|
480
|
+
end
|
|
481
|
+
res
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
def get_list(dockey)
|
|
485
|
+
res = []
|
|
486
|
+
if dockey
|
|
487
|
+
res = @db.lrange(dockey, 0, -1)
|
|
488
|
+
end
|
|
489
|
+
res
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
# Retrieves diff data hash between two sets
|
|
493
|
+
# each entry is in the form of DIFF_OBJ_ELEMENT => [OBJ_KEY, OBJ_DATA_PAIRS]
|
|
494
|
+
def get_diff_data(src_dockey,dst_dockey,p_size=nil)
|
|
495
|
+
res = {}
|
|
496
|
+
return res if p_size == 0
|
|
497
|
+
# return immediately if p_size == 0
|
|
498
|
+
# NOTE: 0 and nil are different, nil means - return all diffs
|
|
499
|
+
if src_dockey and dst_dockey
|
|
500
|
+
# obtain combined indices
|
|
501
|
+
indices = @db.hgetall("#{dst_dockey}:indices")
|
|
502
|
+
indices.keys.each do |index|
|
|
503
|
+
dst_bucket_name = "#{dst_dockey}:#{index}"
|
|
504
|
+
src_bucket_name = "#{src_dockey}:#{index}"
|
|
505
|
+
diff_elements = @db.sdiff(dst_bucket_name,src_bucket_name)
|
|
506
|
+
diff_elements.each do |element|
|
|
507
|
+
keypairs = get_obj_key_and_pairs(element)
|
|
508
|
+
next unless keypairs
|
|
509
|
+
res[element] = keypairs
|
|
510
|
+
return res if p_size and (res.size >= p_size)
|
|
511
|
+
end
|
|
512
|
+
end
|
|
513
|
+
end
|
|
514
|
+
res
|
|
515
|
+
end
|
|
516
|
+
|
|
517
|
+
# Retrieves diff data hash between two sets by using BruteForce approach
|
|
518
|
+
# => download both sets from Redis and compute diffs inside of Ruby
|
|
519
|
+
# worst-cast scenario - it is much slower than doing Redis sdiff
|
|
520
|
+
# but : it allows Redis clustering
|
|
521
|
+
# each entry is in the form of DIFF_OBJ_ELEMENT => [OBJ_KEY, OBJ_DATA_PAIRS]
|
|
522
|
+
def get_diff_data_bruteforce(src_dockey,dst_dockey,p_size=nil)
|
|
523
|
+
inserts = {}
|
|
524
|
+
deletes = {}
|
|
525
|
+
# return immediately if p_size == 0
|
|
526
|
+
# NOTE: 0 and nil are different, nil means - return all diffs
|
|
527
|
+
return res if p_size == 0
|
|
528
|
+
if src_dockey and dst_dockey
|
|
529
|
+
# obtain combined indices
|
|
530
|
+
indices = @db.hgetall("#{dst_dockey}:indices")
|
|
531
|
+
indices.merge!(@db.hgetall("#{src_dockey}:indices"))
|
|
532
|
+
indices.keys.each do |index|
|
|
533
|
+
dst_bucket_name = "#{dst_dockey}:#{index}"
|
|
534
|
+
src_bucket_name = "#{src_dockey}:#{index}"
|
|
535
|
+
src_elements = Set.new(@db.smembers(src_bucket_name))
|
|
536
|
+
dst_elements = Set.new(@db.smembers(dst_bucket_name))
|
|
537
|
+
|
|
538
|
+
insert_diff_elements = dst_elements.dup.subtract(src_elements) unless p_size and (inserts.size >= p_size)
|
|
539
|
+
delete_diff_elements = src_elements.dup.subtract(dst_elements) unless p_size and (deletes.size >= p_size)
|
|
540
|
+
|
|
541
|
+
insert_diff_elements.each do |element|
|
|
542
|
+
keypairs = get_obj_key_and_pairs(element)
|
|
543
|
+
next unless keypairs
|
|
544
|
+
inserts[element] = keypairs
|
|
545
|
+
break if p_size and (inserts.size >= p_size)
|
|
546
|
+
end if insert_diff_elements
|
|
547
|
+
|
|
548
|
+
delete_diff_elements.each do |element|
|
|
549
|
+
keypairs = get_obj_key_and_pairs(element)
|
|
550
|
+
next unless keypairs
|
|
551
|
+
deletes[element] = keypairs
|
|
552
|
+
break if p_size and (deletes.size >= p_size)
|
|
553
|
+
end if delete_diff_elements
|
|
554
|
+
|
|
555
|
+
break if p_size and (inserts.size >= p_size) and (deletes.size >= p_size)
|
|
556
|
+
end
|
|
557
|
+
end
|
|
558
|
+
[inserts, deletes]
|
|
559
|
+
end
|
|
560
|
+
|
|
561
|
+
def get_inserts_deletes(inserts_elements_map, deletes_elements_map)
|
|
562
|
+
inserts_obj_hash = {}
|
|
563
|
+
inserts_elements_map.each do |element,keypairs|
|
|
564
|
+
key,obj_pairs = keypairs[0],keypairs[1]
|
|
565
|
+
next unless (key and obj_pairs)
|
|
566
|
+
inserts_obj_hash[key] = Set.new(obj_pairs)
|
|
636
567
|
end
|
|
637
568
|
|
|
638
|
-
|
|
639
|
-
|
|
569
|
+
deletes_obj_hash = {}
|
|
570
|
+
deletes_elements_map.each do |element,keypairs|
|
|
571
|
+
key,obj_pairs = keypairs[0],keypairs[1]
|
|
572
|
+
next unless (key and obj_pairs)
|
|
573
|
+
deletes_obj_hash[key] = Set.new(obj_pairs)
|
|
640
574
|
end
|
|
575
|
+
# modified attributes
|
|
576
|
+
inserts = {}
|
|
577
|
+
deletes = {}
|
|
641
578
|
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
579
|
+
inserts_obj_hash.each do |key, obj_set|
|
|
580
|
+
deletes_pairs = nil
|
|
581
|
+
inserts_pairs = nil
|
|
582
|
+
if deletes_obj_hash.has_key?(key)
|
|
583
|
+
deletes_pairs = deletes_obj_hash[key].dup.subtract(obj_set).to_a
|
|
584
|
+
inserts_pairs = obj_set.dup.subtract(deletes_obj_hash[key]).to_a
|
|
585
|
+
# remove the key from the deletes set - we already processed it
|
|
586
|
+
deletes_obj_hash.delete(key)
|
|
587
|
+
else
|
|
588
|
+
# if object is not in the deletes set - then, it's all inserts
|
|
589
|
+
inserts_pairs = obj_set.to_a
|
|
590
|
+
end
|
|
591
|
+
# split resulting pairs
|
|
592
|
+
if inserts_pairs and inserts_pairs.size > 0
|
|
593
|
+
inserts[key] = split_obj_pairs(inserts_pairs)
|
|
594
|
+
end
|
|
595
|
+
if deletes_pairs and deletes_pairs.size > 0
|
|
596
|
+
deletes[key] = split_obj_pairs(deletes_pairs)
|
|
597
|
+
end
|
|
598
|
+
end
|
|
599
|
+
# after we analyzed the inserts__obj_hash
|
|
600
|
+
# => deletes_obj_hash should contain only the unmatched deletes
|
|
601
|
+
deletes_obj_hash.each do |key, obj_set|
|
|
602
|
+
if obj_set.size > 0
|
|
603
|
+
deletes[key] = split_obj_pairs(obj_set.to_a)
|
|
604
|
+
end
|
|
647
605
|
end
|
|
648
606
|
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
607
|
+
[inserts, deletes]
|
|
608
|
+
end
|
|
609
|
+
|
|
610
|
+
def update_elements(dockey, inserts_elements_map, deletes_elements_map)
|
|
611
|
+
indices_to_cleanup = Set.new
|
|
612
|
+
@db.pipelined do
|
|
652
613
|
collected_adds = {}
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
614
|
+
collected_rems = {}
|
|
615
|
+
|
|
616
|
+
inserts_elements_map.each do |element,keypairs|
|
|
617
|
+
key = keypairs[0]
|
|
618
|
+
next if not key or not element or element.size == 0
|
|
619
|
+
|
|
620
|
+
obj_bucket_index = _create_obj_index(key)
|
|
621
|
+
bucket_name = "#{dockey}:#{obj_bucket_index}"
|
|
622
|
+
_add_bucket_index(dockey, obj_bucket_index)
|
|
623
|
+
|
|
624
|
+
collected_adds[bucket_name] ||= []
|
|
625
|
+
collected_adds[bucket_name] << element
|
|
626
|
+
end
|
|
627
|
+
|
|
628
|
+
deletes_elements_map.each do |element,keypairs|
|
|
629
|
+
key = keypairs[0]
|
|
630
|
+
next if not key or not element or element.size == 0
|
|
631
|
+
|
|
632
|
+
obj_bucket_index = _create_obj_index(key)
|
|
633
|
+
bucket_name = "#{dockey}:#{obj_bucket_index}"
|
|
634
|
+
indices_to_cleanup << bucket_name
|
|
635
|
+
|
|
636
|
+
collected_rems[bucket_name] ||= []
|
|
637
|
+
collected_rems[bucket_name] << element
|
|
638
|
+
end
|
|
639
|
+
|
|
640
|
+
# now, perform SREM first, then SADD
|
|
641
|
+
collected_rems.each do |bucket, bucket_data|
|
|
642
|
+
@db.srem(bucket, bucket_data)
|
|
643
|
+
end
|
|
644
|
+
collected_adds.each do |bucket,bucket_data|
|
|
645
|
+
@db.sadd(bucket, bucket_data)
|
|
646
|
+
end
|
|
647
|
+
end
|
|
648
|
+
# now, cleanup buckets if necessary
|
|
649
|
+
_cleanup_buckets(dockey, indices_to_cleanup.to_a)
|
|
650
|
+
end
|
|
651
|
+
|
|
652
|
+
def keys(pattern)
|
|
653
|
+
@db.keys(pattern)
|
|
654
|
+
end
|
|
655
|
+
|
|
656
|
+
# Deletes all keys matching a given mask
|
|
657
|
+
def flush_data(keymask)
|
|
658
|
+
if keymask.to_s[/[*\[\]?]/]
|
|
659
|
+
# If the keymask contains any pattern matching characters
|
|
660
|
+
# Use keys command to find all keys matching pattern (this is extremely expensive)
|
|
661
|
+
# Then delete matches
|
|
662
|
+
keys(keymask).each do |key|
|
|
663
|
+
_delete_doc(key)
|
|
664
|
+
end
|
|
665
|
+
else
|
|
666
|
+
# The keymask doesn't contain pattern matching characters
|
|
667
|
+
# A delete call is all that is needed
|
|
668
|
+
_delete_doc(keymask)
|
|
669
|
+
end
|
|
670
|
+
end
|
|
671
|
+
alias_method :flash_data, :flush_data
|
|
672
|
+
|
|
673
|
+
# Lock a given key and release when provided block is finished
|
|
674
|
+
def lock(dockey,timeout=0,raise_on_expire=false, &block)
|
|
675
|
+
m_lock = get_lock(dockey,timeout,raise_on_expire)
|
|
676
|
+
res = yield
|
|
677
|
+
release_lock(dockey,m_lock)
|
|
678
|
+
res
|
|
679
|
+
end
|
|
680
|
+
|
|
681
|
+
def get_lock(dockey,timeout=0,raise_on_expire=false)
|
|
682
|
+
lock_key = _lock_key(dockey)
|
|
683
|
+
current_time = Time.now.to_i
|
|
684
|
+
ts = current_time+(Rhoconnect.lock_duration || timeout)+1
|
|
685
|
+
loop do
|
|
686
|
+
if not @db.setnx(lock_key,ts)
|
|
687
|
+
current_lock = @db.get(lock_key)
|
|
688
|
+
# ensure lock wasn't released between the setnx and get calls
|
|
689
|
+
if current_lock
|
|
690
|
+
current_lock_timeout = current_lock.to_i
|
|
691
|
+
if raise_on_expire or Rhoconnect.raise_on_expired_lock
|
|
692
|
+
if current_lock_timeout <= current_time
|
|
693
|
+
# lock expired before operation which set it up completed
|
|
694
|
+
# this process cannot continue without corrupting locked data
|
|
695
|
+
raise StoreLockException, "Lock \"#{lock_key}\" expired before it was released"
|
|
696
|
+
end
|
|
697
|
+
else
|
|
698
|
+
if current_lock_timeout <= current_time and
|
|
699
|
+
@db.getset(lock_key,ts).to_i <= current_time
|
|
700
|
+
# previous lock expired and we replaced it with our own
|
|
701
|
+
break
|
|
702
|
+
end
|
|
703
|
+
end
|
|
704
|
+
# lock was released between setnx and get - try to acquire it again
|
|
705
|
+
elsif @db.setnx(lock_key,ts)
|
|
706
|
+
break
|
|
707
|
+
end
|
|
708
|
+
sleep(1)
|
|
709
|
+
current_time = Time.now.to_i
|
|
710
|
+
else
|
|
711
|
+
break #no lock was set, so we set ours and leaving
|
|
712
|
+
end
|
|
713
|
+
end
|
|
714
|
+
return ts
|
|
715
|
+
end
|
|
716
|
+
|
|
717
|
+
# Due to redis bug #140, setnx always returns true so this doesn't work
|
|
718
|
+
# def get_lock(dockey,timeout=0)
|
|
719
|
+
# lock_key = _lock_key(dockey)
|
|
720
|
+
# until @db.setnx(lock_key,1) do
|
|
721
|
+
# sleep(1)
|
|
722
|
+
# end
|
|
723
|
+
# @db.expire(lock_key,timeout+1)
|
|
724
|
+
# Time.now.to_i+timeout+1
|
|
725
|
+
# end
|
|
726
|
+
|
|
727
|
+
def release_lock(dockey,lock,raise_on_expire=false)
|
|
728
|
+
@db.del(_lock_key(dockey)) if raise_on_expire or Rhoconnect.raise_on_expired_lock or (lock >= Time.now.to_i)
|
|
729
|
+
end
|
|
730
|
+
|
|
731
|
+
# Create a copy of srckey in dstkey
|
|
732
|
+
def clone(srckey,dstkey)
|
|
733
|
+
buckets = _get_bucket_indices(srckey)
|
|
734
|
+
if buckets.size
|
|
735
|
+
@db.pipelined do
|
|
736
|
+
buckets.each do |bucket_index|
|
|
737
|
+
_add_bucket_index(dstkey, bucket_index)
|
|
738
|
+
@db.sdiffstore("#{dstkey}:#{bucket_index}", "#{srckey}:#{bucket_index}", '')
|
|
664
739
|
end
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
740
|
+
end
|
|
741
|
+
else
|
|
742
|
+
@db.sdiffstore(dstkey,srckey,'')
|
|
743
|
+
end
|
|
744
|
+
end
|
|
745
|
+
|
|
746
|
+
# Rename temp doc srckey to persist dstkey
|
|
747
|
+
def rename_tmp_data(srckey,dstkey)
|
|
748
|
+
rename(srckey,dstkey,true)
|
|
749
|
+
end
|
|
750
|
+
|
|
751
|
+
# Rename srckey to dstkey
|
|
752
|
+
# also, removes TTL if ordered (normally - it is not necessary)
|
|
753
|
+
def rename(srckey,dstkey,make_persist=false)
|
|
754
|
+
buckets = _get_bucket_indices(srckey)
|
|
755
|
+
if buckets.size
|
|
756
|
+
@db.pipelined do
|
|
757
|
+
@db.del("#{srckey}:indices")
|
|
758
|
+
buckets.each do |bucket_index|
|
|
759
|
+
_add_bucket_index(dstkey, bucket_index)
|
|
760
|
+
@db.rename("#{srckey}:#{bucket_index}", "#{dstkey}:#{bucket_index}")
|
|
669
761
|
end
|
|
762
|
+
if make_persist
|
|
763
|
+
@db.persist("#{dstkey}:indices")
|
|
764
|
+
buckets.each do |bucket_index|
|
|
765
|
+
@db.persist("#{dstkey}:#{bucket_index}")
|
|
766
|
+
end
|
|
767
|
+
end
|
|
768
|
+
end
|
|
769
|
+
else
|
|
770
|
+
if @db.exists(srckey)
|
|
771
|
+
@db.rename(srckey,dstkey)
|
|
772
|
+
@db.persist(dstkey) if make_persist
|
|
670
773
|
end
|
|
671
774
|
end
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
775
|
+
end
|
|
776
|
+
|
|
777
|
+
def put_zdata(dockey,assoc_key,data={},append=false)
|
|
778
|
+
return true unless (dockey and assoc_key and data)
|
|
779
|
+
flush_zdata(dockey) unless append
|
|
780
|
+
current_score = 0
|
|
781
|
+
current_score_data = @db.zrevrange(dockey,0,0,:with_scores => true)
|
|
782
|
+
current_score = current_score_data[-1][1].to_i if current_score_data and current_score_data[-1]
|
|
783
|
+
current_score += 1
|
|
784
|
+
data.each do |key,hash_value|
|
|
785
|
+
unique_record_key = setelement(current_score,assoc_key, key)
|
|
786
|
+
@db.zadd(dockey, current_score, unique_record_key)
|
|
787
|
+
put_data("#{dockey}:#{unique_record_key}",{key => hash_value})
|
|
788
|
+
end
|
|
789
|
+
true
|
|
790
|
+
end
|
|
791
|
+
|
|
792
|
+
# Retrieves set for given dockey,associated key (client_id), obj_hashes
|
|
793
|
+
def get_zdata(dockey)
|
|
794
|
+
data = @db.zrange(dockey, 0, -1)
|
|
795
|
+
ret = []
|
|
796
|
+
keys = []
|
|
797
|
+
unless data.nil?
|
|
798
|
+
scores = []
|
|
799
|
+
data.each do |zsetkey|
|
|
800
|
+
obj_hash = get_data "#{dockey}:#{zsetkey}"
|
|
801
|
+
score,key,objkey = getelement(zsetkey)
|
|
802
|
+
if scores[-1] != score
|
|
803
|
+
ret << obj_hash
|
|
804
|
+
keys << key
|
|
805
|
+
scores << score
|
|
806
|
+
else
|
|
807
|
+
ret[-1].merge!(obj_hash)
|
|
689
808
|
end
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
809
|
+
end
|
|
810
|
+
end
|
|
811
|
+
[ret, keys]
|
|
812
|
+
end
|
|
813
|
+
|
|
814
|
+
# Deletes all keys and their hashes from the Redis DB
|
|
815
|
+
def flush_zdata(dockey)
|
|
816
|
+
data = @db.zrange(dockey, 0, -1)
|
|
817
|
+
data.each do |hash_key|
|
|
818
|
+
_delete_doc("#{dockey}:#{hash_key}")
|
|
819
|
+
end
|
|
820
|
+
@db.zremrangebyrank(dockey, 0, -1)
|
|
821
|
+
end
|
|
822
|
+
|
|
823
|
+
def exists?(key)
|
|
824
|
+
@db.exists(key) || @db.exists("#{key}:indices")
|
|
825
|
+
end
|
|
826
|
+
|
|
827
|
+
# low-level operations with sorted sets
|
|
828
|
+
def zadd(dockey, score, value)
|
|
829
|
+
@db.zadd(dockey, score, value)
|
|
830
|
+
end
|
|
831
|
+
|
|
832
|
+
def zrem(dockey, value)
|
|
833
|
+
@db.zrem(dockey, value)
|
|
834
|
+
end
|
|
835
|
+
|
|
836
|
+
def zremrangebyscore(dockey, min_elem, max_elem)
|
|
837
|
+
@db.zremrangebyscore(dockey, min_elem, max_elem)
|
|
838
|
+
end
|
|
839
|
+
|
|
840
|
+
def zscore(dockey, value)
|
|
841
|
+
@db.zscore(dockey, value)
|
|
842
|
+
end
|
|
843
|
+
|
|
844
|
+
def zrevrange(dockey, start, stop)
|
|
845
|
+
@db.zrevrange(dockey, start, stop)
|
|
846
|
+
end
|
|
847
|
+
|
|
848
|
+
def zrange(dockey, start, stop)
|
|
849
|
+
@db.zrange(dockey, start, stop)
|
|
850
|
+
end
|
|
851
|
+
|
|
852
|
+
alias_method :set_value, :put_value
|
|
853
|
+
alias_method :set_data, :put_data
|
|
854
|
+
|
|
855
|
+
# This method should never be accessed by anything except specs
|
|
856
|
+
def db
|
|
857
|
+
return @db
|
|
858
|
+
end
|
|
859
|
+
|
|
860
|
+
private
|
|
861
|
+
|
|
862
|
+
if RUBY_VERSION =~ /1.9/
|
|
863
|
+
def _get_redis(server=nil)
|
|
864
|
+
url = ENV[REDIS_URL] || ENV[REDISTOGO_URL] || nil
|
|
865
|
+
if url
|
|
866
|
+
ConnectionPool::Wrapper.new(:size => Rhoconnect.connection_pool_size,
|
|
867
|
+
:timeout => Rhoconnect.connection_pool_timeout) do
|
|
868
|
+
Redis.connect(:url => url, :timeout => Rhoconnect.redis_timeout, :thread_safe => true)
|
|
869
|
+
end
|
|
870
|
+
elsif server and server.is_a?(String)
|
|
871
|
+
host,port,db,password = server.split(':')
|
|
872
|
+
ConnectionPool::Wrapper.new(:size => Rhoconnect.connection_pool_size,
|
|
873
|
+
:timeout => Rhoconnect.connection_pool_timeout) do
|
|
874
|
+
Redis.connect(:thread_safe => true, :host => host,
|
|
875
|
+
:port => port, :db => db, :password => password, :timeout => Rhoconnect.redis_timeout)
|
|
876
|
+
end
|
|
877
|
+
elsif server and (server.is_a?(Redis) or server.is_a?(ConnectionPool::Wrapper))
|
|
878
|
+
server
|
|
879
|
+
else
|
|
880
|
+
ConnectionPool::Wrapper.new(:size => 5, :timeout => 30) do
|
|
881
|
+
Redis.connect(:timeout => 30, :thread_safe => true)
|
|
882
|
+
end
|
|
883
|
+
end
|
|
884
|
+
end
|
|
885
|
+
else # Ruby 1.8 does not support Connnection Pools
|
|
886
|
+
def _get_redis(server=nil)
|
|
887
|
+
url = ENV[REDIS_URL] || ENV[REDISTOGO_URL] || nil
|
|
888
|
+
if url
|
|
889
|
+
Redis.connect(:url => url, :timeout => Rhoconnect.redis_timeout, :thread_safe => true)
|
|
890
|
+
elsif server and server.is_a?(String)
|
|
891
|
+
host,port,db,password = server.split(':')
|
|
892
|
+
Redis.connect(:thread_safe => true, :host => host,
|
|
893
|
+
:port => port, :db => db, :password => password, :timeout => Rhoconnect.redis_timeout)
|
|
894
|
+
elsif server and (server.is_a?(Redis) or server.is_a?(ConnectionPool::Wrapper))
|
|
895
|
+
server
|
|
896
|
+
else
|
|
897
|
+
Redis.connect(:timeout => 30, :thread_safe => true)
|
|
898
|
+
end
|
|
899
|
+
end
|
|
900
|
+
end # end of if RUBY_VERSION
|
|
901
|
+
|
|
902
|
+
|
|
903
|
+
def _lock_key(dockey)
|
|
904
|
+
"lock:#{dockey}"
|
|
905
|
+
end
|
|
906
|
+
|
|
907
|
+
def _is_reserved?(attrib,value) #:nodoc:
|
|
908
|
+
RESERVED_ATTRIB_NAMES.include? attrib
|
|
909
|
+
end
|
|
910
|
+
|
|
911
|
+
# operations with docs that are split into buckets
|
|
912
|
+
def _delete_doc(dockey)
|
|
913
|
+
# check if this doc has buckets
|
|
914
|
+
if(@db.exists("#{dockey}:indices"))
|
|
915
|
+
buckets_list = _get_buckets(dockey)
|
|
916
|
+
# delete all buckets
|
|
917
|
+
@db.pipelined do
|
|
918
|
+
@db.del("#{dockey}:indices")
|
|
919
|
+
buckets_list.each do |bucket|
|
|
920
|
+
@db.del(bucket)
|
|
695
921
|
end
|
|
696
922
|
end
|
|
697
|
-
_cleanup_buckets(dockey, indices_to_cleanup.to_a)
|
|
698
|
-
deleted_object_count
|
|
699
923
|
end
|
|
700
924
|
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
925
|
+
# delete main doc
|
|
926
|
+
@db.del(dockey)
|
|
927
|
+
end
|
|
928
|
+
|
|
929
|
+
# create object's bucket index
|
|
930
|
+
# using SHA1 hashing
|
|
931
|
+
def _create_obj_index(key)
|
|
932
|
+
Digest::SHA1.hexdigest(key)[0..1]
|
|
933
|
+
end
|
|
934
|
+
|
|
935
|
+
def _add_bucket_index(dockey, bucket_index)
|
|
936
|
+
bucket_name = "#{dockey}:#{bucket_index}"
|
|
937
|
+
@db.hsetnx("#{dockey}:indices", bucket_index, bucket_name)
|
|
938
|
+
bucket_name
|
|
939
|
+
end
|
|
940
|
+
|
|
941
|
+
def _remove_bucket_index(dockey, bucket_index)
|
|
942
|
+
@db.hdel("#{dockey}:indices", bucket_index)
|
|
943
|
+
end
|
|
944
|
+
|
|
945
|
+
def _get_bucket_indices(dockey)
|
|
946
|
+
@db.hkeys("#{dockey}:indices")
|
|
947
|
+
end
|
|
948
|
+
|
|
949
|
+
def _get_buckets(dockey)
|
|
950
|
+
@db.hvals("#{dockey}:indices")
|
|
951
|
+
end
|
|
952
|
+
|
|
953
|
+
def _cleanup_buckets(dockey, indices_to_cleanup)
|
|
954
|
+
indices_to_cleanup.each do |index|
|
|
955
|
+
bucket_name = "#{dockey}:#{index}"
|
|
956
|
+
_remove_bucket_index(dockey, index) unless @db.exists(bucket_name)
|
|
957
|
+
end if indices_to_cleanup
|
|
958
|
+
end
|
|
959
|
+
|
|
960
|
+
def _put_objects(dockey, data={}, ttl=0)
|
|
961
|
+
return if data.empty? or not dockey
|
|
962
|
+
|
|
963
|
+
collected_adds = {}
|
|
964
|
+
@db.pipelined do
|
|
965
|
+
data.each do |key,obj|
|
|
966
|
+
raise ArgumentError, "Invalid value object: #{obj.inspect}. Hash is expected." unless obj.is_a?(Hash)
|
|
967
|
+
next if obj.empty? or not key
|
|
968
|
+
|
|
707
969
|
obj_bucket_index = _create_obj_index(key)
|
|
708
970
|
bucket_name = "#{dockey}:#{obj_bucket_index}"
|
|
709
|
-
|
|
710
|
-
|
|
971
|
+
_add_bucket_index(dockey, obj_bucket_index)
|
|
972
|
+
collected_adds[bucket_name] ||= []
|
|
973
|
+
collected_adds[bucket_name] << set_obj_element(key, obj)
|
|
974
|
+
|
|
711
975
|
end
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
976
|
+
# all SADD operations on a bucket key
|
|
977
|
+
# are combined into one - it proves to perform faster
|
|
978
|
+
collected_adds.each do |bucket,bucket_data|
|
|
979
|
+
@db.sadd(bucket, bucket_data)
|
|
980
|
+
if ttl > 0
|
|
981
|
+
@db.expire(bucket, ttl)
|
|
715
982
|
end
|
|
716
983
|
end
|
|
717
|
-
|
|
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
|
|
726
|
-
end if members
|
|
727
|
-
res
|
|
984
|
+
@db.expire("#{dockey}:indices", ttl) if ttl > 0
|
|
728
985
|
end
|
|
986
|
+
end
|
|
987
|
+
|
|
988
|
+
def _delete_objects(dockey, data={})
|
|
989
|
+
return 0 if data.empty? or not dockey
|
|
729
990
|
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
991
|
+
deleted_object_count = 0
|
|
992
|
+
indices_to_cleanup = Set.new
|
|
993
|
+
collected_rems = {}
|
|
994
|
+
@db.pipelined do
|
|
995
|
+
data.each do |key, obj|
|
|
996
|
+
next if obj.empty? or not key
|
|
997
|
+
obj_bucket_index = _create_obj_index(key)
|
|
998
|
+
bucket_name = "#{dockey}:#{obj_bucket_index}"
|
|
999
|
+
indices_to_cleanup << obj_bucket_index
|
|
1000
|
+
|
|
1001
|
+
collected_rems[bucket_name] ||= []
|
|
1002
|
+
collected_rems[bucket_name] << set_obj_element(key, obj)
|
|
1003
|
+
deleted_object_count += 1
|
|
1004
|
+
end
|
|
1005
|
+
|
|
1006
|
+
# all SREM operations on a bucket
|
|
1007
|
+
# are combined into one
|
|
1008
|
+
collected_rems.each do |bucket,bucket_data|
|
|
1009
|
+
@db.srem(bucket, bucket_data)
|
|
1010
|
+
end
|
|
735
1011
|
end
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
1012
|
+
_cleanup_buckets(dockey, indices_to_cleanup.to_a)
|
|
1013
|
+
deleted_object_count
|
|
1014
|
+
end
|
|
1015
|
+
|
|
1016
|
+
def _get_objects(dockey, keys)
|
|
1017
|
+
return nil unless dockey
|
|
1018
|
+
res = nil
|
|
1019
|
+
keys_map = Set.new
|
|
1020
|
+
buckets = Set.new
|
|
1021
|
+
keys.each do |key|
|
|
1022
|
+
obj_bucket_index = _create_obj_index(key)
|
|
1023
|
+
bucket_name = "#{dockey}:#{obj_bucket_index}"
|
|
1024
|
+
keys_map << key
|
|
1025
|
+
buckets << bucket_name
|
|
740
1026
|
end
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
attrib,value = pair.split(':',2)
|
|
745
|
-
obj[attrib] = value
|
|
1027
|
+
members = @db.pipelined do
|
|
1028
|
+
buckets.to_a.each do |bucket_name|
|
|
1029
|
+
@db.smembers(bucket_name)
|
|
746
1030
|
end
|
|
747
|
-
obj
|
|
748
1031
|
end
|
|
1032
|
+
members.each do |bucket_data|
|
|
1033
|
+
bucket_data.each do |element|
|
|
1034
|
+
key,pairs = get_obj_key_and_pairs(element)
|
|
1035
|
+
next unless keys_map.include?(key)
|
|
1036
|
+
obj = split_obj_pairs(pairs)
|
|
1037
|
+
next if obj.empty?
|
|
1038
|
+
res ||= {}
|
|
1039
|
+
res[key] = obj
|
|
1040
|
+
end if bucket_data
|
|
1041
|
+
end if members
|
|
1042
|
+
res
|
|
1043
|
+
end
|
|
1044
|
+
|
|
1045
|
+
# operations on object elements
|
|
1046
|
+
def get_obj_element(elem)
|
|
1047
|
+
key,pairs = get_obj_key_and_pairs(elem)
|
|
1048
|
+
return unless (key and pairs)
|
|
1049
|
+
[key,split_obj_pairs(pairs)]
|
|
1050
|
+
end
|
|
1051
|
+
def get_obj_key_and_pairs(elem)
|
|
1052
|
+
pairs = elem.split(":^rho&:")
|
|
1053
|
+
return unless pairs
|
|
1054
|
+
[pairs[0], pairs[1..-1]]
|
|
1055
|
+
end
|
|
1056
|
+
def split_obj_pairs(pairs)
|
|
1057
|
+
obj = {}
|
|
1058
|
+
pairs.each do |pair|
|
|
1059
|
+
attrib,value = pair.split(':',2)
|
|
1060
|
+
obj[attrib] = value
|
|
1061
|
+
end
|
|
1062
|
+
obj
|
|
1063
|
+
end
|
|
749
1064
|
|
|
750
1065
|
# Set Obj Element MUST ensure the order of attribs
|
|
751
1066
|
# In 1.8.7 Hash keys are not-sorted - therefore
|
|
752
1067
|
# to ensure same order - we sort them before storing
|
|
753
1068
|
if RUBY_VERSION =~ /1.8/
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
end
|
|
1069
|
+
def set_obj_element(key, obj)
|
|
1070
|
+
return unless (key and key.size > 0 and obj and obj.size > 0)
|
|
1071
|
+
elem = "#{key}"
|
|
1072
|
+
obj.sort.each do |attrib, value|
|
|
1073
|
+
unless _is_reserved?(attrib,value)
|
|
1074
|
+
elem += ":^rho&:#{attrib}:#{value}"
|
|
761
1075
|
end
|
|
762
|
-
elem
|
|
763
1076
|
end
|
|
1077
|
+
elem
|
|
1078
|
+
end
|
|
764
1079
|
# in Ruby 1.9.x Hash keys are sorted (always in the same order), so
|
|
765
1080
|
# we do not need to do redundant sorting
|
|
766
1081
|
else
|
|
@@ -775,6 +1090,5 @@ else
|
|
|
775
1090
|
elem
|
|
776
1091
|
end
|
|
777
1092
|
end # if Ruby 1.8
|
|
778
|
-
end
|
|
779
1093
|
end
|
|
780
1094
|
end
|