moneta 0.7.4 → 0.7.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (165) hide show
  1. data/.gitignore +1 -2
  2. data/.travis.yml +20 -13
  3. data/CHANGES +5 -0
  4. data/Gemfile +24 -25
  5. data/LICENSE +1 -1
  6. data/README.md +32 -16
  7. data/Rakefile +36 -6
  8. data/lib/moneta/builder.rb +20 -6
  9. data/lib/moneta/mixins.rb +11 -4
  10. data/lib/moneta/version.rb +1 -1
  11. data/script/benchmarks +378 -0
  12. data/script/{generate-specs.rb → generate-specs} +92 -83
  13. data/script/install-bundle +26 -0
  14. data/script/kill-travis +16 -0
  15. data/script/start-services +8 -0
  16. data/script/upload-bundle +2 -0
  17. data/spec/helper.rb +9 -3
  18. data/spec/moneta/adapter_activerecord_spec.rb +1 -1
  19. data/spec/moneta/adapter_cassandra_spec.rb +1 -1
  20. data/spec/moneta/adapter_cassandra_with_default_expires_spec.rb +2 -2
  21. data/spec/moneta/adapter_client_spec.rb +1 -1
  22. data/spec/moneta/adapter_cookie_spec.rb +2 -2
  23. data/spec/moneta/adapter_couch_spec.rb +1 -1
  24. data/spec/moneta/adapter_datamapper_spec.rb +1 -1
  25. data/spec/moneta/adapter_daybreak_spec.rb +2 -2
  26. data/spec/moneta/adapter_dbm_spec.rb +1 -1
  27. data/spec/moneta/adapter_file_spec.rb +1 -1
  28. data/spec/moneta/adapter_fog_spec.rb +2 -2
  29. data/spec/moneta/adapter_gdbm_spec.rb +1 -1
  30. data/spec/moneta/adapter_hbase_spec.rb +1 -1
  31. data/spec/moneta/adapter_leveldb_spec.rb +1 -1
  32. data/spec/moneta/adapter_localmemcache_spec.rb +1 -1
  33. data/spec/moneta/adapter_lruhash_spec.rb +2 -2
  34. data/spec/moneta/adapter_memcached_dalli_spec.rb +1 -1
  35. data/spec/moneta/adapter_memcached_dalli_with_default_expires_spec.rb +1 -1
  36. data/spec/moneta/adapter_memcached_native_spec.rb +1 -1
  37. data/spec/moneta/adapter_memcached_native_with_default_expires_spec.rb +1 -1
  38. data/spec/moneta/adapter_memcached_spec.rb +1 -1
  39. data/spec/moneta/adapter_memcached_with_default_expires_spec.rb +1 -1
  40. data/spec/moneta/adapter_memory_spec.rb +19 -1
  41. data/spec/moneta/adapter_mongo_spec.rb +2 -2
  42. data/spec/moneta/adapter_mongo_with_default_expires_spec.rb +1 -1
  43. data/spec/moneta/adapter_pstore_spec.rb +1 -1
  44. data/spec/moneta/adapter_redis_spec.rb +1 -1
  45. data/spec/moneta/adapter_redis_with_default_expires_spec.rb +1 -1
  46. data/spec/moneta/adapter_restclient_spec.rb +1 -1
  47. data/spec/moneta/adapter_riak_spec.rb +1 -1
  48. data/spec/moneta/adapter_sdbm_spec.rb +1 -1
  49. data/spec/moneta/adapter_sequel_spec.rb +1 -1
  50. data/spec/moneta/adapter_sqlite_spec.rb +1 -1
  51. data/spec/moneta/adapter_tdb_spec.rb +1 -1
  52. data/spec/moneta/adapter_tokyocabinet_bdb_spec.rb +1 -1
  53. data/spec/moneta/adapter_tokyocabinet_hdb_spec.rb +1 -1
  54. data/spec/moneta/adapter_yaml_spec.rb +1 -1
  55. data/spec/moneta/cache_file_memory_spec.rb +2 -2
  56. data/spec/moneta/cache_memory_null_spec.rb +2 -2
  57. data/spec/moneta/expires_file_spec.rb +1 -1
  58. data/spec/moneta/expires_memory_spec.rb +19 -19
  59. data/spec/moneta/expires_memory_with_default_expires_spec.rb +19 -19
  60. data/spec/moneta/lock_spec.rb +19 -1
  61. data/spec/moneta/null_adapter_spec.rb +1 -1
  62. data/spec/moneta/optionmerger_spec.rb +14 -1
  63. data/spec/moneta/pool_spec.rb +1 -1
  64. data/spec/moneta/proxy_expires_memory_spec.rb +19 -1
  65. data/spec/moneta/proxy_redis_spec.rb +1 -1
  66. data/spec/moneta/shared_spec.rb +3 -3
  67. data/spec/moneta/simple_activerecord_spec.rb +1 -1
  68. data/spec/moneta/simple_activerecord_with_expires_spec.rb +1 -1
  69. data/spec/moneta/simple_cassandra_spec.rb +1 -1
  70. data/spec/moneta/simple_client_tcp_spec.rb +1 -1
  71. data/spec/moneta/simple_client_unix_spec.rb +1 -1
  72. data/spec/moneta/simple_couch_spec.rb +1 -1
  73. data/spec/moneta/simple_couch_with_expires_spec.rb +1 -1
  74. data/spec/moneta/simple_datamapper_spec.rb +1 -1
  75. data/spec/moneta/simple_datamapper_with_expires_spec.rb +1 -1
  76. data/spec/moneta/simple_datamapper_with_repository_spec.rb +1 -1
  77. data/spec/moneta/simple_daybreak_spec.rb +1 -1
  78. data/spec/moneta/simple_daybreak_with_expires_spec.rb +1 -1
  79. data/spec/moneta/simple_dbm_spec.rb +1 -1
  80. data/spec/moneta/simple_dbm_with_expires_spec.rb +1 -1
  81. data/spec/moneta/simple_file_spec.rb +1 -1
  82. data/spec/moneta/simple_file_with_expires_spec.rb +1 -1
  83. data/spec/moneta/simple_fog_spec.rb +1 -1
  84. data/spec/moneta/simple_fog_with_expires_spec.rb +1 -1
  85. data/spec/moneta/simple_gdbm_spec.rb +1 -1
  86. data/spec/moneta/simple_gdbm_with_expires_spec.rb +1 -1
  87. data/spec/moneta/simple_hashfile_spec.rb +1 -1
  88. data/spec/moneta/simple_hashfile_with_expires_spec.rb +1 -1
  89. data/spec/moneta/simple_hbase_spec.rb +1 -1
  90. data/spec/moneta/simple_hbase_with_expires_spec.rb +1 -1
  91. data/spec/moneta/simple_leveldb_spec.rb +1 -1
  92. data/spec/moneta/simple_leveldb_with_expires_spec.rb +1 -1
  93. data/spec/moneta/simple_localmemcache_spec.rb +1 -1
  94. data/spec/moneta/simple_localmemcache_with_expires_spec.rb +1 -1
  95. data/spec/moneta/simple_lruhash_spec.rb +1 -1
  96. data/spec/moneta/simple_lruhash_with_expires_spec.rb +1 -1
  97. data/spec/moneta/simple_memcached_dalli_spec.rb +1 -1
  98. data/spec/moneta/simple_memcached_native_spec.rb +1 -1
  99. data/spec/moneta/simple_memcached_spec.rb +1 -1
  100. data/spec/moneta/simple_memory_spec.rb +1 -1
  101. data/spec/moneta/simple_memory_with_compress_spec.rb +1 -1
  102. data/spec/moneta/simple_memory_with_expires_spec.rb +1 -1
  103. data/spec/moneta/simple_memory_with_json_key_serializer_spec.rb +1 -1
  104. data/spec/moneta/simple_memory_with_json_serializer_spec.rb +1 -1
  105. data/spec/moneta/simple_memory_with_json_value_serializer_spec.rb +1 -1
  106. data/spec/moneta/simple_memory_with_prefix_spec.rb +1 -1
  107. data/spec/moneta/simple_memory_with_snappy_compress_spec.rb +1 -1
  108. data/spec/moneta/simple_mongo_spec.rb +1 -1
  109. data/spec/moneta/simple_null_spec.rb +1 -1
  110. data/spec/moneta/simple_pstore_spec.rb +1 -1
  111. data/spec/moneta/simple_pstore_with_expires_spec.rb +1 -1
  112. data/spec/moneta/simple_redis_spec.rb +1 -1
  113. data/spec/moneta/simple_restclient_spec.rb +1 -1
  114. data/spec/moneta/simple_riak_spec.rb +1 -1
  115. data/spec/moneta/simple_riak_with_expires_spec.rb +1 -1
  116. data/spec/moneta/simple_sdbm_spec.rb +1 -1
  117. data/spec/moneta/simple_sdbm_with_expires_spec.rb +1 -1
  118. data/spec/moneta/simple_sequel_spec.rb +1 -1
  119. data/spec/moneta/simple_sequel_with_expires_spec.rb +1 -1
  120. data/spec/moneta/simple_sqlite_spec.rb +1 -1
  121. data/spec/moneta/simple_sqlite_with_expires_spec.rb +1 -1
  122. data/spec/moneta/simple_tdb_spec.rb +1 -1
  123. data/spec/moneta/simple_tdb_with_expires_spec.rb +1 -1
  124. data/spec/moneta/simple_tokyocabinet_spec.rb +1 -1
  125. data/spec/moneta/simple_tokyocabinet_with_expires_spec.rb +1 -1
  126. data/spec/moneta/simple_yaml_spec.rb +1 -1
  127. data/spec/moneta/simple_yaml_with_expires_spec.rb +1 -1
  128. data/spec/moneta/stack_file_memory_spec.rb +2 -2
  129. data/spec/moneta/stack_memory_file_spec.rb +3 -3
  130. data/spec/moneta/transformer_bencode_spec.rb +1 -1
  131. data/spec/moneta/transformer_bert_spec.rb +1 -1
  132. data/spec/moneta/transformer_bson_spec.rb +1 -1
  133. data/spec/moneta/transformer_bzip2_spec.rb +1 -1
  134. data/spec/moneta/transformer_json_spec.rb +1 -1
  135. data/spec/moneta/transformer_key_marshal_spec.rb +19 -19
  136. data/spec/moneta/transformer_key_yaml_spec.rb +19 -19
  137. data/spec/moneta/transformer_lzma_spec.rb +1 -1
  138. data/spec/moneta/transformer_lzo_spec.rb +1 -1
  139. data/spec/moneta/transformer_marshal_base64_spec.rb +1 -1
  140. data/spec/moneta/transformer_marshal_escape_spec.rb +1 -1
  141. data/spec/moneta/transformer_marshal_hmac_spec.rb +1 -1
  142. data/spec/moneta/transformer_marshal_md5_spec.rb +1 -1
  143. data/spec/moneta/transformer_marshal_md5_spread_spec.rb +1 -1
  144. data/spec/moneta/transformer_marshal_prefix_spec.rb +1 -1
  145. data/spec/moneta/transformer_marshal_rmd160_spec.rb +1 -1
  146. data/spec/moneta/transformer_marshal_sha1_spec.rb +1 -1
  147. data/spec/moneta/transformer_marshal_sha256_spec.rb +1 -1
  148. data/spec/moneta/transformer_marshal_sha384_spec.rb +1 -1
  149. data/spec/moneta/transformer_marshal_sha512_spec.rb +1 -1
  150. data/spec/moneta/transformer_marshal_spec.rb +1 -1
  151. data/spec/moneta/transformer_marshal_truncate_spec.rb +1 -1
  152. data/spec/moneta/transformer_marshal_uuencode_spec.rb +1 -1
  153. data/spec/moneta/transformer_msgpack_spec.rb +1 -1
  154. data/spec/moneta/transformer_ox_spec.rb +1 -1
  155. data/spec/moneta/transformer_quicklz_spec.rb +1 -1
  156. data/spec/moneta/transformer_snappy_spec.rb +1 -1
  157. data/spec/moneta/transformer_tnet_spec.rb +1 -1
  158. data/spec/moneta/transformer_value_marshal_spec.rb +1 -1
  159. data/spec/moneta/transformer_value_yaml_spec.rb +1 -1
  160. data/spec/moneta/transformer_yaml_spec.rb +1 -1
  161. data/spec/moneta/transformer_zlib_spec.rb +1 -1
  162. data/spec/monetaspecs.rb +655 -151
  163. metadata +8 -6
  164. data/benchmarks/run.rb +0 -327
  165. data/script/install-bundle.rb +0 -35
