moneta 0.7.11 → 0.7.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. data/.travis.yml +2 -0
  2. data/CHANGES +5 -0
  3. data/Gemfile +5 -5
  4. data/Rakefile +2 -2
  5. data/lib/moneta.rb +1 -1
  6. data/lib/moneta/adapters/activerecord.rb +15 -5
  7. data/lib/moneta/adapters/cassandra.rb +1 -2
  8. data/lib/moneta/adapters/datamapper.rb +1 -1
  9. data/lib/moneta/adapters/file.rb +17 -17
  10. data/lib/moneta/adapters/sequel.rb +21 -16
  11. data/lib/moneta/adapters/sqlite.rb +2 -0
  12. data/lib/moneta/transformer/config.rb +1 -1
  13. data/lib/moneta/version.rb +1 -1
  14. data/script/benchmarks +9 -9
  15. data/script/generate-specs +73 -27
  16. data/script/install-bundle +1 -0
  17. data/spec/helper.rb +5 -3
  18. data/spec/moneta/adapter_activerecord_spec.rb +5 -3
  19. data/spec/moneta/adapter_client_spec.rb +2 -0
  20. data/spec/moneta/adapter_datamapper_spec.rb +6 -5
  21. data/spec/moneta/adapter_file_spec.rb +2 -0
  22. data/spec/moneta/adapter_hbase_spec.rb +1 -0
  23. data/spec/moneta/adapter_memcached_dalli_spec.rb +2 -0
  24. data/spec/moneta/adapter_memcached_dalli_with_default_expires_spec.rb +2 -0
  25. data/spec/moneta/adapter_memcached_native_spec.rb +2 -0
  26. data/spec/moneta/adapter_memcached_native_with_default_expires_spec.rb +2 -0
  27. data/spec/moneta/adapter_memcached_spec.rb +2 -0
  28. data/spec/moneta/adapter_memcached_with_default_expires_spec.rb +2 -0
  29. data/spec/moneta/adapter_mongo_spec.rb +2 -0
  30. data/spec/moneta/adapter_mongo_with_default_expires_spec.rb +2 -0
  31. data/spec/moneta/adapter_pstore_spec.rb +2 -0
  32. data/spec/moneta/adapter_redis_spec.rb +2 -0
  33. data/spec/moneta/adapter_redis_with_default_expires_spec.rb +2 -0
  34. data/spec/moneta/adapter_sequel_spec.rb +3 -1
  35. data/spec/moneta/adapter_tokyotyrant_spec.rb +2 -0
  36. data/spec/moneta/adapter_yaml_spec.rb +2 -0
  37. data/spec/moneta/cache_file_memory_spec.rb +2 -0
  38. data/spec/moneta/expires_file_spec.rb +2 -0
  39. data/spec/moneta/pool_spec.rb +2 -0
  40. data/spec/moneta/proxy_redis_spec.rb +2 -0
  41. data/spec/moneta/shared_tcp_spec.rb +2 -0
  42. data/spec/moneta/shared_unix_spec.rb +2 -0
  43. data/spec/moneta/simple_activerecord_spec.rb +3 -1
  44. data/spec/moneta/simple_activerecord_with_expires_spec.rb +3 -1
  45. data/spec/moneta/simple_client_tcp_spec.rb +2 -0
  46. data/spec/moneta/simple_client_unix_spec.rb +2 -0
  47. data/spec/moneta/simple_datamapper_spec.rb +2 -1
  48. data/spec/moneta/simple_datamapper_with_expires_spec.rb +2 -1
  49. data/spec/moneta/simple_datamapper_with_repository_spec.rb +2 -1
  50. data/spec/moneta/simple_file_spec.rb +2 -0
  51. data/spec/moneta/simple_file_with_expires_spec.rb +2 -0
  52. data/spec/moneta/simple_hashfile_spec.rb +2 -0
  53. data/spec/moneta/simple_hashfile_with_expires_spec.rb +2 -0
  54. data/spec/moneta/simple_hbase_spec.rb +1 -0
  55. data/spec/moneta/simple_hbase_with_expires_spec.rb +2 -0
  56. data/spec/moneta/simple_memcached_dalli_spec.rb +2 -0
  57. data/spec/moneta/simple_memcached_native_spec.rb +2 -0
  58. data/spec/moneta/simple_memcached_spec.rb +2 -0
  59. data/spec/moneta/simple_mongo_spec.rb +2 -0
  60. data/spec/moneta/simple_pstore_spec.rb +2 -0
  61. data/spec/moneta/simple_pstore_with_expires_spec.rb +2 -0
  62. data/spec/moneta/simple_redis_spec.rb +2 -0
  63. data/spec/moneta/simple_sequel_spec.rb +3 -1
  64. data/spec/moneta/simple_sequel_with_expires_spec.rb +3 -1
  65. data/spec/moneta/simple_tokyotyrant_spec.rb +2 -0
  66. data/spec/moneta/simple_tokyotyrant_with_expires_spec.rb +2 -0
  67. data/spec/moneta/simple_yaml_spec.rb +2 -0
  68. data/spec/moneta/simple_yaml_with_expires_spec.rb +2 -0
  69. data/spec/moneta/stack_memory_file_spec.rb +2 -0
  70. data/spec/monetaspecs.rb +50 -0
  71. data/spec/rack/moneta_cookies_spec.rb +2 -1
  72. metadata +2 -2
