moneta 0.7.4 → 0.7.5

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 (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