moneta 1.1.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (208) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.rubocop.yml +194 -0
  4. data/.travis.yml +65 -25
  5. data/CHANGES +36 -0
  6. data/CONTRIBUTORS +4 -2
  7. data/Gemfile +94 -65
  8. data/README.md +50 -25
  9. data/feature_matrix.yaml +3 -11
  10. data/lib/action_dispatch/middleware/session/moneta_store.rb +1 -0
  11. data/lib/active_support/cache/moneta_store.rb +5 -5
  12. data/lib/moneta.rb +20 -10
  13. data/lib/moneta/adapters/activerecord.rb +35 -19
  14. data/lib/moneta/adapters/activesupportcache.rb +3 -7
  15. data/lib/moneta/adapters/cassandra.rb +24 -16
  16. data/lib/moneta/adapters/client.rb +62 -21
  17. data/lib/moneta/adapters/couch.rb +225 -80
  18. data/lib/moneta/adapters/datamapper.rb +1 -0
  19. data/lib/moneta/adapters/file.rb +9 -6
  20. data/lib/moneta/adapters/hbase.rb +1 -1
  21. data/lib/moneta/adapters/kyotocabinet.rb +8 -7
  22. data/lib/moneta/adapters/leveldb.rb +1 -1
  23. data/lib/moneta/adapters/lmdb.rb +3 -4
  24. data/lib/moneta/adapters/lruhash.rb +29 -62
  25. data/lib/moneta/adapters/memcached.rb +1 -0
  26. data/lib/moneta/adapters/memcached/dalli.rb +1 -1
  27. data/lib/moneta/adapters/memcached/native.rb +10 -8
  28. data/lib/moneta/adapters/mongo.rb +256 -6
  29. data/lib/moneta/adapters/null.rb +1 -2
  30. data/lib/moneta/adapters/pstore.rb +3 -2
  31. data/lib/moneta/adapters/redis.rb +8 -4
  32. data/lib/moneta/adapters/restclient.rb +12 -3
  33. data/lib/moneta/adapters/riak.rb +2 -2
  34. data/lib/moneta/adapters/sequel.rb +137 -328
  35. data/lib/moneta/adapters/sequel/mysql.rb +66 -0
  36. data/lib/moneta/adapters/sequel/postgres.rb +80 -0
  37. data/lib/moneta/adapters/sequel/postgres_hstore.rb +240 -0
  38. data/lib/moneta/adapters/sequel/sqlite.rb +57 -0
  39. data/lib/moneta/adapters/sqlite.rb +25 -11
  40. data/lib/moneta/adapters/tokyotyrant.rb +1 -1
  41. data/lib/moneta/builder.rb +2 -3
  42. data/lib/moneta/create_support.rb +21 -0
  43. data/lib/moneta/dbm_adapter.rb +31 -0
  44. data/lib/moneta/{mixins.rb → defaults.rb} +3 -302
  45. data/lib/moneta/each_key_support.rb +27 -0
  46. data/lib/moneta/enumerable.rb +38 -0
  47. data/lib/moneta/expires.rb +12 -12
  48. data/lib/moneta/expires_support.rb +60 -0
  49. data/lib/moneta/fallback.rb +84 -0
  50. data/lib/moneta/hash_adapter.rb +68 -0
  51. data/lib/moneta/increment_support.rb +16 -0
  52. data/lib/moneta/lock.rb +7 -2
  53. data/lib/moneta/logger.rb +2 -2
  54. data/lib/moneta/nil_values.rb +35 -0
  55. data/lib/moneta/option_support.rb +51 -0
  56. data/lib/moneta/optionmerger.rb +0 -1
  57. data/lib/moneta/pool.rb +312 -30
  58. data/lib/moneta/proxy.rb +3 -3
  59. data/lib/moneta/server.rb +216 -65
  60. data/lib/moneta/shared.rb +13 -7
  61. data/lib/moneta/stack.rb +6 -6
  62. data/lib/moneta/synchronize.rb +3 -3
  63. data/lib/moneta/transformer.rb +68 -24
  64. data/lib/moneta/transformer/config.rb +63 -43
  65. data/lib/moneta/transformer/helper.rb +3 -3
  66. data/lib/moneta/transformer/helper/bson.rb +7 -14
  67. data/lib/moneta/utils.rb +3 -9
  68. data/lib/moneta/version.rb +1 -1
  69. data/lib/moneta/weak_each_key.rb +2 -4
  70. data/lib/rack/cache/moneta.rb +13 -11
  71. data/lib/rack/moneta_rest.rb +2 -2
  72. data/lib/rack/session/moneta.rb +3 -4
  73. data/moneta.gemspec +18 -4
  74. data/script/benchmarks +145 -46
  75. data/script/contributors +11 -6
  76. data/script/start-couchdb +27 -0
  77. data/script/start-hbase +3 -2
  78. data/script/start-services +3 -11
  79. data/spec/active_support/cache_moneta_store_spec.rb +30 -30
  80. data/spec/features/concurrent_create.rb +31 -10
  81. data/spec/features/concurrent_increment.rb +26 -19
  82. data/spec/features/create_expires.rb +15 -15
  83. data/spec/features/default_expires.rb +11 -12
  84. data/spec/features/expires.rb +215 -210
  85. data/spec/features/increment.rb +41 -41
  86. data/spec/features/store.rb +3 -3
  87. data/spec/helper.rb +23 -82
  88. data/spec/moneta/adapters/activerecord/standard_activerecord_spec.rb +1 -1
  89. data/spec/moneta/adapters/activerecord/standard_activerecord_with_expires_spec.rb +1 -1
  90. data/spec/moneta/adapters/activesupportcache/adapter_activesupportcache_spec.rb +4 -1
  91. data/spec/moneta/adapters/activesupportcache/adapter_activesupportcache_with_default_expires_spec.rb +4 -1
  92. data/spec/moneta/adapters/activesupportcache/standard_activesupportcache_spec.rb +14 -0
  93. data/spec/moneta/adapters/cassandra/standard_cassandra_spec.rb +1 -1
  94. data/spec/moneta/adapters/client/adapter_client_spec.rb +6 -6
  95. data/spec/moneta/adapters/client/client_helper.rb +24 -0
  96. data/spec/moneta/adapters/client/standard_client_tcp_spec.rb +8 -8
  97. data/spec/moneta/adapters/client/standard_client_unix_spec.rb +23 -7
  98. data/spec/moneta/adapters/couch/adapter_couch_spec.rb +199 -2
  99. data/spec/moneta/adapters/couch/standard_couch_spec.rb +9 -3
  100. data/spec/moneta/adapters/couch/standard_couch_with_expires_spec.rb +8 -2
  101. data/spec/moneta/adapters/daybreak/standard_daybreak_spec.rb +1 -1
  102. data/spec/moneta/adapters/daybreak/standard_daybreak_with_expires_spec.rb +1 -1
  103. data/spec/moneta/adapters/dbm/standard_dbm_spec.rb +1 -1
  104. data/spec/moneta/adapters/dbm/standard_dbm_with_expires_spec.rb +1 -1
  105. data/spec/moneta/adapters/faraday_helper.rb +9 -0
  106. data/spec/moneta/adapters/file/standard_file_spec.rb +2 -2
  107. data/spec/moneta/adapters/file/standard_file_with_expires_spec.rb +1 -1
  108. data/spec/moneta/adapters/gdbm/standard_gdbm_spec.rb +1 -1
  109. data/spec/moneta/adapters/gdbm/standard_gdbm_with_expires_spec.rb +1 -1
  110. data/spec/moneta/adapters/kyotocabinet/adapter_kyotocabinet_spec.rb +1 -1
  111. data/spec/moneta/adapters/kyotocabinet/standard_kyotocabinet_spec.rb +2 -2
  112. data/spec/moneta/adapters/kyotocabinet/standard_kyotocabinet_with_expires_spec.rb +2 -2
  113. data/spec/moneta/adapters/leveldb/standard_leveldb_spec.rb +1 -1
  114. data/spec/moneta/adapters/leveldb/standard_leveldb_with_expires_spec.rb +1 -1
  115. data/spec/moneta/adapters/lmdb/standard_lmdb_spec.rb +1 -1
  116. data/spec/moneta/adapters/lmdb/standard_lmdb_with_expires_spec.rb +1 -1
  117. data/spec/moneta/adapters/lruhash/adapter_lruhash_spec.rb +2 -2
  118. data/spec/moneta/adapters/lruhash/standard_lruhash_spec.rb +1 -1
  119. data/spec/moneta/adapters/lruhash/standard_lruhash_with_expires_spec.rb +1 -1
  120. data/spec/moneta/adapters/memcached/adapter_memcached_spec.rb +1 -1
  121. data/spec/moneta/adapters/memcached/dalli/adapter_memcached_dalli_spec.rb +1 -1
  122. data/spec/moneta/adapters/memcached/dalli/adapter_memcached_dalli_with_default_expires_spec.rb +1 -1
  123. data/spec/moneta/adapters/memcached/dalli/standard_memcached_dalli_spec.rb +1 -1
  124. data/spec/moneta/adapters/memcached/native/adapter_memcached_native_spec.rb +1 -1
  125. data/spec/moneta/adapters/memcached/native/adapter_memcached_native_with_default_expires_spec.rb +1 -1
  126. data/spec/moneta/adapters/memcached/native/standard_memcached_native_spec.rb +1 -1
  127. data/spec/moneta/adapters/memcached/standard_memcached_spec.rb +1 -1
  128. data/spec/moneta/adapters/{memcached/helper.rb → memcached_helper.rb} +0 -0
  129. data/spec/moneta/adapters/memory/standard_memory_spec.rb +1 -1
  130. data/spec/moneta/adapters/memory/standard_memory_with_compress_spec.rb +1 -1
  131. data/spec/moneta/adapters/memory/standard_memory_with_expires_spec.rb +1 -1
  132. data/spec/moneta/adapters/memory/standard_memory_with_json_key_serializer_spec.rb +1 -1
  133. data/spec/moneta/adapters/memory/standard_memory_with_json_serializer_spec.rb +1 -1
  134. data/spec/moneta/adapters/memory/standard_memory_with_json_value_serializer_spec.rb +2 -2
  135. data/spec/moneta/adapters/memory/standard_memory_with_prefix_spec.rb +39 -2
  136. data/spec/moneta/adapters/memory/standard_memory_with_snappy_compress_spec.rb +2 -2
  137. data/spec/moneta/adapters/mongo/adapter_mongo_spec.rb +18 -2
  138. data/spec/moneta/adapters/mongo/adapter_mongo_with_default_expires_spec.rb +5 -3
  139. data/spec/moneta/adapters/mongo/standard_mongo_spec.rb +2 -2
  140. data/spec/moneta/adapters/null/null_adapter_spec.rb +1 -1
  141. data/spec/moneta/adapters/pstore/standard_pstore_spec.rb +1 -1
  142. data/spec/moneta/adapters/pstore/standard_pstore_with_expires_spec.rb +1 -1
  143. data/spec/moneta/adapters/redis/standard_redis_spec.rb +1 -1
  144. data/spec/moneta/adapters/restclient/adapter_restclient_spec.rb +7 -5
  145. data/spec/moneta/adapters/restclient/helper.rb +12 -0
  146. data/spec/moneta/adapters/restclient/standard_restclient_spec.rb +9 -6
  147. data/spec/moneta/adapters/riak/standard_riak_with_expires_spec.rb +4 -0
  148. data/spec/moneta/adapters/sdbm/standard_sdbm_spec.rb +1 -1
  149. data/spec/moneta/adapters/sdbm/standard_sdbm_with_expires_spec.rb +1 -1
  150. data/spec/moneta/adapters/sequel/adapter_sequel_spec.rb +31 -79
  151. data/spec/moneta/adapters/sequel/helper.rb +75 -0
  152. data/spec/moneta/adapters/sequel/standard_sequel_spec.rb +5 -11
  153. data/spec/moneta/adapters/sequel/standard_sequel_with_expires_spec.rb +8 -9
  154. data/spec/moneta/adapters/sqlite/standard_sqlite_spec.rb +1 -1
  155. data/spec/moneta/adapters/sqlite/standard_sqlite_with_expires_spec.rb +1 -1
  156. data/spec/moneta/adapters/tdb/standard_tdb_spec.rb +1 -1
  157. data/spec/moneta/adapters/tdb/standard_tdb_with_expires_spec.rb +1 -1
  158. data/spec/moneta/adapters/tokyocabinet/standard_tokyocabinet_spec.rb +1 -1
  159. data/spec/moneta/adapters/tokyocabinet/standard_tokyocabinet_with_expires_spec.rb +1 -1
  160. data/spec/moneta/adapters/tokyotyrant/adapter_tokyotyrant_spec.rb +6 -2
  161. data/spec/moneta/adapters/tokyotyrant/helper.rb +12 -0
  162. data/spec/moneta/adapters/tokyotyrant/standard_tokyotyrant_spec.rb +5 -2
  163. data/spec/moneta/adapters/tokyotyrant/standard_tokyotyrant_with_expires_spec.rb +5 -1
  164. data/spec/moneta/adapters/yaml/standard_yaml_spec.rb +1 -1
  165. data/spec/moneta/adapters/yaml/standard_yaml_with_expires_spec.rb +1 -1
  166. data/spec/moneta/builder_spec.rb +22 -0
  167. data/spec/moneta/proxies/enumerable/enumerable_spec.rb +26 -0
  168. data/spec/moneta/proxies/expires/expires_file_spec.rb +1 -1
  169. data/spec/moneta/proxies/fallback/fallback_spec.rb +42 -0
  170. data/spec/moneta/proxies/pool/pool_spec.rb +319 -6
  171. data/spec/moneta/proxies/shared/shared_tcp_spec.rb +14 -4
  172. data/spec/moneta/proxies/shared/shared_unix_spec.rb +14 -4
  173. data/spec/moneta/proxies/transformer/transformer_bencode_spec.rb +1 -1
  174. data/spec/moneta/proxies/transformer/transformer_bert_spec.rb +3 -3
  175. data/spec/moneta/proxies/transformer/transformer_bson_spec.rb +2 -2
  176. data/spec/moneta/proxies/transformer/transformer_json_spec.rb +1 -1
  177. data/spec/moneta/proxies/transformer/transformer_key_marshal_spec.rb +1 -1
  178. data/spec/moneta/proxies/transformer/transformer_key_yaml_spec.rb +1 -1
  179. data/spec/moneta/proxies/transformer/transformer_marshal_base64_spec.rb +1 -1
  180. data/spec/moneta/proxies/transformer/transformer_marshal_escape_spec.rb +8 -4
  181. data/spec/moneta/proxies/transformer/transformer_marshal_hex_spec.rb +1 -1
  182. data/spec/moneta/proxies/transformer/transformer_marshal_hmac_spec.rb +1 -1
  183. data/spec/moneta/proxies/transformer/transformer_marshal_prefix_base64_spec.rb +33 -0
  184. data/spec/moneta/proxies/transformer/transformer_marshal_prefix_spec.rb +1 -1
  185. data/spec/moneta/proxies/transformer/transformer_marshal_qp_spec.rb +1 -1
  186. data/spec/moneta/proxies/transformer/transformer_marshal_spec.rb +1 -1
  187. data/spec/moneta/proxies/transformer/transformer_marshal_urlsafe_base64_spec.rb +1 -1
  188. data/spec/moneta/proxies/transformer/transformer_marshal_uuencode_spec.rb +1 -1
  189. data/spec/moneta/proxies/transformer/transformer_msgpack_spec.rb +1 -1
  190. data/spec/moneta/proxies/transformer/transformer_ox_spec.rb +1 -1
  191. data/spec/moneta/proxies/transformer/transformer_php_spec.rb +1 -1
  192. data/spec/moneta/proxies/transformer/transformer_tnet_spec.rb +1 -1
  193. data/spec/moneta/proxies/transformer/transformer_yaml_spec.rb +2 -2
  194. data/spec/moneta/proxies/weak_each_key/weak_each_key_spec.rb +0 -2
  195. data/spec/restserver.rb +55 -0
  196. data/spec/support/mongo_helper.rb +12 -0
  197. metadata +140 -32
  198. data/lib/moneta/adapters/mongo/base.rb +0 -103
  199. data/lib/moneta/adapters/mongo/moped.rb +0 -164
  200. data/lib/moneta/adapters/mongo/official.rb +0 -157
  201. data/script/install-kyotocabinet +0 -17
  202. data/spec/moneta/adapters/mongo/adapter_mongo_moped_spec.rb +0 -25
  203. data/spec/moneta/adapters/mongo/adapter_mongo_moped_with_default_expires_spec.rb +0 -12
  204. data/spec/moneta/adapters/mongo/adapter_mongo_official_spec.rb +0 -25
  205. data/spec/moneta/adapters/mongo/adapter_mongo_official_with_default_expires_spec.rb +0 -12
  206. data/spec/moneta/adapters/mongo/standard_mongo_moped_spec.rb +0 -7
  207. data/spec/moneta/adapters/mongo/standard_mongo_official_spec.rb +0 -7
  208. data/spec/quality_spec.rb +0 -51
