moneta 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (212) hide show
  1. data/.gitignore +7 -0
  2. data/.travis.yml +39 -0
  3. data/Gemfile +61 -0
  4. data/LICENSE +2 -2
  5. data/README.md +450 -0
  6. data/Rakefile +29 -51
  7. data/SPEC.md +75 -0
  8. data/benchmarks/run.rb +195 -0
  9. data/lib/action_dispatch/middleware/session/moneta_store.rb +11 -0
  10. data/lib/active_support/cache/moneta_store.rb +55 -0
  11. data/lib/moneta.rb +121 -67
  12. data/lib/moneta/adapters/activerecord.rb +87 -0
  13. data/lib/moneta/adapters/cassandra.rb +91 -0
  14. data/lib/moneta/adapters/client.rb +69 -0
  15. data/lib/moneta/adapters/cookie.rb +35 -0
  16. data/lib/moneta/adapters/couch.rb +57 -0
  17. data/lib/moneta/adapters/datamapper.rb +75 -0
  18. data/lib/moneta/adapters/dbm.rb +25 -0
  19. data/lib/moneta/adapters/file.rb +79 -0
  20. data/lib/moneta/adapters/fog.rb +51 -0
  21. data/lib/moneta/adapters/gdbm.rb +25 -0
  22. data/lib/moneta/adapters/hbase.rb +101 -0
  23. data/lib/moneta/adapters/leveldb.rb +35 -0
  24. data/lib/moneta/adapters/localmemcache.rb +28 -0
  25. data/lib/moneta/adapters/lruhash.rb +85 -0
  26. data/lib/moneta/adapters/memcached.rb +11 -0
  27. data/lib/moneta/adapters/memcached_dalli.rb +69 -0
  28. data/lib/moneta/adapters/memcached_native.rb +70 -0
  29. data/lib/moneta/adapters/memory.rb +10 -0
  30. data/lib/moneta/adapters/mongo.rb +50 -0
  31. data/lib/moneta/adapters/null.rb +30 -0
  32. data/lib/moneta/adapters/pstore.rb +69 -0
  33. data/lib/moneta/adapters/redis.rb +68 -0
  34. data/lib/moneta/adapters/riak.rb +57 -0
  35. data/lib/moneta/adapters/sdbm.rb +35 -0
  36. data/lib/moneta/adapters/sequel.rb +79 -0
  37. data/lib/moneta/adapters/sqlite.rb +65 -0
  38. data/lib/moneta/adapters/tokyocabinet.rb +41 -0
  39. data/lib/moneta/adapters/yaml.rb +15 -0
  40. data/lib/moneta/base.rb +78 -0
  41. data/lib/moneta/builder.rb +39 -0
  42. data/lib/moneta/cache.rb +84 -0
  43. data/lib/moneta/expires.rb +71 -0
  44. data/lib/moneta/lock.rb +25 -0
  45. data/lib/moneta/logger.rb +61 -0
  46. data/lib/moneta/mixins.rb +65 -0
  47. data/lib/moneta/net.rb +18 -0
  48. data/lib/moneta/optionmerger.rb +39 -0
  49. data/lib/moneta/proxy.rb +86 -0
  50. data/lib/moneta/server.rb +81 -0
  51. data/lib/moneta/shared.rb +60 -0
  52. data/lib/moneta/stack.rb +78 -0
  53. data/lib/moneta/transformer.rb +159 -0
  54. data/lib/moneta/transformer/config.rb +42 -0
  55. data/lib/moneta/transformer/helper.rb +37 -0
  56. data/lib/moneta/version.rb +5 -0
  57. data/lib/moneta/wrapper.rb +33 -0
  58. data/lib/rack/cache/moneta.rb +93 -0
  59. data/lib/rack/moneta_cookies.rb +64 -0
  60. data/lib/rack/session/moneta.rb +63 -0
  61. data/moneta.gemspec +19 -0
  62. data/spec/action_dispatch/fixtures/session_autoload_test/foo.rb +10 -0
  63. data/spec/action_dispatch/session_moneta_store_spec.rb +196 -0
  64. data/spec/active_support/cache_moneta_store_spec.rb +197 -0
  65. data/spec/generate.rb +1489 -0
  66. data/spec/helper.rb +91 -0
  67. data/spec/moneta/adapter_activerecord_spec.rb +32 -0
  68. data/spec/moneta/adapter_cassandra_spec.rb +30 -0
  69. data/spec/moneta/adapter_client_spec.rb +19 -0
  70. data/spec/moneta/adapter_cookie_spec.rb +18 -0
  71. data/spec/moneta/adapter_couch_spec.rb +18 -0
  72. data/spec/moneta/adapter_datamapper_spec.rb +49 -0
  73. data/spec/moneta/adapter_dbm_spec.rb +18 -0
  74. data/spec/moneta/adapter_file_spec.rb +18 -0
  75. data/spec/moneta/adapter_fog_spec.rb +23 -0
  76. data/spec/moneta/adapter_gdbm_spec.rb +18 -0
  77. data/spec/moneta/adapter_hbase_spec.rb +18 -0
  78. data/spec/moneta/adapter_leveldb_spec.rb +18 -0
  79. data/spec/moneta/adapter_localmemcache_spec.rb +18 -0
  80. data/spec/moneta/adapter_lruhash_spec.rb +31 -0
  81. data/spec/moneta/adapter_memcached_dalli_spec.rb +30 -0
  82. data/spec/moneta/adapter_memcached_native_spec.rb +31 -0
  83. data/spec/moneta/adapter_memcached_spec.rb +30 -0
  84. data/spec/moneta/adapter_memory_spec.rb +39 -0
  85. data/spec/moneta/adapter_mongo_spec.rb +18 -0
  86. data/spec/moneta/adapter_pstore_spec.rb +21 -0
  87. data/spec/moneta/adapter_redis_spec.rb +30 -0
  88. data/spec/moneta/adapter_riak_spec.rb +22 -0
  89. data/spec/moneta/adapter_sdbm_spec.rb +18 -0
  90. data/spec/moneta/adapter_sequel_spec.rb +18 -0
  91. data/spec/moneta/adapter_sqlite_spec.rb +18 -0
  92. data/spec/moneta/adapter_tokyocabinet_bdb_spec.rb +18 -0
  93. data/spec/moneta/adapter_tokyocabinet_hdb_spec.rb +18 -0
  94. data/spec/moneta/adapter_yaml_spec.rb +21 -0
  95. data/spec/moneta/cache_file_memory_spec.rb +34 -0
  96. data/spec/moneta/cache_memory_null_spec.rb +23 -0
  97. data/spec/moneta/expires_file_spec.rb +76 -0
  98. data/spec/moneta/expires_memory_spec.rb +65 -0
  99. data/spec/moneta/lock_spec.rb +42 -0
  100. data/spec/moneta/null_adapter_spec.rb +26 -0
  101. data/spec/moneta/optionmerger_spec.rb +92 -0
  102. data/spec/moneta/proxy_expires_memory_spec.rb +55 -0
  103. data/spec/moneta/proxy_redis_spec.rb +23 -0
  104. data/spec/moneta/shared_spec.rb +30 -0
  105. data/spec/moneta/simple_activerecord_spec.rb +51 -0
  106. data/spec/moneta/simple_activerecord_with_expires_spec.rb +52 -0
  107. data/spec/moneta/simple_cassandra_spec.rb +52 -0
  108. data/spec/moneta/simple_client_tcp_spec.rb +67 -0
  109. data/spec/moneta/simple_client_unix_spec.rb +53 -0
  110. data/spec/moneta/simple_couch_spec.rb +51 -0
  111. data/spec/moneta/simple_couch_with_expires_spec.rb +52 -0
  112. data/spec/moneta/simple_datamapper_spec.rb +53 -0
  113. data/spec/moneta/simple_datamapper_with_expires_spec.rb +54 -0
  114. data/spec/moneta/simple_datamapper_with_repository_spec.rb +53 -0
  115. data/spec/moneta/simple_dbm_spec.rb +51 -0
  116. data/spec/moneta/simple_dbm_with_expires_spec.rb +52 -0
  117. data/spec/moneta/simple_file_spec.rb +51 -0
  118. data/spec/moneta/simple_file_with_expires_spec.rb +52 -0
  119. data/spec/moneta/simple_fog_spec.rb +56 -0
  120. data/spec/moneta/simple_fog_with_expires_spec.rb +58 -0
  121. data/spec/moneta/simple_gdbm_spec.rb +51 -0
  122. data/spec/moneta/simple_gdbm_with_expires_spec.rb +52 -0
  123. data/spec/moneta/simple_hashfile_spec.rb +51 -0
  124. data/spec/moneta/simple_hashfile_with_expires_spec.rb +52 -0
  125. data/spec/moneta/simple_hbase_spec.rb +51 -0
  126. data/spec/moneta/simple_hbase_with_expires_spec.rb +52 -0
  127. data/spec/moneta/simple_leveldb_spec.rb +51 -0
  128. data/spec/moneta/simple_leveldb_with_expires_spec.rb +52 -0
  129. data/spec/moneta/simple_localmemcache_spec.rb +51 -0
  130. data/spec/moneta/simple_localmemcache_with_expires_spec.rb +52 -0
  131. data/spec/moneta/simple_lruhash_spec.rb +51 -0
  132. data/spec/moneta/simple_lruhash_with_expires_spec.rb +52 -0
  133. data/spec/moneta/simple_memcached_dalli_spec.rb +52 -0
  134. data/spec/moneta/simple_memcached_native_spec.rb +52 -0
  135. data/spec/moneta/simple_memcached_spec.rb +52 -0
  136. data/spec/moneta/simple_memory_spec.rb +51 -0
  137. data/spec/moneta/simple_memory_with_compress_spec.rb +51 -0
  138. data/spec/moneta/simple_memory_with_expires_spec.rb +52 -0
  139. data/spec/moneta/simple_memory_with_json_key_serializer_spec.rb +37 -0
  140. data/spec/moneta/simple_memory_with_json_serializer_spec.rb +28 -0
  141. data/spec/moneta/simple_memory_with_json_value_serializer_spec.rb +35 -0
  142. data/spec/moneta/simple_memory_with_prefix_spec.rb +51 -0
  143. data/spec/moneta/simple_memory_with_snappy_compress_spec.rb +51 -0
  144. data/spec/moneta/simple_mongo_spec.rb +51 -0
  145. data/spec/moneta/simple_mongo_with_expires_spec.rb +52 -0
  146. data/spec/moneta/simple_null_spec.rb +36 -0
  147. data/spec/moneta/simple_pstore_spec.rb +51 -0
  148. data/spec/moneta/simple_pstore_with_expires_spec.rb +52 -0
  149. data/spec/moneta/simple_redis_spec.rb +52 -0
  150. data/spec/moneta/simple_riak_spec.rb +55 -0
  151. data/spec/moneta/simple_riak_with_expires_spec.rb +56 -0
  152. data/spec/moneta/simple_sdbm_spec.rb +51 -0
  153. data/spec/moneta/simple_sdbm_with_expires_spec.rb +52 -0
  154. data/spec/moneta/simple_sequel_spec.rb +51 -0
  155. data/spec/moneta/simple_sequel_with_expires_spec.rb +52 -0
  156. data/spec/moneta/simple_sqlite_spec.rb +51 -0
  157. data/spec/moneta/simple_sqlite_with_expires_spec.rb +52 -0
  158. data/spec/moneta/simple_tokyocabinet_spec.rb +51 -0
  159. data/spec/moneta/simple_tokyocabinet_with_expires_spec.rb +52 -0
  160. data/spec/moneta/simple_yaml_spec.rb +50 -0
  161. data/spec/moneta/simple_yaml_with_expires_spec.rb +51 -0
  162. data/spec/moneta/stack_file_memory_spec.rb +25 -0
  163. data/spec/moneta/stack_memory_file_spec.rb +24 -0
  164. data/spec/moneta/transformer_bencode_spec.rb +30 -0
  165. data/spec/moneta/transformer_bert_spec.rb +30 -0
  166. data/spec/moneta/transformer_bson_spec.rb +30 -0
  167. data/spec/moneta/transformer_bzip2_spec.rb +27 -0
  168. data/spec/moneta/transformer_json_spec.rb +30 -0
  169. data/spec/moneta/transformer_lzma_spec.rb +27 -0
  170. data/spec/moneta/transformer_lzo_spec.rb +27 -0
  171. data/spec/moneta/transformer_marshal_base64_spec.rb +54 -0
  172. data/spec/moneta/transformer_marshal_escape_spec.rb +54 -0
  173. data/spec/moneta/transformer_marshal_hmac_spec.rb +54 -0
  174. data/spec/moneta/transformer_marshal_md5_spec.rb +54 -0
  175. data/spec/moneta/transformer_marshal_md5_spread_spec.rb +54 -0
  176. data/spec/moneta/transformer_marshal_prefix_spec.rb +54 -0
  177. data/spec/moneta/transformer_marshal_rmd160_spec.rb +54 -0
  178. data/spec/moneta/transformer_marshal_sha1_spec.rb +54 -0
  179. data/spec/moneta/transformer_marshal_sha256_spec.rb +54 -0
  180. data/spec/moneta/transformer_marshal_sha384_spec.rb +54 -0
  181. data/spec/moneta/transformer_marshal_sha512_spec.rb +54 -0
  182. data/spec/moneta/transformer_marshal_truncate_spec.rb +54 -0
  183. data/spec/moneta/transformer_marshal_uuencode_spec.rb +54 -0
  184. data/spec/moneta/transformer_msgpack_spec.rb +30 -0
  185. data/spec/moneta/transformer_ox_spec.rb +51 -0
  186. data/spec/moneta/transformer_quicklz_spec.rb +27 -0
  187. data/spec/moneta/transformer_snappy_spec.rb +27 -0
  188. data/spec/moneta/transformer_tnet_spec.rb +30 -0
  189. data/spec/moneta/transformer_yaml_spec.rb +51 -0
  190. data/spec/moneta/transformer_zlib_spec.rb +27 -0
  191. data/spec/monetaspecs.rb +2663 -0
  192. data/spec/rack/cache_moneta_spec.rb +355 -0
  193. data/spec/rack/moneta_cookies_spec.rb +81 -0
  194. data/spec/rack/session_moneta_spec.rb +305 -0
  195. metadata +359 -56
  196. data/README +0 -51
  197. data/TODO +0 -4
  198. data/lib/moneta/basic_file.rb +0 -111
  199. data/lib/moneta/berkeley.rb +0 -53
  200. data/lib/moneta/couch.rb +0 -63
  201. data/lib/moneta/datamapper.rb +0 -117
  202. data/lib/moneta/file.rb +0 -91
  203. data/lib/moneta/lmc.rb +0 -52
  204. data/lib/moneta/memcache.rb +0 -52
  205. data/lib/moneta/memory.rb +0 -11
  206. data/lib/moneta/mongodb.rb +0 -58
  207. data/lib/moneta/redis.rb +0 -49
  208. data/lib/moneta/rufus.rb +0 -41
  209. data/lib/moneta/s3.rb +0 -162
  210. data/lib/moneta/sdbm.rb +0 -33
  211. data/lib/moneta/tyrant.rb +0 -58
  212. data/lib/moneta/xattr.rb +0 -58
