moneta 1.2.1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.rubocop.yml +1 -1
  4. data/.travis.yml +10 -4
  5. data/CHANGES +9 -0
  6. data/Gemfile +7 -5
  7. data/README.md +9 -6
  8. data/feature_matrix.yaml +2 -1
  9. data/lib/moneta/adapters/client.rb +56 -19
  10. data/lib/moneta/adapters/couch.rb +5 -0
  11. data/lib/moneta/adapters/mongo/moped.rb +4 -1
  12. data/lib/moneta/builder.rb +2 -2
  13. data/lib/moneta/lock.rb +6 -1
  14. data/lib/moneta/pool.rb +12 -0
  15. data/lib/moneta/proxy.rb +1 -1
  16. data/lib/moneta/server.rb +215 -61
  17. data/lib/moneta/shared.rb +13 -7
  18. data/lib/moneta/transformer.rb +50 -8
  19. data/lib/moneta/transformer/config.rb +59 -40
  20. data/lib/moneta/version.rb +1 -1
  21. data/moneta.gemspec +2 -2
  22. data/script/benchmarks +6 -1
  23. data/script/contributors +1 -2
  24. data/script/start-couchdb +27 -0
  25. data/script/start-hbase +2 -2
  26. data/script/start-services +3 -3
  27. data/spec/features/store.rb +3 -3
  28. data/spec/features/transform_value.rb +27 -21
  29. data/spec/helper.rb +52 -53
  30. data/spec/moneta/adapters/activerecord/standard_activerecord_spec.rb +1 -1
  31. data/spec/moneta/adapters/activerecord/standard_activerecord_with_expires_spec.rb +1 -1
  32. data/spec/moneta/adapters/cassandra/standard_cassandra_spec.rb +1 -1
  33. data/spec/moneta/adapters/client/adapter_client_spec.rb +6 -6
  34. data/spec/moneta/adapters/client/client_helper.rb +24 -0
  35. data/spec/moneta/adapters/client/standard_client_tcp_spec.rb +8 -8
  36. data/spec/moneta/adapters/client/standard_client_unix_spec.rb +23 -7
  37. data/spec/moneta/adapters/couch/adapter_couch_spec.rb +1 -1
  38. data/spec/moneta/adapters/couch/standard_couch_spec.rb +2 -2
  39. data/spec/moneta/adapters/couch/standard_couch_with_expires_spec.rb +2 -2
  40. data/spec/moneta/adapters/daybreak/standard_daybreak_spec.rb +1 -1
  41. data/spec/moneta/adapters/daybreak/standard_daybreak_with_expires_spec.rb +1 -1
  42. data/spec/moneta/adapters/dbm/standard_dbm_spec.rb +1 -1
  43. data/spec/moneta/adapters/dbm/standard_dbm_with_expires_spec.rb +1 -1
  44. data/spec/moneta/adapters/file/standard_file_spec.rb +2 -2
  45. data/spec/moneta/adapters/file/standard_file_with_expires_spec.rb +1 -1
  46. data/spec/moneta/adapters/gdbm/standard_gdbm_spec.rb +1 -1
  47. data/spec/moneta/adapters/gdbm/standard_gdbm_with_expires_spec.rb +1 -1
  48. data/spec/moneta/adapters/kyotocabinet/adapter_kyotocabinet_spec.rb +1 -1
  49. data/spec/moneta/adapters/kyotocabinet/standard_kyotocabinet_spec.rb +2 -2
  50. data/spec/moneta/adapters/kyotocabinet/standard_kyotocabinet_with_expires_spec.rb +2 -2
  51. data/spec/moneta/adapters/leveldb/standard_leveldb_spec.rb +1 -1
  52. data/spec/moneta/adapters/leveldb/standard_leveldb_with_expires_spec.rb +1 -1
  53. data/spec/moneta/adapters/lmdb/standard_lmdb_spec.rb +1 -1
  54. data/spec/moneta/adapters/lmdb/standard_lmdb_with_expires_spec.rb +1 -1
  55. data/spec/moneta/adapters/lruhash/standard_lruhash_spec.rb +1 -1
  56. data/spec/moneta/adapters/lruhash/standard_lruhash_with_expires_spec.rb +1 -1
  57. data/spec/moneta/adapters/memory/standard_memory_spec.rb +1 -1
  58. data/spec/moneta/adapters/memory/standard_memory_with_compress_spec.rb +1 -1
  59. data/spec/moneta/adapters/memory/standard_memory_with_expires_spec.rb +1 -1
  60. data/spec/moneta/adapters/memory/standard_memory_with_json_key_serializer_spec.rb +1 -1
  61. data/spec/moneta/adapters/memory/standard_memory_with_json_serializer_spec.rb +1 -1
  62. data/spec/moneta/adapters/memory/standard_memory_with_json_value_serializer_spec.rb +2 -2
  63. data/spec/moneta/adapters/memory/standard_memory_with_prefix_spec.rb +39 -2
  64. data/spec/moneta/adapters/memory/standard_memory_with_snappy_compress_spec.rb +2 -2
  65. data/spec/moneta/adapters/mongo/adapter_mongo_moped_spec.rb +4 -3
  66. data/spec/moneta/adapters/mongo/adapter_mongo_moped_with_default_expires_spec.rb +5 -3
  67. data/spec/moneta/adapters/mongo/adapter_mongo_official_spec.rb +4 -2
  68. data/spec/moneta/adapters/mongo/adapter_mongo_official_with_default_expires_spec.rb +5 -3
  69. data/spec/moneta/adapters/mongo/adapter_mongo_spec.rb +3 -2
  70. data/spec/moneta/adapters/mongo/adapter_mongo_with_default_expires_spec.rb +5 -3
  71. data/spec/moneta/adapters/mongo/standard_mongo_moped_spec.rb +2 -2
  72. data/spec/moneta/adapters/mongo/standard_mongo_official_spec.rb +2 -2
  73. data/spec/moneta/adapters/mongo/standard_mongo_spec.rb +2 -2
  74. data/spec/moneta/adapters/pstore/standard_pstore_spec.rb +1 -1
  75. data/spec/moneta/adapters/pstore/standard_pstore_with_expires_spec.rb +1 -1
  76. data/spec/moneta/adapters/redis/standard_redis_spec.rb +1 -1
  77. data/spec/moneta/adapters/sdbm/standard_sdbm_spec.rb +1 -1
  78. data/spec/moneta/adapters/sdbm/standard_sdbm_with_expires_spec.rb +1 -1
  79. data/spec/moneta/adapters/sequel/standard_sequel_spec.rb +1 -1
  80. data/spec/moneta/adapters/sequel/standard_sequel_with_expires_spec.rb +1 -1
  81. data/spec/moneta/adapters/sqlite/standard_sqlite_spec.rb +1 -1
  82. data/spec/moneta/adapters/sqlite/standard_sqlite_with_expires_spec.rb +1 -1
  83. data/spec/moneta/adapters/tdb/standard_tdb_spec.rb +1 -1
  84. data/spec/moneta/adapters/tdb/standard_tdb_with_expires_spec.rb +1 -1
  85. data/spec/moneta/adapters/tokyocabinet/standard_tokyocabinet_spec.rb +1 -1
  86. data/spec/moneta/adapters/tokyocabinet/standard_tokyocabinet_with_expires_spec.rb +1 -1
  87. data/spec/moneta/adapters/yaml/standard_yaml_spec.rb +1 -1
  88. data/spec/moneta/adapters/yaml/standard_yaml_with_expires_spec.rb +1 -1
  89. data/spec/moneta/builder_spec.rb +22 -0
  90. data/spec/moneta/proxies/expires/expires_file_spec.rb +1 -1
  91. data/spec/moneta/proxies/shared/shared_tcp_spec.rb +14 -4
  92. data/spec/moneta/proxies/shared/shared_unix_spec.rb +4 -4
  93. data/spec/moneta/proxies/transformer/transformer_bencode_spec.rb +1 -1
  94. data/spec/moneta/proxies/transformer/transformer_bert_spec.rb +3 -3
  95. data/spec/moneta/proxies/transformer/transformer_bson_spec.rb +2 -2
  96. data/spec/moneta/proxies/transformer/transformer_json_spec.rb +1 -1
  97. data/spec/moneta/proxies/transformer/transformer_key_marshal_spec.rb +1 -1
  98. data/spec/moneta/proxies/transformer/transformer_key_yaml_spec.rb +1 -1
  99. data/spec/moneta/proxies/transformer/transformer_marshal_base64_spec.rb +1 -1
  100. data/spec/moneta/proxies/transformer/transformer_marshal_escape_spec.rb +1 -1
  101. data/spec/moneta/proxies/transformer/transformer_marshal_hex_spec.rb +1 -1
  102. data/spec/moneta/proxies/transformer/transformer_marshal_hmac_spec.rb +1 -1
  103. data/spec/moneta/proxies/transformer/transformer_marshal_prefix_base64_spec.rb +33 -0
  104. data/spec/moneta/proxies/transformer/transformer_marshal_prefix_spec.rb +1 -1
  105. data/spec/moneta/proxies/transformer/transformer_marshal_qp_spec.rb +1 -1
  106. data/spec/moneta/proxies/transformer/transformer_marshal_spec.rb +1 -1
  107. data/spec/moneta/proxies/transformer/transformer_marshal_urlsafe_base64_spec.rb +1 -1
  108. data/spec/moneta/proxies/transformer/transformer_marshal_uuencode_spec.rb +1 -1
  109. data/spec/moneta/proxies/transformer/transformer_msgpack_spec.rb +1 -1
  110. data/spec/moneta/proxies/transformer/transformer_ox_spec.rb +1 -1
  111. data/spec/moneta/proxies/transformer/transformer_php_spec.rb +1 -1
  112. data/spec/moneta/proxies/transformer/transformer_tnet_spec.rb +1 -1
  113. data/spec/moneta/proxies/transformer/transformer_yaml_spec.rb +2 -2
  114. data/spec/moneta/proxies/weak_each_key/weak_each_key_spec.rb +0 -2
  115. data/spec/support/mongo_helper.rb +12 -0
  116. metadata +18 -12
  117. data/script/reconfigure-couchdb +0 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 535578909c47f29047f67d577e053809a637f77ea8ba2dde7920198f8b08ac49