data/.travis.yml CHANGED
@@ -14,6 +14,8 @@ before_install:
14
14
  - script/install-bundle
15
15
  - script/upload-bundle
16
16
  install: 'echo "Bundle installed"'
17
+ before_script:
18
+ - mysql -e 'create database moneta;'
17
19
  env:
18
20
  global:
19
21
  - secure: "B0vx1g1CB1A6mM3B/iy2ATicfS4OXT80bb2RVe8mSRsPzez1B4q4Q4hJcaMI\nrMARONN8Krtnti+IqvmDnB0Z0AKYMEyIc+zT37zJOCjLdkLJl+x/thuU/MbC\nvlLVwjMf6JE2EUzTfORDRFYc5ycCqfsfgNk1Go0D2CPT6P9u9uQ="
data/CHANGES CHANGED
@@ -1,3 +1,8 @@
1
+ 0.7.12
2
+
3
+ * Concurrency tests added
4
+ * Bugfixes for File, Sqlite, Sequel and Datamapper, ActiveRecord
5
+
1
6
  0.7.11
2
7
 
3
8
  * Logger: Add option :file
data/Gemfile CHANGED
@@ -29,9 +29,9 @@ gem 'faraday'
29
29
  gem 'daybreak'
30
30
  gem 'dm-core'
31
31
  gem 'dm-migrations'
32
- gem 'dm-sqlite-adapter'
32
+ gem 'dm-mysql-adapter'
33
33
  gem 'fog'
34
- gem 'activerecord', '>= 3.2.9'
34
+ gem 'activerecord', '>= 3.2.11'
35
35
  gem 'redis'
36
36
  gem 'mongo'
37
37
  gem 'sequel'
@@ -53,9 +53,9 @@ end
53
53
  gem 'memcached', :platforms => :ruby
54
54
  gem 'jruby-memcached', :platforms => :jruby
55
55
  gem 'sqlite3', :platforms => :ruby
56
- gem 'jdbc-sqlite3', :platforms => :jruby
57
56
  gem 'activerecord-jdbc-adapter', :platforms => :jruby
58
- gem 'activerecord-jdbcsqlite3-adapter', :platforms => :jruby
57
+ gem 'activerecord-jdbcmysql-adapter', :platforms => :jruby
58
+ gem 'mysql2', '>= 0.3.12b5', :platforms => :ruby
59
59
  # gdbm for jruby needs ffi
60
60
  gem 'ffi', :platforms => :jruby
61
61
  gem 'gdbm', :platforms => :jruby
@@ -65,5 +65,5 @@ gem 'rack'
65
65
  gem 'rack-cache'
66
66
 
67
67
  # Rails integration testing
68
- gem 'actionpack'
68
+ gem 'actionpack', '>= 3.2.11'
69
69
  gem 'minitest'
data/Rakefile CHANGED
@@ -33,8 +33,8 @@ task :test do
33
33
  # QuickLZ is also not maintained on Github, but on Bitbucket
34
34
  # and I don't know where the issue tracker is.
35
35
  #
36
- # * action_dispatch cannot be required for an unknown reason
37
- unstable = specs.select {|s| s =~ /quicklz|action_dispatch/ }
36
+ # * Cassandra fails spuriously (An expert has to check the adapter!)
37
+ unstable = specs.select {|s| s =~ /quicklz|cassandra/ }
38
38
  specs -= unstable
39
39
 
40
40
  if group =~ /^(\d+)\/(\d+)$/
data/lib/moneta.rb CHANGED
@@ -103,7 +103,7 @@ module Moneta
103
103
  raise ArgumentError, 'Name must be Symbol' unless Symbol === name
104
104
  case name
105
105
  when :Sequel, :ActiveRecord, :Couch, :DataMapper
106
- # Sequel accept only base64 keys and values
106
+ # Sequel, DataMapper and AR accept only base64 keys and values
107
107
  # FIXME: Couch should work only with :marshal but this raises an error on 1.9
108
108
  transformer[:key] << :base64
109
109
  transformer[:value] << :base64
@@ -78,11 +78,21 @@ module Moneta
78
78
 
79
79
  # (see Proxy#increment)