data/script/benchmarks ADDED
@@ -0,0 +1,378 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $: << File.join(File.dirname(__FILE__), '..', 'lib')
4
+ require 'benchmark'
5
+ require 'moneta'
6
+ require 'fileutils'
7
+
8
+ class String
9
+ def random(n)
10
+ (1..n).map { self[rand(size),1] }.join
11
+ end
12
+ end
13
+
14
+ class Array
15
+ def sum
16
+ inject(0, &:+)
17
+ end
18
+
19
+ def randomize
20
+ rest, result = dup, []
21
+ result << rest.slice!(rand(rest.size)) until result.size == size
22
+ result
23
+ end
24
+ end
25
+
26
+ class MonetaBenchmarks
27
+ DIR = __FILE__ + '.tmp'
28
+ FileUtils.mkpath(DIR)
29
+
30
+ STORES = {
31
+ # SDBM is unstable
32
+ # :SDBM => { :file => "#{DIR}/sdbm" },
33
+ # YAML is so fucking slow
34
+ # :YAML => { :file => "#{DIR}/yaml" },
35
+ :ActiveRecord => { :connection => { :adapter => 'sqlite3', :database => ':memory:' } },
36
+ :Cassandra => {},
37
+ :Client => {},
38
+ :Couch => {},
39
+ :DBM => { :file => "#{DIR}/dbm" },
40
+ :DataMapper => { :setup => "sqlite3:#{DIR}/datamapper" },
41
+ :Daybreak => { :file => "#{DIR}/daybreak" },
42
+ :File => { :dir => "#{DIR}/file" },
43
+ :GDBM => { :file => "#{DIR}/gdbm" },
44
+ :HBase => {},
45
+ :HashFile => { :dir => "#{DIR}/hashfile" },
46
+ :LRUHash => {},
47
+ :LevelDB => { :dir => "#{DIR}/leveldb" },
48
+ :LocalMemCache => { :file => "#{DIR}/lmc" },
49
+ :MemcachedDalli => {},
50
+ :MemcachedNative => {},
51
+ :Memory => {},
52
+ :Mongo => {},
53
+ :PStore => { :file => "#{DIR}/pstore" },
54
+ :Redis => {},
55
+ :RestClient => { :url => 'http://localhost:8808/' },
56
+ :Riak => {},
57
+ :Sequel => { :db => 'sqlite:/' },
58
+ :Sqlite => { :file => ':memory:' },
59
+ :TDB => { :file => "#{DIR}/tdb" },
60
+ }
61
+
62
+ CONFIGS = {
63
+ :uniform_small => {
64
+ :runs => 3,
65
+ :keys => 1000,
66
+ :min_key_length => 1,
67
+ :max_key_length => 32,
68
+ :key_dist => :uniform,
69
+ :min_val_length => 0,
70
+ :max_val_length => 256,
71
+ :val_dist => :uniform
72
+ },
73
+ :uniform_medium => {
74
+ :runs => 3,
75
+ :keys => 100,
76
+ :min_key_length => 3,
77
+ :max_key_length => 200,
78
+ :key_dist => :uniform,
79
+ :min_val_length => 0,
80
+ :max_val_length => 1024,
81
+ :val_dist => :uniform
82
+ },
83
+ :uniform_large => {
84
+ :runs => 3,
85
+ :keys => 100,
86
+ :min_key_length => 3,
87
+ :max_key_length => 200,
88
+ :key_dist => :uniform,
89
+ :min_val_length => 0,
90
+ :max_val_length => 10240,
91
+ :val_dist => :uniform
92
+ },
93
+ :normal_small => {
94
+ :runs => 3,
95
+ :keys => 1000,
96
+ :min_key_length => 1,
97
+ :max_key_length => 32,
98
+ :key_dist => :normal,
99
+ :min_val_length => 0,
100
+ :max_val_length => 256,
101
+ :val_dist => :normal
102
+ },
103
+ :normal_medium => {
104
+ :runs => 3,
105
+ :keys => 100,
106
+ :min_key_length => 3,
107
+ :max_key_length => 200,
108
+ :key_dist => :normal,
109
+ :min_val_length => 0,
110
+ :max_val_length => 1024,
111
+ :val_dist => :normal
112
+ },
113
+ :normal_large => {
114
+ :runs => 3,
115
+ :keys => 100,
116
+ :min_key_length => 3,
117
+ :max_key_length => 200,
118
+ :key_dist => :normal,
119
+ :min_val_length => 0,
120
+ :max_val_length => 10240,
121
+ :val_dist => :normal
122
+ },
123
+ }
124
+
125
+ DICT = 'ABCDEFGHIJKLNOPQRSTUVWXYZabcdefghijklnopqrstuvwxyz123456789'.freeze
126
+ HEADER = "\n Minimum Maximum Total Average Ops/s"
127
+ SEPARATOR = '=' * 68
128
+
129
+ module Rand
130
+ extend self
131
+
132
+ def normal_rand(mean, stddev)
133
+ # Box-Muller transform
134
+ theta = 2 * Math::PI * (rand(1e10) / 1e10)
135
+ scale = stddev * Math.sqrt(-2 * Math.log(1 - (rand(1e10) / 1e10)))
136
+ [mean + scale * Math.cos(theta),
137
+ mean + scale * Math.sin(theta)]
138
+ end
139
+
140
+ def uniform(min, max)
141
+ rand(max - min) + min
142
+ end
143
+
144
+ def normal(min, max)
145
+ mean = (min + max) / 2
146
+ stddev = (max - min) / 4
147
+ loop do
148
+ val = normal_rand(mean, stddev)
149
+ return val.first if val.first >= min && val.first <= max
150
+ return val.last if val.last >= min && val.last <= max
151
+ end
152
+ end
153
+ end
154
+
155
+ def parallel(&block)
156
+ if defined?(JRUBY_VERSION)
157
+ Thread.new(&block)
158
+ else
159
+ Process.fork(&block)
160
+ end
161
+ end
162
+
163
+ def write_histogram(file, sizes)
164
+ min = sizes.min
165
+ delta = sizes.max - min
166
+ histogram = []
167
+ sizes.each do |s|
168
+ s = 10 * (s - min) / delta
169
+ histogram[s] ||= 0
170
+ histogram[s] += 1
171
+ end
172
+ File.open(file, 'w') do |f|
173
+ histogram.each_with_index { |n,i| f.puts "#{i*delta/10+min} #{n}" }
174
+ end
175
+ end
176
+
177
+ def start_servers
178
+ parallel do
179
+ begin
180
+ Moneta::Server.new(Moneta.new(:Memory)).run
181
+ rescue Exception => ex
182
+ puts "\e[31mFailed to start Moneta server - #{ex.message}\e[0m"
183
+ end
184
+ end
185
+
186
+ parallel do
187
+ begin
188
+ require 'rack'
189
+ require 'webrick'
190
+ require 'rack/moneta_rest'
191
+
192
+ # Keep webrick quiet
193
+ ::WEBrick::HTTPServer.class_eval do
194
+ def access_log(config, req, res); end
195
+ end
196
+ ::WEBrick::BasicLog.class_eval do
197
+ def log(level, data); end
198
+ end
199
+
200
+ Rack::Server.start(:app => Rack::Builder.app do
201
+ use Rack::Lint
202
+ run Rack::MonetaRest.new(:store => :Memory)
203
+ end,
204
+ :environment => :none,
205
+ :server => :webrick,
206
+ :Port => 8808)
207
+ rescue Exception => ex
208
+ puts "\e[31mFailed to start Rack server - #{ex.message}\e[0m"
209
+ end
210
+ end
211
+
212
+ sleep 1 # Wait for servers
213
+ end
214
+
215
+ def test_stores
216
+ STORES.each do |name, options|
217
+ begin
218
+ if name == :DataMapper
219
+ begin
220
+ require 'dm-core'
221
+ DataMapper.setup(:default, :adapter => :in_memory)
222
+ rescue LoadError => ex
223
+ puts "\e[31mFailed to load DataMapper - #{ex.message}\e[0m"
224
+ end
225
+ elsif name == :Riak
226
+ require 'riak'
227
+ Riak.disable_list_keys_warnings = true
228
+ end
229
+
230
+ cache = Moneta.new(name, options.dup)
231
+ cache['test'] = 'test'
232
+ rescue Exception => ex
233
+ puts "\e[31m#{name} not benchmarked - #{ex.message}\e[0m"
234
+ STORES.delete(name)
235
+ ensure
236
+ cache.close if cache
237
+ end
238
+ end
239
+ end
240
+
241
+ def generate_data
242
+ until @data.size == @config[:keys]
243
+ key = DICT.random(Rand.send(@config[:key_dist], @config[:min_key_length], @config[:max_key_length]))
244
+ @data[key] = DICT.random(Rand.send(@config[:val_dist], @config[:min_val_length], @config[:max_val_length]))
245
+ end
246
+
247
+ key_lengths, val_lengths = @data.keys.map(&:size), @data.values.map(&:size)
248
+ @data = @data.to_a
249
+
250
+ write_histogram("#{DIR}/key.histogram", key_lengths)
251
+ write_histogram("#{DIR}/value.histogram", val_lengths)
252
+
253
+ puts "\n\e[1m\e[34m#{SEPARATOR}\n\e[34mComputing keys and values...\n\e[34m#{SEPARATOR}\e[0m"
254
+ puts %{ Minimum Maximum Total Average}
255
+ puts 'Key Length % 8d % 8d % 8d % 8d ' % [key_lengths.min, key_lengths.max, key_lengths.sum, key_lengths.sum / @data.size]
256
+ puts 'Value Length % 8d % 8d % 8d % 8d ' % [val_lengths.min, val_lengths.max, val_lengths.sum, val_lengths.sum / @data.size]
257
+ end
258
+
259
+ def print_config
260
+ puts "\e[1m\e[36m#{SEPARATOR}\n\e[36mConfig #{@config_name}\n\e[36m#{SEPARATOR}\e[0m"
261
+ @config.each do |k,v|
262
+ puts '%-16s = %-10s' % [k,v]
263
+ end
264
+ end
265
+
266
+ def print_store_stats(name)
267
+ puts HEADER
268
+ [:write, :read, :sum].each do |i|
269
+ total = @stats[name][i].sum
270
+ ops = (@config[:runs] * @data.size) / total
271
+ line = '%-17.17s %-5s % 8d % 8d % 8d % 8d % 8d' %
272
+ [name, i, @stats[name][i].min * 1000, @stats[name][i].max * 1000,
273
+ total * 1000, total * 1000 / @config[:runs], ops]
274
+ @summary << [-ops, line << "\n"] if i == :sum
275
+ puts line
276
+ end
277
+
278
+ errors = @stats[name][:error].sum
279
+ if errors > 0
280
+ puts "\e[31m%-23.23s % 8d % 8d % 8d % 8d\e[0m" %
281
+ ['Read errors', @stats[name][:error].min, @stats[name][:error].max, errors, errors / @config[:runs]]
282
+ else
283
+ puts "\e[32mNo read errors"
284
+ end
285
+ end
286
+
287
+ def benchmark_store(name, options)
288
+ puts "\n\e[1m\e[34m#{SEPARATOR}\n\e[34m#{name}\n\e[34m#{SEPARATOR}\e[0m"
289
+
290
+ store = Moneta.new(name, options.dup)
291
+
292
+ @stats[name] = {
293
+ :write => [],
294
+ :read => [],
295
+ :sum => [],
296
+ :error => []
297
+ }
298
+
299
+ %w(Rehearse Measure).each do |type|
300
+ state = ''
301
+ print "%s [%#{2 * @config[:runs]}s] " % [type, state]
302
+
303
+ @config[:runs].times do |run|
304
+ store.clear
305
+ print "%s[%-#{2 * @config[:runs]}s] " % ["\b" * (2 * @config[:runs] + 3), state << 'W']
306
+
307
+ @data = @data.randomize
308
+ m1 = Benchmark.measure do
309
+ @data.each {|k,v| store[k] = v }
310
+ end
311
+
312
+ print "%s[%-#{2 * @config[:runs]}s] " % ["\b" * (2 * @config[:runs] + 3), state << 'R']
313
+
314
+ @data = @data.randomize
315
+ error = 0
316
+ m2 = Benchmark.measure do
317
+ @data.each do |k, v|
318
+ error += 1 if v != store[k]
319
+ end
320
+ end
321
+
322
+ if type == 'Measure'
323
+ @stats[name][:write] << m1.real
324
+ @stats[name][:error] << error
325
+ @stats[name][:read] << m2.real
326
+ @stats[name][:sum] << (m1.real + m2.real)
327
+ end
328
+ end
329
+ end
330
+
331
+ print_store_stats(name)
332
+ rescue StandardError => ex
333
+ puts "\n\e[31mFailed to benchmark #{name} - #{ex.message}\e[0m\n"
334
+ ensure
335
+ store.close if store
336
+ end
337
+
338
+ def run_benchmarks
339
+ STORES.each {|name, options| benchmark_store(name, options) }
340
+ end
341
+
342
+ def print_summary
343
+ puts "\n\e[1m\e[36m#{SEPARATOR}\n\e[36mSummary #{@config_name}: #{@config[:runs]} runs, #{@data.size} keys\n\e[36m#{SEPARATOR}\e[0m#{HEADER}\n"
344
+ @summary.sort_by(&:first).each do |entry|
345
+ puts entry.last
346
+ end
347
+ end
348
+
349
+ def initialize(args)
350
+ @config_name = args.size == 1 ? args.first.to_sym : :uniform_medium
351
+ unless @config = CONFIGS[@config_name]
352
+ puts "Configuration #{@config_name} not found"
353
+ exit
354
+ end
355
+
356
+ # Disable jruby stdout pollution by memcached
357
+ if defined?(JRUBY_VERSION)
358
+ require 'java'
359
+ properties = java.lang.System.getProperties();
360
+ properties.put('net.spy.log.LoggerImpl', 'net.spy.memcached.compat.log.SunLogger');
361
+ java.lang.System.setProperties(properties);
362
+ java.util.logging.Logger.getLogger('').setLevel(java.util.logging.Level::OFF)
363
+ end
364
+
365
+ @stats, @data, @summary = {}, {}, []
366
+ end
367
+
368
+ def run
369
+ start_servers
370
+ test_stores
371
+ print_config
372
+ generate_data
373
+ run_benchmarks
374
+ print_summary
375
+ end
376
+ end
377
+
378
+ MonetaBenchmarks.new(ARGV).run
@@ -2,43 +2,55 @@
2
2
 
