juno 0.1.0

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 (67) hide show
  1. data/.gitignore +4 -0
  2. data/.travis.yml +25 -0
  3. data/Gemfile +19 -0
  4. data/LICENSE +20 -0
  5. data/README.md +83 -0
  6. data/Rakefile +15 -0
  7. data/SPEC.md +95 -0
  8. data/juno.gemspec +22 -0
  9. data/lib/juno.rb +31 -0
  10. data/lib/juno/activerecord.rb +58 -0
  11. data/lib/juno/base.rb +113 -0
  12. data/lib/juno/couch.rb +43 -0
  13. data/lib/juno/datamapper.rb +63 -0
  14. data/lib/juno/dbm.rb +15 -0
  15. data/lib/juno/expires.rb +45 -0
  16. data/lib/juno/file.rb +62 -0
  17. data/lib/juno/gdbm.rb +15 -0
  18. data/lib/juno/hashfile.rb +12 -0
  19. data/lib/juno/localmemcache.rb +16 -0
  20. data/lib/juno/memcached.rb +7 -0
  21. data/lib/juno/memcached_dalli.rb +55 -0
  22. data/lib/juno/memcached_native.rb +56 -0
  23. data/lib/juno/memory.rb +7 -0
  24. data/lib/juno/mongodb.rb +43 -0
  25. data/lib/juno/proxy.rb +5 -0
  26. data/lib/juno/pstore.rb +49 -0
  27. data/lib/juno/redis.rb +34 -0
  28. data/lib/juno/riak.rb +45 -0
  29. data/lib/juno/sdbm.rb +15 -0
  30. data/lib/juno/sequel.rb +51 -0
  31. data/lib/juno/sqlite.rb +50 -0
  32. data/lib/juno/tokyocabinet.rb +36 -0
  33. data/lib/juno/version.rb +3 -0
  34. data/lib/juno/yaml.rb +9 -0
  35. data/test/helper.rb +182 -0
  36. data/test/test_active_record.rb +36 -0
  37. data/test/test_couch.rb +13 -0
  38. data/test/test_datamapper.rb +64 -0
  39. data/test/test_dbm.rb +13 -0
  40. data/test/test_expires.rb +10 -0
  41. data/test/test_file.rb +9 -0
  42. data/test/test_gdbm.rb +13 -0
  43. data/test/test_hashfile.rb +9 -0
  44. data/test/test_localmemcache.rb +13 -0
  45. data/test/test_memcached.rb +15 -0
  46. data/test/test_memcached_dalli.rb +15 -0
  47. data/test/test_memcached_native.rb +15 -0
  48. data/test/test_memory.rb +9 -0
  49. data/test/test_mongodb.rb +13 -0
  50. data/test/test_proxy.rb +9 -0
  51. data/test/test_pstore.rb +9 -0
  52. data/test/test_redis.rb +13 -0
  53. data/test/test_riak.rb +13 -0
  54. data/test/test_sdbm.rb +13 -0
  55. data/test/test_sequel.rb +13 -0
  56. data/test/test_sqlite.rb +13 -0
  57. data/test/test_tokyocabinet.rb +13 -0
  58. data/test/test_yaml.rb +9 -0
  59. data/unsupported/benchmarks.rb +234 -0
  60. data/unsupported/cassandra.rb +45 -0
  61. data/unsupported/fog.rb +60 -0
  62. data/unsupported/test_cassandra.rb +13 -0
  63. data/unsupported/test_rackspace.rb +15 -0
  64. data/unsupported/test_s3.rb +15 -0
  65. data/unsupported/test_tokyotyrant.rb +13 -0
  66. data/unsupported/tokyotyrant.rb +29 -0
  67. metadata +165 -0