@@ -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
@@ -128,8 +128,8 @@ module Moneta
128
128
  end
129
129
 
130
130
  # (see Defaults::ClassMethods#not_supports)
131
- def not_supports *features
132
- @features_mask = (self.features_mask | features).freeze
131
+ def not_supports(*features)
132
+ @features_mask = (features_mask | features).freeze
133
133
  super
134
134
  end
135
135
  end
@@ -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,97 +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
- until @stop
34
- mainloop
35
- end
183
+ mainloop until @stop
36
184
  ensure
37
- File.unlink(@socket) if @socket
38
- @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
39
191
  end
40
192
  end
41
193
 
42
194
  # Stop the server
43
195
  def stop
44
- raise 'Not running' unless @running
196
+ raise 'Not running' unless running?
45
197
  @stop = true
46
198
  @server.close
47
- @server = nil
199
+ nil
48
200
  end
49
201
 
50
202
  private
51
203
 
52
- TIMEOUT = 1
53
- MAXSIZE = 0x100000
54
-
55
204
  def mainloop
56
- if ios = IO.select(@ios, nil, @ios, TIMEOUT)
57
- ios[2].each do |io|
58
- io.close
59
- delete_client(io)
60
- end
61
- ios[0].each do |io|
62
- if io == @server
63
- if client = @server.accept
64
- @ios << client
65
- @clients[client] = ''
66
- end
67
- elsif io.closed? || io.eof?
68
- delete_client(io)
69
- else
70
- handle(io, @clients[io] << io.readpartial(0xFFFF))
71
- 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)
72
212
  end