3
3
  PATH = File.expand_path(File.join(__FILE__, '..', '..', 'spec'))
4
4
 
5
+ class Array
6
+ def without(*x)
7
+ a = dup
8
+ x.each {|y| a.delete(y) }
9
+ a
10
+ end
11
+
12
+ def with(*x)
13
+ a = dup
14
+ x.each {|y| a << y }
15
+ a
16
+ end
17
+ end
18
+
5
19
  class Specs
6
20
  attr_reader :key, :value, :specs
7
21
 
8
- def initialize(specs, key = nil, value = nil)
9
- @specs = specs
10
- @key = key || %w(object string hash boolean nil integer)
11
- @value = value || %w(object string hash boolean nil integer)
22
+ def initialize(options = {})
23
+ @specs = options.delete(:specs).to_a
24
+ @key = options.delete(:key) || %w(object string hash boolean nil integer)
25
+ @value = options.delete(:value) || %w(object string hash boolean nil integer)
26
+ end
27
+
28
+ def new(options)
29
+ Specs.new({:specs => specs, :key => key, :value => value}.merge(options))
12
30
  end
13
31
 
14
32
  def stringkeys_only
15
- Specs.new(specs, %w(string), value)
33
+ new(:key => %w(string))
16
34
  end
17
35
 
18
36
  def stringvalues_only