80
80
  def increment(key, amount = 1, options = {})
81
- record = @table.where(:k => key).lock.first_or_initialize
82
- value = Utils.to_int(record.v) + amount
83
- record.v = value.to_s
84
- record.save
85
- value
81
+ @table.transaction do
82
+ if record = @table.where(:k => key).lock.first
83
+ value = Utils.to_int(record.v) + amount
84
+ record.v = value.to_s
85
+ record.save
86
+ value
87
+ elsif create(key, amount.to_s, options)
88
+ amount
89
+ else
90
+ raise 'Concurrent modification'
91
+ end
92
+ end
93
+ rescue
94
+ tries ||= 0
95
+ (tries += 1) < 10 ? retry : raise
86
96
  end
87
97
 
88
98
  # (see Proxy#create)
@@ -62,8 +62,7 @@ module Moneta
62
62
 
63
63
  # (see Proxy#load)
64
64
  def load(key, options = {})
65
- value = @backend.get(@cf, key)
66
- if value
65
+ if value = @backend.get(@cf, key)
67
66
  expires = expires_value(options, nil)
68
67
  @backend.insert(@cf, key, {'value' => value['value'] }, :ttl => expires || nil) if expires != nil
69
68
  value['value']
@@ -12,7 +12,7 @@ module Moneta
12
12
 
13
13
  class Store
14
14
  include ::DataMapper::Resource
15
- property :k, Text, :key => true
15
+ property :k, String, :key => true, :length => 255
16
16
  property :v, Text, :lazy => false
17
17
  self.raise_on_save_failure = true
18
18
  end
@@ -6,9 +6,7 @@ module Moneta
6
6
  # @api public
7
7
  class File
8
8
  include Defaults
9
- include IncrementSupport
10
-
11
- supports :create
9
+ supports :create, :increment
12
10
 
13
11
  # @param [Hash] options
14
12
  # @option options [String] :dir Directory where files will be stored
@@ -64,7 +62,22 @@ module Moneta
64
62
 
65
63
  # (see Proxy#increment)
66
64
  def increment(key, amount = 1, options = {})
67
- lock(key) { super }
65
+ path = store_path(key)
66
+ ::File.open(path, 'rb+') do |f|
67
+ Thread.pass until f.flock(::File::LOCK_EX)
68
+ value = Utils.to_int(f.read) + amount
69
+ f.truncate(0)
70
+ f.pos = 0
71
+ f.write(value.to_s)
72
+ value
73
+ end
74
+ rescue Errno::ENOENT
75
+ if create(key, amount.to_s, options)
76
+ amount
77
+ else
78
+ # Concurrent modification
79
+ retry
80
+ end
68
81
  end
69
82
 
70
83
  # (see Proxy#create)
@@ -82,19 +95,6 @@ module Moneta
82
95
 
83
96
  protected
84
97
 
85
- def lock(key, &block)
86
- path = store_path(key)
87
- return yield unless ::File.exist?(path)
88
- ::File.open(path, 'r+') do |f|
89
- begin
90
- Thread.pass until f.flock(::File::LOCK_EX)
91
- yield
92
- ensure
93
- f.flock(::File::LOCK_UN)
94
- end
95
- end
96
- end
97
-
98
98
  def store_path(key)
99
99
  ::File.join(@dir, key)
100
100
  end
@@ -16,7 +16,7 @@ module Moneta
16
16
  # @option options All other options passed to `Sequel#connect`
17
17
  # @option options [Sequel connection] :backend Use existing backend instance
18
18
  def initialize(options = {})
19
- table = options.delete(:table) || :moneta
19
+ table = (options.delete(:table) || :moneta).to_sym
20
20
  @backend = options[:backend] ||
21
21
  begin
22
22
  raise ArgumentError, 'Option :db is required' unless db = options.delete(:db)
@@ -42,12 +42,10 @@ module Moneta
42
42
 
43
43
  # (see Proxy#store)
44
44
  def store(key, value, options = {})
45
- @backend.transaction do
46
- begin
47
- @table.insert(:k => key, :v => value)
48
- rescue ::Sequel::DatabaseError
49
- @table.update(:k => key, :v => value)
50
- end
45
+ begin
46
+ @table.insert(:k => key, :v => value)
47
+ rescue ::Sequel::DatabaseError
48
+ @table.where(:k => key).update(:v => value)
51
49
  end
52
50
  value
53
51
  rescue ::Sequel::DatabaseError
@@ -57,9 +55,7 @@ module Moneta
57
55
 
58
56
  # (see Proxy#store)
59
57
  def create(key, value, options = {})
60
- @backend.transaction do
61
- @table.insert(:k => key, :v => value)
62
- end
58
+ @table.insert(:k => key, :v => value)
63
59
  true