data/Rakefile CHANGED
@@ -1,60 +1,38 @@
1
- require 'rubygems'
2
- require 'rake/gempackagetask'
3
- require 'rubygems/specification'
4
- require 'spec/rake/spectask'
5
- require 'date'
6
-
7
- GEM = "moneta"
8
- GEM_VERSION = "0.6.0"
9
- AUTHOR = "Yehuda Katz"
10
- EMAIL = "wycats@gmail.com"
11
- HOMEPAGE = "http://www.yehudakatz.com"
12
- SUMMARY = "A unified interface to key/value stores"
13
-
14
- spec = Gem::Specification.new do |s|
15
- s.name = GEM
16
- s.version = GEM_VERSION
17
- s.platform = Gem::Platform::RUBY
18
- s.has_rdoc = true
19
- s.extra_rdoc_files = ["README", "LICENSE", 'TODO']
20
- s.summary = SUMMARY
21
- s.description = s.summary
22
- s.author = AUTHOR
23
- s.email = EMAIL
24
- s.homepage = HOMEPAGE
25
-
26
- # Uncomment this to add a dependency
27
- # s.add_dependency "foo"
28
-
29
- s.require_path = 'lib'
30
- s.autorequire = GEM
31
- s.files = %w(LICENSE README Rakefile TODO) + Dir.glob("{lib,specs}/**/*")
1
+ begin
2
+ require 'bundler'
3
+ Bundler::GemHelper.install_tasks
4
+ rescue Exception
32
5
  end