19
- Specs.new(specs, key, %w(string))
37
+ new(:value => %w(string))
20
38
  end
21
39
 
22
40
  def simplekeys_only
23
- Specs.new(specs, %w(string hash integer), value)
41
+ new(:key => %w(string hash integer))
24
42
  end
25
43
 
26
44
  def simplevalues_only
27
- Specs.new(specs, key, %w(string hash integer))
45
+ new(:value => %w(string hash integer))
28
46
  end
29
47
 
30
48
  def without_increment
31
- a = specs.dup
32
- a.delete(:increment)
33
- a << :not_increment
34
- Specs.new(a, key, value)
49
+ new(:specs => specs.without(:increment).with(:not_increment))
35
50
  end
36
51
 
37
52
  def without_persist
38
- a = specs.dup
39
- a.delete(:persist)
40
- a << :not_persist
41
- Specs.new(a, key, value)
53
+ new(:specs => specs.without(:persist).with(:not_persist))
42
54
  end
43
55
 
44
56
  def with_expires
@@ -48,66 +60,45 @@ class Specs
48
60
  a << :transform_value_with_expires
49
61
  end
50
62
  a << :expires
51
- Specs.new(a, key, value)
63
+ new(:specs => a)
52
64
  end
53
65
 
54
66
  def with_native_expires
