juno 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/.gitignore +1 -0
  2. data/.travis.yml +2 -5
  3. data/Gemfile +21 -8
  4. data/README.md +3 -1
  5. data/{unsupported/benchmarks.rb → benchmarks/run.rb} +20 -83
  6. data/lib/juno.rb +4 -4
  7. data/lib/juno/activerecord.rb +2 -5
  8. data/lib/juno/base.rb +4 -0
  9. data/{unsupported → lib/juno}/cassandra.rb +11 -11
  10. data/lib/juno/datamapper.rb +1 -1
  11. data/{unsupported → lib/juno}/fog.rb +7 -19
  12. data/lib/juno/null.rb +23 -0
  13. data/lib/juno/redis.rb +13 -1
  14. data/lib/juno/sequel.rb +3 -6
  15. data/lib/juno/stack.rb +41 -0
  16. data/lib/juno/version.rb +1 -1
  17. data/test/helper.rb +160 -130
  18. data/test/{test_active_record.rb → test_activerecord.rb} +2 -5
  19. data/{unsupported → test}/test_cassandra.rb +1 -1
  20. data/test/test_couch.rb +1 -1
  21. data/test/test_datamapper.rb +2 -2
  22. data/test/test_dbm.rb +1 -1
  23. data/test/test_expires.rb +1 -2
  24. data/test/test_file.rb +1 -1
  25. data/test/test_fog.rb +17 -0
  26. data/test/test_gdbm.rb +1 -1
  27. data/test/test_hashfile.rb +1 -1
  28. data/test/test_localmemcache.rb +1 -1
  29. data/test/test_memcached.rb +1 -2
  30. data/test/test_memcached_dalli.rb +1 -2
  31. data/test/test_memcached_native.rb +1 -2
  32. data/test/test_memory.rb +1 -1
  33. data/test/test_mongodb.rb +1 -1
  34. data/test/test_null.rb +9 -0
  35. data/test/test_proxy.rb +1 -1
  36. data/test/test_pstore.rb +1 -1
  37. data/test/test_redis.rb +1 -1
  38. data/test/test_riak.rb +1 -1
  39. data/test/test_sdbm.rb +1 -1
  40. data/test/test_sequel.rb +4 -4
  41. data/test/test_sqlite.rb +1 -1
  42. data/test/test_stack.rb +10 -0
  43. data/test/test_tokyocabinet.rb +1 -1
  44. data/test/test_yaml.rb +1 -1
  45. metadata +20 -13
  46. data/unsupported/test_rackspace.rb +0 -15
  47. data/unsupported/test_s3.rb +0 -15
data/.gitignore CHANGED
@@ -2,3 +2,4 @@ test/tmp
2
2
  *~
3
3
  *.swp
4
4
  *.rdb
5
+ benchmarks/bench.*
data/.travis.yml CHANGED
@@ -2,7 +2,7 @@ rvm:
2
2
  - 1.8.7
3
3
  - 1.9.3
4
4
  # - ruby-head
5
- # - jruby
5
+ - jruby
6
6
  - rbx-18mode
7
7
  - rbx-19mode
8
8
  services:
@@ -18,8 +18,5 @@ before_install:
18
18
  - memcached -d -p 22122
19
19
  matrix:
20
20
  allow_failures:
21
- - rvm: 1.8.7
22
21
  # - rvm: ruby-head
23
- - rvm: rbx-18mode
24
- - rvm: rbx-19mode
25
-
22
+ - rvm: jruby
data/Gemfile CHANGED
@@ -1,19 +1,32 @@
1
1
  source :rubygems
2
2
  gemspec
3
3
 
4
+ if RUBY_VERSION > '1.9'
5
+ # HACK - Juno::DataMapper and CouchRest don't work currently on 1.8
6
+ gem 'datamapper'
7
+ gem 'dm-sqlite-adapter'
8
+ gem 'couchrest'
9
+ end
10
+
11
+ gem 'fog'
4
12
  gem 'activerecord'
5
- gem 'sqlite3'
6
13
  gem 'redis'
7
- gem 'datamapper'
8
- gem 'dm-sqlite-adapter'
9
- gem 'ripple'
10
- gem 'tokyocabinet'
11
14
  gem 'mongo'
12
- gem 'couchrest'
13
15
  gem 'sequel'
14
16
  gem 'dalli'
