moneta 0.7.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +4 -0
- data/.travis.yml +39 -6
- data/CHANGES +18 -0
- data/README.md +69 -31
- data/Rakefile +1 -1
- data/SPEC.md +6 -0
- data/benchmarks/run.rb +235 -133
- data/lib/active_support/cache/moneta_store.rb +1 -0
- data/lib/moneta.rb +29 -19
- data/lib/moneta/adapters/activerecord.rb +18 -26
- data/lib/moneta/adapters/cassandra.rb +5 -7
- data/lib/moneta/adapters/client.rb +3 -5
- data/lib/moneta/adapters/cookie.rb +2 -0
- data/lib/moneta/adapters/couch.rb +1 -3
- data/lib/moneta/adapters/datamapper.rb +4 -6
- data/lib/moneta/adapters/dbm.rb +1 -3
- data/lib/moneta/adapters/file.rb +4 -0
- data/lib/moneta/adapters/fog.rb +2 -4
- data/lib/moneta/adapters/gdbm.rb +1 -3
- data/lib/moneta/adapters/hbase.rb +5 -7
- data/lib/moneta/adapters/leveldb.rb +2 -4
- data/lib/moneta/adapters/localmemcache.rb +1 -3
- data/lib/moneta/adapters/lruhash.rb +1 -3
- data/lib/moneta/adapters/memcached.rb +2 -2
- data/lib/moneta/adapters/{memcached_dalli.rb → memcached/dalli.rb} +4 -6
- data/lib/moneta/adapters/{memcached_native.rb → memcached/native.rb} +8 -7
- data/lib/moneta/adapters/mongo.rb +8 -9
- data/lib/moneta/adapters/pstore.rb +1 -3
- data/lib/moneta/adapters/redis.rb +2 -4
- data/lib/moneta/adapters/riak.rb +3 -5
- data/lib/moneta/adapters/sdbm.rb +1 -13
- data/lib/moneta/adapters/sequel.rb +3 -5
- data/lib/moneta/adapters/sqlite.rb +2 -4
- data/lib/moneta/adapters/tokyocabinet.rb +2 -4
- data/lib/moneta/base.rb +22 -2
- data/lib/moneta/builder.rb +3 -3
- data/lib/moneta/cache.rb +1 -1
- data/lib/moneta/expires.rb +27 -35
- data/lib/moneta/lock.rb +1 -3
- data/lib/moneta/logger.rb +3 -5
- data/lib/moneta/net.rb +7 -3
- data/lib/moneta/proxy.rb +2 -1
- data/lib/moneta/server.rb +33 -21
- data/lib/moneta/shared.rb +11 -7
- data/lib/moneta/stack.rb +1 -1
- data/lib/moneta/transformer.rb +15 -13
- data/lib/moneta/transformer/config.rb +33 -31
- data/lib/moneta/version.rb +1 -1
- data/lib/rack/cache/moneta.rb +6 -0
- data/lib/rack/moneta_cookies.rb +1 -0
- data/lib/rack/session/moneta.rb +1 -0
- data/moneta.gemspec +1 -1
- data/spec/generate.rb +403 -232
- data/spec/helper.rb +23 -2
- data/spec/moneta/adapter_activerecord_spec.rb +3 -2
- data/spec/moneta/adapter_cassandra_spec.rb +4 -3
- data/spec/moneta/adapter_client_spec.rb +4 -3
- data/spec/moneta/adapter_cookie_spec.rb +3 -2
- data/spec/moneta/adapter_couch_spec.rb +3 -2
- data/spec/moneta/adapter_datamapper_spec.rb +3 -2
- data/spec/moneta/adapter_dbm_spec.rb +3 -2
- data/spec/moneta/adapter_file_spec.rb +3 -2
- data/spec/moneta/adapter_fog_spec.rb +3 -2
- data/spec/moneta/adapter_gdbm_spec.rb +3 -2
- data/spec/moneta/adapter_hbase_spec.rb +3 -2
- data/spec/moneta/adapter_leveldb_spec.rb +3 -2
- data/spec/moneta/adapter_localmemcache_spec.rb +3 -2
- data/spec/moneta/adapter_lruhash_spec.rb +3 -2
- data/spec/moneta/adapter_memcached_dalli_spec.rb +4 -3
- data/spec/moneta/adapter_memcached_native_spec.rb +4 -3
- data/spec/moneta/adapter_memcached_spec.rb +4 -3
- data/spec/moneta/adapter_memory_spec.rb +50 -1
- data/spec/moneta/adapter_mongo_spec.rb +3 -2
- data/spec/moneta/adapter_pstore_spec.rb +124 -4
- data/spec/moneta/adapter_redis_spec.rb +4 -3
- data/spec/moneta/adapter_riak_spec.rb +3 -2
- data/spec/moneta/adapter_sdbm_spec.rb +3 -2
- data/spec/moneta/adapter_sequel_spec.rb +3 -2
- data/spec/moneta/adapter_sqlite_spec.rb +3 -2
- data/spec/moneta/adapter_tokyocabinet_bdb_spec.rb +3 -2
- data/spec/moneta/adapter_tokyocabinet_hdb_spec.rb +3 -2
- data/spec/moneta/adapter_yaml_spec.rb +32 -5
- data/spec/moneta/cache_file_memory_spec.rb +3 -2
- data/spec/moneta/cache_memory_null_spec.rb +3 -2
- data/spec/moneta/expires_file_spec.rb +23 -41
- data/spec/moneta/expires_memory_spec.rb +69 -13
- data/spec/moneta/lock_spec.rb +50 -1
- data/spec/moneta/null_adapter_spec.rb +26 -0
- data/spec/moneta/proxy_expires_memory_spec.rb +51 -12
- data/spec/moneta/proxy_redis_spec.rb +4 -3
- data/spec/moneta/shared_spec.rb +4 -3
- data/spec/moneta/simple_activerecord_spec.rb +105 -12
- data/spec/moneta/simple_activerecord_with_expires_spec.rb +106 -13
- data/spec/moneta/simple_cassandra_spec.rb +106 -13
- data/spec/moneta/simple_client_tcp_spec.rb +106 -13
- data/spec/moneta/simple_client_unix_spec.rb +106 -14
- data/spec/moneta/simple_couch_spec.rb +105 -12
- data/spec/moneta/simple_couch_with_expires_spec.rb +106 -13
- data/spec/moneta/simple_datamapper_spec.rb +105 -12
- data/spec/moneta/simple_datamapper_with_expires_spec.rb +106 -13
- data/spec/moneta/simple_datamapper_with_repository_spec.rb +105 -12
- data/spec/moneta/simple_dbm_spec.rb +105 -12
- data/spec/moneta/simple_dbm_with_expires_spec.rb +106 -13
- data/spec/moneta/simple_file_spec.rb +105 -12
- data/spec/moneta/simple_file_with_expires_spec.rb +106 -13
- data/spec/moneta/simple_fog_spec.rb +105 -12
- data/spec/moneta/simple_fog_with_expires_spec.rb +106 -13
- data/spec/moneta/simple_gdbm_spec.rb +105 -12
- data/spec/moneta/simple_gdbm_with_expires_spec.rb +106 -13
- data/spec/moneta/simple_hashfile_spec.rb +105 -12
- data/spec/moneta/simple_hashfile_with_expires_spec.rb +106 -13
- data/spec/moneta/simple_hbase_spec.rb +105 -12
- data/spec/moneta/simple_hbase_with_expires_spec.rb +106 -13
- data/spec/moneta/simple_leveldb_spec.rb +105 -12
- data/spec/moneta/simple_leveldb_with_expires_spec.rb +106 -13
- data/spec/moneta/simple_localmemcache_spec.rb +105 -12
- data/spec/moneta/simple_localmemcache_with_expires_spec.rb +106 -13
- data/spec/moneta/simple_lruhash_spec.rb +70 -12
- data/spec/moneta/simple_lruhash_with_expires_spec.rb +71 -13
- data/spec/moneta/simple_memcached_dalli_spec.rb +106 -13
- data/spec/moneta/simple_memcached_native_spec.rb +106 -13
- data/spec/moneta/simple_memcached_spec.rb +106 -13
- data/spec/moneta/simple_memory_spec.rb +70 -12
- data/spec/moneta/simple_memory_with_compress_spec.rb +70 -12
- data/spec/moneta/simple_memory_with_expires_spec.rb +71 -13
- data/spec/moneta/simple_memory_with_json_key_serializer_spec.rb +41 -15
- data/spec/moneta/simple_memory_with_json_serializer_spec.rb +24 -11
- data/spec/moneta/simple_memory_with_json_value_serializer_spec.rb +45 -14
- data/spec/moneta/simple_memory_with_prefix_spec.rb +70 -12
- data/spec/moneta/simple_memory_with_snappy_compress_spec.rb +70 -12
- data/spec/moneta/simple_mongo_spec.rb +105 -12
- data/spec/moneta/simple_mongo_with_expires_spec.rb +106 -13
- data/spec/moneta/simple_null_spec.rb +36 -1
- data/spec/moneta/simple_pstore_spec.rb +105 -12
- data/spec/moneta/simple_pstore_with_expires_spec.rb +106 -13
- data/spec/moneta/simple_redis_spec.rb +106 -13
- data/spec/moneta/simple_riak_spec.rb +105 -12
- data/spec/moneta/simple_riak_with_expires_spec.rb +106 -13
- data/spec/moneta/simple_sdbm_spec.rb +105 -12
- data/spec/moneta/simple_sdbm_with_expires_spec.rb +106 -13
- data/spec/moneta/simple_sequel_spec.rb +105 -12
- data/spec/moneta/simple_sequel_with_expires_spec.rb +106 -13
- data/spec/moneta/simple_sqlite_spec.rb +105 -12
- data/spec/moneta/simple_sqlite_with_expires_spec.rb +106 -13
- data/spec/moneta/simple_tokyocabinet_spec.rb +105 -12
- data/spec/moneta/simple_tokyocabinet_with_expires_spec.rb +106 -13
- data/spec/moneta/simple_yaml_spec.rb +104 -11
- data/spec/moneta/simple_yaml_with_expires_spec.rb +105 -12
- data/spec/moneta/stack_file_memory_spec.rb +3 -2
- data/spec/moneta/stack_memory_file_spec.rb +3 -1
- data/spec/moneta/transformer_bencode_spec.rb +23 -10
- data/spec/moneta/transformer_bert_spec.rb +23 -10
- data/spec/moneta/transformer_bson_spec.rb +23 -10
- data/spec/moneta/transformer_bzip2_spec.rb +13 -3
- data/spec/moneta/transformer_json_spec.rb +23 -10
- data/spec/moneta/transformer_lzma_spec.rb +13 -3
- data/spec/moneta/transformer_lzo_spec.rb +13 -3
- data/spec/moneta/transformer_marshal_base64_spec.rb +70 -12
- data/spec/moneta/transformer_marshal_escape_spec.rb +70 -12
- data/spec/moneta/transformer_marshal_hmac_spec.rb +70 -12
- data/spec/moneta/transformer_marshal_md5_spec.rb +70 -12
- data/spec/moneta/transformer_marshal_md5_spread_spec.rb +70 -12
- data/spec/moneta/transformer_marshal_prefix_spec.rb +70 -12
- data/spec/moneta/transformer_marshal_rmd160_spec.rb +70 -12
- data/spec/moneta/transformer_marshal_sha1_spec.rb +70 -12
- data/spec/moneta/transformer_marshal_sha256_spec.rb +70 -12
- data/spec/moneta/transformer_marshal_sha384_spec.rb +70 -12
- data/spec/moneta/transformer_marshal_sha512_spec.rb +70 -12
- data/spec/moneta/transformer_marshal_truncate_spec.rb +70 -12
- data/spec/moneta/transformer_marshal_uuencode_spec.rb +70 -12
- data/spec/moneta/transformer_msgpack_spec.rb +23 -10
- data/spec/moneta/transformer_ox_spec.rb +67 -9
- data/spec/moneta/transformer_quicklz_spec.rb +13 -3
- data/spec/moneta/transformer_snappy_spec.rb +13 -3
- data/spec/moneta/transformer_tnet_spec.rb +23 -10
- data/spec/moneta/transformer_yaml_spec.rb +67 -9
- data/spec/moneta/transformer_zlib_spec.rb +13 -3
- data/spec/monetaspecs.rb +4649 -1096
- metadata +8 -5
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
|
@@ -17,7 +17,12 @@ before_install:
|
|
|
17
17
|
env:
|
|
18
18
|
- "TASK=test:parallel"
|
|
19
19
|
- "TASK=test:non_parallel"
|
|
20
|
-
- "TASK=benchmarks"
|
|
20
|
+
- "TASK=benchmarks CONFIG=uniform_small"
|
|
21
|
+
- "TASK=benchmarks CONFIG=uniform_medium"
|
|
22
|
+
- "TASK=benchmarks CONFIG=uniform_large"
|
|
23
|
+
- "TASK=benchmarks CONFIG=normal_small"
|
|
24
|
+
- "TASK=benchmarks CONFIG=normal_medium"
|
|
25
|
+
- "TASK=benchmarks CONFIG=normal_large"
|
|
21
26
|
matrix:
|
|
22
27
|
allow_failures:
|
|
23
28
|
# - rvm: ruby-head
|
|
@@ -28,12 +33,40 @@ matrix:
|
|
|
28
33
|
exclude:
|
|
29
34
|
- rvm: jruby
|
|
30
35
|
env: "TASK=test:parallel"
|
|
31
|
-
- rvm: 1.8.7
|
|
32
|
-
env: "TASK=benchmarks"
|
|
33
36
|
- rvm: jruby
|
|
34
|
-
env: "TASK=benchmarks"
|
|
37
|
+
env: "TASK=benchmarks CONFIG=uniform_small"
|
|
38
|
+
- rvm: jruby
|
|
39
|
+
env: "TASK=benchmarks CONFIG=uniform_medium"
|
|
40
|
+
- rvm: jruby
|
|
41
|
+
env: "TASK=benchmarks CONFIG=uniform_large"
|
|
42
|
+
- rvm: jruby
|
|
43
|
+
env: "TASK=benchmarks CONFIG=normal_small"
|
|
44
|
+
- rvm: jruby
|
|
45
|
+
env: "TASK=benchmarks CONFIG=normal_medium"
|
|
46
|
+
- rvm: jruby
|
|
47
|
+
env: "TASK=benchmarks CONFIG=normal_large"
|
|
48
|
+
- rvm: rbx-18mode
|
|
49
|
+
env: "TASK=benchmarks CONFIG=uniform_small"
|
|
50
|
+
- rvm: rbx-18mode
|
|
51
|
+
env: "TASK=benchmarks CONFIG=uniform_medium"
|
|
52
|
+
- rvm: rbx-18mode
|
|
53
|
+
env: "TASK=benchmarks CONFIG=uniform_large"
|
|
35
54
|
- rvm: rbx-18mode
|
|
36
|
-
env: "TASK=benchmarks"
|
|
55
|
+
env: "TASK=benchmarks CONFIG=normal_small"
|
|
56
|
+
- rvm: rbx-18mode
|
|
57
|
+
env: "TASK=benchmarks CONFIG=normal_medium"
|
|
58
|
+
- rvm: rbx-18mode
|
|
59
|
+
env: "TASK=benchmarks CONFIG=normal_large"
|
|
60
|
+
- rvm: rbx-19mode
|
|
61
|
+
env: "TASK=benchmarks CONFIG=uniform_small"
|
|
62
|
+
- rvm: rbx-19mode
|
|
63
|
+
env: "TASK=benchmarks CONFIG=uniform_medium"
|
|
64
|
+
- rvm: rbx-19mode
|
|
65
|
+
env: "TASK=benchmarks CONFIG=uniform_large"
|
|
66
|
+
- rvm: rbx-19mode
|
|
67
|
+
env: "TASK=benchmarks CONFIG=normal_small"
|
|
68
|
+
- rvm: rbx-19mode
|
|
69
|
+
env: "TASK=benchmarks CONFIG=normal_medium"
|
|
37
70
|
- rvm: rbx-19mode
|
|
38
|
-
env: "TASK=benchmarks"
|
|
71
|
+
env: "TASK=benchmarks CONFIG=normal_large"
|
|
39
72
|
script: "bundle exec rake $TASK"
|
data/CHANGES
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
0.7.1
|
|
2
|
+
|
|
3
|
+
* Memcached: Use binary protocol and no base64 encoding of the keys
|
|
4
|
+
* Transformer: Remove newlines from base64 encodes values
|
|
5
|
+
* Server: Add method #run which will block and #running? to allow forking
|
|
6
|
+
* SDBM: #store might raise errors (Don't use SDBM, it is unstable!)
|
|
7
|
+
* Add #decrement method
|
|
8
|
+
* Fix #fetch to handle false correctly
|
|
9
|
+
* Fix Expires middleware to handle boolean and nil values correctly
|
|
10
|
+
* Base64 encode Riak keys since Riak needs valid UTF-8 for the REST interface
|
|
11
|
+
|
|
12
|
+
0.7.0
|
|
13
|
+
|
|
14
|
+
* Major rewrite by Daniel Mendler
|
|
15
|
+
|
|
16
|
+
0.6.0
|
|
17
|
+
|
|
18
|
+
* First public release by Yehuda Katz
|
data/README.md
CHANGED
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
[](http://travis-ci.org/minad/moneta) [](https://gemnasium.com/minad/moneta) [](https://codeclimate.com/github/minad/moneta)
|
|
4
4
|
|
|
5
|
-
Moneta provides a standard interface for interacting with various kinds of key/value stores.
|
|
5
|
+
Moneta provides a standard interface for interacting with various kinds of key/value stores. A short overview of the features:
|
|
6
6
|
|
|
7
7
|
* Supports a lot of backends (See below)
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* Allows a full configuration of the serialization -> compression -> adapter stack using proxies (Similar to [Rack middlewares](http://rack.github.com/))
|
|
9
|
+
* Configurable serialization via `Moneta::Transformer` proxy (Marshal/JSON/YAML and many more)
|
|
10
|
+
* Configurable value compression via `Moneta::Transformer` proxy (Zlib, Snappy, LZMA, ...)
|
|
11
|
+
* Configurable key transformation via `Moneta::Transformer` proxy
|
|
12
12
|
* Expiration for all stores (Added via proxy `Moneta::Expires` if not supported natively)
|
|
13
|
-
* Atomic incrementation and decrementation for most stores (Method `#increment`)
|
|
13
|
+
* Atomic incrementation and decrementation for most stores (Method `#increment` and `#decrement`)
|
|
14
14
|
* Includes a very simple key/value server (`Moneta::Server`) and client (`Moneta::Adapters::Client`)
|
|
15
15
|
* Integration with [Rails](http://rubyonrails.org/), [Rack](http://rack.github.com/) as cookie and session store and [Rack-Cache](https://github.com/rtomayko/rack-cache)
|
|
16
16
|
|
|
@@ -110,6 +110,10 @@ Special transformers:
|
|
|
110
110
|
|
|
111
111
|
## Moneta API
|
|
112
112
|
|
|
113
|
+
The Moneta API is purposely extremely similar to the Hash API with a few minor additions.
|
|
114
|
+
There are the additional methods `#load`, `#increment`, `#decrement` and `#close`. Every method takes also a optional
|
|
115
|
+
option hash. In order so support an identical API across stores, Moneta does not support iteration or partial matches.
|
|
116
|
+
|
|
113
117
|
~~~
|
|
114
118
|
#initialize(options) options differs per-store, and is used to set up the store.
|
|
115
119
|
|
|
@@ -134,14 +138,15 @@ Special transformers:
|
|
|
134
138
|
#increment(key, amount = 1, options = {}) increment numeric value. This is a atomic operation
|
|
135
139
|
which is not supported by all stores. Returns current value.
|
|
136
140
|
|
|
141
|
+
#decrement(key, amount = 1, options = {}) increment numeric value. This is a atomic operation
|
|
142
|
+
which is not supported by all stores. Returns current value.
|
|
143
|
+
This is just syntactic sugar for incrementing with a negative value.
|
|
144
|
+
|
|
137
145
|
#clear(options = {}) clear all keys in this store.
|
|
138
146
|
|
|
139
147
|
#close close database connection.
|
|
140
148
|
~~~
|
|
141
149
|
|
|
142
|
-
The Moneta API is purposely extremely similar to the Hash API. In order so support an
|
|
143
|
-
identical API across stores, it does not support iteration or partial matches.
|
|
144
|
-
|
|
145
150
|
### Creating a Store
|
|
146
151
|
|
|
147
152
|
There is a simple interface to create a store using `Moneta.new`:
|
|
@@ -205,35 +210,36 @@ The stores support the `#increment` which allows atomic increments of unsigned i
|
|
|
205
210
|
a non existing value, it will be created. If you increment a non integer value an exception will be raised.
|
|
206
211
|
|
|
207
212
|
~~~ ruby
|
|
208
|
-
store.increment('counter')
|
|
209
|
-
store.increment('counter')
|
|
210
|
-
store.increment('counter', -1)
|
|
211
|
-
store.increment('counter', 13)
|
|
212
|
-
store.increment('counter', 0)
|
|
213
|
+
store.increment('counter') # returns 1, counter created
|
|
214
|
+
store.increment('counter') # returns 2
|
|
215
|
+
store.increment('counter', -1) # returns 1
|
|
216
|
+
store.increment('counter', 13) # returns 14
|
|
217
|
+
store.increment('counter', 0) # returns 14
|
|
218
|
+
store.decrement('counter') # returns 13
|
|
213
219
|
store['name'] = 'Moneta'
|
|
214
|
-
store.increment('name')
|
|
220
|
+
store.increment('name') # raises an Exception
|
|
215
221
|
~~~
|
|
216
222
|
|
|
217
223
|
If you want to access the counter value you have to use raw access to the datastore. This is only important
|
|
218
|
-
if you have a `Moneta::Transformer` somewhere in your proxy
|
|
224
|
+
if you have a `Moneta::Transformer` somewhere in your proxy stack which transforms the values e.g. with `Marshal`.
|
|
219
225
|
|
|
220
226
|
~~~ ruby
|
|
221
|
-
store.increment('counter')
|
|
222
|
-
store.load('counter', :raw => true)
|
|
227
|
+
store.increment('counter') # returns 1, counter created
|
|
228
|
+
store.load('counter', :raw => true) # returns 1
|
|
223
229
|
|
|
224
230
|
store.store('counter', '10', :raw => true)
|
|
225
|
-
store.increment('counter')
|
|
231
|
+
store.increment('counter') # returns 11
|
|
226
232
|
~~~
|
|
227
233
|
|
|
228
234
|
Fortunately there is a nicer way to do this using some syntactic sugar!
|
|
229
235
|
|
|
230
236
|
~~~ ruby
|
|
231
|
-
store.increment('counter')
|
|
232
|
-
store.raw['counter']
|
|
233
|
-
store.raw.load('counter')
|
|
237
|
+
store.increment('counter') # returns 1, counter created
|
|
238
|
+
store.raw['counter'] # returns 1
|
|
239
|
+
store.raw.load('counter') # returns 1
|
|
234
240
|
|
|
235
241
|
store.raw['counter'] = '10'
|
|
236
|
-
store.increment('counter')
|
|
242
|
+
store.increment('counter') # returns 11
|
|
237
243
|
~~~
|
|
238
244
|
|
|
239
245
|
You can also keep the `raw` store in a variable and use it like this:
|
|
@@ -241,12 +247,12 @@ You can also keep the `raw` store in a variable and use it like this:
|
|
|
241
247
|
~~~ ruby
|
|
242
248
|
counters = store.raw
|
|
243
249
|
|
|
244
|
-
counters.increment('counter')
|
|
245
|
-
counters['counter']
|
|
246
|
-
counters.load('counter')
|
|
250
|
+
counters.increment('counter') # returns 1, counter created
|
|
251
|
+
counters['counter'] # returns 1
|
|
252
|
+
counters.load('counter') # returns 1
|
|
247
253
|
|
|
248
254
|
counters['counter'] = '10'
|
|
249
|
-
counters.increment('counter')
|
|
255
|
+
counters.increment('counter') # returns 11
|
|
250
256
|
~~~
|
|
251
257
|
|
|
252
258
|
Stores which support incrementation (you have to use `Moneta::Lock` if you want to use the store in a multithreading environment.)
|
|
@@ -280,7 +286,7 @@ Stores which don't support incrementation:
|
|
|
280
286
|
For raw data access as described before the class `Moneta::OptionMerger` is used. It works like this:
|
|
281
287
|
|
|
282
288
|
~~~ ruby
|
|
283
|
-
# All methods after
|
|
289
|
+
# All methods after 'with' get the options passed
|
|
284
290
|
store.with(:raw => true).load('key')
|
|
285
291
|
|
|
286
292
|
# You can also specify the methods
|
|
@@ -293,8 +299,8 @@ store.raw.load('key')
|
|
|
293
299
|
# Access substore where all keys get a prefix
|
|
294
300
|
substore = store.prefix('sub')
|
|
295
301
|
substore['key'] = 'value'
|
|
296
|
-
store['key']
|
|
297
|
-
store['subkey']
|
|
302
|
+
store['key'] # returns nil
|
|
303
|
+
store['subkey'] # returns 'value'
|
|
298
304
|
|
|
299
305
|
# Set expiration time for all keys
|
|
300
306
|
short_lived_store = long_lived_store.expires(60)
|
|
@@ -408,7 +414,9 @@ config.cache_store :moneta_store, :store => Moneta.build do
|
|
|
408
414
|
end
|
|
409
415
|
~~~
|
|
410
416
|
|
|
411
|
-
## Advanced
|
|
417
|
+
## Advanced
|
|
418
|
+
|
|
419
|
+
### Build your own key value server
|
|
412
420
|
|
|
413
421
|
You can use Moneta to build your own key/value server which is shared between
|
|
414
422
|
multiple processes. If you run the following code in two different processes,
|
|
@@ -432,6 +440,36 @@ store = Moneta.build do
|
|
|
432
440
|
end
|
|
433
441
|
~~~
|
|
434
442
|
|
|
443
|
+
If you want to go further, you might want to take a look at `Moneta::Server` and `Moneta::Adapters::Client` which
|
|
444
|
+
are used by `Moneta::Shared` and provide the networking communication. But be aware that they are experimental
|
|
445
|
+
and subjected to change. They provide an acceptable performance (for being ruby only), but don't have a stable protocol yet.
|
|
446
|
+
|
|
447
|
+
You might wonder why I didn't use [DRb](http://www.ruby-doc.org/stdlib-1.9.3/libdoc/drb/rdoc/DRb.html) to implement server and client -
|
|
448
|
+
in fact my first versions used it, but with much worse performance and it was real fun to implement the networking directly :)
|
|
449
|
+
There is still much room for improvement and experiments, try [EventMachine](http://eventmachine.rubyforge.org/),
|
|
450
|
+
try [Kgio](http://bogomips.org/kgio/), ...
|
|
451
|
+
|
|
452
|
+
### ToyStore ORM
|
|
453
|
+
|
|
454
|
+
If you want something more advanced to handle your objects and relations,
|
|
455
|
+
use John Nunemaker's [ToyStore](https://github.com/jnunemaker/toystore) which works
|
|
456
|
+
together with Moneta. Assuming that `Person` is a `ToyStore::Object` you can
|
|
457
|
+
add persistence using Moneta as follows:
|
|
458
|
+
|
|
459
|
+
~~~ ruby
|
|
460
|
+
# Use the Moneta Redis backend
|
|
461
|
+
Person.adapter :memory, Moneta.new(:Redis)
|
|
462
|
+
~~~
|
|
463
|
+
|
|
464
|
+
## Testing and Benchmarks
|
|
465
|
+
|
|
466
|
+
Testing is done using [Travis-CI](http://travis-ci.org/minad/moneta). Currently we support Ruby 1.8.7 and 1.9.3.
|
|
467
|
+
|
|
468
|
+
Benchmarks for each store are done on [Travis-CI](http://travis-ci.org/minad/moneta) for each build. Take a look there
|
|
469
|
+
to compare the speed of the different key value stores for different key/value sizes and size distributions.
|
|
470
|
+
Feel free to add your own configurations! The impact of Moneta should be minimal since it is only a thin layer
|
|
471
|
+
on top of the different stores.
|
|
472
|
+
|
|
435
473
|
## More information
|
|
436
474
|
|
|
437
475
|
* http://yehudakatz.com/2009/02/12/whats-the-point/
|
data/Rakefile
CHANGED
data/SPEC.md
CHANGED
|
@@ -51,6 +51,12 @@ Behaves the same as <code>[]=</code>, but allows the client to send additional o
|
|
|
51
51
|
### <code>increment(key[Object], amount[Integer] = 1, options[Hash] => {}) => Integer(value)</code>
|
|
52
52
|
|
|
53
53
|
Increments a value atomically. This method is not supported by all stores and might raise a <code>NotImplementedError</code>.
|
|
54
|
+
This method MUST accept negative values, but the result MUST be unsigned.
|
|
55
|
+
|
|
56
|
+
### <code>decrement(key[Object], amount[Integer] = 1, options[Hash] => {}) => Integer(value)</code>
|
|
57
|
+
|
|
58
|
+
Decrements a value atomically. This method is not supported by all stores and might raise a <code>NotImplementedError</code>.
|
|
59
|
+
This method MUST accept negative values, but the result MUST be unsigned.
|
|
54
60
|
|
|
55
61
|
### <code>clear(options[Hash] => {})</code>
|
|
56
62
|
|
data/benchmarks/run.rb
CHANGED
|
@@ -4,35 +4,8 @@ $: << File.join(File.dirname(__FILE__), '..', 'lib')
|
|
|
4
4
|
require 'benchmark'
|
|
5
5
|
require 'moneta'
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
DataMapper.setup(:default, :adapter => :in_memory)
|
|
10
|
-
rescue LoadError => ex
|
|
11
|
-
puts "Failed to load DataMapper - #{ex.message}"
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
begin
|
|
15
|
-
server = Moneta::Server.new(Moneta.new(:Memory))
|
|
16
|
-
rescue
|
|
17
|
-
puts "Failed to start Moneta server - #{ex.message}"
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
class Array
|
|
21
|
-
def random_index
|
|
22
|
-
rand(size)
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def random_value
|
|
26
|
-
self[random_index]
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def random_subset(n)
|
|
30
|
-
(1..n).map{|x| random_value }
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
stores = {
|
|
35
|
-
:ActiveRecord => { :connection => { :adapter => 'sqlite3', :database => 'bench.activerecord' } },
|
|
7
|
+
STORES = {
|
|
8
|
+
:ActiveRecord => { :connection => { :adapter => 'sqlite3', :database => ':memory:' } },
|
|
36
9
|
:Cassandra => {},
|
|
37
10
|
:Client => {},
|
|
38
11
|
:Couch => {},
|
|
@@ -41,7 +14,6 @@ stores = {
|
|
|
41
14
|
:File => { :dir => 'bench.file' },
|
|
42
15
|
:GDBM => { :file => 'bench.gdbm' },
|
|
43
16
|
:HBase => {},
|
|
44
|
-
:HBase => {},
|
|
45
17
|
:HashFile => { :dir => 'bench.hashfile' },
|
|
46
18
|
:LRUHash => {},
|
|
47
19
|
:LevelDB => { :dir => 'bench.leveldb' },
|
|
@@ -53,143 +25,273 @@ stores = {
|
|
|
53
25
|
:PStore => { :file => 'bench.pstore' },
|
|
54
26
|
:Redis => {},
|
|
55
27
|
:Riak => {},
|
|
56
|
-
|
|
28
|
+
# SDBM is unstable
|
|
29
|
+
# :SDBM => { :file => 'bench.sdbm' },
|
|
57
30
|
:Sequel => { :db => 'sqlite:/' },
|
|
58
|
-
:Sqlite => { :file => '
|
|
59
|
-
|
|
31
|
+
:Sqlite => { :file => ':memory:' },
|
|
32
|
+
# YAML is so fucking slow
|
|
33
|
+
# :YAML => { :file => 'bench.yaml' },
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
CONFIGS = {
|
|
37
|
+
:uniform_small => {
|
|
38
|
+
:runs => 3,
|
|
39
|
+
:keys => 1000,
|
|
40
|
+
:min_key_length => 1,
|
|
41
|
+
:max_key_length => 32,
|
|
42
|
+
:key_dist => :uniform,
|
|
43
|
+
:min_val_length => 0,
|
|
44
|
+
:max_val_length => 256,
|
|
45
|
+
:val_dist => :uniform
|
|
46
|
+
},
|
|
47
|
+
:uniform_medium => {
|
|
48
|
+
:runs => 3,
|
|
49
|
+
:keys => 100,
|
|
50
|
+
:min_key_length => 3,
|
|
51
|
+
:max_key_length => 200,
|
|
52
|
+
:key_dist => :uniform,
|
|
53
|
+
:min_val_length => 0,
|
|
54
|
+
:max_val_length => 1024,
|
|
55
|
+
:val_dist => :uniform
|
|
56
|
+
},
|
|
57
|
+
:uniform_large => {
|
|
58
|
+
:runs => 3,
|
|
59
|
+
:keys => 100,
|
|
60
|
+
:min_key_length => 3,
|
|
61
|
+
:max_key_length => 200,
|
|
62
|
+
:key_dist => :uniform,
|
|
63
|
+
:min_val_length => 0,
|
|
64
|
+
:max_val_length => 10240,
|
|
65
|
+
:val_dist => :uniform
|
|
66
|
+
},
|
|
67
|
+
:normal_small => {
|
|
68
|
+
:runs => 3,
|
|
69
|
+
:keys => 1000,
|
|
70
|
+
:min_key_length => 1,
|
|
71
|
+
:max_key_length => 32,
|
|
72
|
+
:key_dist => :normal,
|
|
73
|
+
:min_val_length => 0,
|
|
74
|
+
:max_val_length => 256,
|
|
75
|
+
:val_dist => :normal
|
|
76
|
+
},
|
|
77
|
+
:normal_medium => {
|
|
78
|
+
:runs => 3,
|
|
79
|
+
:keys => 100,
|
|
80
|
+
:min_key_length => 3,
|
|
81
|
+
:max_key_length => 200,
|
|
82
|
+
:key_dist => :normal,
|
|
83
|
+
:min_val_length => 0,
|
|
84
|
+
:max_val_length => 1024,
|
|
85
|
+
:val_dist => :normal
|
|
86
|
+
},
|
|
87
|
+
:normal_large => {
|
|
88
|
+
:runs => 3,
|
|
89
|
+
:keys => 100,
|
|
90
|
+
:min_key_length => 3,
|
|
91
|
+
:max_key_length => 200,
|
|
92
|
+
:key_dist => :normal,
|
|
93
|
+
:min_val_length => 0,
|
|
94
|
+
:max_val_length => 10240,
|
|
95
|
+
:val_dist => :normal
|
|
96
|
+
},
|
|
60
97
|
}
|
|
61
98
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
99
|
+
config_name = ARGV.size == 1 ? ARGV.first.to_sym : :uniform_medium
|
|
100
|
+
unless config = CONFIGS[config_name]
|
|
101
|
+
puts "Configuration #{config_name} not found"
|
|
102
|
+
exit
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
DICT = 'ABCDEFGHIJKLNOPQRSTUVWXYZabcdefghijklnopqrstuvwxyz123456789'.freeze
|
|
106
|
+
|
|
107
|
+
class String
|
|
108
|
+
def random(n)
|
|
109
|
+
(1..n).map { self[rand(size),1] }.join
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
class Array
|
|
114
|
+
def sum
|
|
115
|
+
inject(0, &:+)
|
|
116
|
+
end
|
|
66
117
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
118
|
+
def randomize
|
|
119
|
+
rest, result = dup, []
|
|
120
|
+
result << rest.slice!(rand(rest.size)) until result.size == size
|
|
121
|
+
result
|
|
122
|
+
end
|
|
123
|
+
end
|
|
73
124
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
125
|
+
Process.fork do
|
|
126
|
+
begin
|
|
127
|
+
Moneta::Server.new(Moneta.new(:Memory)).run
|
|
128
|
+
rescue Exception => ex
|
|
129
|
+
puts "\e[31mFailed to start Moneta server - #{ex.message}\e[0m"
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
sleep 1 # Wait for server
|
|
77
133
|
|
|
78
|
-
|
|
134
|
+
STORES.each do |name, options|
|
|
79
135
|
begin
|
|
136
|
+
if name == :DataMapper
|
|
137
|
+
begin
|
|
138
|
+
require 'dm-core'
|
|
139
|
+
DataMapper.setup(:default, :adapter => :in_memory)
|
|
140
|
+
rescue LoadError => ex
|
|
141
|
+
puts "\e[31mFailed to load DataMapper - #{ex.message}\e[0m"
|
|
142
|
+
end
|
|
143
|
+
elsif name == :Riak
|
|
144
|
+
require 'riak'
|
|
145
|
+
Riak.disable_list_keys_warnings = true
|
|
146
|
+
end
|
|
147
|
+
|
|
80
148
|
cache = Moneta.new(name, options.dup)
|
|
81
149
|
cache['test'] = 'test'
|
|
82
150
|
rescue Exception => ex
|
|
83
|
-
puts "#{name} not benchmarked - #{ex.message}"
|
|
84
|
-
|
|
151
|
+
puts "\e[31m#{name} not benchmarked - #{ex.message}\e[0m"
|
|
152
|
+
STORES.delete(name)
|
|
85
153
|
ensure
|
|
86
154
|
cache.close if cache
|
|
87
155
|
end
|
|
88
156
|
end
|
|
89
157
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
klen = rand(MAX_KEY_SIZE - MIN_KEY_SIZE) + MIN_KEY_SIZE
|
|
93
|
-
vlen = rand(MAX_VALUE_SIZE - MIN_VALUE_SIZE) + MIN_VALUE_SIZE
|
|
158
|
+
HEADER = "\n Minimum Maximum Total Average Ops/s"
|
|
159
|
+
SEPARATOR = '=' * 68
|
|
94
160
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
161
|
+
puts "\e[1m\e[36m#{SEPARATOR}\n\e[36mConfig #{config_name}\n\e[36m#{SEPARATOR}\e[0m"
|
|
162
|
+
config.each do |k,v|
|
|
163
|
+
puts '%-16s = %-10s' % [k,v]
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
module Rand
|
|
167
|
+
extend self
|
|
168
|
+
|
|
169
|
+
def normal_rand(mean, stddev)
|
|
170
|
+
# Box-Muller transform
|
|
171
|
+
theta = 2 * Math::PI * (rand(1e10) / 1e10)
|
|
172
|
+
scale = stddev * Math.sqrt(-2 * Math.log(1 - (rand(1e10) / 1e10)))
|
|
173
|
+
[mean + scale * Math.cos(theta),
|
|
174
|
+
mean + scale * Math.sin(theta)]
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def uniform(min, max)
|
|
178
|
+
rand(max - min) + min
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def normal(min, max)
|
|
182
|
+
mean = (min + max) / 2
|
|
183
|
+
stddev = (max - min) / 4
|
|
184
|
+
loop do
|
|
185
|
+
val = normal_rand(mean, stddev)
|
|
186
|
+
return val.first if val.first >= min && val.first <= max
|
|
187
|
+
return val.last if val.last >= min && val.last <= max
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
stats, data, summary = {}, {}, []
|
|
193
|
+
|
|
194
|
+
until data.size == config[:keys]
|
|
195
|
+
key = DICT.random(Rand.send(config[:key_dist], config[:min_key_length], config[:max_key_length]))
|
|
196
|
+
data[key] = DICT.random(Rand.send(config[:val_dist], config[:min_val_length], config[:max_val_length]))
|
|
197
|
+
end
|
|
99
198
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
vlen_total = vlen_total + value.size
|
|
199
|
+
key_lengths, val_lengths = data.keys.map(&:size), data.values.map(&:size)
|
|
200
|
+
data = data.to_a
|
|
103
201
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
202
|
+
def write_histogram(file, sizes)
|
|
203
|
+
min = sizes.min
|
|
204
|
+
delta = sizes.max - min
|
|
205
|
+
histogram = []
|
|
206
|
+
sizes.each do |s|
|
|
207
|
+
s = 10 * (s - min) / delta
|
|
208
|
+
histogram[s] ||= 0
|
|
209
|
+
histogram[s] += 1
|
|
210
|
+
end
|
|
211
|
+
File.open(file, 'w') do |f|
|
|
212
|
+
histogram.each_with_index { |n,i| f.puts "#{i*delta/10+min} #{n}" }
|
|
213
|
+
end
|
|
107
214
|
end
|
|
108
|
-
vlen_average = vlen_total / KEYS
|
|
109
215
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
puts
|
|
114
|
-
puts
|
|
115
|
-
puts 'Key Length
|
|
116
|
-
puts 'Value Length
|
|
216
|
+
write_histogram('key.histogram', key_lengths)
|
|
217
|
+
write_histogram('value.histogram', val_lengths)
|
|
218
|
+
|
|
219
|
+
puts "\n\e[1m\e[34m#{SEPARATOR}\n\e[34mComputing keys and values...\n\e[34m#{SEPARATOR}\e[0m"
|
|
220
|
+
puts %{ Minimum Maximum Total Average}
|
|
221
|
+
puts 'Key Length % 8d % 8d % 8d % 8d ' % [key_lengths.min, key_lengths.max, key_lengths.sum, key_lengths.sum / data.size]
|
|
222
|
+
puts 'Value Length % 8d % 8d % 8d % 8d ' % [val_lengths.min, val_lengths.max, val_lengths.sum, val_lengths.sum / data.size]
|
|
117
223
|
|
|
118
|
-
|
|
224
|
+
STORES.each do |name, options|
|
|
119
225
|
begin
|
|
120
|
-
puts
|
|
121
|
-
|
|
122
|
-
puts '----------------------------------------------------------------------'
|
|
226
|
+
puts "\n\e[1m\e[34m#{SEPARATOR}\n\e[34m#{name}\n\e[34m#{SEPARATOR}\e[0m"
|
|
227
|
+
|
|
123
228
|
cache = Moneta.new(name, options.dup)
|
|
124
229
|
|
|
125
230
|
stats[name] = {
|
|
126
|
-
:
|
|
127
|
-
:
|
|
128
|
-
:
|
|
129
|
-
:
|
|
231
|
+
:write => [],
|
|
232
|
+
:read => [],
|
|
233
|
+
:sum => [],
|
|
234
|
+
:error => []
|
|
130
235
|
}
|
|
131
236
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
print "[
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
237
|
+
%w(Rehearse Measure).each do |type|
|
|
238
|
+
state = ''
|
|
239
|
+
print "%s [%#{2 * config[:runs]}s] " % [type, state]
|
|
240
|
+
|
|
241
|
+
config[:runs].times do |run|
|
|
242
|
+
cache.clear
|
|
243
|
+
print "%s[%-#{2 * config[:runs]}s] " % ["\b" * (2 * config[:runs] + 3), state << 'W']
|
|
244
|
+
|
|
245
|
+
data = data.randomize
|
|
246
|
+
m1 = Benchmark.measure do
|
|
247
|
+
data.each {|k,v| cache[k] = v }
|
|
139
248
|
end
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
249
|
+
|
|
250
|
+
print "%s[%-#{2 * config[:runs]}s] " % ["\b" * (2 * config[:runs] + 3), state << 'R']
|
|
251
|
+
|
|
252
|
+
data = data.randomize
|
|
253
|
+
error = 0
|
|
254
|
+
m2 = Benchmark.measure do
|
|
255
|
+
data.each do |k, v|
|
|
256
|
+
error += 1 if v != cache[k]
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
if type == 'Measure'
|
|
261
|
+
stats[name][:write] << m1.real
|
|
262
|
+
stats[name][:error] << error
|
|
263
|
+
stats[name][:read] << m2.real
|
|
264
|
+
stats[name][:sum] << (m1.real + m2.real)
|
|
148
265
|
end
|
|
149
266
|
end
|
|
150
|
-
stats[name][:reads] << m2.real
|
|
151
|
-
stats[name][:totals] << (m1.real + m2.real)
|
|
152
|
-
stats[name][:averages] << (m1.real + m2.real)
|
|
153
267
|
end
|
|
154
|
-
|
|
155
|
-
puts
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
cmax = val if val > cmax
|
|
165
|
-
tcmax = val if val > tcmax
|
|
166
|
-
ctot = ctot + val
|
|
167
|
-
tctot = tctot + val
|
|
168
|
-
end
|
|
169
|
-
cavg = ctot / RUNS
|
|
170
|
-
puts '%-14.14s % 10.4f % 10.4f % 10.4f % 10.4f % 10.4f ' % ["#{name} #{sname}", cmin, cmax, ctot, cavg, KEYS / cavg]
|
|
268
|
+
|
|
269
|
+
puts HEADER
|
|
270
|
+
[:write, :read, :sum].each do |i|
|
|
271
|
+
total = stats[name][i].sum
|
|
272
|
+
ops = (config[:runs] * data.size) / total
|
|
273
|
+
line = '%-17.17s %-5s % 8d % 8d % 8d % 8d % 8d' %
|
|
274
|
+
[name, i, stats[name][i].min * 1000, stats[name][i].max * 1000,
|
|
275
|
+
total * 1000, total * 1000 / config[:runs], ops]
|
|
276
|
+
summary << [-ops, line << "\n"] if i == :sum
|
|
277
|
+
puts line
|
|
171
278
|
end
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
279
|
+
|
|
280
|
+
errors = stats[name][:error].sum
|
|
281
|
+
if errors > 0
|
|
282
|
+
puts "\e[31m%-23.23s % 8d % 8d % 8d % 8d\e[0m" %
|
|
283
|
+
['Read errors', stats[name][:error].min, stats[name][:error].max, errors, errors / config[:runs]]
|
|
284
|
+
else
|
|
285
|
+
puts "\e[32mNo read errors"
|
|
286
|
+
end
|
|
287
|
+
rescue StandardError => ex
|
|
288
|
+
puts "\n\e[31mFailed to benchmark #{name} - #{ex.message}\e[0m\n"
|
|
177
289
|
ensure
|
|
178
290
|
cache.close if cache
|
|
179
291
|
end
|
|
180
292
|
end
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
else
|
|
186
|
-
puts 'No errors in reading!'
|
|
187
|
-
end
|
|
188
|
-
puts '======================================================================'
|
|
189
|
-
puts "Summary: #{RUNS} runs, #{KEYS} keys"
|
|
190
|
-
puts '======================================================================'
|
|
191
|
-
puts ' Minimum Maximum Total Average xps '
|
|
192
|
-
puts '----------------------------------------------------------------------'
|
|
193
|
-
summary.each do |sry|
|
|
194
|
-
puts '%-14.14s % 10.4f % 10.4f % 10.4f % 10.4f % 10.4f ' % sry
|
|
293
|
+
|
|
294
|
+
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"
|
|
295
|
+
summary.sort_by(&:first).each do |entry|
|
|
296
|
+
puts entry.last
|
|
195
297
|
end
|