55
- a = specs.dup
56
- a << :expires
57
- Specs.new(a, key, value)
67
+ new(:specs => specs.with(:expires))
58
68
  end
59
69
 
60
70
  def without_marshallable
61
- a = specs.dup
62
- a.delete(:marshallable_value)
63
- a.delete(:marshallable_key)
64
- Specs.new(a, key, value)
71
+ new(:specs => specs.without(:marshallable_value, :marshallable_key))
65
72
  end
66
73
 
67
74
  def without_transform
68
- a = specs.dup
69
- a.delete(:marshallable_value)
70
- a.delete(:marshallable_key)
71
- a.delete(:transform_value)
72
- Specs.new(a, key, value)
75
+ new(:specs => specs.without(:marshallable_value, :marshallable_key, :transform_value))
73
76
  end
74
77
 
75
- def without_returndifferent
76
- a = specs.dup
77
- a.delete(:returndifferent)
78
- Specs.new(a, key, value)
78
+ def returnsame
79
+ new(:specs => specs.without(:returndifferent).with(:returnsame))
79
80
  end
80
81
 
81
82
  def without_marshallable_key
82
- a = specs.dup
83
- a.delete(:marshallable_key)
84
- Specs.new(a, key, value)
83
+ new(:specs => specs.without(:marshallable_key))
85
84
  end