15
- gem 'memcached'
17
+ gem 'json' # Ripple/Riak needs json
18
+ gem 'ripple'
19
+
20
+ if defined?(JRUBY_VERSION)
21
+ gem 'jdbc-sqlite3'
22
+ gem 'activerecord-jdbc-adapter'
23
+ gem 'activerecord-jdbcsqlite3-adapter'
24
+ else
25
+ gem 'tokyocabinet'
26
+ gem 'memcached'
27
+ gem 'sqlite3'
28
+ end
29
+
16
30
  #gem 'cassandra'
17
31
  #gem 'localmemcache'
18
- #gem 'fog'
19
32
  #gem 'tokyotyrant'
data/README.md CHANGED
@@ -27,6 +27,8 @@ Out of the box, it supports:
27
27
  * LocalMemCache
28
28
  * Sequel
29
29
  * Sqlite3
30
+ * Fog cloud storage (Amazon S3, Rackspace, ...)
31
+ * Cassandra
30
32
 
31
33
  The Juno API is purposely extremely similar to the Hash API. In order so support an
32
34
  identical API across stores, it does not support iteration or partial matches.
@@ -61,7 +63,7 @@ The API
61
63
  Proxy store & Expiry
62
64
  ====================
63
65
 
64
- The memcached backend supports expires values directly:
66
+ The memcached and redis backends supports expires values directly:
65
67
 