4
- data.tar.gz: 275edd79995139f4918749d01ccdf8129748ea96e1ba4302716dd7cb1ed57bf9
3
+ metadata.gz: 9aebec0b5bcf8b12d295aff540c30c453e88883e5d5d498f92f3666adcbf4eeb
4
+ data.tar.gz: f62cbb548556e6d4a36e0465edb468a7421262620469d7329c34cb7d160553bc
5
5
  SHA512:
6
- metadata.gz: c07c093306f66138db9db041a15103420fdd0e5fef93ca429098de02a732055d441c0968570704ba39dcb08608b7dbfa12b5cd5797e257df1c8be28a7b3b028d
7
- data.tar.gz: a654005281162398bf492fd7fa3e3c59ee40f675d6a187579d4cbfcb0bfbd14e0a78a9f9bb3227e5c64c02e4c97e60bb9b39075457803882aeaa3d585b0e9a51
6
+ metadata.gz: e9544cff0b2b3550776646d83131b8c7db4c3a9e79083ddb56af23a4cffaa9ce5a3de4a8c1db2db3571e768fe00c409692c3a2787ac9e7e8312a3f145d36f270
7
+ data.tar.gz: 96cf85c80a0dbb86c820351e711470f873139e6544ec450e427a5cba92cf23f615bedd7f2ff5fda7eca8f005631e0dbcb5e79550ef001234b6d231fcaa71c924
data/.gitignore CHANGED
@@ -11,3 +11,5 @@ Gemfile.lock
11
11
  logs