86
85
 
87
86
  def without_marshallable_value
88
- a = specs.dup
89
- a.delete(:marshallable_value)
90
- Specs.new(a, key, value)
87
+ new(:specs => specs.without(:marshallable_value))
91
88
  end
92
89
 
93
90
  def without_store
94
- a = specs.dup
95
- a.delete(:store)
96
- a.delete(:transform_value)
97
- a.delete(:marshallable_value)
98
- Specs.new(a, key, value)
91
+ new(:specs => specs.without(:store, :transform_value, :marshallable_value))
99
92
  end
100
93
 
101
94
  def with_default_expires
102
- a = specs.dup
103
- a << :default_expires
104
- Specs.new(a, key, value)
95
+ new(:specs => specs.with(:default_expires))
105
96
  end
106
97
  end
107
98
 
108
- ADAPTER_SPECS = Specs.new([:null, :store, :returndifferent, :increment, :persist], %w(string), %w(string))
109
- STANDARD_SPECS = Specs.new([:null, :store, :returndifferent, :marshallable_key, :marshallable_value, :transform_value, :increment, :persist])
110
- TRANSFORMER_SPECS = Specs.new([:null, :store, :returndifferent, :transform_value, :increment])
99
+ ADAPTER_SPECS = Specs.new(:specs => [:null, :store, :returndifferent, :increment, :persist], :key => %w(string), :value => %w(string))
100
+ STANDARD_SPECS = Specs.new(:specs => [:null, :store, :returndifferent, :marshallable_key, :marshallable_value, :transform_value, :increment, :persist])
101
+ TRANSFORMER_SPECS = Specs.new(:specs => [:null, :store, :returndifferent, :transform_value, :increment])
111
102
 
112
103
  header = "# Generated by #{File.basename(__FILE__)}\n"
113
104
 
@@ -402,7 +393,7 @@ end
402
393
  },
403
394
  'null_adapter' => {
404
395
  :build => 'Moneta::Adapters::Null.new',
405
- :specs => Specs.new([:null, :not_increment, :not_persist])
396
+ :specs => Specs.new(:specs => [:null, :not_increment, :not_persist])
406
397
  },
407
398
  'simple_sequel' => {
408
399
  :store => :Sequel,
@@ -478,14 +469,14 @@ end
478
469
  use :Expires
479
470
  adapter :Memory
480
471
  end},
481
- :specs => STANDARD_SPECS.without_transform.with_expires.without_persist
472
+ :specs => STANDARD_SPECS.without_transform.with_expires.without_persist.returnsame
482
473
  },
483
474
  'expires_memory_with_default_expires' => {
484
475
  :build => %{Moneta.build do
485
476
  use :Expires, :expires => 1
486
477
  adapter :Memory
487
478
  end},
488
- :specs => STANDARD_SPECS.without_transform.with_expires.with_default_expires.without_persist
479
+ :specs => STANDARD_SPECS.without_transform.with_expires.with_default_expires.without_persist.returnsame
489
480
  },
490
481
  'expires_file' => {
491
482
  :build => %{Moneta.build do
@@ -522,7 +513,7 @@ end},
522
513
  use :Proxy
523
514
  adapter :Memory
524
515
  end},
525
- :specs => STANDARD_SPECS.without_transform.with_expires.without_returndifferent.without_persist
516
+ :specs => STANDARD_SPECS.without_transform.with_expires.returnsame.without_persist
526
517
  },