213
+
214
+ @writes -= writes
215
+ writes.each { |io| resume(io) }
73
216
  end
74
- rescue SignalException => ex
75
- warn "Moneta::Server - #{ex.message}"
76
- raise if ex.signo == 15 || ex.signo == 2 # SIGTERM or SIGINT
77
- rescue Exception => 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
78
227
  warn "Moneta::Server - #{ex.message}"
79
228
  end
80
229
 
81
- 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)
82
240
  @ios.delete(io)
83
- @clients.delete(io)
241
+ @reads.delete(io)
242
+ @writes.delete(io)
84
243
  end
85
244
 
86
- def pack(o)
87
- s = Marshal.dump(o)
88
- [s.bytesize].pack('N') << s
245
+ def close_connection(io)
246
+ delete_connection(io)
247
+ @connections.delete(io).resume(:close)
89
248
  end
90
249
 
91
- def handle(io, buffer)
92
- buffer = @clients[io]
93
- return if buffer.bytesize < 8 # At least 4 bytes for the marshalled array
94
- size = buffer[0,4].unpack('N').first
95
- if size > MAXSIZE
96
- delete_client(io)
97
- return
98
- end
99
- return if buffer.bytesize < 4 + size
100
- buffer.slice!(0, 4)
101
- method, *args = Marshal.load(buffer.slice!(0, size))
102
- case method
103
- when :key?, :load, :delete, :increment, :create
104
- io.write(pack @store.send(method, *args))
105
- when :features
106
- # all features except each_key are supported
107
- io.write(pack(@store.features - [:each_key]))
108
- when :store, :clear
109
- @store.send(method, *args)
110
- io.write(@nil ||= pack(nil))
111
- else
112
- 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
113
261
  end