12
12
  secure
13
13
  .bundle
14
+ .ruby-version
15
+ .byebug_history
data/.rubocop.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  AllCops:
2
- TargetRubyVersion: 2.2.2
2
+ TargetRubyVersion: 2.3.0
3
3
  UseCache: true
4
4
  Exclude:
5
5
  - hbase/**/*
data/.travis.yml CHANGED
@@ -1,12 +1,12 @@
1
1
  dist: xenial
2
2
  language: ruby
3
3
  rvm:
4
+ - 2.7
4
5
  - 2.6
5
6
  - 2.5
6
7
  - 2.4
7
8
  - 2.3
8
- - 2.2
9
- - jruby-9.2
9
+ - jruby-9.2.9.0
10
10
  - rbx-3
11
11
  services:
12
12
  - mysql
@@ -26,7 +26,6 @@ addons:
26
26
  key_url: https://packagecloud.io/basho/riak/gpgkey
27
27
  packages:
28
28
  - cassandra
29
- - couchdb
30
29
  - libkyotocabinet-dev
31
30
  - libleveldb-dev
32
31
  - liblzo2-dev
@@ -36,6 +35,7 @@ addons:
36
35
  - riak
37
36
  postgresql: '9.6'
38
37
  before_install:
38
+ - gem install bundler
39
39
  - script/start-services
40
40
  cache:
41
41
  bundler: true
@@ -47,6 +47,10 @@ before_script:
47
47
  - psql -c 'create database moneta1;' -U postgres
48
48
  - psql -c 'create extension hstore;' -U postgres moneta1
49
49
  - psql -c 'create database moneta2;' -U postgres
50
+ # FIXME: remove this once moneta no longer generates hundreds of warnings
51
+ # in Ruby 2.7
52
+ - if echo $RUBY_VERSION | grep -q 'ruby-2.7'; then export RUBYOPT='-W0'; fi
53
+ - export
50
54
  env:
51
55
  matrix:
52
56
  - SCRIPT='script/parallel-tests ~unstable --
@@ -120,6 +124,8 @@ matrix:
120
124
  - rvm: rbx-3
121
125
  - env: SCRIPT='script/parallel-tests unstable'
122
126
  exclude:
127
+ - rvm: 2.6
128
+ env: SCRIPT='rubocop lib'
123
129
  - rvm: 2.5
124
130
  env: SCRIPT='rubocop lib'
125
131
  - rvm: 2.4
@@ -128,7 +134,7 @@ matrix:
128
134
  env: SCRIPT='rubocop lib'
129
135
  - rvm: 2.2
130
136
  env: SCRIPT='rubocop lib'
131
- - rvm: jruby-9.2
137
+ - rvm: jruby-9.2.9.0
132
138
  env: SCRIPT='rubocop lib'
133
139
  - rvm: rbx-3
134
140
  env: SCRIPT='rubocop lib'