527
518
  'cache_file_memory' => {
528
519
  :build => %{Moneta.build do
@@ -531,7 +522,7 @@ end},
531
522
  cache { adapter :Memory }
532
523
  end
533
524
  end},
534
- :specs => ADAPTER_SPECS,
525
+ :specs => ADAPTER_SPECS.returnsame,
535
526
  :tests => %{
536
527
  it 'stores loaded values in cache' do
537
528
  store.backend['foo'] = 'bar'
@@ -552,15 +543,15 @@ end
552
543
  cache(Moneta::Adapters::Null.new)
553
544
  end
554
545
  end},
555
- :specs => ADAPTER_SPECS.without_persist
546
+ :specs => ADAPTER_SPECS.without_persist.returnsame
556
547
  },
557
548
  'shared' => {
558
549
  :build => %{Moneta.build do
559
550
  use(:Shared, :port => 9001) do
560
- adapter :Memory
551
+ adapter :PStore, :file => File.join(make_tempdir, 'shared')
561
552
  end
562
553
  end},
563
- :specs => ADAPTER_SPECS.without_persist,
554
+ :specs => ADAPTER_SPECS,
564
555
  :tests => %{
565
556
  it 'shares values' do
566
557
  store['shared_key'] = 'shared_value'
@@ -576,7 +567,7 @@ end
576
567
  use(:Stack) do
577
568
  add(Moneta.new(:Null))
578
569
  add(Moneta::Adapters::Null.new)
579
- add { adapter :File, :dir => File.join(make_tempdir, "stack-file1") }
570
+ add { adapter :File, :dir => File.join(make_tempdir, "stack_file_memory") }
580
571
  add { adapter :Memory }
581
572
  end
582
573
  end},
@@ -588,17 +579,17 @@ end},
588
579
  add(Moneta.new(:Null))
589
580
  add(Moneta::Adapters::Null.new)
590
581
  add { adapter :Memory }
591
- add { adapter :File, :dir => File.join(make_tempdir, "stack-file2") }
582
+ add { adapter :File, :dir => File.join(make_tempdir, "stack_memory_file") }
592
583
  end
593
584
  end},
594
- :specs => STANDARD_SPECS.without_increment.without_transform.stringkeys_only.stringvalues_only
585
+ :specs => STANDARD_SPECS.without_increment.returnsame.without_transform.stringkeys_only.stringvalues_only
595
586
  },
596
587
  'lock' => {
597
588
  :build => %{Moneta.build do
598
589
  use :Lock
599
590
  adapter :Memory
600
591
  end},
601
- :specs => STANDARD_SPECS.without_transform.without_returndifferent.without_persist
592
+ :specs => STANDARD_SPECS.without_transform.returnsame.without_persist
602
593
  },
603
594
  'pool' => {
604
595
  :build => %{Moneta.build do
@@ -725,7 +716,7 @@ end},
725
716
  use :Transformer, :key => :marshal
726
717
  adapter :Memory
727
718
  end},
728
- :specs => TRANSFORMER_SPECS,
719
+ :specs => TRANSFORMER_SPECS.returnsame,
729
720
  :load_value => 'value'
730
721
  },
731
722
  'transformer_value_marshal' => {
@@ -749,7 +740,7 @@ end},
749
740
  use :Transformer, :key => :yaml
750
741
  adapter :Memory
751
742
  end},
752
- :specs => TRANSFORMER_SPECS,
743
+ :specs => TRANSFORMER_SPECS.returnsame,
753
744
  :load_value => 'value'
754
745
  },
755
746
  'transformer_value_yaml' => {
@@ -888,7 +879,7 @@ end
888
879
  :specs => ADAPTER_SPECS.without_increment.with_native_expires
889
880
  },
890
881
  'adapter_cassandra_with_default_expires' => {
891
- :build => %{Moneta::Adapters::Cassandra.new(:expires => 1)},
882
+ :build => %{Moneta::Adapters::Cassandra.new(:keyspace => 'adapter_cassandra_with_default_expires', :expires => 1)},
892
883
  :specs => ADAPTER_SPECS.without_increment.with_native_expires.with_default_expires
893
884
  },
894
885
  'adapter_hbase' => {
@@ -897,7 +888,7 @@ end
897
888
  },
898
889
  'adapter_cookie' => {
899
890
  :build => 'Moneta::Adapters::Cookie.new',
900
- :specs => ADAPTER_SPECS.without_persist
891
+ :specs => ADAPTER_SPECS.without_persist.returnsame
901
892
  },
902
893
  'adapter_couch' => {
903
894
  :build => "Moneta::Adapters::Couch.new(:db => 'adapter_couch')",
@@ -949,7 +940,7 @@ end
949
940
  },
950
941
  'adapter_daybreak' => {
951
942
  :build => 'Moneta::Adapters::Daybreak.new(:file => File.join(make_tempdir, "adapter_daybreak"))',
952
- :specs => ADAPTER_SPECS
943
+ :specs => ADAPTER_SPECS.returnsame
953
944
  },
954
945
  'adapter_file' => {
955
946
  :build => 'Moneta::Adapters::File.new(:dir => File.join(make_tempdir, "adapter_file"))',
@@ -962,7 +953,8 @@ end
962
953
  :dir => 'moneta')",
963
954
  # Put Fog into testing mode
964
955
  :preamble => "require 'fog'\nFog.mock!\n",
965
- :specs => ADAPTER_SPECS.without_increment
956
+ # Fog returns same object in mocking mode (in-memory store)
957
+ :specs => ADAPTER_SPECS.without_increment.returnsame
966
958
  },
967
959
  'adapter_gdbm' => {
968
960
  :build => 'Moneta::Adapters::GDBM.new(:file => File.join(make_tempdir, "adapter_gdbm"))',
@@ -998,11 +990,11 @@ end
998
990
  },