64
60
  rescue ::Sequel::DatabaseError
65
61
  # FIXME: This catches too many errors
@@ -73,22 +69,25 @@ module Moneta
73
69
  locked_table = @table.for_update
74
70
  if record = locked_table[:k => key]
75
71
  value = Utils.to_int(record[:v]) + amount
76
- locked_table.update(:k => key, :v => value.to_s)
72
+ locked_table.where(:k => key).update(:v => value.to_s)
77
73
  value
78
74
  else
79
75
  locked_table.insert(:k => key, :v => amount.to_s)
80
76
  amount
81
77
  end
82
78
  end
79
+ rescue ::Sequel::DatabaseError
80
+ # FIXME: This catches too many errors
81
+ # it should only catch a not-unique-exception
82
+ tries ||= 0
83
+ (tries += 1) < 10 ? retry : raise
83
84
  end
84
85
 
85
86
  # (see Proxy#delete)
86
87
  def delete(key, options = {})
87
- @backend.transaction do
88
- if value = load(key, options)
89
- @table.filter(:k => key).delete
90
- value
91
- end
88
+ if value = load(key, options)
89
+ @table.filter(:k => key).delete
90
+ value
92
91
  end
93
92
  end
94
93
 
@@ -97,6 +96,12 @@ module Moneta
97
96
  @table.delete
98
97
  self
99
98
  end
99
+
100
+ # (see Proxy#close)
101
+ def close
102
+ @backend.disconnect
103
+ nil
104
+ end
100
105
  end
101
106
  end
102
107
  end
@@ -14,6 +14,7 @@ module Moneta
14
14
  # @param [Hash] options
15
15
  # @option options [String] :file Database file
16
16
  # @option options [String] :table ('moneta') Table name
17
+ # @option options [Fixnum] :busy_timeout (1000) Sqlite timeout if database is busy
17
18
  # @option options [::Sqlite3::Database] :backend Use existing backend instance
18
19
  def initialize(options = {})
19
20
  table = options[:table] || 'moneta'
@@ -22,6 +23,7 @@ module Moneta
22
23
  raise ArgumentError, 'Option :file is required' unless options[:file]
23
24
  ::SQLite3::Database.new(options[:file])
24
25
  end
26
+ @backend.busy_timeout(options[:busy_timeout] || 1000)
25
27
  @backend.execute("create table if not exists #{table} (k blob not null primary key, v blob)")
26
28
  @stmts =
27
29
  [@select = @backend.prepare("select v from #{table} where k = ?"),
@@ -25,7 +25,7 @@ module Moneta
25
25
  [ :encode, "%s.unpack('m').first", "[%s].pack('m').gsub(\"\n\", '')" ],
26
26
  :escape => [ :encode, 'Helper.unescape(%s)', 'Helper.escape(%s)' ],
27
27
  :hmac => [ :hmac, 'Helper.hmacverify(%s, options[:secret] || @secret)',
28
- 'Helper.hmacsign(%s, options[:secret] || @secret)', 'openssl' ],
28
+ 'Helper.hmacsign(%s, options[:secret] || @secret)', 'openssl' ],
29
29
  :truncate => [ :truncate, nil, 'Helper.truncate(%s, @maxlen)', 'digest/md5' ],
30
30
  :md5 => [ :digest, nil, '::Digest::MD5.hexdigest(%s)', 'digest/md5' ],
31
31
  :rmd160 => [ :digest, nil, '::Digest::RMD160.hexdigest(%s)', 'digest/rmd160' ],
@@ -1,5 +1,5 @@
1
1
  module Moneta
2
2
  # Moneta version number
3
3
  # @api public
4
- VERSION = '0.7.11'
4
+ VERSION = '0.7.12'
5
5
  end
data/script/benchmarks CHANGED
@@ -34,18 +34,18 @@ class MonetaBenchmarks
34
34
  # SDBM is unstable, YAML is too slow
35
35
  # :SDBM => { :file => "#{DIR}/sdbm" },
36
36
  # :YAML => { :file => "#{DIR}/yaml" },
37
- :ActiveRecord => { :connection => { :adapter => 'sqlite3', :database => ':memory:' } },
37
+ :ActiveRecord => { :table => 'activerecord', :connection => { :adapter => (defined?(JRUBY_VERSION) ? 'jdbcmysql' : 'mysql2'), :database => 'moneta' } },
38
38
  :Cassandra => {},
39
39
  :Client => {},
40
40
  :Couch => {},
41
41
  :DBM => { :file => "#{DIR}/dbm" },
42
- :DataMapper => { :setup => "sqlite3:#{DIR}/datamapper" },
42
+ :DataMapper => { :setup => 'mysql://root:@localhost/moneta', :table => 'datamapper' },
43
43
  :Daybreak => { :file => "#{DIR}/daybreak" },