114
- rescue IOError => ex
115
- warn "Moneta::Server - #{ex.message}" unless ex.message =~ /closed/
116
- delete_client(io)
117
- rescue Exception => ex
118
- warn "Moneta::Server - #{ex.message}"
119
- io.write(pack Exception.new(ex.message))
120
262
  end
121
263
 
122
264
  def start(options)
@@ -136,5 +278,14 @@ module Moneta
136
278
  TCPServer.open(options[:host] || '127.0.0.1', options[:port] || 9000)
137
279
  end
138
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
139
290
  end
140
291
  end
@@ -11,8 +11,6 @@ module Moneta
11
11
  #
12
12
  # @api public
13
13
  class Shared < Wrapper
14
- not_supports :each_key
15
-
16
14
  # @param [Hash] options
17
15
  # @option options [Integer] :port (9000) TCP port
18
16
  # @option options [String] :host Server hostname
@@ -25,7 +23,7 @@ module Moneta
25
23
 
26
24
  # (see Proxy#close)
27
25
  def close
28
- if @server
26
+ if server?
29
27
  @server.stop
30
28
  @thread.join
31
29
  @server = @thread = nil
@@ -36,13 +34,20 @@ module Moneta
36
34
  end
37
35
  end
38
36
 
37
+ # Returns true if this wrapper is running as the server
38
+ #
39
+ # @return [Boolean] wrapper is a server
40
+ def server?
41
+ @server != nil
42
+ end
43
+
39
44
  protected