data/CHANGES CHANGED
@@ -1,3 +1,12 @@
1
+ 1.3.0
2
+
3
+ * Transformer - add :each_key support (#170)
4
+ * Server - add :each_key support, use non-blocking IO (#165)
5
+ * Builder - dup options before passing to adapter/proxy (#174)
6
+ * Adapter::Couch - add HTTP basic auth support
7
+ * Support MRI 2.7.0 (#172)
8
+ * Minimum required MRI version is now 2.3.0 (#172)
9
+
1
10
  1.2.1
2
11
 
3
12
  * Transformer - fix :escape transformer deserialize implementation (#168)
data/Gemfile CHANGED
@@ -23,7 +23,7 @@ group :msgpack do
23
23
  end
24
24
 
25
25
  # Compressors used by Transformer
26
- gem 'rbzip2', '~> 0.3.0', group: :bzip2
26
+ gem 'rbzip2', '>= 0.3.0', group: :bzip2
27
27
  gem 'lz4-ruby', platforms: :ruby, group: :lz4
28
28
  gem 'ruby-lzma', platforms: :ruby, group: :lzma
29
29
  gem 'lzoruby', platforms: :ruby, group: :lzo
@@ -37,8 +37,8 @@ gem 'cityhash', platforms: :ruby, group: :city
37
37
  gem 'daybreak', group: :daybreak
38
38
  gem 'activerecord', '~> 5.2', group: :activerecord
39
39
  gem 'redis', '~> 4.0.0', group: :redis
40
- gem 'mongo', '~> 2.1.0', group: :mongo_official
41
- gem 'moped', '>= 2.0.0', group: :mongo_moped
40
+ gem 'mongo', '>= 2', group: :mongo_official
41
+ gem 'moped', '>= 2', group: :mongo_moped
42
42
  gem 'sequel', group: :sequel
43
43
  gem 'dalli', group: :memcached_dalli
44
44
  gem 'riak-client', group: :riak
@@ -50,7 +50,7 @@ gem 'tdb', platforms: :ruby, group: :tdb
50
50
  gem 'leveldb-ruby', platforms: :ruby, group: :leveldb
51
51
  gem 'lmdb', platforms: :mri, group: :lmdb
52
52
  gem 'tokyocabinet', platforms: :ruby, group: :tokyocabinet
53
- gem 'kyotocabinet-ruby-reanimated', platforms: :ruby, group: :kyotocabinet
53
+ gem 'kyotocabinet-ruby-reanimated', platforms: [:ruby_23, :ruby_24, :ruby_25, :ruby_26], group: :kyotocabinet
54
54
  gem 'memcached', platforms: :ruby, group: :memcached_native
55
55
  gem 'jruby-memcached', platforms: :jruby, group: :memcached_native
56
56
  gem 'activerecord-jdbch2-adapter', platforms: :jruby, group: :h2, github: 'jruby/activerecord-jdbc-adapter', glob: 'activerecord-jdbch2-adapter/*.gemspec', branch: '52-stable'
@@ -94,7 +94,9 @@ end
94
94
 
95
95
  # Rails integration testing
96
96
  group :rails do
97
- gem 'actionpack', '~> 5.0'
97
+ git 'https://github.com/rails/rails.git', branch: '5-2-stable' do
98
+ gem 'actionpack'
99
+ end
98
100
  gem 'minitest', '~> 5.0'
99
101
  end
100
102
 
data/README.md CHANGED
@@ -214,7 +214,7 @@ __NOTE:__ <a name="backend-matrix"></a>The backend matrix is much more readable
214
214
 
215
215
  <tr><th colspan="2">Network clients</th><th colspan="12"></th></tr>
216
216
 
217
- <tr><td>Client</td><td>-</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#F44">✗</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#55F">?<sup>13</sup></td><td style="text-align:center;background:#55F">?<sup>13</sup></td><td style="text-align:center;background:#55F">?<sup>13</sup></td><td style="text-align:center;background:#55F">?<sup>13</sup></td><td style="text-align:center;background:#F44">✗</td><td style="text-align:center;background:#F44">✗</td><td style="text-align:center;background:#F44">✗</td><td>Moneta client adapter</td></tr>
217
+ <tr><td>Client</td><td>-</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#F44">✗</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#55F">?<sup>13</sup></td><td style="text-align:center;background:#55F">?<sup>13</sup></td><td style="text-align:center;background:#55F">?<sup>13</sup></td><td style="text-align:center;background:#55F">?<sup>13</sup></td><td style="text-align:center;background:#55F">?<sup>13</sup></td><td style="text-align:center;background:#F44">✗</td><td style="text-align:center;background:#F44">✗</td><td>Moneta client adapter</td></tr>
218
218
 
219
219
  <tr><td>RestClient</td><td>-</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#F44">✗</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#F44">✗</td><td style="text-align:center;background:#F44">✗</td><td style="text-align:center;background:#F44">✗</td><td style="text-align:center;background:#55F">?<sup>13</sup></td><td style="text-align:center;background:#F44">✗</td><td style="text-align:center;background:#F44">✗</td><td style="text-align:center;background:#F44">✗</td><td>Moneta REST client adapter</td></tr>
220
220
 
@@ -913,12 +913,15 @@ Person.adapter :memory, Moneta.new(:Redis)
913
913
 
914
914
  ## Testing and Benchmarks
915
915
 
916
- Testing is done using [Travis-CI](http://travis-ci.org/moneta-rb/moneta). Currently we support MRI Ruby >= 2.2.2 and JRuby 9.2.x.
916
+ Testing is done using [Travis-CI](http://travis-ci.org/moneta-rb/moneta).
917
+ Currently we support MRI Ruby >= 2.3.0 and the JRuby >= 9.2.9.0.
917
918
 
918
- Benchmarks for each store are done on [Travis-CI](http://travis-ci.org/moneta-rb/moneta) for each build. Take a look there
919
- to compare the speed of the different key value stores for different key/value sizes and size distributions.
920
- Feel free to add your own configurations! The impact of Moneta should be minimal since it is only a thin layer
921
- on top of the different stores.
919
+ Benchmarks for each store are done on
920
+ [Travis-CI](http://travis-ci.org/moneta-rb/moneta) for each build. Take a look
921
+ there to compare the speed of the different key value stores for different
922
+ key/value sizes and size distributions. Feel free to add your own
923
+ configurations! The impact of Moneta should be minimal since it is only a thin
924
+ layer on top of the different stores.
922
925
 
923
926
 
924
927
  ------
data/feature_matrix.yaml CHANGED
@@ -220,13 +220,14 @@ backends:
220
220
  - adapter: Client
221
221
  platforms: [ MRI, JRuby ]
222
222
  features: [multiprocess]
223
- unknown: [ increment, create, expires, persist ]
223
+ unknown: [ increment, create, expires, persist, each_key ]
224
224
  description: "Moneta client adapter"
225
225
  notes:
226
226
  increment: depends on server
227
227
  create: depends on server
228
228
  expires: depends on server
229
229
  persist: depends on server
230
+ each_key: depends on server
230
231
  - adapter: RestClient
231
232
  platforms: [ MRI, JRuby ]
232
233
  features: [ multiprocess ]
@@ -23,44 +23,44 @@ module Moneta
23
23
  # (see Proxy#key?)
24
24
  def key?(key, options = {})
25
25
  write(:key?, key, options)
26
- read
26
+ read_msg
27
27
  end
28
28
 
29
29
  # (see Proxy#load)
30
30
  def load(key, options = {})
31
31
  write(:load, key, options)
32
- read
32
+ read_msg
33
33
  end
34
34
 
35
35
  # (see Proxy#store)
36
36
  def store(key, value, options = {})
37
37
  write(:store, key, value, options)
38
- read
38
+ read_msg
39
39
  value
40
40
  end
41
41
 
42
42
  # (see Proxy#delete)
43
43
  def delete(key, options = {})
44
44
  write(:delete, key, options)
45
- read
45
+ read_msg
46
46
  end
47
47
 
48
48
  # (see Proxy#increment)
49
49
  def increment(key, amount = 1, options = {})
50
50
  write(:increment, key, amount, options)
51
- read
51
+ read_msg
52
52
  end
53
53
 
54
54
  # (see Proxy#create)
55
55
  def create(key, value, options = {})
56
56
  write(:create, key, value, options)
57
- read
57
+ read_msg
58
58
  end
59
59
 
60
60
  # (see Proxy#clear)
61
61
  def clear(options = {})
62
62
  write(:clear, options)
63
- read
63
+ read_msg
64
64
  self
65
65
  end
66
66
 
@@ -70,12 +70,43 @@ module Moneta
70
70
  nil
71
71
  end
72
72
 
73
+ # (see Proxy#each_key)
74
+ def each_key
75
+ raise NotImplementedError, 'each_key is not supported' unless supports?(:each_key)
76
+ return enum_for(:each_key) unless block_given?
77
+
78
+ begin
79
+ write(:each_key)
80
+ yield_break = false
81
+
82
+ loop do
83
+ write('NEXT')
84
+
85
+ # A StopIteration error will be raised by this call if the server
86
+ # reached the end of the enumeration. This will stop the loop
87
+ # automatically.
88
+ result = read_msg
89
+
90
+ # yield_break will be true in the ensure block (below) if anything
91
+ # happened during the yield to stop further enumeration.
92
+ yield_break = true
93
+ yield result
94
+ yield_break = false
95
+ end
96
+ ensure
97
+ write('BREAK') if yield_break
98
+ read_msg # nil return from each_key
99
+ end
100
+
101
+ self
102
+ end
103
+
73
104
  # (see Default#features)
74
105
  def features
75
106
  @features ||=
76
107
  begin
77
108
  write(:features)
78
- read.freeze
109
+ read_msg.freeze
79
110
  end
80
111
  end
81
112
 
@@ -86,18 +117,24 @@ module Moneta
86
117
  @socket.write([s.bytesize].pack('N') << s)
87
118
  end
88
119
 
89
- def read
90
- size = @socket
91
- .recv(4).tap do |bytes|
92
- raise EOFError, 'failed to read size' unless bytes.bytesize == 4
93
- end
94
- .unpack('N')
95
- .first
96
-
97
- result = Marshal.load(@socket.recv(size).tap do |bytes|
98
- raise EOFError, 'Not enough bytes read' unless bytes.bytesize == size
99
- end)
120
+ # JRuby doesn't support socket#recv with flags
121
+ if defined?(JRUBY_VERSION)
122
+ def read(bytes)
123
+ received = @socket.read(bytes)
124
+ raise EOFError, "Server closed socket" unless received && received.bytesize == bytes
125
+ received
126
+ end
127
+ else
128
+ def read(bytes)
129
+ received = @socket.recv(bytes, Socket::MSG_WAITALL)
130
+ raise EOFError, "Server closed socket" unless received && received.bytesize == bytes
131
+ received
132
+ end
133
+ end
100
134
 
135
+ def read_msg
136
+ size = read(4).unpack('N').first
137
+ result = Marshal.load(read(size))
101
138
  raise result if Exception === result
102
139
  result
103
140
  end
@@ -39,6 +39,8 @@ module Moneta
39
39
  # @option options [String] :scheme ('http') HTTP scheme to use
40
40
  # @option options [String] :value_field ('value') Document field to store value
41
41
  # @option options [String] :type_field ('type') Document field to store value type
42
+ # @option options [String] :login Login name to use for HTTP basic authentication
43
+ # @option options [String] :password Password to use for HTTP basic authentication
42
44
  # @option options [Symbol] :adapter Adapter to use with Faraday
43
45
  # @option options [Faraday::Connecton] :backend Use existing backend instance
44
46
  # @option options Other options passed to {Faraday::new} (unless
@@ -46,6 +48,8 @@ module Moneta
46
48
  def initialize(options = {})
47
49
  @value_field = options.delete(:value_field) || 'value'
48
50
  @type_field = options.delete(:type_field) || 'type'
51
+ login = options.delete(:login)
52
+ password = options.delete(:password)
49
53
  @backend = options.delete(:backend) || begin
50
54
  host = options.delete(:host) || '127.0.0.1'
51
55
  port = options.delete(:port) || 5984
@@ -56,6 +60,7 @@ module Moneta
56
60
  end
57
61
  ::Faraday.new("#{scheme}://#{host}:#{port}/#{db}", options, &block)
58
62
  end
63
+ @backend.basic_auth(login, password) if login && password
59
64
  @rev_cache = Moneta.build do
60
65
  use :Lock
61
66
  adapter :LRUHash
@@ -44,7 +44,10 @@ module Moneta
44
44
  @backend.use(db)
45
45
  @backend.login(user, password) if user && password
46
46
  @collection = @backend[collection]
47
- if @backend.command(buildinfo: 1)['version'] >= '2.2'
47
+ if @backend.command(buildinfo: 1)['version'] >= '3.0'
48
+ # Moped creates indexes in the system.indexes collection which is not writable anymore since Mongo v3
49
+ warn 'Moneta::Adapters::MongoMoped - You are using the unmaintained Moped gem, expired documents will not be deleted'
50
+ elsif @backend.command(buildinfo: 1)['version'] >= '2.2'
48
51
  @collection.indexes.create({ @expires_field => 1 }, expireAfterSeconds: 0)
49
52
  else
50
53
  warn 'Moneta::Adapters::Mongo - You are using MongoDB version < 2.2, expired documents will not be deleted'
@@ -17,12 +17,12 @@ module Moneta
17
17
  adapter = @proxies.first
18
18
  if Array === adapter
19
19
  klass, options, block = adapter
20
- adapter = new_proxy(klass, options, &block)
20
+ adapter = new_proxy(klass, options.dup, &block)
21
21
  check_arity(klass, adapter, 1)
22
22
  end
23
23
  @proxies[1..-1].inject([adapter]) do |result, proxy|
24
24
  klass, options, block = proxy
25
- proxy = new_proxy(klass, result.last, options, &block)
25
+ proxy = new_proxy(klass, result.last, options.dup, &block)
26
26
  check_arity(klass, proxy, 2)
27
27
  result << proxy
28
28
  end
data/lib/moneta/lock.rb CHANGED
@@ -15,6 +15,7 @@ module Moneta
15
15
  protected
16
16
 
17
17
  def wrap(name, *args, &block)
18
+ self.locks ||= Set.new
18
19
  if locked?
19
20
  yield
20
21
  else
@@ -22,8 +23,12 @@ module Moneta
22
23
  end
23
24
  end
24
25
 
26
+ def locks=(locks)
27
+ Thread.current.thread_variable_set('Moneta::Lock', locks)
28
+ end
29
+
25
30
  def locks
26
- Thread.current['Moneta::Lock'] ||= Set.new
31
+ Thread.current.thread_variable_get('Moneta::Lock')
27
32
  end
28
33
 
29
34
  def lock!(&block)
data/lib/moneta/pool.rb CHANGED
@@ -298,6 +298,18 @@ module Moneta
298
298
  # by the manager after the ttl
299
299
  def close; end
300
300
 
301
+ def each_key(&block)
302
+ wrap(:each_key) do
303
+ raise NotImplementedError, "each_key is not supported on this proxy" \
304
+ unless supports? :each_key
305
+
306
+ return enum_for(:each_key) { adapter ? adapter.each_key.size : check_out! { adapter.each_key.size } } unless block_given?
307
+
308
+ adapter.each_key(&block)
309
+ self
310
+ end
311
+ end
312
+
301
313
  # Tells the manager to close all stores. It will not be possible to use
302
314
  # the store after this.
303
315
  def stop
data/lib/moneta/proxy.rb CHANGED
@@ -22,7 +22,7 @@ module Moneta
22
22
  raise NotImplementedError, "each_key is not supported on this proxy" \
23
23
  unless supports? :each_key
24
24
 
25
- return enum_for(:each_key) unless block_given?
25
+ return enum_for(:each_key) { adapter.each_key.size } unless block_given?
26
26
  adapter.each_key(&block)
27
27
  self
28
28
  end
data/lib/moneta/server.rb CHANGED
@@ -4,6 +4,154 @@ module Moneta
4
4
  # Moneta server to be used together with Moneta::Adapters::Client
5
5
  # @api public
6
6
  class Server
7
+ TIMEOUT = 1
8
+ MAXSIZE = 0x100000
9
+
10
+ # @api private
11
+ class Connection
12
+ def initialize(io, store)
13
+ @io = io
14
+ @store = store
15
+ @fiber = Fiber.new { run }
16
+ end
17
+
18
+ def resume(result = nil)
19
+ @fiber.resume result
20
+ end
21
+
22
+ private
23
+
24
+ # The return value of this function will be sent to the reactor.
25
+ #
26
+ # @return [:closed,Exception]
27
+ def run
28
+ catch :closed do
29
+ loop { write_dispatch(read_msg) }
30
+ end
31
+ :closed
32
+ rescue => ex
33
+ ex
34
+ ensure
35
+ @io.close unless @io.closed?
36
+ end
37
+
38
+ def dispatch(method, args)
39
+ case method
40
+ when :key?, :load, :delete, :increment, :create, :features
41
+ @store.public_send(method, *args)
42
+ when :store, :clear
43
+ @store.public_send(method, *args)
44
+ nil
45
+ when :each_key
46
+ yield_each(@store.each_key)
47
+ nil
48
+ end
49
+ rescue => ex
50
+ ex
51
+ end
52
+
53
+ def write_dispatch(msg)
54
+ method, *args = msg
55
+ result = dispatch(method, args)
56
+ write(result)
57
+ end
58
+
59
+ def read_msg
60
+ size = read(4).unpack('N').first
61
+ throw :closed, 'Message too big' if size > MAXSIZE
62
+ Marshal.load(read(size))
63
+ end
64
+
65
+ def read(len)
66
+ buffer = ''
67
+ loop do
68
+ begin
69
+ case received = @io.recv_nonblock(len)
70
+ when '', nil
71
+ throw :closed, 'Closed during read'
72
+ else
73
+ buffer << received
74
+ len -= received.bytesize
75
+ end
76
+ rescue IO::WaitReadable, IO::WaitWritable
77
+ yield_to_reactor(:read)
78
+ rescue Errno::ECONNRESET
79
+ throw :closed, 'Closed during read'
80
+ rescue IOError => ex
81
+ if ex.message =~ /closed stream/
82
+ throw :closed, 'Closed during read'
83
+ else
84
+ raise
85
+ end
86
+ end
87
+ break if len == 0
88
+ end
89
+ buffer
90
+ end
91
+
92
+ def write(obj)
93
+ buffer = pack(obj)
94
+ until buffer.empty?
95
+ begin
96
+ len = sendmsg(buffer)
97
+ buffer = buffer.byteslice(len...buffer.length)
98
+ rescue IO::WaitWritable, Errno::EINTR
99
+ yield_to_reactor(:write)
100
+ end
101
+ end
102
+ nil
103
+ end
104
+
105
+ # Detect support for socket#sendmsg_nonblock
106
+ Socket.new(Socket::AF_INET, Socket::SOCK_STREAM).tap do |socket|
107
+ begin
108
+ socket.sendmsg_nonblock('probe')
109
+ rescue Errno::EPIPE, Errno::ENOTCONN
110
+ def sendmsg(msg)
111
+ @io.sendmsg_nonblock(msg)
112
+ end
113
+ rescue NotImplementedError
114
+ def sendmsg(msg)
115
+ @io.write_nonblock(msg)
116
+ end
117
+ end
118
+ end
119
+
120
+ def yield_to_reactor(mode = :read)
121
+ if Fiber.yield(mode) == :close
122
+ throw :closed, 'Closed by reactor'
123
+ end
124
+ end
125
+
126
+ def pack(obj)
127
+ s = Marshal.dump(obj)
128
+ [s.bytesize].pack('N') << s
129
+ end
130
+
131
+ def yield_each(enumerator)
132
+ received_break = false
133
+ loop do
134
+ case msg = read_msg
135
+ when %w{NEXT}
136
+ # This will raise a StopIteration at the end of the enumeration,
137
+ # which will exit the loop.
138
+ write(enumerator.next)
139
+ when %w{BREAK}
140
+ # This is received when the client wants to stop the enumeration.
141
+ received_break = true
142
+ break
143
+ else
144
+ # Otherwise, the client is attempting to call another method within
145
+ # an `each` block.
146
+ write_dispatch(msg)
147
+ end
148
+ end
149
+ ensure
150
+ # This tells the client to stop enumerating
151
+ write(StopIteration.new("Server initiated stop")) unless received_break
152
+ end
153
+ end
154
+
7
155
  # @param [Hash] options
8
156
  # @option options [Integer] :port (9000) TCP port
9
157
  # @option options [String] :socket Alternative Unix socket file name
@@ -11,7 +159,9 @@ module Moneta
11
159
  @store = store
12
160
  @server = start(options)
13
161
  @ios = [@server]
14
- @clients = {}
162
+ @reads = @ios.dup
163
+ @writes = []
164
+ @connections = {}
15
165
  @running = false
16
166
  end
17
167
 
@@ -26,94 +176,89 @@ module Moneta
26
176
  #
27
177
  # @note This method blocks!
28
178
  def run
29
- raise 'Already running' if @running
179
+ raise 'Already running' if running?
30
180
  @stop = false
31
181
  @running = true
32
182
  begin
33
183
  mainloop until @stop
34
184
  ensure
35
- File.unlink(@socket) if @socket
36
- @ios.each { |io| io.close rescue nil }
185
+ @running = false
186
+ @server.close unless @server.closed?
187
+ @ios
188
+ .reject { |io| io == @server }
189
+ .each { |io| close_connection(io) }
190
+ File.unlink(@socket) if @socket rescue nil
37
191
  end
38
192
  end
39
193
 
40
194
  # Stop the server
41
195
  def stop
42
- raise 'Not running' unless @running
196
+ raise 'Not running' unless running?
43
197
  @stop = true
44
198
  @server.close
45
- @server = nil
199
+ nil
46
200
  end
47
201
 
48
202
  private
49
203
 
50
- TIMEOUT = 1
51
- MAXSIZE = 0x100000
52
-
53
204
  def mainloop
54
- if ios = IO.select(@ios, nil, @ios, TIMEOUT)
55
- ios[2].each do |io|
56
- io.close
57
- delete_client(io)
58
- end
59
- ios[0].each do |io|
60
- if io == @server
61
- if client = @server.accept
62
- @ios << client
63
- @clients[client] = ''
64
- end
65
- elsif io.closed? || io.eof?
66
- delete_client(io)
67
- else
68
- handle(io, @clients[io] << io.readpartial(0xFFFF))
69
- end
205
+ if ready = IO.select(@reads, @writes, @ios, TIMEOUT)
206
+ reads, writes, errors = ready
207
+ errors.each { |io| close_connection(io) }
208
+
209
+ @reads -= reads
210
+ reads.each do |io|
211
+ io == @server ? accept_connection : resume(io)
70
212
  end
213
+
214
+ @writes -= writes
215
+ writes.each { |io| resume(io) }
71
216
  end
72
- rescue SignalException => ex
73
- warn "Moneta::Server - #{ex.message}"
74
- raise if ex.signo == 15 || ex.signo == 2 # SIGTERM or SIGINT
75
- rescue => ex
217
+ rescue SignalException => signal
218
+ warn "Moneta::Server - received #{signal}"
219
+ case signal.signo
220
+ when Signal.list['INT'], Signal.list['TERM']
221
+ @stop = true # graceful exit
222
+ end
223
+ rescue IOError => ex
224
+ # We get a lot of these "closed stream" errors, which we ignore
225
+ raise unless ex.message =~ /closed stream/
226
+ rescue Errno::EBADF => ex
76
227
  warn "Moneta::Server - #{ex.message}"
77
228
  end
78
229
 
79
- def delete_client(io)
230
+ def accept_connection
231
+ io = @server.accept
232
+ @connections[io] = Connection.new(io, @store)
233
+ @ios << io
234
+ resume(io)
235
+ ensure
236
+ @reads << @server
237
+ end
238
+
239
+ def delete_connection(io)
80
240
  @ios.delete(io)
81
- @clients.delete(io)
241
+ @reads.delete(io)
242
+ @writes.delete(io)
82
243
  end
83
244
 
84
- def pack(obj)
85
- s = Marshal.dump(obj)
86
- [s.bytesize].pack('N') << s
245
+ def close_connection(io)
246
+ delete_connection(io)
247
+ @connections.delete(io).resume(:close)
87
248
  end
88
249
 
89
- def handle(io, buffer)
90
- return if buffer.bytesize < 8 # At least 4 bytes for the marshalled array
91
- size = buffer[0, 4].unpack('N').first
92
- if size > MAXSIZE
93
- delete_client(io)
94
- return
95
- end
96
- return if buffer.bytesize < 4 + size
97
- buffer.slice!(0, 4)
98
- method, *args = Marshal.load(buffer.slice!(0, size))
99
- case method
100
- when :key?, :load, :delete, :increment, :create
101
- io.write(pack(@store.send(method, *args)))
102
- when :features
103
- # all features except each_key are supported
104
- io.write(pack(@store.features - [:each_key]))
105
- when :store, :clear
106
- @store.send(method, *args)
107
- io.write(@nil ||= pack(nil))
108
- else
109
- raise 'Invalid method call'
250
+ def resume(io)
251
+ case result = @connections[io].resume
252
+ when :closed # graceful exit
253
+ delete_connection(io)
254
+ when Exception # messy exit
255
+ delete_connection(io)
256
+ raise result
257
+ when :read
258
+ @reads << io
259
+ when :write
260
+ @writes << io
110
261
  end
111
- rescue IOError => ex
112
- warn "Moneta::Server - #{ex.message}" unless ex.message =~ /closed/
113
- delete_client(io)
114
- rescue => ex
115
- warn "Moneta::Server - #{ex.message}"
116
- io.write(pack(Exception.new(ex.message)))
117
262
  end
118
263
 
119
264
  def start(options)
@@ -133,5 +278,14 @@ module Moneta
133
278
  TCPServer.open(options[:host] || '127.0.0.1', options[:port] || 9000)
134
279
  end
135
280
  end
281
+
282
+ def stats
283
+ {
284
+ connections: @connections.length,
285
+ reading: @reads.length,
286
+ writing: @writes.length,
287
+ total: @ios.length
288
+ }
289
+ end
136
290
  end
137
291
  end