44
44
  :File => { :dir => "#{DIR}/file" },
45
45
  :GDBM => { :file => "#{DIR}/gdbm" },
46
46
  :HBase => {},
47
47
  :HashFile => { :dir => "#{DIR}/hashfile" },
48
- :KyotoCabinet => { :file => 'kyotocabinet.kch' },
48
+ :KyotoCabinet => { :file => "#{DIR}/kyotocabinet.kch" },
49
49
  :LRUHash => {},
50
50
  :LevelDB => { :dir => "#{DIR}/leveldb" },
51
51
  :LocalMemCache => { :file => "#{DIR}/lmc" },
@@ -57,10 +57,10 @@ class MonetaBenchmarks
57
57
  :Redis => {},
58
58
  :RestClient => { :url => 'http://localhost:8808/' },
59
59
  :Riak => {},
60
- :Sequel => { :db => 'sqlite:/' },
60
+ :Sequel => { :db => (defined?(JRUBY_VERSION) ? 'jdbc:mysql' : 'mysql2') + '://root:@localhost/moneta', :table => 'sequel' },
61
61
  :Sqlite => { :file => ':memory:' },
62
62
  :TDB => { :file => "#{DIR}/tdb" },
63
- :TokyoCabinet => { :file => 'tokyocabinet' },
63
+ :TokyoCabinet => { :file => "#{DIR}/tokyocabinet" },
64
64
  :TokyoTyrant => {},
65
65
  }
66
66
 
@@ -79,7 +79,7 @@ class MonetaBenchmarks
79
79
  :runs => 3,
80
80
  :keys => 1000,
81
81
  :min_key_len => 3,
82
- :max_key_len => 200,
82
+ :max_key_len => 128,
83
83
  :key_dist => :uniform,
84
84
  :min_val_len => 0,
85
85
  :max_val_len => 1024,
@@ -89,7 +89,7 @@ class MonetaBenchmarks
89
89
  :runs => 3,
90
90
  :keys => 100,
91
91
  :min_key_len => 3,
92
- :max_key_len => 200,
92
+ :max_key_len => 128,
93
93
  :key_dist => :uniform,
94
94
  :min_val_len => 0,
95
95
  :max_val_len => 10240,
@@ -109,7 +109,7 @@ class MonetaBenchmarks
109
109
  :runs => 3,
110
110
  :keys => 1000,
111
111
  :min_key_len => 3,
112
- :max_key_len => 200,
112
+ :max_key_len => 128,
113
113
  :key_dist => :normal,
114
114
  :min_val_len => 0,
115
115
  :max_val_len => 1024,
@@ -119,7 +119,7 @@ class MonetaBenchmarks
119
119
  :runs => 3,
120
120
  :keys => 100,
121
121
  :min_key_len => 3,
122
- :max_key_len => 200,
122
+ :max_key_len => 128,
123
123
  :key_dist => :normal,
124
124
  :min_val_len => 0,
125
125
  :max_val_len => 10240,
@@ -38,15 +38,19 @@ class Specs
38
38
  end
39
39
 
40
40
  def without_increment
41
- new(:specs => specs - [:increment] + [:not_increment])
41
+ new(:specs => specs - [:increment, :concurrent_increment] + [:not_increment])
42
+ end
43
+
44
+ def without_concurrent
45
+ new(:specs => specs - [:concurrent_increment, :concurrent_create])
42
46
  end
43
47
 
44
48
  def without_persist
45
- new(:specs => specs - [:persist, :multiprocess] + [:not_persist])
49
+ new(:specs => specs - [:persist, :multiprocess, :concurrent_increment, :concurrent_create] + [:not_persist])
46
50
  end
47
51
 
48
52
  def without_multiprocess
49
- new(:specs => specs - [:multiprocess])
53
+ new(:specs => specs - [:multiprocess, :concurrent_increment, :concurrent_create])
50
54
  end
51
55
 
52
56
  def with_expires
@@ -95,12 +99,12 @@ class Specs
95
99
  end
96
100
 
97
101
  def without_create
98
- new(:specs => specs - [:create, :create_expires] + [:not_create])
102
+ new(:specs => specs - [:create, :concurrent_create, :create_expires] + [:not_create])
99
103
  end
100
104
  end
101
105
 