40
45
 
41
46
  def wrap(*args)
42
47
  connect
43
48
  yield
44
49
  rescue Errno::ECONNRESET, Errno::EPIPE, IOError, SystemCallError
45
- @connect_lock.synchronize{ close unless @server }
50
+ @connect_lock.synchronize { close unless server? }
46
51
  tries ||= 0
47
52
  (tries += 1) < 3 ? retry : raise
48
53
  end
@@ -52,21 +57,22 @@ module Moneta
52
57
  @connect_lock.synchronize do
53
58
  @adapter ||= Adapters::Client.new(@options)
54
59
  end
55
- rescue Errno::ECONNREFUSED, Errno::ENOENT => ex
60
+ rescue Errno::ECONNREFUSED, Errno::ENOENT, IOError => ex
56
61
  start_server
57
62
  tries ||= 0
58
63
  warn "Moneta::Shared - Failed to connect: #{ex.message}" if tries > 0
59
- (tries += 1) < 3 ? retry : raise
64
+ (tries += 1) < 10 ? retry : raise
60
65
  end
61
66
 
62
67
  # TODO: Implement this using forking (MRI) and threading (JRuby)
63
68
  # to get maximal performance
64
69
  def start_server
65
70
  @connect_lock.synchronize do
71
+ return if server?
66
72
  begin