33
6
 
34
- Rake::GemPackageTask.new(spec) do |pkg|
35
- pkg.gem_spec = spec
36
- end
7
+ task :test => %w(test:parallel test:non_parallel)
37
8
 
38
- desc "install the gem locally"
39
- task :install => [:package] do
40
- sh %{sudo gem install pkg/#{GEM}-#{GEM_VERSION}}
41
- end
9
+ # memcached and redis specs cannot be used in parallel
10
+ # because of flushing and namespace lacking in redis
42
11
 
43
- desc "create a gemspec file"
44
- task :make_spec do
45
- File.open("#{GEM}.gemspec", "w") do |file|
46
- file.puts spec.to_ruby
12
+ namespace :test do
13
+ task :parallel do
14
+ if defined?(JRUBY_VERSION)
15
+ puts 'No tests executed in parallel in JRuby'
16
+ else
17
+ specs = Dir['spec/*/*_spec.rb'].reject {|s| s =~ /memcached|redis|client|shared|riak/ }
18
+ sh("parallel_rspec -m 5 #{specs.join(' ')}")
19
+ end
47
20
  end
48
- end
49
21
 
50
- desc "Run all examples (or a specific spec with TASK=xxxx)"
51
- Spec::Rake::SpecTask.new('spec') do |t|
52
- t.spec_opts = ["-cfs"]
53
- t.spec_files = begin
54
- if ENV["TASK"]
55
- ENV["TASK"].split(',').map { |task| "spec/**/#{task}_spec.rb" }
22
+ task :non_parallel do
23
+ if defined?(JRUBY_VERSION)
24
+ # Run all tests in jruby non-parallel
25
+ sh('rspec spec/*/*_spec.rb')
56
26
  else
57
- FileList['spec/**/*_spec.rb']
27
+ specs = Dir['spec/*/*_spec.rb'].select {|s| s =~ /memcached|redis|client|shared|riak/ }
28
+ sh("rspec #{specs.join(' ')}")
58
29
  end
59
30
  end
60
- end
31
+ end
32
+
33
+ task :benchmarks do
34
+ Dir.chdir('benchmarks')
35
+ ruby('run.rb')
36
+ end
37
+
38
+ task :default => :test
data/SPEC.md ADDED
@@ -0,0 +1,75 @@
1
+ # Moneta Specification
2
+
3
+ The purpose of the moneta specification is to create a general-purpose API for interacting with key-value stores. In general, libraries that need to interact with key-value stores should be able to specify that they can use any "moneta-compliant store".
4
+
5
+ # Moneta Executable Specs
6
+
7
+ Moneta ships with a set of executable specs which you can use to verify spec-compliance with your moneta adapter.
8
+
9
+ # Requirements for a Moneta Adapter
10
+
11
+ (See RFC 2119 for use of MUST, SHOULD, MAY, MUST NOT, and SHOULD NOT)
12
+
13
+ A Moneta adapter must expose a class with the following characteristics:
14
+
15
+ ## Class Methods
16
+
17
+ ### <code>new(options[Hash] => {}) => Object</code>
18
+
19
+ Return an instance of the moneta adapter, with the instance methods listed below. The <code>options</code> hash is a required parameter, and the adapter may specify whatever additional requirements it needs to properly instantiate it.
20
+
21
+ ## Instance Methods
22
+
23
+ ### <code>\[\](key[Object]) => Object</code>
24
+
25
+ Return the value stored in the key-value-store under the provided key. Adapters MUST return a duplicate of the original value, and consumers should expect that adapters might serialize and deserialize the key and value. As a result, both the key and value MUST be objects that can be serialized using Ruby's Marshal system.
26
+
27
+ ### <code>\[\]=(key[Object], value[Object]) => Object(value)</code>
28
+
29
+ Store the value in the key-value-store under the provided key. Adapters MAY serialize the value using Ruby's Marshal system, and MUST NOT store a reference to the original value in the store, unless Ruby disallows duplication of the original value. Adapters SHOULD NOT simply call <code>dup</code> on the value, unless the value stores no references to other Object. For example, an adapter MAY store a <code>dup</code> of a String, but SHOULD NOT store a <code>dup</code> of <code>["hello", "world"]</code>.
30
+
31
+ ### <code>fetch(key[Object], options[Hash] => {}, &block) => Object</code>
32
+
33
+ Return the value stored in the key-value-store under the provided key. If no value is stored under the provided key, the adapter MUST yield to the block, and return the value. The adapter MUST NOT store the value returned from the block in the key-value-store.
34
+
35
+ ### <code>fetch(key[Object], value[Object], options[Hash] => {}) => Object</code>
36
+
37
+ Return the value stored in the key-value-store under the provided key. If no value is stored under the provided key, the adapter MUST return the default value provided. The adapter MUST NOT store the default value in the key-value-store.
38
+
39
+ ### <code>delete(key[Object], options[Hash] => {}) => Object</code>
40
+
41
+ Delete the value stored in the key-value-store for the key provided, and return the value previously stored there. After this operation, the key-value-store MUST behave as though no value was stored for the provided key.
42
+
43
+ ### <code>key?(key[Object], options[Hash] => {}) => [TrueClass, FalseClass]</code>
44
+
45
+ Determine whether a value exists in the key-value-store for the key provided. If a value exists, the adapter MUST return <code>true</code>. Otherwise, the adapter MUST return <code>false</code>.
46
+
47
+ ### <code>store(key[Object], value[Object], options[Hash] => {}) => Object(value)</code>
48
+
49
+ Behaves the same as <code>[]=</code>, but allows the client to send additional options which can be specified by the adapter (and which may be specified by extensions to this specification).
50
+
51
+ ### <code>increment(key[Object], amount[Integer] = 1, options[Hash] => {}) => Integer(value)</code>
52
+
53
+ Increments a value atomically. This method is not supported by all stores and might raise a <code>NotImplementedError</code>.
54
+
55
+ ### <code>clear(options[Hash] => {})</code>
56
+
57
+ Completely empty all keys and values from the key-value-store. Adapters MAY allow a namespace during initialization, which can scope this operation to a particular subset of keys. After calling <code>clear</code>, a <code>[]</code> operation MUST return nil for every possible key, and a <code>key?</code> query MUST return false for every possible key.
58
+
59
+ # Additional Options Hashes
60
+
61
+ The following methods may all take an additional Hash as a final argument. This allows the client to send additional options which can be specified by the adapter (and which may be specified by extensions to this specification).
62
+
63
+ * fetch
64
+ * load
65
+ * store
66
+ * delete
67
+ * key?
68
+ * increment
69
+ * clear
70
+
71
+ In the case of methods with optional arguments, the Hash MUST be provided as the final argument. Keys in this Hash MUST be Symbols.
72
+
73
+ # Atomicity
74
+
75
+ The base Moneta specification does not specify any atomicity guarantees. However, extensions to this spec may specify extensions that define additional guarantees for any of the defined operations.
@@ -0,0 +1,195 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $: << File.join(File.dirname(__FILE__), '..', 'lib')
4
+ require 'benchmark'
5
+ require 'moneta'
6
+
7
+ begin
8
+ require 'dm-core'
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' } },
36
+ :Cassandra => {},
37
+ :Client => {},
38
+ :Couch => {},
39
+ :DBM => { :file => 'bench.dbm' },
40
+ :DataMapper => { :setup => 'sqlite3:bench.datamapper' },
41
+ :File => { :dir => 'bench.file' },
42
+ :GDBM => { :file => 'bench.gdbm' },
43
+ :HBase => {},
44
+ :HBase => {},
45
+ :HashFile => { :dir => 'bench.hashfile' },
46
+ :LRUHash => {},
47
+ :LevelDB => { :dir => 'bench.leveldb' },
48
+ :LocalMemCache => { :file => 'bench.lmc' },
49
+ :MemcachedDalli => {},
50
+ :MemcachedNative => {},
51
+ :Memory => {},
52
+ :Mongo => {},
53
+ :PStore => { :file => 'bench.pstore' },
54
+ :Redis => {},
55
+ :Riak => {},
56
+ :SDBM => { :file => 'bench.sdbm' },
57
+ :Sequel => { :db => 'sqlite:/' },
58
+ :Sqlite => { :file => 'bench.sqlite' },
59
+ :YAML => { :file => 'bench.yaml' },
60
+ }
61
+
62
+ stats, keys, data, errors, summary = {}, [], [], [], []
63
+ dict = 'ABCDEFGHIJKLNOPQRSTUVWXYZabcdefghijklnopqrstuvwxyz123456789'.split('')
64
+ vlen_min, vlen_max, vlen_total, vlen_average = 99999, 0, 0, 0
65
+ klen_min, klen_max, klen_total, klen_average = 99999, 0, 0, 0
66
+
67
+ RUNS = 3
68
+ KEYS = 100
69
+ MIN_KEY_SIZE = 3
70
+ MAX_KEY_SIZE = 64
71
+ MIN_VALUE_SIZE = 1
72
+ MAX_VALUE_SIZE = 1024 * 10
73
+
74
+ puts '======================================================================'
75
+ puts 'Comparison of write/read between Moneta Stores'
76
+ puts '======================================================================'
77
+
78
+ stores.each do |name, options|
79
+ begin
80
+ cache = Moneta.new(name, options.dup)
81
+ cache['test'] = 'test'
82
+ rescue Exception => ex
83
+ puts "#{name} not benchmarked - #{ex.message}"
84
+ stores.delete(name)
85
+ ensure
86
+ cache.close if cache
87
+ end
88
+ end
89
+
90
+ puts 'Data loading...'
91
+ KEYS.times do |x|
92
+ klen = rand(MAX_KEY_SIZE - MIN_KEY_SIZE) + MIN_KEY_SIZE
93
+ vlen = rand(MAX_VALUE_SIZE - MIN_VALUE_SIZE) + MIN_VALUE_SIZE
94
+
95
+ key = dict.random_subset(klen).join
96
+ value = dict.random_subset(vlen).join
97
+ keys << key
98
+ data << [key, value]
99
+
100
+ vlen_min = value.size if value.size < vlen_min
101
+ vlen_max = value.size if value.size > vlen_max
102
+ vlen_total = vlen_total + value.size
103
+
104
+ klen_min = key.size if key.size < klen_min
105
+ klen_max = key.size if key.size > klen_max
106
+ klen_total = klen_total + key.size
107
+ end
108
+ vlen_average = vlen_total / KEYS
109
+
110
+ puts '----------------------------------------------------------------------'
111
+ puts "Total keys: #{keys.size}, unique: #{keys.uniq.size}"
112
+ puts '----------------------------------------------------------------------'
113
+ puts ' Minimum Maximum Total Average xps '
114
+ puts '----------------------------------------------------------------------'
115
+ puts 'Key Length % 10i % 10i % 10i % 10i ' % [klen_min, klen_max, klen_total, klen_average]
116
+ puts 'Value Length % 10i % 10i % 10i % 10i ' % [vlen_min, vlen_max, vlen_total, vlen_average]
117
+
118
+ stores.each do |name, options|
119
+ begin
120
+ puts '======================================================================'
121
+ puts name
122
+ puts '----------------------------------------------------------------------'
123
+ cache = Moneta.new(name, options.dup)
124
+
125
+ stats[name] = {
126
+ :writes => [],
127
+ :reads => [],
128
+ :totals => [],
129
+ :averages => [],
130
+ }
131
+
132
+ RUNS.times do |round|
133
+ cache.clear
134
+ print "[#{round + 1}] W"
135
+ m1 = Benchmark.measure do
136
+ KEYS.times do
137
+ key, value = data.random_value
138
+ cache[key] = value
139
+ end
140
+ end
141
+ stats[name][:writes] << m1.real
142
+ print 'R '
143
+ m2 = Benchmark.measure do
144
+ KEYS.times do
145
+ key, value = data.random_value
146
+ res = cache[key]
147
+ errors << [name, key, value, res] unless res == value
148
+ end
149
+ end
150
+ stats[name][:reads] << m2.real
151
+ stats[name][:totals] << (m1.real + m2.real)
152
+ stats[name][:averages] << (m1.real + m2.real)
153
+ end
154
+ puts ''
155
+ puts '----------------------------------------------------------------------'
156
+ puts ' Minimum Maximum Total Average xps '
157
+ puts '----------------------------------------------------------------------'
158
+ tcmin, tcmax, tctot, tcavg = 99999, 0, 0, 0
159
+ [:writes, :reads].each do |sname|
160
+ cmin, cmax, ctot, cavg = 99999, 0, 0, 0
161
+ stats[name][sname].each do |val|
162
+ cmin = val if val < cmin
163
+ tcmin = val if val < tcmin
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]
171
+ end
172
+ tcavg = tctot / (RUNS * 2)
173
+ puts '%-14.14s % 10.4f % 10.4f % 10.4f % 10.4f % 10.4f ' % ["#{name} avgs", tcmin, tcmax, tctot, tcavg, KEYS / tcavg]
174
+ summary << [name, tcmin, tcmax, tctot, tcavg, KEYS / tcavg]
175
+ rescue Exception => ex
176
+ puts "Failed to benchmark #{name} - #{ex.message}"
177
+ ensure
178
+ cache.close if cache
179
+ end
180
+ end
181
+ puts '----------------------------------------------------------------------'
182
+ if errors.size > 0
183
+ puts "Errors : #{errors.size}"
184
+ # puts errors.inspect
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
195
+ end
@@ -0,0 +1,11 @@
1
+ require 'rack/session/moneta'
2
+ require 'action_dispatch/middleware/session/abstract_store'
3
+
4
+ module ActionDispatch
5
+ module Session
6
+ class MonetaStore < Rack::Session::Moneta
7
+ include Compatibility
8
+ include StaleSessionCheck
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,55 @@
1
+ module ActiveSupport
2
+ module Cache
3
+ class MonetaStore < Store
4
+ def initialize(options = nil)
5
+ raise ArgumentError, 'Option :store is required' unless @store = options.delete(:store)
6
+ @store = ::Moneta.new(@store, :expires => true) if Symbol === @store
7
+ super(options)
8
+ extend Strategy::LocalCache
9
+ end
10
+
11
+ def increment(key, amount = 1, options = nil)
12
+ instrument(:increment, key, :amount => amount) do
13
+ @store.increment(key, amount, moneta_options(options))
14
+ end
15
+ end
16
+
17
+ def decrement(key, amount = 1, options = nil)
18
+ instrument(:decrement, key, :amount => amount) do
19
+ @store.increment(key, -amount, moneta_options(options))
20
+ end
21
+ end
22
+
23
+ def clear(options = nil)
24
+ instrument(:clear, nil, nil) do
25
+ @store.clear(moneta_options(options))
26
+ end
27
+ end
28
+
29
+ protected
30
+
31
+ def read_entry(key, options)
32
+ entry = @store.load(key, moneta_options(options))
33
+ entry && (ActiveSupport::Cache::Entry === entry ? entry : ActiveSupport::Cache::Entry.new(entry))
34
+ end
35
+
36
+ def write_entry(key, entry, options)
37
+ @store.store(key, entry, moneta_options(options))
38
+ true
39
+ end
40
+
41
+ def delete_entry(key, options)
42
+ @store.delete(key, moneta_options(options))
43
+ true
44
+ end
45
+
46
+ private
47
+
48
+ def moneta_options(options)
49
+ options ||= {}
50
+ options[:expires] = options.delete(:expires_in).to_i if options.include?(:expires_in)
51
+ options
52
+ end
53
+ end
54
+ end
55
+ end
@@ -1,76 +1,130 @@
1
1
  module Moneta