999
991
  'adapter_memory' => {
1000
992
  :build => 'Moneta::Adapters::Memory.new',
1001
- :specs => STANDARD_SPECS.without_transform.without_returndifferent.without_persist
993
+ :specs => STANDARD_SPECS.without_transform.returnsame.without_persist
1002
994
  },
1003
995
  'adapter_lruhash' => {
1004
996
  :build => 'Moneta::Adapters::LRUHash.new',
1005
- :specs => ADAPTER_SPECS.without_persist,
997
+ :specs => ADAPTER_SPECS.without_persist.returnsame,
1006
998
  :tests => %{
1007
999
  it 'deletes oldest' do
1008
1000
  store = Moneta::Adapters::LRUHash.new(:max_size => 10)
@@ -1023,7 +1015,7 @@ end}
1023
1015
  :specs => ADAPTER_SPECS.with_native_expires,
1024
1016
  :tests => %{
1025
1017
  it 'automatically deletes expired document' do
1026
- store.store('key', 'val', :expires => 30)
1018
+ store.store('key', 'val', :expires => 5)
1027
1019
  store.instance_variable_get(:@collection).find_one('_id' => ::BSON::Binary.new('key')).should_not be_nil
1028
1020
  sleep 70 # Mongo needs up to 60 seconds
1029
1021
  store.instance_variable_get(:@collection).find_one('_id' => ::BSON::Binary.new('key')).should be_nil
@@ -1082,7 +1074,7 @@ end}
1082
1074
  },
1083
1075
  'optionmerger' => {
1084
1076
  :store => :Memory,
1085
- :specs => Specs.new([]),
1077
+ :specs => Specs.new,
1086
1078
  :tests => %{
1087
1079
  it '#with should return OptionMerger' do
1088
1080
  options = {:optionname => :optionvalue}
@@ -1160,6 +1152,19 @@ it 'has method #prefix' do
1160
1152
 
1161
1153
  store.prefix('a').raw.default_options.should == {:store=>{:raw=>true,:prefix=>'a'},:load=>{:raw=>true,:prefix=>'a'},
1162
1154
  :delete=>{:raw=>true,:prefix=>'a'},:key? => {:prefix=>'a'},:increment=>{:prefix=>'a'}}
1155
+ end
1156
+
1157
+ it 'supports adding proxis using #with' do
1158
+ compressed_store = store.with(:prefix => 'compressed') do
1159
+ use :Transformer, :value => :zlib
1160
+ end
1161
+ store['key'] = 'uncompressed value'
1162
+ compressed_store['key'] = 'compressed value'
1163
+ store['key'].should == 'uncompressed value'
1164
+ compressed_store['key'].should == 'compressed value'
1165
+ store.key?('compressedkey').should be_true
1166
+ # Check if value is compressed
1167
+ compressed_store['key'].should_not == store['compressedkey']
1163
1168
  end}
1164
1169
  },
1165
1170
  }
@@ -1299,13 +1304,17 @@ end}
1299
1304
 
1300
1305
  (SPECS["store_#{key_type}key_#{val_type}value"] ||= []) << code
1301
1306
 
1302
- code = %{it 'guarantees that a different value is retrieved' do
1307
+ if val_type != 'boolean' && val_type != 'nil' && val_type != 'integer'
1308
+ (SPECS["returndifferent_#{key_type}key_#{val_type}value"] ||= []) << %{it 'guarantees that a different value is retrieved' do
1303
1309
  value = #{val1}
1304
- store[#{key1}] = #{val1}
1305
- store[#{key1}].should_not be_equal(#{val1})
1310
+ store[#{key1}] = value
1311
+ store[#{key1}].should_not be_equal(value)
1312
+ end}
1313
+ (SPECS["returnsame_#{key_type}key_#{val_type}value"] ||= []) << %{it 'guarantees that the same value is retrieved' do
1314
+ value = #{val1}
1315
+ store[#{key1}] = value
1316
+ store[#{key1}].should be_equal(value)
1306
1317
  end}
1307
- if val_type != 'boolean' && val_type != 'nil' && val_type != 'integer'
1308
- (SPECS["returndifferent_#{key_type}key_#{val_type}value"] ||= []) << code
1309
1318
  end
1310
1319
 
1311
1320
  code = %{it 'persists values' do
@@ -1374,9 +1383,9 @@ it 'supports updating the expiration time in load' do
1374
1383
  sleep 1
1375
1384
  store.load('key2', :expires => 3).should == 'val2'
1376
1385
  store['key2'].should == 'val2'
1377
- sleep 1
1386
+ sleep 2
1378
1387
  store['key2'].should == 'val2'
1379
- sleep 3
1388
+ sleep 2
1380
1389
  store['key2'].should be_nil
1381
1390
  end
1382
1391
 
@@ -1400,9 +1409,9 @@ it 'supports updating the expiration time in key?' do
1400
1409
  sleep 1
1401
1410
  store.key?('key2', :expires => 3).should be_true
1402
1411
  store['key2'].should == 'val2'
1403
- sleep 1
1412
+ sleep 2
1404
1413
  store['key2'].should == 'val2'
1405
- sleep 3
1414
+ sleep 2
1406
1415
  store['key2'].should be_nil
1407
1416
  end
1408
1417
 
@@ -1426,9 +1435,9 @@ it 'supports updating the expiration time in fetch' do
1426
1435
  sleep 1
1427
1436
  store.fetch('key1', nil, :expires => 3).should == 'val1'
1428
1437
  store['key1'].should == 'val1'
1429
- sleep 1
1438
+ sleep 2
1430
1439
  store['key1'].should == 'val1'
1431
- sleep 3
1440
+ sleep 2
1432
1441
  store['key1'].should be_nil
1433
1442
  end
1434
1443