67
73
  raise "Adapter already set" if @adapter
68
74
  @adapter = Lock.new(@builder.build.last)
69
- raise "Server already set" if @server
75
+ raise "Server already set" if server?
70
76
  @server = Server.new(@adapter, @options)
71
77
  @thread = Thread.new { @server.run }
72
78
  sleep 0.1 until @server.running?
@@ -42,7 +42,7 @@ module Moneta
42
42
 
43
43
  # (see Proxy#key?)
44
44
  def key?(key, options = {})
45
- @stack.any? {|s| s.key?(key, options) }
45
+ @stack.any? { |s| s.key?(key, options) }
46
46
  end
47
47
 
48
48
  # (see Proxy#load)
@@ -56,21 +56,21 @@ module Moneta
56
56
 
57
57
  # (see Proxy#store)
58
58
  def store(key, value, options = {})
59
- @stack.each {|s| s.store(key, value, options) }
59
+ @stack.each { |s| s.store(key, value, options) }
60
60
  value
61
61
  end
62
62
 
63
63
  # (see Proxy#increment)
64
64
  def increment(key, amount = 1, options = {})
65
65
  last = nil
66
- @stack.each {|s| last = s.increment(key, amount, options) }
66
+ @stack.each { |s| last = s.increment(key, amount, options) }
67
67
  last
68
68
  end
69
69
 
70
70
  # (see Proxy#create)
71
71
  def create(key, value, options = {})
72
72
  last = false
73
- @stack.each {|s| last = s.create(key, value, options) }
73
+ @stack.each { |s| last = s.create(key, value, options) }
74
74
  last
75
75
  end
76
76
 
@@ -84,13 +84,13 @@ module Moneta
84
84
 
85
85
  # (see Proxy#clear)
86
86
  def clear(options = {})
87
- @stack.each {|s| s.clear(options) }
87
+ @stack.each { |s| s.clear(options) }
88
88
  self
89
89
  end
90
90
 
91
91
  # (see Proxy#close)
92
92
  def close
93
- @stack.each {|s| s.close }
93
+ @stack.each { |s| s.close }
94
94
  nil
95
95
  end
96
96