102
- ADAPTER_SPECS = Specs.new(:specs => [:null, :store, :returndifferent, :increment, :persist, :multiprocess, :create, :features], :key => %w(string), :value => %w(string))
103
- STANDARD_SPECS = Specs.new(:specs => [:null, :store, :returndifferent, :marshallable_key, :marshallable_value, :transform_value, :increment, :persist, :multiprocess, :create, :features])
106
+ ADAPTER_SPECS = Specs.new(:specs => [:null, :store, :returndifferent, :increment, :concurrent_increment, :concurrent_create, :persist, :multiprocess, :create, :features], :key => %w(string), :value => %w(string))
107
+ STANDARD_SPECS = Specs.new(:specs => [:null, :store, :returndifferent, :marshallable_key, :marshallable_value, :transform_value, :increment, :concurrent_increment, :concurrent_create, :persist, :multiprocess, :create, :features])
104
108
  TRANSFORMER_SPECS = Specs.new(:specs => [:null, :store, :returndifferent, :transform_value, :increment, :create, :features])
105
109
 
106
110
  header = "# Generated by #{File.basename(__FILE__)}\n"
@@ -352,12 +356,12 @@ end
352
356
  'simple_sqlite' => {
353
357
  :store => :Sqlite,
354
358
  :options => ':file => File.join(make_tempdir, "simple_sqlite")',
355
- :specs => STANDARD_SPECS
359
+ :specs => STANDARD_SPECS.without_concurrent
356
360
  },
357
361
  'simple_sqlite_with_expires' => {
358
362
  :store => :Sqlite,
359
363
  :options => ':file => File.join(make_tempdir, "simple_sqlite_with_expires"), :expires => true',
360
- :specs => STANDARD_SPECS.with_expires
364
+ :specs => STANDARD_SPECS.with_expires.without_concurrent
361
365
  },
362
366
  'simple_redis' => {
363
367
  :store => :Redis,
@@ -419,27 +423,27 @@ end
419
423
  },
420
424
  'simple_sequel' => {
421
425
  :store => :Sequel,
422
- :options => ':db => (defined?(JRUBY_VERSION) ? "jdbc:sqlite:" : "sqlite:") + File.join(make_tempdir, "simple_sequel")',
426
+ :options => ':db => (defined?(JRUBY_VERSION) ? "jdbc:mysql" : "mysql2") + "://root:@localhost/moneta", :table => "simple_sequel"',
423
427
  :load_value => '::Marshal.load(value.unpack(\'m\').first)',
424
428
  :specs => STANDARD_SPECS
425
429
  },
426
430
  'simple_sequel_with_expires' => {
427
431
  :store => :Sequel,
428
- :options => ':db => (defined?(JRUBY_VERSION) ? "jdbc:sqlite:" : "sqlite:") + File.join(make_tempdir, "simple_sequel_with_expires"), :expires => true',
432
+ :options => ':db => (defined?(JRUBY_VERSION) ? "jdbc:mysql" : "mysql2") + "://root:@localhost/moneta", :table => "simple_sequel_with_expires", :expires => true',
429
433
  :specs => STANDARD_SPECS.with_expires,
430
434
  :load_value => '::Marshal.load(value.unpack(\'m\').first)'
431
435
  },
432
436
  'simple_datamapper' => {
433
437
  :store => :DataMapper,
434
438
  :specs => STANDARD_SPECS.without_increment,
435
- :options => ':setup => "sqlite3://#{make_tempdir}/simple_datamapper"',
439
+ :options => ':setup => "mysql://root:@localhost/moneta", :table => "simple_datamapper"',
436
440
  # DataMapper needs default repository to be setup
437
441
  :preamble => "require 'dm-core'\nDataMapper.setup(:default, :adapter => :in_memory)\n",
438
442
  :load_value => '::Marshal.load(value.unpack(\'m\').first)'
439
443
  },
