rhosync 2.0.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. data/CHANGELOG +5 -0
  2. data/LICENSE +674 -0
  3. data/README.md +26 -0
  4. data/Rakefile +109 -0
  5. data/bench/bench +6 -0
  6. data/bench/benchapp/Rakefile +14 -0
  7. data/bench/benchapp/application.rb +13 -0
  8. data/bench/benchapp/config.ru +32 -0
  9. data/bench/benchapp/settings/license.key +1 -0
  10. data/bench/benchapp/settings/settings.yml +18 -0
  11. data/bench/benchapp/sources/mock_adapter.rb +55 -0
  12. data/bench/benchapp/sources/queue_mock_adapter.rb +2 -0
  13. data/bench/benchapp/vendor/rhosync/lib/rhosync.rb +7 -0
  14. data/bench/lib/bench/cli.rb +16 -0
  15. data/bench/lib/bench/logging.rb +18 -0
  16. data/bench/lib/bench/mock_client.rb +41 -0
  17. data/bench/lib/bench/result.rb +50 -0
  18. data/bench/lib/bench/runner.rb +44 -0
  19. data/bench/lib/bench/session.rb +65 -0
  20. data/bench/lib/bench/statistics.rb +56 -0
  21. data/bench/lib/bench/test_data.rb +55 -0
  22. data/bench/lib/bench/timer.rb +10 -0
  23. data/bench/lib/bench/utils.rb +49 -0
  24. data/bench/lib/bench.rb +128 -0
  25. data/bench/lib/testdata/100-data.txt +148 -0
  26. data/bench/lib/testdata/5-data.txt +11 -0
  27. data/bench/scripts/cud_script.rb +77 -0
  28. data/bench/scripts/helpers.rb +101 -0
  29. data/bench/scripts/query_md_script.rb +46 -0
  30. data/bench/scripts/query_script.rb +46 -0
  31. data/bench/spec/bench_spec_helper.rb +65 -0
  32. data/bench/spec/logging_spec.rb +19 -0
  33. data/bench/spec/mock_adapter_spec.rb +61 -0
  34. data/bench/spec/mock_client_spec.rb +64 -0
  35. data/bench/spec/result_spec.rb +59 -0
  36. data/bench/spec/utils_spec.rb +35 -0
  37. data/bin/rhosync +34 -0
  38. data/doc/protocol.html +1901 -0
  39. data/doc/public/css/print.css +29 -0
  40. data/doc/public/css/screen.css +257 -0
  41. data/doc/public/css/style.css +20 -0
  42. data/examples/simple/application.rb +13 -0
  43. data/examples/simple/sources/sample_adapter.rb +5 -0
  44. data/examples/simple/sources/simple_adapter.rb +5 -0
  45. data/examples/simple/vendor/rhosync/lib/rhosync.rb +7 -0
  46. data/generators/rhosync.rb +98 -0
  47. data/generators/templates/application/Rakefile +19 -0
  48. data/generators/templates/application/application.rb +27 -0
  49. data/generators/templates/application/config.ru +33 -0
  50. data/generators/templates/application/settings/license.key +1 -0
  51. data/generators/templates/application/settings/settings.yml +14 -0
  52. data/generators/templates/source/source_adapter.rb +49 -0
  53. data/lib/rhosync/api/create_client.rb +3 -0
  54. data/lib/rhosync/api/create_user.rb +7 -0
  55. data/lib/rhosync/api/delete_client.rb +5 -0
  56. data/lib/rhosync/api/delete_user.rb +5 -0
  57. data/lib/rhosync/api/get_api_token.rb +7 -0
  58. data/lib/rhosync/api/get_client_params.rb +3 -0
  59. data/lib/rhosync/api/get_db_doc.rb +7 -0
  60. data/lib/rhosync/api/get_license_info.rb +7 -0
  61. data/lib/rhosync/api/get_source_params.rb +3 -0
  62. data/lib/rhosync/api/list_client_docs.rb +12 -0
  63. data/lib/rhosync/api/list_clients.rb +3 -0
  64. data/lib/rhosync/api/list_source_docs.rb +10 -0
  65. data/lib/rhosync/api/list_sources.rb +15 -0
  66. data/lib/rhosync/api/list_users.rb +3 -0
  67. data/lib/rhosync/api/ping.rb +7 -0
  68. data/lib/rhosync/api/push_deletes.rb +6 -0
  69. data/lib/rhosync/api/push_objects.rb +6 -0
  70. data/lib/rhosync/api/reset.rb +10 -0
  71. data/lib/rhosync/api/set_db_doc.rb +8 -0
  72. data/lib/rhosync/api/set_refresh_time.rb +8 -0
  73. data/lib/rhosync/api/update_user.rb +4 -0
  74. data/lib/rhosync/api/upload_file.rb +4 -0
  75. data/lib/rhosync/api_token.rb +19 -0
  76. data/lib/rhosync/app.rb +69 -0
  77. data/lib/rhosync/bulk_data/bulk_data.rb +75 -0
  78. data/lib/rhosync/bulk_data/syncdb.index.schema +3 -0
  79. data/lib/rhosync/bulk_data/syncdb.schema +37 -0
  80. data/lib/rhosync/bulk_data.rb +2 -0
  81. data/lib/rhosync/client.rb +74 -0
  82. data/lib/rhosync/client_sync.rb +296 -0
  83. data/lib/rhosync/console/app/helpers/auth_helper.rb +18 -0
  84. data/lib/rhosync/console/app/helpers/extensions.rb +19 -0
  85. data/lib/rhosync/console/app/helpers/helpers.rb +52 -0
  86. data/lib/rhosync/console/app/public/main.css +7 -0
  87. data/lib/rhosync/console/app/public/text.txt +0 -0
  88. data/lib/rhosync/console/app/routes/auth.rb +29 -0
  89. data/lib/rhosync/console/app/routes/client.rb +32 -0
  90. data/lib/rhosync/console/app/routes/docs.rb +84 -0
  91. data/lib/rhosync/console/app/routes/home.rb +22 -0
  92. data/lib/rhosync/console/app/routes/user.rb +63 -0
  93. data/lib/rhosync/console/app/views/client.erb +30 -0
  94. data/lib/rhosync/console/app/views/doc.erb +56 -0
  95. data/lib/rhosync/console/app/views/docs.erb +29 -0
  96. data/lib/rhosync/console/app/views/index.erb +50 -0
  97. data/lib/rhosync/console/app/views/layout.erb +12 -0
  98. data/lib/rhosync/console/app/views/newuser.erb +17 -0
  99. data/lib/rhosync/console/app/views/ping.erb +28 -0
  100. data/lib/rhosync/console/app/views/result.erb +11 -0
  101. data/lib/rhosync/console/app/views/user.erb +32 -0
  102. data/lib/rhosync/console/app/views/users.erb +14 -0
  103. data/lib/rhosync/console/rhosync_api.rb +102 -0
  104. data/lib/rhosync/console/server.rb +27 -0
  105. data/lib/rhosync/credential.rb +9 -0
  106. data/lib/rhosync/document.rb +43 -0
  107. data/lib/rhosync/indifferent_access.rb +132 -0
  108. data/lib/rhosync/jobs/bulk_data_job.rb +104 -0
  109. data/lib/rhosync/jobs/ping_job.rb +19 -0
  110. data/lib/rhosync/jobs/source_job.rb +16 -0
  111. data/lib/rhosync/license.rb +79 -0
  112. data/lib/rhosync/lock_ops.rb +11 -0
  113. data/lib/rhosync/model.rb +410 -0
  114. data/lib/rhosync/ping/blackberry.rb +55 -0
  115. data/lib/rhosync/ping/iphone.rb +44 -0
  116. data/lib/rhosync/ping.rb +2 -0
  117. data/lib/rhosync/read_state.rb +27 -0
  118. data/lib/rhosync/server/views/index.erb +12 -0
  119. data/lib/rhosync/server.rb +242 -0
  120. data/lib/rhosync/source.rb +112 -0
  121. data/lib/rhosync/source_adapter.rb +95 -0
  122. data/lib/rhosync/source_sync.rb +245 -0
  123. data/lib/rhosync/store.rb +199 -0
  124. data/lib/rhosync/tasks.rb +151 -0
  125. data/lib/rhosync/user.rb +83 -0
  126. data/lib/rhosync/version.rb +3 -0
  127. data/lib/rhosync.rb +251 -0
  128. data/spec/api/api_helper.rb +44 -0
  129. data/spec/api/create_client_spec.rb +13 -0
  130. data/spec/api/create_user_spec.rb +16 -0
  131. data/spec/api/delete_client_spec.rb +13 -0
  132. data/spec/api/delete_user_spec.rb +18 -0
  133. data/spec/api/get_api_token_spec.rb +25 -0
  134. data/spec/api/get_client_params_spec.rb +18 -0
  135. data/spec/api/get_db_doc_spec.rb +21 -0
  136. data/spec/api/get_license_info_spec.rb +16 -0
  137. data/spec/api/get_source_params_spec.rb +26 -0
  138. data/spec/api/list_client_docs_spec.rb +33 -0
  139. data/spec/api/list_clients_spec.rb +23 -0
  140. data/spec/api/list_source_docs_spec.rb +26 -0
  141. data/spec/api/list_sources_spec.rb +27 -0
  142. data/spec/api/list_users_spec.rb +21 -0
  143. data/spec/api/ping_spec.rb +24 -0
  144. data/spec/api/push_deletes_spec.rb +16 -0
  145. data/spec/api/push_objects_spec.rb +27 -0
  146. data/spec/api/reset_spec.rb +22 -0
  147. data/spec/api/set_db_doc_spec.rb +20 -0
  148. data/spec/api/set_refresh_time_spec.rb +43 -0
  149. data/spec/api/update_user_spec.rb +31 -0
  150. data/spec/api/upload_file_spec.rb +26 -0
  151. data/spec/api_token_spec.rb +13 -0
  152. data/spec/app_spec.rb +20 -0
  153. data/spec/apps/rhotestapp/Rakefile +1 -0
  154. data/spec/apps/rhotestapp/application.rb +16 -0
  155. data/spec/apps/rhotestapp/config.ru +1 -0
  156. data/spec/apps/rhotestapp/settings/apple_fake_cert.pem +1 -0
  157. data/spec/apps/rhotestapp/settings/license.key +1 -0
  158. data/spec/apps/rhotestapp/settings/settings.yml +23 -0
  159. data/spec/apps/rhotestapp/sources/base_adapter.rb +9 -0
  160. data/spec/apps/rhotestapp/sources/sample_adapter.rb +66 -0
  161. data/spec/apps/rhotestapp/sources/simple_adapter.rb +39 -0
  162. data/spec/apps/rhotestapp/sources/sub_adapter.rb +7 -0
  163. data/spec/apps/rhotestapp/vendor/mygem-0.1.0/lib/mygem/mygem.rb +8 -0
  164. data/spec/apps/rhotestapp/vendor/mygem-0.1.0/lib/mygem.rb +1 -0
  165. data/spec/bulk_data/bulk_data_spec.rb +79 -0
  166. data/spec/client_spec.rb +58 -0
  167. data/spec/client_sync_spec.rb +377 -0
  168. data/spec/doc/base.html +72 -0
  169. data/spec/doc/doc_spec.rb +303 -0
  170. data/spec/doc/footer.html +4 -0
  171. data/spec/doc/header.html +30 -0
  172. data/spec/document_spec.rb +27 -0
  173. data/spec/generator/generator_spec.rb +53 -0
  174. data/spec/generator/generator_spec_helper.rb +8 -0
  175. data/spec/jobs/bulk_data_job_spec.rb +76 -0
  176. data/spec/jobs/ping_job_spec.rb +26 -0
  177. data/spec/jobs/source_job_spec.rb +25 -0
  178. data/spec/license_spec.rb +48 -0
  179. data/spec/model_spec.rb +269 -0
  180. data/spec/perf/bulk_data_perf_spec.rb +33 -0
  181. data/spec/perf/perf_spec_helper.rb +51 -0
  182. data/spec/perf/store_perf_spec.rb +28 -0
  183. data/spec/ping/blackberry_spec.rb +62 -0
  184. data/spec/ping/iphone_spec.rb +50 -0
  185. data/spec/read_state_spec.rb +25 -0
  186. data/spec/rhosync_spec.rb +43 -0
  187. data/spec/server/server_spec.rb +341 -0
  188. data/spec/source_adapter_spec.rb +114 -0
  189. data/spec/source_spec.rb +77 -0
  190. data/spec/source_sync_spec.rb +248 -0
  191. data/spec/spec_helper.rb +240 -0
  192. data/spec/store_spec.rb +149 -0
  193. data/spec/sync_states_spec.rb +101 -0
  194. data/spec/testdata/1000-data.txt +1414 -0
  195. data/spec/testdata/compressed/compress-data.txt +1 -0
  196. data/spec/testdata/upload1.txt +1 -0
  197. data/spec/testdata/upload2.txt +1 -0
  198. data/spec/user_spec.rb +79 -0
  199. data/tasks/redis.rake +134 -0
  200. metadata +545 -0