@@ -0,0 +1,36 @@
1
+ require 'helper'
2
+
3
+ begin
4
+ describe Juno::ActiveRecord do
5
+ describe 'with connection option set' do
6
+ def new_store
7
+ store = Juno::ActiveRecord.new(:connection => { :adapter => 'sqlite3', :database => File.join(make_tempdir, 'db.sqlite3')})
8
+ store.migrate
9
+ store
10
+ end
11
+
12
+ class_eval(&JunoSpecification)
13
+
14
+ it 'updates an existing key/value' do
15
+ @store['foo/bar'] = 4
16
+ @store['foo/bar'] += 4
17
+ records = @store.table.find :all, :conditions => { :key => 'foo/bar' }
18
+ records.count.must_equal 1
19
+ end
20
+ end
21
+
22
+ describe 'using preexisting ActiveRecord connection' do
23
+ include Helper
24
+
25
+ it 'uses an existing connection' do
26
+ ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => File.join(make_tempdir, 'db.sqlite3')
27
+
28
+ store = Juno::ActiveRecord.new
29
+ store.migrate
30
+ store.table.table_exists?.must_equal true
31
+ end
32
+ end
33
+ end
34
+ rescue LoadError => ex
35
+ puts "Juno::ActiveRecord not tested: #{ex.message}"
36
+ end
@@ -0,0 +1,13 @@
1
+ require 'helper'
2
+
3
+ begin
4
+ describe Juno::Couch do
5
+ def new_store
6
+ Juno::Couch.new :db => 'juno'
7
+ end
8
+
9
+ class_eval(&JunoSpecification)
10
+ end
11
+ rescue LoadError => ex
12
+ puts "Juno::Couch not tested: #{ex.message}"
13
+ end
@@ -0,0 +1,64 @@
1
+ require 'helper'
2
+
3
+ begin
4
+ describe Juno::DataMapper do
5
+ before do
6
+ DataMapper.setup(:default, :adapter => :in_memory)
7
+ end
8
+
9
+ describe 'with the default repository' do
10
+ def new_store
11
+ Juno::DataMapper.new(:setup => "sqlite3://#{make_tempdir}/datamapper.sqlite3")
12
+ end
13
+
14
+ after do
15
+ Juno::DataMapper::Store.auto_migrate!(:juno)
16
+ end
17
+
18
+ class_eval(&JunoSpecification)
19
+ end
20
+
21
+ describe 'when :repository specified' do
22
+ def new_store
23
+ Juno::DataMapper.new(:repository => :sample, :setup => "sqlite3://#{make_tempdir}/datamapper.sqlite3")
24
+ end
25
+
26
+ after do
27
+ Juno::DataMapper::Store.auto_migrate!(:sample)
28
+ end
29
+
30
+ class_eval(&JunoSpecification)
31
+ end
32
+
33
+ describe 'with multiple stores' do
34
+ include Helper
35
+
36
+ before do
37
+ @first_store = Juno::DataMapper.new(:setup => "sqlite3://#{make_tempdir}/first.sqlite3")
38
+ @first_store.clear
39
+
40
+ @second_store = Juno::DataMapper.new(:repository => :sample, :setup => "sqlite3://#{make_tempdir}/second.sqlite3")
41
+ @second_store.clear
42
+ end
43
+
44
+ it 'does not cross contaminate when storing' do
45
+ @first_store['key'] = 'value'
46
+ @second_store['key'] = 'value2'
47
+
48
+ @first_store['key'].must_equal 'value'
49
+ @second_store['key'].must_equal 'value2'
50
+ end
51
+
52
+ it 'does not cross contaminate when deleting' do
53
+ @first_store['key'] = 'value'
54
+ @second_store['key'] = 'value2'
55
+
56
+ @first_store.delete('key').must_equal 'value'
57
+ @first_store.key?('key').must_equal false
58
+ @second_store['key'].must_equal 'value2'
59
+ end
60
+ end
61
+ end
62
+ rescue LoadError => ex
63
+ puts "Juno::Datamapper not tested: #{ex.message}"
64
+ end
@@ -0,0 +1,13 @@
1
+ require 'helper'
2
+
3
+ begin
4
+ describe Juno::DBM do
5
+ def new_store
6
+ Juno::DBM.new(:file => File.join(make_tempdir, 'dbm.db'))
7
+ end
8
+
9
+ class_eval(&JunoSpecification)
10
+ end
11
+ rescue LoadError => ex
12
+ puts "Juno::DBM not tested: #{ex.message}"
13
+ end
@@ -0,0 +1,10 @@
1
+ require 'helper'
2
+
3
+ describe Juno::Expires do
4
+ def new_store
5
+ Juno::Expires.new(Juno::Memory.new)
6
+ end
7
+
8
+ class_eval(&JunoSpecification)
9
+ class_eval(&JunoExpiresSpecification)
10
+ end
@@ -0,0 +1,9 @@
1
+ require 'helper'
2
+
3
+ describe Juno::File do
4
+ def new_store
5
+ Juno::File.new(:dir => File.join(make_tempdir, 'file'))
6
+ end
7
+
8
+ class_eval(&JunoSpecification)
9
+ end
@@ -0,0 +1,13 @@
1
+ require 'helper'
2
+
3
+ begin
4
+ describe Juno::GDBM do
5
+ def new_store
6
+ Juno::GDBM.new(:file => File.join(make_tempdir, 'gdbm.db'))
7
+ end
8
+
9
+ class_eval(&JunoSpecification)
10
+ end
11
+ rescue LoadError => ex
12
+ puts "Juno::GDBM not tested: #{ex.message}"
13
+ end
@@ -0,0 +1,9 @@
1
+ require 'helper'
2
+
3
+ describe Juno::HashFile do
4
+ def new_store
5
+ Juno::HashFile.new(:dir => File.join(make_tempdir, 'hashfile'))
6
+ end
7
+
8
+ class_eval(&JunoSpecification)
9
+ end
@@ -0,0 +1,13 @@
1
+ require 'helper'
2
+
3
+ begin
4
+ describe Juno::LocalMemCache do
5
+ def new_store
6
+ Juno::LocalMemCache.new(:file => File.join(make_tempdir, 'lmc'))
7
+ end
8
+
9
+ class_eval(&JunoSpecification)
10
+ end
11
+ rescue LoadError => ex
12
+ puts "Juno::LocalMemCache not tested: #{ex.message}"
13
+ end
@@ -0,0 +1,15 @@
1
+ require 'helper'
2
+
3
+ begin
4
+ describe Juno::Memcached do
5
+ def new_store
6
+ # HACK: memcached is running on 221122 because of travis-ci
7
+ Juno::Memcached.new(:server => 'localhost:22122', :namespace => 'juno')
8
+ end
9
+
10
+ class_eval(&JunoSpecification)
11
+ class_eval(&JunoExpiresSpecification)
12
+ end
13
+ rescue LoadError => ex
14
+ puts "Juno::Memcached not tested: #{ex.message}"
15
+ end
@@ -0,0 +1,15 @@
1
+ require 'helper'
2
+
3
+ begin
4
+ describe Juno::MemcachedDalli do
5
+ def new_store
6
+ # HACK: memcached is running on 221122 because of travis-ci
7
+ Juno::MemcachedDalli.new(:server => 'localhost:22122', :namespace => 'juno')
8
+ end
9
+
10
+ class_eval(&JunoSpecification)
11
+ class_eval(&JunoExpiresSpecification)
12
+ end
13
+ rescue LoadError => ex
14
+ puts "Juno::MemcachedDalli not tested: #{ex.message}"
15
+ end
@@ -0,0 +1,15 @@
1
+ require 'helper'
2
+
3
+ begin
4
+ describe Juno::MemcachedNative do
5
+ def new_store
6
+ # HACK: memcached is running on 221122 because of travis-ci
7
+ Juno::MemcachedNative.new(:server => 'localhost:22122', :namespace => 'juno')
8
+ end
9
+
10
+ class_eval(&JunoSpecification)
11
+ class_eval(&JunoExpiresSpecification)
12
+ end
13
+ rescue LoadError => ex
14
+ puts "Juno::MemcachedNative not tested: #{ex.message}"
15
+ end
@@ -0,0 +1,9 @@
1
+ require 'helper'
2
+
3
+ describe Juno::Memory do
4
+ def new_store
5
+ Juno::Memory.new
6
+ end
7
+
8
+ class_eval(&JunoSpecification)
9
+ end
@@ -0,0 +1,13 @@
1
+ require 'helper'
2
+
3
+ begin
4
+ describe Juno::MongoDB do
5
+ def new_store
6
+ Juno::MongoDB.new
7
+ end
8
+
9
+ class_eval(&JunoSpecification)
10
+ end
11
+ rescue LoadError => ex
12
+ puts "Juno::MongoDB not tested: #{ex.message}"
13
+ end
@@ -0,0 +1,9 @@
1
+ require 'helper'
2
+
3
+ describe Juno::Proxy do
4
+ def new_store
5
+ Juno::Proxy.new(Juno::Proxy.new(Juno::Memory.new))
6
+ end
7
+
8
+ class_eval(&JunoSpecification)
9
+ end
@@ -0,0 +1,9 @@
1
+ require 'helper'
2
+
3
+ describe Juno::PStore do
4
+ def new_store
5
+ Juno::PStore.new(:file => File.join(make_tempdir, 'pstore'))
6
+ end
7
+
8
+ class_eval(&JunoSpecification)
9
+ end
@@ -0,0 +1,13 @@
1
+ require 'helper'
2
+
3
+ begin
4
+ describe Juno::Redis do
5
+ def new_store
6
+ Juno::Redis.new
7
+ end
8
+
9
+ class_eval(&JunoSpecification)
10
+ end
11
+ rescue LoadError => ex
12
+ puts "Juno::Redis not tested: #{ex.message}"
13
+ end
@@ -0,0 +1,13 @@
1
+ require 'helper'
2
+
3
+ begin
4
+ describe Juno::Riak do
5
+ def new_store
6
+ Juno::Riak.new
7
+ end
8
+
9
+ class_eval(&JunoSpecification)
10
+ end
11
+ rescue LoadError => ex
12
+ puts "Juno::Riak not tested: #{ex.message}"
13
+ end
@@ -0,0 +1,13 @@
1
+ require 'helper'
2
+
3
+ begin
4
+ describe Juno::SDBM do
5
+ def new_store
6
+ Juno::GDBM.new(:file => File.join(make_tempdir, 'gdbm.db'))
7
+ end
8
+
9
+ class_eval(&JunoSpecification)
10
+ end
11
+ rescue LoadError => ex
12
+ puts "Juno::SDBM not tested: #{ex.message}"
13
+ end
@@ -0,0 +1,13 @@
1
+ require 'helper'
2
+
3
+ begin
4
+ describe Juno::Sequel do
5
+ def new_store
6
+ store = Juno::Sequel.new(:db => 'sqlite:/')
7
+ store.migrate
8
+ store
9
+ end
10
+
11
+ class_eval(&JunoSpecification)
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require 'helper'
2
+
3
+ begin
4
+ describe Juno::Sqlite do
5
+ def new_store
6
+ Juno::Sqlite.new(:file => ':memory:')
7
+ end
8
+
9
+ class_eval(&JunoSpecification)
10
+ end
11
+ rescue LoadError => ex
12
+ puts "Juno::Sqlite not tested: #{ex.message}"
13
+ end
@@ -0,0 +1,13 @@
1
+ require 'helper'
2
+
3
+ begin
4
+ describe Juno::TokyoCabinet do
5
+ def new_store
6
+ Juno::TokyoCabinet.new(:file => File.join(make_tempdir, 'tokyocabinet.db'))
7
+ end
8
+
9
+ class_eval(&JunoSpecification)
10
+ end
11
+ rescue LoadError => ex
12
+ puts "Juno::TokyoCabinet not tested: #{ex.message}"
13
+ end
@@ -0,0 +1,9 @@
1
+ require 'helper'
2
+
3
+ describe Juno::YAML do
4
+ def new_store
5
+ Juno::YAML.new(:file => File.join(make_tempdir, 'yaml'))
6
+ end
7
+
8
+ class_eval(&JunoSpecification)
9
+ end
@@ -0,0 +1,234 @@
1
+ #!/usr/bin/env ruby
2
+ require 'benchmark'
3
+ require "rubygems"
4
+
5
+ # Hacked arrays
6
+ # Array modifications
7
+ class HackedArray < Array
8
+ # Random keys/values
9
+ attr_reader :keys_used
10
+ def random_key(no_repeat = true, clean_keys_used = false)
11
+ @keys_used = [] if clean_keys_used or @keys_used.nil? or @keys_used.size == self.size
12
+ begin key = rand(self.size) end while no_repeat and @keys_used.include?(key)
13
+ @keys_used << key
14
+ return key
15
+ end
16
+
17
+ def random_value(no_repeat = true, clean_keys_used = false)
18
+ values_at(random_key(no_repeat, clean_keys_used)).first
19
+ end
20
+ alias_method :random, :random_value
21
+
22
+ def random_subset(n, no_repeat = true, clean_keys_used = true)
23
+ (1..n).map{|x| random_value(no_repeat, (clean_keys_used && x == 1))}
24
+ end
25
+
26
+ def self.new_from_string(str)
27
+ res = new
28
+ str.split('').each{|x| res << x}
29
+ res
30
+ end
31
+ end
32
+
33
+ require "../lib/juno"
34
+ require "juno/memcache"
35
+ require "juno/tyrant"
36
+ require "juno/berkeley"
37
+
38
+
39
+ stores = {
40
+ 'Redis' => { },
41
+ 'Memcached' => { :class_name => "Memcache", :server => "localhost:11211", :namespace => 'juno_bench' },
42
+ 'Tyrant' => { :host => 'localhost', :port => 1978 }, # Breaks for any n > 50 on my machine
43
+ 'MongoDB' => { :host => 'localhost', :port => 27017, :db => 'juno_bench' },
44
+ 'LMC' => { :filename => "bench.lmc" },
45
+ 'Berkeley' => { :file => "bench.bdb" },
46
+ 'Rufus' => {:file => "bench.rufus"},
47
+ 'Memory' => { },
48
+ 'DataMapper' => { :setup => "sqlite3::memory:" },
49
+ # 'Couch' => {:db => "couch_test"},
50
+ 'TC (Tyrant)' =>
51
+ {:name => "test.tieredtyrant", :backup => Juno::Tyrant.new(:host => "localhost", :port => 1978), :class_name => "TieredCache"},
52
+ 'TC (Memcached)' =>
53
+ {:name => "test.tieredmc", :backup => Juno::Memcache.new(:server => "localhost:11211", :namespace => "various"), :class_name => "TieredCache"}
54
+ }
55
+
56
+ stats, keys, data, errors, summary = {}, [], HackedArray.new, HackedArray.new, HackedArray.new
57
+ dict = HackedArray.new_from_string 'abcdefghijklnopq123456789'
58
+ n = ARGV[0] ? ARGV[0].to_i : 100
59
+ m = ARGV[1] ? ARGV[1].to_i : 10
60
+ c = ARGV[2] ? ARGV[2].to_i : 3
61
+ vlen_min, vlen_max, vlen_ttl, vlen_avg = 99999, 0, 0, 0
62
+ ds = dict.size
63
+
64
+ puts "======================================================================"
65
+ puts "Comparison of write/read between Juno Stores"
66
+ puts "======================================================================"
67
+
68
+ puts "Data loading..."
69
+ n.times do |x|
70
+ klen = 6 + rand(3)
71
+ vlen = (rand(m) + 1) * (rand(m) + rand(m) + 1)
72
+ key = dict.random_subset(klen).join
73
+ keys << key
74
+ value = key * vlen
75
+ data << [key, value]
76
+ vs = value.size
77
+ vlen_min = vs if vs < vlen_min
78
+ vlen_max = vs if vs > vlen_max
79
+ vlen_ttl = vlen_ttl + vs
80
+ end
81
+ vlen_avg = vlen_ttl / n
82
+
83
+ puts "----------------------------------------------------------------------"
84
+ #puts data.inspect
85
+ puts "Total keys: #{keys.size}, unique: #{keys.uniq.size}"
86
+ #puts keys.sort.inspect
87
+
88
+ puts "----------------------------------------------------------------------"
89
+ puts " Minimum Maximum Total Average xps "
90
+ puts "----------------------------------------------------------------------"
91
+ puts "Lenght Stats % 10i % 10i % 10i % 10i " % [vlen_min, vlen_max, vlen_ttl, vlen_avg]
92
+
93
+ module Juno
94
+ class TieredCache
95
+ include Juno::Defaults
96
+
97
+ def initialize(options)
98
+ @bdb = Juno::Berkeley.new(:file => File.join(File.dirname(__FILE__), options[:name]))
99
+ @mc = options[:backup]
100
+ # @mc = Juno::Tyrant.new(:host => "localhost", :port => 1978)
101
+ # @mc = Juno::Memcache.new(:server => "localhost:11211", :namespace => options[:name])
102
+ end
103
+
104
+ def [](key)
105
+ val = @bdb[key]
106
+ unless val
107
+ @bdb[key] = val if val = @mc[key]
108
+ end
109
+ val
110
+ end
111
+
112
+ def []=(key, val)
113
+ @bdb[key] = val
114
+ @mc[key] = val
115
+ end
116
+
117
+ def store(key, value, options = {})
118
+ @bdb.store(key, value, options)
119
+ @mc.store(key, value, options)
120
+ end
121
+
122
+ def delete(key)
123
+ bdb_val = @bdb.delete(key)
124
+ mc_val = @mc.delete(key)
125
+ bdb_val || mc_val
126
+ end
127
+
128
+ def clear
129
+ @mc.clear
130
+ @bdb.clear
131
+ end
132
+
133
+ def update_key(name, options)
134
+ @mc.update_key(name, options)
135
+ @bdb.update_key(name, options)
136
+ end
137
+
138
+ def key?(key)
139
+ @bdb.key?(key) || @mc.key?(key)
140
+ end
141
+ end
142
+ end
143
+
144
+ stores.each do |name, options|
145
+ cname = options.delete(:class_name) || name
146
+ puts "======================================================================"
147
+ puts name
148
+ puts "----------------------------------------------------------------------"
149
+ begin
150
+ require "../lib/juno/#{cname.downcase}"
151
+ rescue LoadError
152
+ end
153
+ klass = Juno.const_get(cname)
154
+ @cache = klass.new(options)
155
+ stats[name] = {
156
+ :writes => [],
157
+ :reads => [],
158
+ :totals => [],
159
+ :avgs => [],
160
+ }
161
+ c.times do |round|
162
+ @cache.clear
163
+ print "[#{round + 1}] R"
164
+ m1 = Benchmark.measure do
165
+ n.times do
166
+ key, value = data.random
167
+
168
+ @cache[key] = value
169
+ end
170
+ end
171
+ stats[name][:writes] << m1.real
172
+ print "W "
173
+ m2 = Benchmark.measure do
174
+ n.times do
175
+ key, value = data.random
176
+ res = @cache[key]
177
+ errors << [name, key, value, res] unless res == value
178
+ end
179
+ end
180
+ stats[name][:reads] << m2.real
181
+ stats[name][:totals] << (m1.real + m2.real)
182
+ stats[name][:avgs] << (m1.real + m2.real)
183
+ end
184
+ print "\n"
185
+ puts "----------------------------------------------------------------------"
186
+ puts " Minimum Maximum Total Average xps "
187
+ puts "----------------------------------------------------------------------"
188
+ tcmin, tcmax, tctot, tcavg = 99999, 0, 0, 0
189
+ [:writes, :reads].each do |sname|
190
+ cmin, cmax, ctot, cavg = 99999, 0, 0, 0
191
+ stats[name][sname].each do |val|
192
+ cmin = val if val < cmin
193
+ tcmin = val if val < tcmin
194
+ cmax = val if val > cmax
195
+ tcmax = val if val > tcmax
196
+ ctot = ctot + val
197
+ tctot = tctot + val
198
+ end
199
+ cavg = ctot / c
200
+ puts "%-14.14s % 10.4f % 10.4f % 10.4f % 10.4f % 10.4f " % ["#{name} #{sname}", cmin, cmax, ctot, cavg, n / cavg]
201
+ end
202
+ tcavg = tctot / (c * 2)
203
+ puts "%-14.14s % 10.4f % 10.4f % 10.4f % 10.4f % 10.4f " % ["#{name} avgs", tcmin, tcmax, tctot, tcavg, n / tcavg]
204
+ summary << [name, tcmin, tcmax, tctot, tcavg, n / tcavg]
205
+ end
206
+ puts "----------------------------------------------------------------------"
207
+ if errors.size > 0
208
+ puts "Errors : #{errors.size}"
209
+ # puts errors.inspect
210
+ else
211
+ puts "No errors in reading!"
212
+ end
213
+ puts "======================================================================"
214
+ puts "Summary :: #{c} runs, #{n} keys"
215
+ puts "======================================================================"
216
+ puts " Minimum Maximum Total Average xps "
217
+ puts "----------------------------------------------------------------------"
218
+ summary.each do |sry|
219
+ puts "%-14.14s % 10.4f % 10.4f % 10.4f % 10.4f % 10.4f " % sry
220
+ end
221
+ puts "======================================================================"
222
+ puts "THE END"
223
+ puts "======================================================================"
224
+
225
+ #======================================================================
226
+ #Summary :: 3 runs, 1000 keys
227
+ #======================================================================
228
+ # Minimum Maximum Total Average xps
229
+ #----------------------------------------------------------------------
230
+ #MemcacheDB 0.6202 2.7850 7.0099 1.1683 855.9366
231
+ #Memcached 0.4483 0.6563 3.3251 0.5542 1804.4385
232
+ #Redis 0.3282 0.5221 2.2965 0.3828 2612.6444
233
+ #MongoDB 0.6660 1.0539 5.1667 0.8611 1161.2745
234
+ #======================================================================