440
444
  'simple_datamapper_with_expires' => {
441
445
  :store => :DataMapper,
442
- :options => ':setup => "sqlite3://#{make_tempdir}/simple_datamapper_with_expires", :expires => true',
446
+ :options => ':setup => "mysql://root:@localhost/moneta", :table => "simple_datamapper_with_expires", :expires => true',
443
447
  # DataMapper needs default repository to be setup
444
448
  :preamble => "require 'dm-core'\nDataMapper.setup(:default, :adapter => :in_memory)\n",
445
449
  :specs => STANDARD_SPECS.without_increment.with_expires,
@@ -448,7 +452,7 @@ end
448
452
  'simple_datamapper_with_repository' => {
449
453
  :store => :DataMapper,
450
454
  :specs => STANDARD_SPECS.without_increment,
451
- :options => ':repository => :repo, :setup => "sqlite3://#{make_tempdir}/simple_datamapper_with_repository"',
455
+ :options => ':repository => :repo, :setup => "mysql://root:@localhost/moneta", :table => "simple_datamapper_with_repository"',
452
456
  # DataMapper needs default repository to be setup
453
457
  :preamble => "require 'dm-core'\nDataMapper.setup(:default, :adapter => :in_memory)\n",
454
458
  :load_value => '::Marshal.load(value.unpack(\'m\').first)'
@@ -456,12 +460,12 @@ end
456
460
  'simple_activerecord' => {
457
461
  :store => :ActiveRecord,
458
462
  :specs => STANDARD_SPECS,
459
- :options => ":connection => { :adapter => (defined?(JRUBY_VERSION) ? 'jdbcsqlite3' : 'sqlite3'), :database => File.join(make_tempdir, 'simple_activerecord') }",
463
+ :options => ":table => 'simple_activerecord', :connection => { :adapter => (defined?(JRUBY_VERSION) ? 'jdbcmysql' : 'mysql2'), :database => 'moneta' }",
460
464
  :load_value => '::Marshal.load(value.unpack(\'m\').first)'
461
465
  },
462
466
  'simple_activerecord_with_expires' => {
463
467
  :store => :ActiveRecord,
464
- :options => ":connection => { :adapter => (defined?(JRUBY_VERSION) ? 'jdbcsqlite3' : 'sqlite3'), :database => File.join(make_tempdir, 'simple_activerecord_with_expires') }, :expires => true",
468
+ :options => ":table => 'simple_activerecord_with_expires', :connection => { :adapter => (defined?(JRUBY_VERSION) ? 'jdbcmysql' : 'mysql2'), :database => 'moneta' }, :expires => true",
465
469
  :specs => STANDARD_SPECS.with_expires,
466
470
  :load_value => '::Marshal.load(value.unpack(\'m\').first)'
467
471
  },
@@ -491,14 +495,14 @@ end
491
495
  use :WeakCreate
492
496
  adapter :Couch, :db => 'weak_create'
493
497
  end},
494
- :specs => ADAPTER_SPECS.without_increment
498
+ :specs => ADAPTER_SPECS.without_increment.without_concurrent
495
499
  },
496
500
  'weak_increment' => {
497
501
  :build => %{Moneta.build do
498
502
  use :WeakIncrement
499
503
  adapter :Couch, :db => 'weak_increment'
500
504
  end},
501
- :specs => ADAPTER_SPECS.without_create
505
+ :specs => ADAPTER_SPECS.without_create.without_concurrent
502
506
  },
503
507
  'expires_memory' => {
504
508
  :build => %{Moneta.build do
@@ -1097,7 +1101,7 @@ it 'compile transformer class' do
1097
1101
  end}
1098
1102
  },
1099
1103
  'adapter_activerecord' => {
1100
- :build => "Moneta::Adapters::ActiveRecord.new(:connection => { :adapter => (defined?(JRUBY_VERSION) ? 'jdbcsqlite3' : 'sqlite3'), :database => File.join(make_tempdir, 'adapter_activerecord') })",
1104
+ :build => "Moneta::Adapters::ActiveRecord.new(:table => 'adapter_activerecord', :connection => { :adapter => (defined?(JRUBY_VERSION) ? 'jdbcmysql' : 'mysql2'), :database => 'moneta' })",
1101
1105
  :specs => ADAPTER_SPECS,
1102
1106
  :tests => %{
1103
1107
  it 'updates an existing key/value' do
@@ -1108,9 +1112,9 @@ it 'updates an existing key/value' do
1108
1112
  end
1109
1113
 
1110
1114
  it 'uses an existing connection' do
1111
- ActiveRecord::Base.establish_connection :adapter => (defined?(JRUBY_VERSION) ? 'jdbcsqlite3' : 'sqlite3'), :database => File.join(make_tempdir, 'activerecord-existing')
1115
+ ActiveRecord::Base.establish_connection :adapter => (defined?(JRUBY_VERSION) ? 'jdbcmysql' : 'mysql2'), :database => 'moneta'
1112
1116
 
1113
- store = Moneta::Adapters::ActiveRecord.new
1117
+ store = Moneta::Adapters::ActiveRecord.new(:table => 'activerecord_existing')
1114
1118
  store.table.should be_table_exists
1115
1119
  end
1116
1120
  }
@@ -1146,16 +1150,16 @@ end
1146
1150
  :specs => ADAPTER_SPECS.without_increment.without_create
1147
1151
  },