2
- module Expires
3
- def check_expired(key)
4
- if @expiration[key] && Time.now > @expiration[key]
5
- @expiration.delete(key)
6
- self.delete(key)
7
- end
8
- end
9
-
10
- def key?(key)
11
- check_expired(key)
12
- super
13
- end
14
-
15
- def [](key)
16
- check_expired(key)
17
- super
18
- end
19
-
20
- def fetch(key, default = nil, &blk)
21
- check_expired(key)
22
- super
23
- end
24
-
25
- def delete(key)
26
- check_expired(key)
27
- super
28
- end
2
+ autoload :Base, 'moneta/base'
3
+ autoload :Builder, 'moneta/builder'
4
+ autoload :Cache, 'moneta/cache'
5
+ autoload :Expires, 'moneta/expires'
6
+ autoload :Lock, 'moneta/lock'
7
+ autoload :Logger, 'moneta/logger'
8
+ autoload :Mixins, 'moneta/mixins'
9
+ autoload :Net, 'moneta/net'
10
+ autoload :OptionMerger, 'moneta/optionmerger'
11
+ autoload :Proxy, 'moneta/proxy'
12
+ autoload :Server, 'moneta/server'
13
+ autoload :Shared, 'moneta/shared'
14
+ autoload :Stack, 'moneta/stack'
15
+ autoload :Transformer, 'moneta/transformer'
16
+ autoload :Wrapper, 'moneta/wrapper'
29
17
 