66
68
  ```ruby
67
69
  cache = Juno::Memcached.new
@@ -1,6 +1,9 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'benchmark'
3
- require "rubygems"
3
+ require 'juno'
4
+ require 'dm-core'
5
+
6
+ DataMapper.setup(:default, :adapter => :in_memory)
4
7
 
5
8
  # Hacked arrays
6
9
  # Array modifications
@@ -30,27 +33,26 @@ class HackedArray < Array
30
33
  end
31
34
  end
32
35
 
33
- require "../lib/juno"
34
- require "juno/memcache"
35
- require "juno/tyrant"
36
- require "juno/berkeley"
37
-
38
-
39
36
  stores = {
40
37
  '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"},
38
+ 'MemcachedDalli' => { :server => "localhost:11211", :namespace => 'juno_dalli' },
39
+ 'MemcachedNative' => { :server => "localhost:11211", :namespace => 'juno_native' },
40
+ #'MongoDB' => { :host => 'localhost', :port => 27017, :db => 'juno_bench' },
41
+ 'LocalMemCache' => { :file => "bench.lmc" },
42
+ 'DBM' => { :file => "bench.dbm" },
43
+ # 'SDBM' => { :file => "bench.sdbm" },
44
+ 'GDBM' => { :file => "bench.gdbm" },
45
+ 'Sqlite' => { :file => ":memory:" },
47
46
  'Memory' => { },
47
+ 'YAML' => { :file => "bench.yaml" },
48
+ 'PStore' => { :file => "bench.pstore" },
49
+ 'File' => { :dir => "bench.file" },
50
+ 'HashFile' => { :dir => "bench.hashfile" },
48
51
  'DataMapper' => { :setup => "sqlite3::memory:" },
52
+ 'ActiveRecord' => { :db => "sqlite:/" },
53
+ 'ActiveRecord' => { :connection => { :adapter => 'sqlite3', :database => ':memory:' } },
54
+ 'Sequel' => { :db => "sqlite:/" },
49
55
  # '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
56
  }
55
57
 
56
58
  stats, keys, data, errors, summary = {}, [], HackedArray.new, HackedArray.new, HackedArray.new
@@ -90,66 +92,12 @@ puts " Minimum Maximum Total Average xps "
90
92
  puts "----------------------------------------------------------------------"
91
93
  puts "Lenght Stats % 10i % 10i % 10i % 10i " % [vlen_min, vlen_max, vlen_ttl, vlen_avg]
92
94
 
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
95
 
144
96
  stores.each do |name, options|
145
97
  cname = options.delete(:class_name) || name
146
98
  puts "======================================================================"
147
99
  puts name
148
100
  puts "----------------------------------------------------------------------"
149
- begin
150
- require "../lib/juno/#{cname.downcase}"
151
- rescue LoadError
152
- end
153
101
  klass = Juno.const_get(cname)
154
102
  @cache = klass.new(options)
155
103
  stats[name] = {
@@ -164,7 +112,7 @@ stores.each do |name, options|
164
112
  m1 = Benchmark.measure do
165
113
  n.times do
166
114
  key, value = data.random
167
-
115
+
168
116
  @cache[key] = value
169
117
  end
170
118
  end
@@ -221,14 +169,3 @@ end
221
169
  puts "======================================================================"
222
170
  puts "THE END"
223
171
  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
- #======================================================================
data/lib/juno.rb CHANGED
@@ -1,15 +1,13 @@
1
1
  module Juno
2
2
  autoload :ActiveRecord, 'juno/activerecord'
3
3
  autoload :Base, 'juno/base'
4
- #autoload :Cassandra, 'juno/cassandra'
4
+ autoload :Cassandra, 'juno/cassandra'
5
5
  autoload :Couch, 'juno/couch'
6
6
  autoload :DataMapper, 'juno/datamapper'
7
7
  autoload :DBM, 'juno/dbm'
8
8
  autoload :Expires, 'juno/expires'
9
9
  autoload :File, 'juno/file'
10
- #autoload :Fog, 'juno/fog'
11
- #autoload :S3, 'juno/fog'
12
- #autoload :Rackspace, 'juno/fog'
10
+ autoload :Fog, 'juno/fog'
13
11
  autoload :GDBM, 'juno/gdbm'
14
12
  autoload :HashFile, 'juno/hashfile'
15
13
  autoload :LocalMemCache, 'juno/localmemcache'
@@ -18,12 +16,14 @@ module Juno
18
16
  autoload :MemcachedNative, 'juno/memcached_native'
19
17
  autoload :Memory, 'juno/memory'
20
18
  autoload :MongoDB, 'juno/mongodb'
19
+ autoload :Null, 'juno/null'
21
20
  autoload :Proxy, 'juno/proxy'
22
21
  autoload :PStore, 'juno/pstore'
23
22
  autoload :Redis, 'juno/redis'
24
23
  autoload :Riak, 'juno/riak'
25
24
  autoload :SDBM, 'juno/sdbm'
26
25
  autoload :Sequel, 'juno/sequel'
26
+ autoload :Stack, 'juno/stack'
27
27
  autoload :Sqlite, 'juno/sqlite'
28
28
  autoload :TokyoCabinet, 'juno/tokyocabinet'
29
29
  #autoload :TokyoTyrant, 'juno/tokyotyrant'
@@ -16,12 +16,9 @@ module Juno
16
16
  c
17
17
  end
18
18
  @table.establish_connection(options[:connection]) if options[:connection]
19
- end
20
-
21
- def migrate
22
19
  @table.connection.create_table @table.table_name do |t|
23
- t.string 'key', :primary => :true
24
- t.string 'value'
20
+ t.binary 'key', :primary => :true
21
+ t.binary 'value'
25
22
  end unless @table.table_exists?
26
23
  end
27
24
 
data/lib/juno/base.rb CHANGED
@@ -13,6 +13,10 @@ module Juno
13
13
  @store.has_key?(key_for(key))
14
14
  end
15
15
 
16
+ def has_key?(key, options = {})
17
+ key?(key, options)
18
+ end
19
+
16
20
  # Fetch value with key. Return nil if the key doesn't exist
17
21
  #
18
22
  # @param [Object] key
@@ -9,30 +9,30 @@ module Juno
9
9
  options[:keyspace] ||= 'Juno'
10
10
  options[:host] ||= '127.0.0.1'
11
11
  options[:port] ||= 9160
12
- @client = ::Cassandra.new(options[:keyspace], "#{options[:host]}:#{options[:port]}")
13
12
  @column_family = options[:column_family] || :Juno
13
+ @client = ::Cassandra.new(options[:keyspace], "#{options[:host]}:#{options[:port]}")
14
14
  end
15
15
 
16
16
  def key?(key, options = {})
17
- key = key_for(key)
18
- @client.exists?(@column_family, key)
17
+ @client.exists?(@column_family, key_for(key))
19
18
  end
20
19
 
21
20
  def [](key)
22
- key = key_for(key)
23
- deserialize(@client.get(@column_family, key)['value'])
21
+ value = @client.get(@column_family, key_for(key))
22
+ value ? deserialize(value['value']) : nil
24
23
  end
25
24
 
26
25
  def delete(key, options = {})
27
- key = key_for(key)
28
- value = self[key]
29
- @client.remove(@column_family, key)
30
- value
26
+ if value = self[key]
27
+ @client.remove(@column_family, key_for(key))
28
+ value
29
+ end
31
30
  end
32
31
 
33
32
  def store(key, value, options = {})
34
- key = key_for(key)
35
- @client.insert(@column_family, key, {'value' => serialize(value)})
33
+ @client.insert(@column_family, key_for(key),
34
+ {'value' => serialize(value)}, :ttl => options[:expires])
35
+ value
36
36
  end
37
37
 
38
38
  def clear(options = {})
@@ -12,7 +12,7 @@ module Juno
12
12
  def initialize(options = {})
13
13
  raise 'No option :setup specified' unless options[:setup]
14
14
  @repository = options.delete(:repository) || :juno
15
- Store.storage_names[@repository] = options.delete(:table) || :juno
15
+ Store.storage_names[@repository] = (options.delete(:table) || :juno).to_s
16
16
  ::DataMapper.setup(@repository, options[:setup])
17
17
  context { Store.auto_upgrade! }
18
18
  end
@@ -3,13 +3,13 @@ require 'fog'
3
3
  module Juno
4
4
  class Fog < Base
5
5
  def initialize(options = {})
6
- bucket = options.delete(:namespace)
7
- cloud = options.delete(:cloud).new(options)
8
- @directory = cloud.directories.create(:key => bucket)
6
+ raise 'No option :dir specified' unless dir = options.delete(:dir)
7
+ storage = ::Fog::Storage.new(options)
8
+ @directory = storage.directories.create(:key => dir)
9
9
  end
10
10
 
11
11
  def key?(key, options = {})
12
- !@directory.files.head(key_for(key)).nil?
12
+ !!@directory.files.head(key_for(key))
13
13
  end
14
14
 
15
15
  def [](key)
@@ -21,13 +21,15 @@ module Juno
21
21
  def delete(key, options = {})
22
22
  value = get(key)
23
23
  if value
24
+ body = deserialize(value.body)
24
25
  value.destroy
25
- deserialize(value.body)
26
+ body
26
27
  end
27
28
  end
28
29
 
29
30
  def store(key, value, options = {})
30
31
  @directory.files.create(:key => key_for(key), :body => serialize(value))
32
+ value
31
33
  end
32
34
 
33
35
  def clear(options = {})
@@ -43,18 +45,4 @@ module Juno
43
45
  @directory.files.get(key_for(key))
44
46
  end
45
47
  end
46
-
47
- class S3 < Fog
48
- def initialize(options = {})
49
- options[:cloud] = ::Fog::AWS::S3
50
- super
51
- end
52
- end
53
-
54
- class Rackspace < Fog
55
- def initialize(options = {})
56
- options[:cloud] = ::Fog::Rackspace::Files
57
- super
58
- end
59
- end
60
48
  end
data/lib/juno/null.rb ADDED
@@ -0,0 +1,23 @@
1
+ module Juno
2
+ class Null < Base
3
+ def key?(key, options = {})
4
+ false
5
+ end
6
+
7
+ def [](key)
8
+ nil
9
+ end
10
+
11
+ def store(key, value, options = {})
12
+ value
13
+ end
14
+
15
+ def delete(key, options = {})
16
+ nil
17
+ end
18
+
19
+ def clear(options = {})
20
+ nil
21
+ end
22
+ end
23
+ end
data/lib/juno/redis.rb CHANGED
@@ -10,6 +10,14 @@ module Juno
10
10
  @store.exists(key_for(key))
11
11
  end
12
12
 
13
+ def fetch(key, value = nil, options = {})
14
+ result = super
15
+ if result && expires = options[:expires]
16
+ @store.expire(key_for(key), expires)
17
+ end
18
+ result
19
+ end
20
+
13
21
  def [](key)
14
22
  deserialize(@store.get(key_for(key)))
15
23
  end
@@ -22,7 +30,11 @@ module Juno
22
30
  end
23
31
 
24
32
  def store(key, value, options = {})
25
- @store.set(key_for(key), serialize(value))
33
+ key = key_for(key)
34
+ @store.set(key, serialize(value))
35
+ if expires = options[:expires]
36
+ @store.expire(key, expires)
37
+ end
26
38
  value
27
39
  end
28
40