1148
1152
  'adapter_datamapper' => {
1149
- :build => 'Moneta::Adapters::DataMapper.new(:setup => "sqlite3://#{make_tempdir}/adapter_datamapper")',
1153
+ :build => 'Moneta::Adapters::DataMapper.new(:setup => "mysql://root:@localhost/moneta", :table => "adapter_datamapper")',
1150
1154
  # DataMapper needs default repository to be setup
1151
1155
  :preamble => "require 'dm-core'\nDataMapper.setup(:default, :adapter => :in_memory)\n",
1152
1156
  :specs => ADAPTER_SPECS.without_increment,
1153
1157
  :tests => %q{
1154
1158
  it 'does not cross contaminate when storing' do
1155
- first = Moneta::Adapters::DataMapper.new(:setup => "sqlite3://#{make_tempdir}/datamapper-first")
1159
+ first = Moneta::Adapters::DataMapper.new(:setup => "mysql://root:@localhost/moneta", :table => "datamapper_first")
1156
1160
  first.clear
1157
1161
 
1158
- second = Moneta::Adapters::DataMapper.new(:repository => :sample, :setup => "sqlite3://#{make_tempdir}/datamapper-second")
1162
+ second = Moneta::Adapters::DataMapper.new(:repository => :sample, :setup => "mysql://root:@localhost/moneta", :table => "datamapper_second")
1159
1163
  second.clear
1160
1164
 
1161
1165
  first['key'] = 'value'
@@ -1166,10 +1170,10 @@ it 'does not cross contaminate when storing' do
1166
1170
  end
1167
1171
 
1168
1172
  it 'does not cross contaminate when deleting' do
1169
- first = Moneta::Adapters::DataMapper.new(:setup => "sqlite3://#{make_tempdir}/datamapper-first")
1173
+ first = Moneta::Adapters::DataMapper.new(:setup => "mysql://root:@localhost/moneta", :table => "datamapper_first")
1170
1174
  first.clear
1171
1175
 
1172
- second = Moneta::Adapters::DataMapper.new(:repository => :sample, :setup => "sqlite3://#{make_tempdir}/datamapper-second")
1176
+ second = Moneta::Adapters::DataMapper.new(:repository => :sample, :setup => "mysql://root:@localhost/moneta", :table => "datamapper_second")
1173
1177
  second.clear
1174
1178
 
1175
1179
  first['key'] = 'value'
@@ -1304,12 +1308,12 @@ end}
1304
1308
  :specs => ADAPTER_SPECS.without_multiprocess
1305
1309
  },
1306
1310
  'adapter_sequel' => {
1307
- :build => 'Moneta::Adapters::Sequel.new(:db => (defined?(JRUBY_VERSION) ? "jdbc:sqlite:" : "sqlite:") + File.join(make_tempdir, "adapter_sequel"))',
1311
+ :build => 'Moneta::Adapters::Sequel.new(:db => (defined?(JRUBY_VERSION) ? "jdbc:mysql" : "mysql2") + "://root:@localhost/moneta", :table => "adapter_sequel")',
1308
1312
  :specs => ADAPTER_SPECS
1309
1313
  },
1310
1314
  'adapter_sqlite' => {
1311
1315
  :build => 'Moneta::Adapters::Sqlite.new(:file => File.join(make_tempdir, "adapter_sqlite"))',
1312
- :specs => ADAPTER_SPECS
1316
+ :specs => ADAPTER_SPECS.without_concurrent
1313
1317
  },
1314
1318
  'adapter_kyotocabinet' => {
1315
1319
  :build => 'Moneta::Adapters::KyotoCabinet.new(:file => File.join(make_tempdir, "adapter_kyotocabinet.kch"))',
@@ -1966,6 +1970,48 @@ it 'does not support #decrement' do
1966
1970
  end.to raise_error(NotImplementedError)
1967
1971
  end}
1968
1972
 
1973
+ SPECS['concurrent_increment'] = %{def increment_thread(name)
1974
+ Thread.new do
1975
+ s = new_store
1976
+ 1000.times do |i|
1977
+ s.increment('counter')
1978
+ s.store("\#{name}\#{i}", i.to_s, :expires => false)
1979
+ sleep 0.01 if i % 100 == 0
1980
+ end
1981
+ s.close
1982
+ end
1983
+ end
1984
+
1985
+ it 'have atomic increment across multiple processes' do
1986
+ a = increment_thread('a')
1987
+ b = increment_thread('b')
1988
+ a.join
1989
+ b.join
1990
+ 1000.times do |i|
1991
+ store["a\#{i}"].should == i.to_s
1992
+ store["b\#{i}"].should == i.to_s
1993
+ end
1994
+ store.raw['counter'].should == 2000.to_s
1995
+ end}
1996
+
1997
+ SPECS['concurrent_create'] = %{def create_thread(name)
1998
+ a = Thread.new do
1999
+ s = new_store
2000
+ 1000.times do |i|
2001
+ s[i.to_s].should == name if s.create(i.to_s, name, :expires => false)
2002
+ sleep 0.01 if i % 100 == 0
2003
+ end
2004
+ s.close
2005
+ end
2006
+ end
2007
+
2008
+ it 'have atomic create across multiple processes' do
2009
+ a = create_thread('a')
2010
+ b = create_thread('b')
2011
+ a.join
2012
+ b.join
2013
+ end}
2014
+
1969
2015
  SPECS['increment'] = %{it 'initializes in #increment with 1' do
1970
2016
  store.key?('inckey').should be_false
1971
2017
  store.increment('inckey').should == 1