30
- def update_key(key, options)
31
- update_options(key, options)
32
- end
33
-
34
- def store(key, value, options = {})
35
- ret = super(key, value)
36
- update_options(key, options)
37
- ret
38
- end
39
-
40
- private
41
- def update_options(key, options)
42
- if options[:expires_in]
43
- @expiration[key] = (Time.now + options[:expires_in])
44
- end
45
- end
18
+ module Adapters
19
+ autoload :ActiveRecord, 'moneta/adapters/activerecord'
20
+ autoload :Cassandra, 'moneta/adapters/cassandra'
21
+ autoload :Client, 'moneta/adapters/client'
22
+ autoload :Cookie, 'moneta/adapters/cookie'
23
+ autoload :Couch, 'moneta/adapters/couch'
24
+ autoload :DataMapper, 'moneta/adapters/datamapper'
25
+ autoload :DBM, 'moneta/adapters/dbm'
26
+ autoload :File, 'moneta/adapters/file'
27
+ autoload :Fog, 'moneta/adapters/fog'
28
+ autoload :GDBM, 'moneta/adapters/gdbm'
29
+ autoload :HBase, 'moneta/adapters/hbase'
30
+ autoload :LevelDB, 'moneta/adapters/leveldb'
31
+ autoload :LocalMemCache, 'moneta/adapters/localmemcache'
32
+ autoload :LRUHash, 'moneta/adapters/lruhash'
33
+ autoload :Memcached, 'moneta/adapters/memcached'
34
+ autoload :MemcachedDalli, 'moneta/adapters/memcached_dalli'
35
+ autoload :MemcachedNative, 'moneta/adapters/memcached_native'
36
+ autoload :Memory, 'moneta/adapters/memory'
37
+ autoload :Mongo, 'moneta/adapters/mongo'
38
+ autoload :Null, 'moneta/adapters/null'
39
+ autoload :PStore, 'moneta/adapters/pstore'
40
+ autoload :Redis, 'moneta/adapters/redis'
41
+ autoload :Riak, 'moneta/adapters/riak'
42
+ autoload :SDBM, 'moneta/adapters/sdbm'
43
+ autoload :Sequel, 'moneta/adapters/sequel'
44
+ autoload :Sqlite, 'moneta/adapters/sqlite'
45
+ autoload :TokyoCabinet, 'moneta/adapters/tokyocabinet'
46
+ autoload :YAML, 'moneta/adapters/yaml'
46
47
  end