data/README.md ADDED
@@ -0,0 +1,26 @@
1
+ Rhosync
2
+ -------------------------------------------------------------
3
+
4
+ Redis-powered rhosync with built-in sinatra application.
5
+
6
+ INSTALL
7
+ -------------------------------------------------------------
8
+ 1. Make sure you have the following gems installed:
9
+
10
+ * rspec rcov json sqlite3-ruby faker redis redis-namespace sinatra rack-test rubyzip uuidtools resque
11
+
12
+ 2. Install and start a redis server (v1.2 or greater is required) (see <http://code.google.com/p/redis/>)
13
+
14
+ 3. Install hsqldata.jar to vendor/ directory. See <http://github.com/rhomobile/hsqldata> for instructions on how to build hsqldata.
15
+
16
+ 4. "rake" to run all specs
17
+
18
+ Windows Notes: when run any spec task error message box (ruby.exe - Unable to locate component) will appear. Just press 'OK'. This is problem with rcov.
19
+
20
+ DOCS
21
+ -------------------------------------------------------------
22
+ * Intro to Rhodes & Rhosync: <http://wiki.rhomobile.com/index.php/Rhomobile> -> Read this first!
23
+ * Tutorial: <http://wiki.rhomobile.com/index.php/Tutorial>
24
+ * Architecture: <http://wiki.rhomobile.com/index.php/RhoSync_2.0>
25
+ * Rdoc (still rough): <http://rdoc.info/projects/rhomobile/rhosync>
26
+ * Client/Server Protocol: See doc/protocol.html
data/Rakefile ADDED
@@ -0,0 +1,109 @@
1
+ require 'yaml'
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__),'lib')
4
+ require 'rhosync'
5
+
6
+ task :default => 'spec:all'
7
+ task :spec => 'spec:spec'
8
+
9
+ begin
10
+ require 'spec/rake/spectask'
11
+ require 'rcov/rcovtask'
12
+
13
+ SPEC_OPTS = ['-fn', '--color', '-b']
14
+
15
+ TYPES = {
16
+ :spec => 'spec/*_spec.rb',
17
+ :perf => 'spec/perf/*_spec.rb',
18
+ :server => 'spec/server/*_spec.rb',
19
+ :api => 'spec/api/*_spec.rb',
20
+ :bulk => 'spec/bulk_data/*_spec.rb',
21
+ :jobs => 'spec/jobs/*_spec.rb',
22
+ :ping => 'spec/ping/*_spec.rb',
23
+ :doc => 'spec/doc/*_spec.rb',
24
+ :generator => 'spec/generator/*_spec.rb',
25
+ :bench => 'bench/spec/*_spec.rb'
26
+ }
27
+
28
+ TYPES.each do |type,files|
29
+ desc "Run specs in #{files}"
30
+ Spec::Rake::SpecTask.new("spec:#{type}") do |t|
31
+ t.spec_files = FileList[TYPES[type]]
32
+ t.spec_opts = SPEC_OPTS
33
+ end
34
+ end
35
+
36
+ desc "Run specs in spec/**/*_spec.rb "
37
+ Spec::Rake::SpecTask.new('spec:all') do |t|
38
+ t.spec_files = FileList[TYPES.values]
39
+ t.spec_opts = SPEC_OPTS
40
+ t.rcov = true
41
+ t.rcov_opts = ['--exclude', 'spec/*,gems/*,apps/*,bench/spec/*']
42
+ end
43
+
44
+ rescue LoadError => e
45
+ puts "rspec / rcov not available. Install with: "
46
+ puts "gem install rspec rcov\n\n"
47
+ end
48
+
49
+ desc "Build rhosync gem"
50
+ task :gem => [ 'spec:all', 'clobber_spec:all', :gemspec, :build ]
51
+
52
+ begin
53
+ require 'jeweler'
54
+
55
+ Jeweler::Tasks.new do |gemspec|
56
+ gemspec.name = "rhosync"
57
+ gemspec.summary = %q{Rhosync Server}
58
+ gemspec.description = %q{Rhosync Server and related command-line utilities for using Rhosync}
59
+ gemspec.homepage = %q{http://rhomobile.com/products/rhosync}
60
+ gemspec.authors = ["Rhomobile"]
61
+ gemspec.version = Rhosync::VERSION
62
+ gemspec.files = FileList["[A-Z]*", "{bench,bin,doc,generators,lib,spec,tasks}/**/*"]
63
+
64
+ gemspec.add_dependency "json", ">=1.2.3"
65
+ gemspec.add_dependency "log4r", ">=1.1.7"
66
+ gemspec.add_dependency "sqlite3-ruby", ">=1.2.5"
67
+ gemspec.add_dependency "rubyzip", ">=0.9.4"
68
+ gemspec.add_dependency "uuidtools", ">=2.1.1"
69
+ gemspec.add_dependency "redis", ">=0.2.0"
70
+ gemspec.add_dependency "resque", ">=1.8.0"
71
+ gemspec.add_dependency "rest-client", ">=1.4.2"
72
+ gemspec.add_dependency "sinatra", ">=0.9.2"
73
+ gemspec.add_dependency "templater", ">=1.0.0"
74
+ gemspec.add_development_dependency "jeweler", ">=1.4.0"
75
+ gemspec.add_development_dependency "rspec", ">=1.3.0"
76
+ gemspec.add_development_dependency "rcov", ">=0.9.8"
77
+ gemspec.add_development_dependency "faker", ">=0.3.1"
78
+ gemspec.add_development_dependency "rack-test", ">=0.5.3"
79
+ gemspec.add_development_dependency "mechanize", ">=1.0.0"
80
+ end
81
+ rescue LoadError
82
+ puts "Jeweler not available. Install it with: "
83
+ puts "gem install jeweler\n\n"
84
+ end
85
+
86
+ desc "Load console environment"
87
+ task :console do
88
+ sh "irb -rubygems -r #{File.join(File.dirname(__FILE__),'lib','rhosync','server.rb')}"
89
+ end
90
+
91
+ desc "Run benchmark scripts"
92
+ task :bench do
93
+ login = ask "login: "
94
+ password = ask "password: "
95
+ prefix = 'bench/scripts/'
96
+ suffix = '_script.rb'
97
+ list = ask "scripts(default is '*'): "
98
+ file_list = list.empty? ? FileList[prefix+'*'+suffix] : FileList[prefix+list+suffix]
99
+ file_list.each do |script|
100
+ sh "bench/bench start #{script} #{login} #{password}"
101
+ end
102
+ end
103
+
104
+ def ask(msg)
105
+ print msg
106
+ STDIN.gets.chomp
107
+ end
108
+
109
+ load 'tasks/redis.rake'
data/bench/bench ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require File.join(File.dirname(__FILE__),'lib','bench')
5
+
6
+ Bench::Cli.start
@@ -0,0 +1,14 @@
1
+ $:.unshift File.join(File.dirname(__FILE__),'..','..','lib')
2
+ require 'rhosync/tasks'
3
+
4
+ begin
5
+ require 'resque/tasks'
6
+
7
+ task "resque:setup" do
8
+ require 'rhosync'
9
+ require 'application'
10
+ end
11
+ rescue LoadError
12
+ puts "Resque not available. Install it with: "
13
+ puts "gem install resque\n\n"
14
+ end
@@ -0,0 +1,13 @@
1
+ class Application < Rhosync::Base
2
+ class << self
3
+ def authenticate(username,password,session)
4
+ true # do some interesting authentication here...
5
+ end
6
+
7
+ def initializer(path)
8
+ super
9
+ end
10
+ end
11
+ end
12
+
13
+ Application.initializer(ROOT_PATH)
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Try to load vendor-ed rhosync, otherwise load the gem
4
+ begin
5
+ require 'vendor/rhosync/lib/rhosync'
6
+ rescue LoadError
7
+ require 'rhosync/server'
8
+ require 'rhosync/console/server'
9
+ end
10
+
11
+ # By default, turn on the resque web console
12
+ require 'resque/server'
13
+
14
+ ROOT_PATH = File.expand_path(File.dirname(__FILE__))
15
+
16
+ # Rhosync server flags
17
+ Rhosync::Server.disable :run
18
+ Rhosync::Server.disable :clean_trace
19
+ Rhosync::Server.enable :raise_errors
20
+ Rhosync::Server.set :environment, :development
21
+ Rhosync::Server.set :secret, '<changeme>'
22
+ Rhosync::Server.set :root, ROOT_PATH
23
+ Rhosync::Server.use Rack::Static, :urls => ["/data"], :root => Rhosync::Server.root
24
+
25
+ # Load our rhosync application
26
+ require 'application'
27
+
28
+ # Setup the url map
29
+ run Rack::URLMap.new \
30
+ "/" => Rhosync::Server.new,
31
+ "/resque" => Resque::Server.new, # If you don't want resque frontend, disable it here
32
+ "/console" => RhosyncConsole::Server.new # If you don't want rhosync frontend, disable it here
@@ -0,0 +1 @@
1
+ 858fc60fadfde40273d0ac505906969318aa4931d1a2c4aeb24d98393c74379f60e226651601969874257f7f1fbda9b099ecd551a641519aa46819947fda0191
@@ -0,0 +1,18 @@
1
+ :sources:
2
+ MockAdapter:
3
+ poll_interval: 300
4
+ QueueMockAdapter:
5
+ queue: abc
6
+
7
+ :development:
8
+ :licensefile: settings/license.key
9
+ :redis: localhost:6379
10
+ :syncserver: http://localhost:9292/application/
11
+ :test:
12
+ :licensefile: settings/license.key
13
+ :redis: localhost:6379
14
+ :syncserver: http://localhost:9292/application/
15
+ :production:
16
+ :licensefile: settings/license.key
17
+ :redis: localhost:6379
18
+ :syncserver: http://localhost:9292/application/
@@ -0,0 +1,55 @@
1
+ class MockAdapter < SourceAdapter
2
+ def initialize(source,credential)
3
+ super(source,credential)
4
+ end
5
+
6
+ def login
7
+ true
8
+ end
9
+
10
+ def query(params=nil)
11
+ Store.lock(lock_name,1) do
12
+ @result = Store.get_data(db_name)
13
+ end
14
+ @result
15
+ end
16
+
17
+ def create(name_value_list,blob=nil)
18
+ id = name_value_list['mock_id']
19
+ Store.lock(lock_name,1) do
20
+ Store.put_data(db_name,{id=>name_value_list},true) if id
21
+ end
22
+ id
23
+ end
24
+
25
+ def update(name_value_list)
26
+ id = name_value_list.delete('id')
27
+ return unless id
28
+ Store.lock(lock_name,1) do
29
+ data = Store.get_data(db_name)
30
+ return unless data and data[id]
31
+ name_value_list.each do |attrib,value|
32
+ data[id][attrib] = value
33
+ end
34
+ Store.put_data(db_name,data)
35
+ end
36
+ end
37
+
38
+ def delete(name_value_list)
39
+ id = name_value_list.delete('id')
40
+ Store.lock(lock_name,1) do
41
+ Store.delete_data(db_name,{id=>name_value_list}) if id
42
+ end
43
+ end
44
+
45
+ def db_name
46
+ "test_db_storage:#{@source.app_id}:#{@source.user_id}"
47
+ end
48
+
49
+ def lock_name()
50
+ "#{db_name}:lock"
51
+ end
52
+
53
+ private
54
+
55
+ end
@@ -0,0 +1,2 @@
1
+ require 'mock_adapter'
2
+ class QueueMockAdapter < MockAdapter; end
@@ -0,0 +1,7 @@
1
+
2
+ # We're "vendoring" the rhosync/lib directory here so we can use the working copy of
3
+ # rhosync. Normally, you would require rhosync as a gem or vendor it here.
4
+ path = File.join(File.dirname(__FILE__),'..','..','..','..','..','lib')
5
+ $:.unshift path
6
+ require File.join(path,'rhosync','server')
7
+ require File.join(path,'rhosync','console','server')
@@ -0,0 +1,16 @@
1
+ require 'thor'
2
+
3
+ module Bench
4
+ class Cli < Thor
5
+ include Logging
6
+ desc "start path/to/bench/script", "Start performance test"
7
+ def start(script,login,password='')
8
+ Bench.admin_login = login
9
+ Bench.admin_password = password
10
+ load(script)
11
+ Statistics.new(Bench.concurrency,Bench.iterations,
12
+ Bench.total_time,Bench.sessions).process.print_stats
13
+ logger.info "Bench completed..."
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,18 @@
1
+ module Bench
2
+ module Logging
3
+ def logger
4
+ init_logger if Log4r::Logger['main'].nil?
5
+ Log4r::Logger['main']
6
+ end
7
+
8
+ def log_prefix
9
+ "[T:%03d|I:%03d]" % [@thread_id,@iteration]
10
+ end
11
+
12
+ protected
13
+ def init_logger
14
+ logger = Log4r::Logger.new('main')
15
+ logger.outputters = Log4r::Outputter.stdout
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,41 @@
1
+ require 'redis'
2
+ $:.unshift File.join(File.dirname(__FILE__),'..','..','..','lib')
3
+ require 'rhosync'
4
+ include Rhosync
5
+
6
+ module Bench
7
+ class MockClient
8
+ include Logging
9
+
10
+ def initialize(thread_id,iteration,client_id)
11
+ @thread_id,@iteration,@client_id = thread_id,iteration,client_id
12
+ end
13
+
14
+ def parse(message)
15
+ msg = JSON.parse(message)
16
+ raise Exception.new("#{log_prefix} Wrong message format. Message: #{message.inspect}") if msg.size < 6
17
+ raise Exception.new("#{log_prefix} Wrong protocol version. Message: #{message.inspect}") if msg[0]['version'] != 3
18
+ msg.each do |p|
19
+ insert(p['insert']) if p['insert']
20
+ delete(p['delete']) if p['delete']
21
+ end
22
+ end
23
+
24
+ def insert(objects)
25
+ Store.put_data(doc_type,objects,true)
26
+ end
27
+
28
+ def delete(objects)
29
+ Store.delete_data(doc_type,objects)
30
+ end
31
+
32
+ def verify(objects)
33
+ Store.get_data(doc_type) == objects
34
+ end
35
+
36
+ def doc_type
37
+ "#{@client_id.to_s}:mock:cd"
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,50 @@
1
+ require 'set'
2
+
3
+ module Bench
4
+ class Result
5
+ attr_accessor :last_response,:time,:marker,:url,:verb,:error,:verification_error
6
+ include Logging
7
+ include Utils
8
+
9
+ def initialize(marker,verb,url,thread_id,iteration)
10
+ @marker,@verb,@url,@thread_id,@iteration = marker,verb,url,thread_id,iteration
11
+ @verification_error = 0
12
+ @time = 0
13
+ end
14
+
15
+ def code
16
+ @last_response.code
17
+ end
18
+
19
+ def body
20
+ @last_response.to_s
21
+ end
22
+
23
+ def cookies
24
+ @last_response.cookies
25
+ end
26
+
27
+ def headers
28
+ @last_response.headers
29
+ end
30
+
31
+ def verify_body(expected)
32
+ expected,actual = JSON.parse(expected),JSON.parse(@last_response.to_s)
33
+ @verification_error += compare_and_log(expected,actual,caller(1)[0].to_s)
34
+ end
35
+
36
+ def verify_code(expected)
37
+ if expected != @last_response.code
38
+ logger.error "#{log_prefix} Verify error at: " + caller(1)[0].to_s
39
+ logger.error "#{log_prefix} Code diff: "
40
+ logger.error "#{log_prefix} expected: #{expected.inspect}"
41
+ logger.error "#{log_prefix} but got: #{@last_response.code}"
42
+ @verification_error += 1
43
+ end
44
+ end
45
+
46
+ def verify_headers
47
+
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,44 @@
1
+ module Bench
2
+ class Runner
3
+ include Logging
4
+ include Timer
5
+ attr_reader :threads
6
+
7
+ def initialize
8
+ @threads = []
9
+ @sessions = []
10
+ end
11
+
12
+ def test(concurrency,iterations,&block)
13
+ thread_id = 0
14
+ total_time = time do
15
+ concurrency.times do
16
+ sleep rand(2)
17
+ thread = Thread.new(block) do |t|
18
+ tid, iteration = thread_id,0
19
+ iterations.times do
20
+ s = Session.new(tid,iteration)
21
+ @sessions << s
22
+ begin
23
+ yield Bench,s
24
+ rescue Exception => e
25
+ puts "error running script: #{e.inspect}"
26
+ end
27
+ iteration += 1
28
+ end
29
+ end
30
+ thread_id += 1
31
+ threads << thread
32
+ end
33
+ begin
34
+ threads.each { |t| t.join }
35
+ rescue RestClient::RequestTimeout => e
36
+ logger.info "Request timed out #{e}"
37
+ end
38
+ end
39
+ Bench.sessions = @sessions
40
+ Bench.total_time = total_time
41
+ end
42
+ end
43
+ end
44
+
@@ -0,0 +1,65 @@
1
+ module Bench
2
+ class Session
3
+ include Logging
4
+ include Timer
5
+ attr_accessor :cookies, :last_result, :results, :thread_id, :iteration, :client_id
6
+
7
+ def initialize(thread_id,iteration)
8
+ @cookies = {}
9
+ @results = {}
10
+ @thread_id,@iteration = thread_id,iteration
11
+ end
12
+
13
+ def post(marker,url,headers={})
14
+ @body = yield
15
+ _request(marker,:_post,url,headers)
16
+ end
17
+
18
+ def get(marker,url,headers={})
19
+ params = yield if block_given?
20
+ url_params = url.clone
21
+ url_params << "?" + _url_params(params) if params
22
+ _request(marker,:_get,url_params,headers)
23
+ end
24
+
25
+ protected
26
+ def _request(marker,verb,url,headers)
27
+ result = Result.new(marker,verb,url,@thread_id,@iteration)
28
+ @results[result.marker] ||= []
29
+ @results[result.marker] << result
30
+ begin
31
+ result.time = time do
32
+ headers.merge!(:cookies => @cookies)
33
+ result.last_response = send(verb,url,headers)
34
+ @last_result = result
35
+ end
36
+ logger.info "#{log_prefix} #{verb.to_s.upcase.gsub(/_/,'')} #{url} #{@last_result.code} #{result.time}"
37
+ rescue RestClient::Exception => e
38
+ result.error = e
39
+ logger.info "#{log_prefix} #{verb.to_s.upcase.gsub(/_/,'')} #{url}"
40
+ logger.error "#{log_prefix} #{e.http_code.to_s} #{e.message}\n"
41
+ raise e
42
+ end
43
+ @cookies = @cookies.merge(@last_result.cookies)
44
+ @last_result
45
+ end
46
+
47
+ def _get(url,headers)
48
+ #logger.info "GET #{url}"
49
+ RestClient.get(url, headers)
50
+ end
51
+
52
+ def _post(url,headers)
53
+ #logger.info "POST #{url}"
54
+ RestClient.post(url, @body, headers)
55
+ end
56
+
57
+ def _url_params(params)
58
+ elements = []
59
+ params.each do |key,value|
60
+ elements << "#{key}=#{value}"
61
+ end
62
+ elements.join('&')
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,56 @@
1
+ module Bench
2
+ class Statistics
3
+ include Logging
4
+
5
+ def initialize(concurrency,iterations,total_time,sessions)
6
+ @sessions = sessions
7
+ @rows = {} # row key is result.marker;
8
+ @total_count = 0
9
+ @total_time = total_time
10
+ @concurrency,@iterations = concurrency,iterations
11
+ end
12
+
13
+ def process
14
+ @sessions.each do |session|
15
+ session.results.each do |marker,results|
16
+ results.each do |result|
17
+ @rows[result.marker] ||= {}
18
+ row = @rows[result.marker]
19
+ row[:min] ||= 0.0
20
+ row[:max] ||= 0.0
21
+ row[:count] ||= 0
22
+ row[:total_time] ||= 0.0
23
+ row[:errors] ||= 0
24
+ row[:verification_errors] ||= 0
25
+ row[:min] = result.time if result.time < row[:min] || row[:min] == 0
26
+ row[:max] = result.time if result.time > row[:max]
27
+ row[:count] += 1.0
28
+ row[:total_time] += result.time
29
+ row[:errors] += 1 if result.error
30
+ row[:verification_errors] += result.verification_error
31
+ @total_count += 1
32
+ end
33
+ end
34
+ end
35
+ self
36
+ end
37
+
38
+ def average(row)
39
+ row[:total_time] / row[:count]
40
+ end
41
+
42
+ def print_stats
43
+ logger.info "Statistics:"
44
+ @rows.each do |marker,row|
45
+ logger.info "Request %-15s: min: %0.4f, max: %0.4f, avg: %0.4f, err: %d, verification err: %d" % [marker, row[:min], row[:max], average(row), row[:errors], row[:verification_errors]]
46
+ end
47
+ logger.info "Verify Error : #{Bench.verify_error}"
48
+ logger.info "Concurrency : #{@concurrency}"
49
+ logger.info "Iterations : #{@iterations}"
50
+ logger.info "Total Count : #{@total_count}"
51
+ logger.info "Total Time : #{@total_time}"
52
+ logger.info "Throughput(req/s) : #{@total_count / @total_time}"
53
+ logger.info "Throughput(req/min): #{(@total_count / @total_time) * 60.0}"
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,55 @@
1
+ require 'faker'
2
+ require 'uuidtools'
3
+
4
+ module Bench
5
+ module TestData
6
+ def get_test_data(num=1000,generate=false)
7
+ file = File.join(File.dirname(__FILE__),'..',"testdata","#{num}-data.txt")
8
+ data = nil
9
+ if File.exists?(file) and not generate
10
+ data = open(file, 'r') {|f| Marshal.load(f)}
11
+ else
12
+ data = generate_fake_data(num)
13
+ f = File.new(file, 'w')
14
+ f.write Marshal.dump(data)
15
+ f.close
16
+ end
17
+ data
18
+ end
19
+
20
+ private
21
+
22
+ PREFIX = ["Account", "Administrative", "Advertising", "Assistant", "Banking", "Business Systems",
23
+ "Computer", "Distribution", "IT", "Electronics", "Environmental", "Financial", "General", "Head",
24
+ "Laboratory", "Maintenance", "Medical", "Production", "Quality Assurance", "Software", "Technical",
25
+ "Chief", "Senior"] unless defined? PREFIX
26
+ SUFFIX = ["Clerk", "Analyst", "Manager", "Supervisor", "Plant Manager", "Mechanic", "Technician", "Engineer",
27
+ "Director", "Superintendent", "Specialist", "Technologist", "Estimator", "Scientist", "Foreman", "Nurse",
28
+ "Worker", "Helper", "Intern", "Sales", "Mechanic", "Planner", "Recruiter", "Officer", "Superintendent",
29
+ "Vice President", "Buyer", "Production Supervisor", "Chef", "Accountant", "Executive"] unless defined? SUFFIX
30
+
31
+ def title
32
+ prefix = PREFIX[rand(PREFIX.length)]
33
+ suffix = SUFFIX[rand(SUFFIX.length)]
34
+
35
+ "#{prefix} #{suffix}"
36
+ end
37
+
38
+ def generate_fake_data(num=1000)
39
+ res = {}
40
+ num.times do |n|
41
+ mock_id = UUIDTools::UUID.random_create.to_s.gsub(/\-/,'')
42
+ res[mock_id] = {
43
+ "mock_id" => mock_id,
44
+ "FirstName" => Faker::Name.first_name,
45
+ "LastName" => Faker::Name.last_name,
46
+ "Email" => Faker::Internet.free_email,
47
+ "Company" => Faker::Company.name,
48
+ "JobTitle" => title,
49
+ "Phone1" => Faker::PhoneNumber.phone_number
50
+ }
51
+ end
52
+ res
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,10 @@
1
+ module Bench
2
+ module Timer
3
+ def time
4
+ start = Time.now
5
+ yield
6
+ end_time = Time.now
7
+ end_time.to_f - start.to_f
8
+ end
9
+ end
10
+ end