47
48
 
48
- module StringExpires
49
- include Expires
50
-
51
- def check_expired(key)
52
- if @expiration[key] && Time.now > Time.at(@expiration[key].to_i)
53
- @expiration.delete(key)
54
- delete(key)
55
- end
49
+ # Create new Moneta store with default proxies
50
+ # which works in most cases if you don't want fine
51
+ # control over the proxy chain. It uses Marshal on the
52
+ # keys and values. Use Moneta#build if you want to have fine control!
53
+ #
54
+ # @param [Symbol] name Name of adapter (See Moneta::Adapters)
55
+ # @param [Hash] options
56
+ #
57
+ # Options:
58
+ # * :expires - If true or integer, ensure that store supports expiration by inserting
59
+ # Moneta::Expires if the underlying adapter doesn't support it natively
60
+ # * :threadsafe - If true, ensure that the store is thread safe by inserting Moneta::Lock
61
+ # * :logger - If true or Hash, add logger to chain (Hash is passed to logger as options)
62
+ # * :compress - If true, compress value with zlib, or specify custom compress, e.g. :quicklz
63
+ # * :serializer - Serializer used for key and value, disable with nil (default :marshal)
64
+ # * :key_serializer - Serializer used for key, disable with nil (default options[:serializer] if not provided)
65
+ # * :value_serializer - Serializer used for key, disable with nil (default options[:serializer] if not provided)
66
+ # * :prefix - Key prefix used for namespacing (default none)
67
+ # * All other options passed to the adapter
68
+ #
69
+ # Supported adapters:
70
+ # * :HashFile (Store which spreads the entries using a md5 hash, e.g. cache/42/391dd7535aebef91b823286ac67fcd)
71
+ # * :File (normal file store)
72
+ # * :Memcached (Memcached store)
73
+ # * ... (All other adapters from Moneta::Adapters)
74
+ def self.new(name, options = {})
75
+ expires = options.delete(:expires)
76
+ logger = options.delete(:logger)
77
+ threadsafe = options.delete(:threadsafe)
78
+ compress = options.delete(:compress)
79
+ serializer = options.include?(:serializer) ? options.delete(:serializer) : :marshal
80
+ key_serializer = options.include?(:key_serializer) ? options.delete(:key_serializer) : serializer
81
+ value_serializer = options.include?(:value_serializer) ? options.delete(:value_serializer) : serializer
82
+ transformer = { :key => [key_serializer, :prefix], :value => [value_serializer], :prefix => options.delete(:prefix) }
83
+ transformer[:value] << (Symbol === compress ? compress : :zlib) if compress
84
+ raise ArgumentError, 'Name must be Symbol' unless Symbol === name
85
+ case name
86
+ when :Sequel, :ActiveRecord, :Couch, :DataMapper
87
+ # Sequel accept only base64 keys and values
88
+ # FIXME: Couch should work only with :marshal but this raises an error on 1.9
89
+ transformer[:key] << :base64
90
+ transformer[:value] << :base64
91
+ when :Memcached, :MemcachedDalli, :MemcachedNative
92
+ # Memcached accept only base64 keys, expires already supported
93
+ options[:expires] = expires if Integer === expires
94
+ expires = false
95
+ transformer[:key] << :base64
96
+ when :PStore, :YAML, :Null
97
+ # For PStore and YAML only the key has to be a string
98
+ transformer.delete(:value) if transformer[:value] == [:marshal]
99
+ when :HashFile
100
+ # Use spreading hashes
101
+ transformer[:key] << :md5 << :spread
102
+ name = :File
103
+ when :File
104
+ # Use escaping
105
+ transformer[:key] << :escape
106
+ when :Cassandra, :Redis
107
+ # Expires already supported
108
+ options[:expires] = expires if Integer === expires
109
+ expires = false
56
110
  end
57
-
58
- private
59
- def update_options(key, options)
60
- if options[:expires_in]
61
- @expiration[key] = (Time.now + options[:expires_in]).to_i.to_s
62
- end
111
+ build do
112
+ use :Logger, Hash === logger ? logger : {} if logger
113
+ use :Expires, :expires => (Integer === expires ? expires : nil) if expires
114
+ use :Transformer, transformer
115
+ use :Lock if threadsafe
116
+ adapter name, options
63
117
  end
64
118
  end
65
-
66
- module Defaults
67
- def fetch(key, value = nil)
68
- value ||= block_given? ? yield(key) : default
69
- self[key] || value
70
- end
71
-
72
- def store(key, value, options = {})
73
- self[key] = value
74
- end
119
+
120
+ # Build your own store chain!
121
+ #
122
+ # @example Moneta builder
123
+ # Moneta.build do
124
+ # use :Expires
125
+ # adapter :Memory
126
+ # end
127
+ def self.build(&block)
128
+ Builder.new(&block).build.last
75
129
  end
76
130
  end