memcached-northscale 0.19.5.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,32 @@
1
+
2
+ =begin rdoc
3
+ The generated SWIG module for accessing libmemcached's C API.
4
+
5
+ Includes the full set of libmemcached static methods (as defined in <tt>$INCLUDE_PATH/libmemcached/memcached.h</tt>), and classes for the available structs:
6
+
7
+ * <b>Rlibmemcached::MemcachedResultSt</b>
8
+ * <b>Rlibmemcached::MemcachedServerSt</b>
9
+ * <b>Rlibmemcached::MemcachedSt</b>
10
+ * <b>Rlibmemcached::MemcachedStatSt</b>
11
+ * <b>Rlibmemcached::MemcachedStringSt</b>
12
+
13
+ A number of SWIG typemaps and C helper methods are also defined in <tt>ext/libmemcached.i</tt>.
14
+
15
+ =end
16
+ module Rlibmemcached
17
+ end
18
+
19
+ require 'rlibmemcached'
20
+
21
+ class Memcached
22
+ Lib = Rlibmemcached
23
+ raise "libmemcached 0.32 required; you somehow linked to #{Lib.memcached_lib_version}." unless "0.32" == Lib.memcached_lib_version
24
+ VERSION = File.read("#{File.dirname(__FILE__)}/../CHANGELOG")[/v([\d\.]+)\./, 1]
25
+ end
26
+
27
+ require 'memcached/integer'
28
+ require 'memcached/exceptions'
29
+ require 'memcached/behaviors'
30
+ require 'memcached/auth'
31
+ require 'memcached/memcached'
32
+ require 'memcached/rails'
@@ -0,0 +1,16 @@
1
+ class Memcached
2
+ def destroy_credentials
3
+ if options[:credentials] != nil
4
+ check_return_code(Lib.memcached_destroy_sasl_auth_data(@struct))
5
+ end
6
+ end
7
+
8
+ def set_credentials
9
+ # If credentials aren't provided, try to get them from the environment
10
+ if options[:credentials] != nil
11
+ username, password = options[:credentials]
12
+ check_return_code(Lib.memcached_set_sasl_auth_data(@struct, username, password))
13
+ end
14
+ end
15
+ end
16
+
@@ -0,0 +1,78 @@
1
+
2
+ class Memcached
3
+
4
+ #:stopdoc:
5
+
6
+ def self.load_constants(prefix, hash = {})
7
+ Lib.constants.grep(/^#{prefix}/).each do |const_name|
8
+ hash[const_name[prefix.length..-1].downcase.to_sym] = Lib.const_get(const_name)
9
+ end
10
+ hash
11
+ end
12
+
13
+ BEHAVIORS = load_constants("MEMCACHED_BEHAVIOR_")
14
+
15
+ BEHAVIOR_VALUES = {
16
+ false => 0,
17
+ true => 1
18
+ }
19
+
20
+ HASH_VALUES = {}
21
+ BEHAVIOR_VALUES.merge!(load_constants("MEMCACHED_HASH_", HASH_VALUES))
22
+
23
+ DISTRIBUTION_VALUES = {}
24
+ BEHAVIOR_VALUES.merge!(load_constants("MEMCACHED_DISTRIBUTION_", DISTRIBUTION_VALUES))
25
+
26
+ DIRECT_VALUE_BEHAVIORS = [:retry_timeout, :connect_timeout, :rcv_timeout, :socket_recv_size, :poll_timeout, :socket_send_size, :server_failure_limit]
27
+
28
+ CONVERSION_FACTORS = {
29
+ :rcv_timeout => 1_000_000,
30
+ :poll_timeout => 1_000,
31
+ :connect_timeout => 1_000
32
+ }
33
+
34
+ #:startdoc:
35
+
36
+ private
37
+
38
+ # Set a behavior option for this Memcached instance. Accepts a Symbol <tt>behavior</tt> and either <tt>true</tt>, <tt>false</tt>, or a Symbol for <tt>value</tt>. Arguments are validated and converted into integers for the struct setter method.
39
+ def set_behavior(behavior, value) #:doc:
40
+ raise ArgumentError, "No behavior #{behavior.inspect}" unless b_id = BEHAVIORS[behavior]
41
+
42
+ # Scoped validations; annoying
43
+ msg = "Invalid behavior value #{value.inspect} for #{behavior.inspect}"
44
+ case behavior
45
+ when :hash then raise(ArgumentError, msg) unless HASH_VALUES[value]
46
+ when :distribution then raise(ArgumentError, msg) unless DISTRIBUTION_VALUES[value]
47
+ when *DIRECT_VALUE_BEHAVIORS then raise(ArgumentError, msg) unless value.is_a?(Numeric) and value >= 0
48
+ else
49
+ raise(ArgumentError, msg) unless BEHAVIOR_VALUES[value]
50
+ end
51
+
52
+ lib_value = BEHAVIOR_VALUES[value] || (value * (CONVERSION_FACTORS[behavior] || 1)).to_i
53
+ #STDERR.puts "Setting #{behavior}:#{b_id} => #{value} (#{lib_value})"
54
+ Lib.memcached_behavior_set(@struct, b_id, lib_value)
55
+ #STDERR.puts " -> set to #{get_behavior(behavior).inspect}"
56
+ end
57
+
58
+ # Get a behavior value for this Memcached instance. Accepts a Symbol.
59
+ def get_behavior(behavior)
60
+ raise ArgumentError, "No behavior #{behavior.inspect}" unless b_id = BEHAVIORS[behavior]
61
+ value = Lib.memcached_behavior_get(@struct, b_id)
62
+
63
+ if BEHAVIOR_VALUES.invert.has_key?(value)
64
+ # False, nil are valid values so we can not rely on direct lookups
65
+ case behavior
66
+ # Scoped values; still annoying
67
+ when :hash then HASH_VALUES.invert[value]
68
+ when :distribution then DISTRIBUTION_VALUES.invert[value]
69
+ when *DIRECT_VALUE_BEHAVIORS then value
70
+ else
71
+ BEHAVIOR_VALUES.invert[value]
72
+ end
73
+ else
74
+ value
75
+ end
76
+ end
77
+
78
+ end
@@ -0,0 +1,84 @@
1
+
2
+ class Memcached
3
+
4
+ =begin rdoc
5
+
6
+ Superclass for all Memcached runtime exceptions.
7
+
8
+ Subclasses correspond one-to-one with server response strings or libmemcached errors. For example, raising <b>Memcached::NotFound</b> means that the server returned <tt>"NOT_FOUND\r\n"</tt>.
9
+
10
+ == Subclasses
11
+
12
+ * Memcached::ABadKeyWasProvidedOrCharactersOutOfRange
13
+ * Memcached::AKeyLengthOfZeroWasProvided
14
+ * Memcached::ATimeoutOccurred
15
+ * Memcached::ActionNotSupported
16
+ * Memcached::ActionQueued
17
+ * Memcached::ClientError
18
+ * Memcached::ConnectionBindFailure
19
+ * Memcached::ConnectionDataDoesNotExist
20
+ * Memcached::ConnectionDataExists
21
+ * Memcached::ConnectionFailure
22
+ * Memcached::ConnectionSocketCreateFailure
23
+ * Memcached::CouldNotOpenUnixSocket
24
+ * Memcached::EncounteredAnUnknownStatKey
25
+ * Memcached::Failure
26
+ * Memcached::FetchWasNotCompleted
27
+ * Memcached::HostnameLookupFailure
28
+ * Memcached::ItemValue
29
+ * Memcached::MemoryAllocationFailure
30
+ * Memcached::NoServersDefined
31
+ * Memcached::NotFound
32
+ * Memcached::NotStored
33
+ * Memcached::PartialRead
34
+ * Memcached::ProtocolError
35
+ * Memcached::ReadFailure
36
+ * Memcached::ServerDelete
37
+ * Memcached::ServerEnd
38
+ * Memcached::ServerError
39
+ * Memcached::ServerIsMarkedDead
40
+ * Memcached::ServerValue
41
+ * Memcached::SomeErrorsWereReported
42
+ * Memcached::StatValue
43
+ * Memcached::SystemError
44
+ * Memcached::TheHostTransportProtocolDoesNotMatchThatOfTheClient
45
+ * Memcached::UnknownReadFailure
46
+ * Memcached::WriteFailure
47
+
48
+ =end
49
+ class Error < RuntimeError
50
+ attr_accessor :no_backtrace
51
+
52
+ def set_backtrace(*args)
53
+ @no_backtrace ? [] : super
54
+ end
55
+
56
+ def backtrace(*args)
57
+ @no_backtrace ? [] : super
58
+ end
59
+ end
60
+
61
+ #:stopdoc:
62
+
63
+ class << self
64
+ private
65
+ def camelize(string)
66
+ string.downcase.gsub('/', ' or ').split(' ').map {|s| s.capitalize}.join
67
+ end
68
+ end
69
+
70
+ ERRNO_HASH = Hash[*Errno.constants.map{ |c| [Errno.const_get(c)::Errno, Errno.const_get(c).new.message] }.flatten]
71
+
72
+ EXCEPTIONS = []
73
+ EMPTY_STRUCT = Rlibmemcached::MemcachedSt.new
74
+ Rlibmemcached.memcached_create(EMPTY_STRUCT)
75
+
76
+ # Generate exception classes
77
+ Rlibmemcached::MEMCACHED_MAXIMUM_RETURN.times do |index|
78
+ description = Rlibmemcached.memcached_strerror(EMPTY_STRUCT, index).gsub("!", "")
79
+ exception_class = eval("class #{camelize(description)} < Error; self; end")
80
+ EXCEPTIONS << exception_class
81
+ end
82
+
83
+ #:startdoc:
84
+ end
@@ -0,0 +1,6 @@
1
+
2
+ class Integer
3
+ def tv_sec
4
+ self
5
+ end
6
+ end
@@ -0,0 +1,656 @@
1
+
2
+ =begin rdoc
3
+ The Memcached client class.
4
+ =end
5
+ class Memcached
6
+ FLAGS = 0x0
7
+
8
+ DEFAULTS = {
9
+ :hash => :fnv1_32,
10
+ :no_block => false,
11
+ :distribution => :consistent_ketama,
12
+ :ketama_weighted => true,
13
+ :buffer_requests => false,
14
+ :cache_lookups => true,
15
+ :support_cas => false,
16
+ :tcp_nodelay => false,
17
+ :show_backtraces => false,
18
+ :retry_timeout => 30,
19
+ :timeout => 0.25,
20
+ :rcv_timeout => nil,
21
+ :poll_timeout => nil,
22
+ :connect_timeout => 4,
23
+ :prefix_key => '',
24
+ :prefix_delimiter => '',
25
+ :hash_with_prefix_key => true,
26
+ :default_ttl => 604800,
27
+ :default_weight => 8,
28
+ :sort_hosts => false,
29
+ :auto_eject_hosts => true,
30
+ :server_failure_limit => 2,
31
+ :verify_key => true,
32
+ :use_udp => false,
33
+ :binary_protocol => false,
34
+ :credentials => nil,
35
+ :exception_retry_limit => 5,
36
+ :exceptions_to_retry => [
37
+ Memcached::ServerIsMarkedDead,
38
+ Memcached::ATimeoutOccurred,
39
+ Memcached::ConnectionBindFailure,
40
+ Memcached::ConnectionFailure,
41
+ Memcached::ConnectionSocketCreateFailure,
42
+ Memcached::Failure,
43
+ Memcached::MemoryAllocationFailure,
44
+ Memcached::ReadFailure,
45
+ Memcached::ServerError,
46
+ Memcached::SystemError,
47
+ Memcached::UnknownReadFailure,
48
+ Memcached::WriteFailure]
49
+ }
50
+
51
+ #:stopdoc:
52
+ IGNORED = 0
53
+ #:startdoc:
54
+
55
+ attr_reader :options # Return the options Hash used to configure this instance.
56
+
57
+ ###### Configuration
58
+
59
+ =begin rdoc
60
+ Create a new Memcached instance. Accepts string or array of server strings, as well an an optional configuration hash.
61
+
62
+ Memcached.new('localhost', ...) # A single server
63
+ Memcached.new(['web001:11212', 'web002:11212'], ...) # Two servers with custom ports
64
+ Memcached.new(['web001:11211:2', 'web002:11211:8'], ...) # Two servers with default ports and explicit weights
65
+
66
+ Weights only affect Ketama hashing. If you use Ketama hashing and don't specify a weight, the client will poll each server's stats and use its size as the weight.
67
+
68
+ Valid option parameters are:
69
+
70
+ <tt>:prefix_key</tt>:: A string to prepend to every key, for namespacing. Max length is 127. Defaults to the empty string.
71
+ <tt>:prefix_delimiter</tt>:: A character to postpend to the prefix key. Defaults to the empty string.
72
+ <tt>:hash</tt>:: The name of a hash function to use. Possible values are: <tt>:crc</tt>, <tt>:default</tt>, <tt>:fnv1_32</tt>, <tt>:fnv1_64</tt>, <tt>:fnv1a_32</tt>, <tt>:fnv1a_64</tt>, <tt>:hsieh</tt>, <tt>:md5</tt>, and <tt>:murmur</tt>. <tt>:fnv1_32</tt> is fast and well known, and is the default. Use <tt>:md5</tt> for compatibility with other ketama clients.
73
+ <tt>:distribution</tt>:: Either <tt>:modula</tt>, <tt>:consistent_ketama</tt>, <tt>:consistent_wheel</tt>, or <tt>:ketama</tt>. Defaults to <tt>:ketama</tt>.
74
+ <tt>:server_failure_limit</tt>:: How many consecutive failures to allow before marking a host as dead. Has no effect unless <tt>:retry_timeout</tt> is also set.
75
+ <tt>:retry_timeout</tt>:: How long to wait until retrying a dead server. Has no effect unless <tt>:server_failure_limit</tt> is non-zero. Defaults to <tt>30</tt>.
76
+ <tt>:auto_eject_hosts</tt>:: Whether to temporarily eject dead hosts from the pool. Defaults to <tt>true</tt>. Note that in the event of an ejection, <tt>:auto_eject_hosts</tt> will remap the entire pool unless <tt>:distribution</tt> is set to <tt>:consistent</tt>.
77
+ <tt>:exception_retry_limit</tt>:: How many times to retry before raising exceptions in <tt>:exceptions_to_retry</tt>. Defaults to <tt>5</tt>.
78
+ <tt>:exceptions_to_retry</tt>:: Which exceptions to retry. Defaults to <b>ServerIsMarkedDead</b>, <b>ATimeoutOccurred</b>, <b>ConnectionBindFailure</b>, <b>ConnectionFailure</b>, <b>ConnectionSocketCreateFailure</b>, <b>Failure</b>, <b>MemoryAllocationFailure</b>, <b>ReadFailure</b>, <b>ServerError</b>, <b>SystemError</b>, <b>UnknownReadFailure</b>, and <b>WriteFailure</b>.
79
+ <tt>:cache_lookups</tt>:: Whether to cache hostname lookups for the life of the instance. Defaults to <tt>true</tt>.
80
+ <tt>:support_cas</tt>:: Flag CAS support in the client. Accepts <tt>true</tt> or <tt>false</tt>. Defaults to <tt>false</tt> because it imposes a slight performance penalty. Note that your server must also support CAS or you will trigger <b>ProtocolError</b> exceptions.
81
+ <tt>:tcp_nodelay</tt>:: Turns on the no-delay feature for connecting sockets. Accepts <tt>true</tt> or <tt>false</tt>. Performance may or may not change, depending on your system.
82
+ <tt>:no_block</tt>:: Whether to use pipelining for writes. Accepts <tt>true</tt> or <tt>false</tt>.
83
+ <tt>:buffer_requests</tt>:: Whether to use an internal write buffer. Accepts <tt>true</tt> or <tt>false</tt>. Calling <tt>get</tt> or closing the connection will force the buffer to flush. Note that <tt>:buffer_requests</tt> might not work well without <tt>:no_block</tt> also enabled.
84
+ <tt>:show_backtraces</tt>:: Whether <b>NotFound</b> and <b>NotStored</b> exceptions should include backtraces. Generating backtraces is slow, so this is off by default. Turn it on to ease debugging.
85
+ <tt>:connect_timeout</tt>:: How long to wait for a connection to a server. Defaults to 2 seconds. Set to <tt>0</tt> if you want to wait forever.
86
+ <tt>:timeout</tt>:: How long to wait for a response from the server. Defaults to 0.25 seconds. Set to <tt>0</tt> if you want to wait forever.
87
+ <tt>:default_ttl</tt>:: The <tt>ttl</tt> to use on set if no <tt>ttl</tt> is specified, in seconds. Defaults to one week. Set to <tt>0</tt> if you want things to never expire.
88
+ <tt>:default_weight</tt>:: The weight to use if <tt>:ketama_weighted</tt> is <tt>true</tt>, but no weight is specified for a server.
89
+ <tt>:hash_with_prefix_key</tt>:: Whether to include the prefix when calculating which server a key falls on. Defaults to <tt>true</tt>.
90
+ <tt>:use_udp</tt>:: Use the UDP protocol to reduce connection overhead. Defaults to false.
91
+ <tt>:binary_protocol</tt>:: Use the binary protocol to reduce query processing overhead. Defaults to false.
92
+ <tt>:sort_hosts</tt>:: Whether to force the server list to stay sorted. This defeats consistent hashing and is rarely useful.
93
+ <tt>:verify_key</tt>:: Validate keys before accepting them. Never disable this.
94
+
95
+ Please note that when <tt>:no_block => true</tt>, update methods do not raise on errors. For example, if you try to <tt>set</tt> an invalid key, it will appear to succeed. The actual setting of the key occurs after libmemcached has returned control to your program, so there is no way to backtrack and raise the exception.
96
+
97
+ =end
98
+
99
+ def initialize(servers = nil, opts = {})
100
+ @struct = Lib.memcached_create(nil)
101
+
102
+ # Merge option defaults and discard meaningless keys
103
+ @options = DEFAULTS.merge(opts)
104
+ @options.delete_if { |k,v| not DEFAULTS.keys.include? k }
105
+ @default_ttl = options[:default_ttl]
106
+
107
+ if servers == nil || servers == []
108
+ if ENV.key?("MEMCACHE_SERVERS")
109
+ servers = ENV["MEMCACHE_SERVERS"].split(",").map do | s | s.strip end
110
+ else
111
+ servers = "127.0.0.1:11211"
112
+ end
113
+ end
114
+
115
+ if options[:credentials] == nil && ENV.key?("MEMCACHE_USERNAME") && ENV.key?("MEMCACHE_PASSWORD")
116
+ options[:credentials] = [ENV["MEMCACHE_USERNAME"], ENV["MEMCACHE_PASSWORD"]]
117
+ end
118
+
119
+ options[:binary_protocol] = true if options[:credentials] != nil
120
+
121
+ # Force :buffer_requests to use :no_block
122
+ # XXX Deleting the :no_block key should also work, but libmemcached doesn't seem to set it
123
+ # consistently
124
+ options[:no_block] = true if options[:buffer_requests]
125
+
126
+ # Disallow weights without ketama
127
+ options.delete(:ketama_weighted) if options[:distribution] != :consistent_ketama
128
+
129
+ # Disallow :sort_hosts with consistent hashing
130
+ if options[:sort_hosts] and options[:distribution] == :consistent
131
+ raise ArgumentError, ":sort_hosts defeats :consistent hashing"
132
+ end
133
+
134
+ # Read timeouts
135
+ options[:rcv_timeout] ||= options[:timeout] || 0
136
+ options[:poll_timeout] ||= options[:timeout] || 0
137
+
138
+ # Set the prefix key. Support the legacy name.
139
+ set_prefix_key(options.delete(:prefix_key) || options.delete(:namespace))
140
+
141
+ # Set the behaviors and credentials on the struct
142
+ set_behaviors
143
+ set_credentials
144
+
145
+ # Freeze the hash
146
+ options.freeze
147
+
148
+ # Set the servers on the struct
149
+ set_servers(servers)
150
+
151
+ # Not found exceptions
152
+ unless options[:show_backtraces]
153
+ @not_found = NotFound.new
154
+ @not_found.no_backtrace = true
155
+ @not_stored = NotStored.new
156
+ @not_stored.no_backtrace = true
157
+ end
158
+ end
159
+
160
+ # Set the server list.
161
+ # FIXME Does not necessarily free any existing server structs.
162
+ def set_servers(servers)
163
+ server_structs.each do |server|
164
+ Lib.memcached_server_remove(server)
165
+ end
166
+ Array(servers).each_with_index do |server, index|
167
+ # Socket
168
+ check_return_code(
169
+ if server.is_a?(String) and File.socket?(server)
170
+ args = [@struct, server, options[:default_weight].to_i]
171
+ Lib.memcached_server_add_unix_socket_with_weight(*args)
172
+ # Network
173
+ elsif server.is_a?(String) and server =~ /^[\w\d\.-]+(:\d{1,5}){0,2}$/
174
+ host, port, weight = server.split(":")
175
+ args = [@struct, host, port.to_i, (weight || options[:default_weight]).to_i]
176
+ if options[:use_udp]
177
+ Lib.memcached_server_add_udp_with_weight(*args)
178
+ else
179
+ Lib.memcached_server_add_with_weight(*args)
180
+ end
181
+ else
182
+ raise ArgumentError, "Servers must be either in the format 'host:port[:weight]' (e.g., 'localhost:11211' or 'localhost:11211:10') for a network server, or a valid path to a Unix domain socket (e.g., /var/run/memcached)."
183
+ end
184
+ )
185
+ end
186
+ # For inspect
187
+ @servers = send(:servers)
188
+ end
189
+
190
+ # Return the array of server strings used to configure this instance.
191
+ def servers
192
+ server_structs.map do |server|
193
+ inspect_server(server)
194
+ end
195
+ end
196
+
197
+ # Set the prefix key.
198
+ def set_prefix_key(key)
199
+ check_return_code(
200
+ if key
201
+ key += options[:prefix_delimiter]
202
+ raise ArgumentError, "Max prefix key + prefix delimiter size is #{Lib::MEMCACHED_PREFIX_KEY_MAX_SIZE - 1}" unless
203
+ key.size < Lib::MEMCACHED_PREFIX_KEY_MAX_SIZE
204
+ Lib.memcached_callback_set(@struct, Lib::MEMCACHED_CALLBACK_PREFIX_KEY, key)
205
+ else
206
+ Lib.memcached_callback_set(@struct, Lib::MEMCACHED_CALLBACK_PREFIX_KEY, "")
207
+ end
208
+ )
209
+ end
210
+ alias :set_namespace :set_prefix_key
211
+
212
+ # Return the current prefix key.
213
+ def prefix_key
214
+ @struct.prefix_key[0..-1 - options[:prefix_delimiter].size] if @struct.prefix_key.size > 0
215
+ end
216
+ alias :namespace :prefix_key
217
+
218
+ # Safely copy this instance. Returns a Memcached instance.
219
+ #
220
+ # <tt>clone</tt> is useful for threading, since each thread must have its own unshared Memcached
221
+ # object.
222
+ #
223
+ def clone
224
+ # FIXME Memory leak
225
+ # memcached = super
226
+ # struct = Lib.memcached_clone(nil, @struct)
227
+ # memcached.instance_variable_set('@struct', struct)
228
+ # memcached
229
+ self.class.new(servers, options.merge(:prefix_key => prefix_key))
230
+ end
231
+
232
+ # Reset the state of the libmemcached struct. This is useful for changing the server list at runtime.
233
+ def reset(current_servers = nil, with_prefix_key = true)
234
+ # Store state and teardown
235
+ current_servers ||= servers
236
+ prev_prefix_key = prefix_key
237
+
238
+ # Create
239
+ # FIXME Duplicates logic with initialize()
240
+ @struct = Lib.memcached_create(nil)
241
+ set_prefix_key(prev_prefix_key) if with_prefix_key
242
+ set_behaviors
243
+ set_credentials
244
+ set_servers(current_servers)
245
+ end
246
+
247
+ # Disconnect from all currently connected servers
248
+ def quit
249
+ Lib.memcached_quit(@struct)
250
+ self
251
+ end
252
+
253
+ # Should retry the exception
254
+ def should_retry(e)
255
+ options[:exceptions_to_retry].each {|ex_class| return true if e.instance_of?(ex_class)}
256
+ false
257
+ end
258
+
259
+ #:stopdoc:
260
+ alias :dup :clone #:nodoc:
261
+ #:startdoc:
262
+
263
+ # change the prefix_key after we're in motion
264
+ def prefix_key=(key)
265
+ unless key.size < Lib::MEMCACHED_PREFIX_KEY_MAX_SIZE
266
+ raise ArgumentError, "Max prefix_key size is #{Lib::MEMCACHED_PREFIX_KEY_MAX_SIZE - 1}"
267
+ end
268
+ check_return_code(
269
+ Lib.memcached_callback_set(@struct, Lib::MEMCACHED_CALLBACK_PREFIX_KEY, "#{key}#{options[:prefix_delimiter]}")
270
+ )
271
+ end
272
+ alias namespace= prefix_key=
273
+
274
+ # report the prefix_key
275
+ def prefix_key
276
+ @struct.prefix_key[0..-1 - options[:prefix_delimiter].size]
277
+ end
278
+ alias namespace prefix_key
279
+
280
+
281
+ ### Configuration helpers
282
+
283
+ private
284
+
285
+ # Return an array of raw <tt>memcached_host_st</tt> structs for this instance.
286
+ def server_structs
287
+ array = []
288
+ if @struct.hosts
289
+ @struct.hosts.count.times do |i|
290
+ array << Lib.memcached_select_server_at(@struct, i)
291
+ end
292
+ end
293
+ array
294
+ end
295
+
296
+ ###### Operations
297
+
298
+ public
299
+
300
+ ### Setters
301
+
302
+ # Set a key/value pair. Accepts a String <tt>key</tt> and an arbitrary Ruby object. Overwrites any existing value on the server.
303
+ #
304
+ # Accepts an optional <tt>ttl</tt> value to specify the maximum lifetime of the key on the server. <tt>ttl</tt> can be either an integer number of seconds, or a Time elapsed time object. <tt>0</tt> means no ttl. Note that there is no guarantee that the key will persist as long as the <tt>ttl</tt>, but it will not persist longer.
305
+ #
306
+ # Also accepts a <tt>marshal</tt> value, which defaults to <tt>true</tt>. Set <tt>marshal</tt> to <tt>false</tt> if you want the <tt>value</tt> to be set directly.
307
+ #
308
+ def set(key, value, ttl=@default_ttl, marshal=true, flags=FLAGS)
309
+ value = marshal ? Marshal.dump(value) : value.to_s
310
+ begin
311
+ check_return_code(
312
+ Lib.memcached_set(@struct, key, value, ttl, flags),
313
+ key
314
+ )
315
+ rescue => e
316
+ tries ||= 0
317
+ retry if e.instance_of?(ClientError) && !tries
318
+ raise unless tries < options[:exception_retry_limit] && should_retry(e)
319
+ tries += 1
320
+ retry
321
+ end
322
+ end
323
+
324
+ # Add a key/value pair. Raises <b>Memcached::NotStored</b> if the key already exists on the server. The parameters are the same as <tt>set</tt>.
325
+ def add(key, value, ttl=@default_ttl, marshal=true, flags=FLAGS)
326
+ value = marshal ? Marshal.dump(value) : value.to_s
327
+ begin
328
+ check_return_code(
329
+ Lib.memcached_add(@struct, key, value, ttl, flags),
330
+ key
331
+ )
332
+ rescue => e
333
+ tries ||= 0
334
+ raise unless tries < options[:exception_retry_limit] && should_retry(e)
335
+ tries += 1
336
+ retry
337
+ end
338
+ end
339
+
340
+ # Increment a key's value. Accepts a String <tt>key</tt>. Raises <b>Memcached::NotFound</b> if the key does not exist.
341
+ #
342
+ # Also accepts an optional <tt>offset</tt> paramater, which defaults to 1. <tt>offset</tt> must be an integer.
343
+ #
344
+ # Note that the key must be initialized to an unmarshalled integer first, via <tt>set</tt>, <tt>add</tt>, or <tt>replace</tt> with <tt>marshal</tt> set to <tt>false</tt>.
345
+ def increment(key, offset=1)
346
+ ret, value = Lib.memcached_increment(@struct, key, offset)
347
+ check_return_code(ret, key)
348
+ value
349
+ rescue => e
350
+ tries ||= 0
351
+ raise unless tries < options[:exception_retry_limit] && should_retry(e)
352
+ tries += 1
353
+ retry
354
+ end
355
+
356
+ # Decrement a key's value. The parameters and exception behavior are the same as <tt>increment</tt>.
357
+ def decrement(key, offset=1)
358
+ ret, value = Lib.memcached_decrement(@struct, key, offset)
359
+ check_return_code(ret, key)
360
+ value
361
+ rescue => e
362
+ tries ||= 0
363
+ raise unless tries < options[:exception_retry_limit] && should_retry(e)
364
+ tries += 1
365
+ retry
366
+ end
367
+
368
+ #:stopdoc:
369
+ alias :incr :increment
370
+ alias :decr :decrement
371
+ #:startdoc:
372
+
373
+ # Replace a key/value pair. Raises <b>Memcached::NotFound</b> if the key does not exist on the server. The parameters are the same as <tt>set</tt>.
374
+ def replace(key, value, ttl=@default_ttl, marshal=true, flags=FLAGS)
375
+ value = marshal ? Marshal.dump(value) : value.to_s
376
+ begin
377
+ check_return_code(
378
+ Lib.memcached_replace(@struct, key, value, ttl, flags),
379
+ key
380
+ )
381
+ rescue => e
382
+ tries ||= 0
383
+ raise unless tries < options[:exception_retry_limit] && should_retry(e)
384
+ tries += 1
385
+ retry
386
+ end
387
+ end
388
+
389
+ # Appends a string to a key's value. Accepts a String <tt>key</tt> and a String <tt>value</tt>. Raises <b>Memcached::NotFound</b> if the key does not exist on the server.
390
+ #
391
+ # Note that the key must be initialized to an unmarshalled string first, via <tt>set</tt>, <tt>add</tt>, or <tt>replace</tt> with <tt>marshal</tt> set to <tt>false</tt>.
392
+ def append(key, value)
393
+ # Requires memcached 1.2.4
394
+ check_return_code(
395
+ Lib.memcached_append(@struct, key, value.to_s, IGNORED, IGNORED),
396
+ key
397
+ )
398
+ rescue => e
399
+ tries ||= 0
400
+ raise unless tries < options[:exception_retry_limit] && should_retry(e)
401
+ tries += 1
402
+ retry
403
+ end
404
+
405
+ # Prepends a string to a key's value. The parameters and exception behavior are the same as <tt>append</tt>.
406
+ def prepend(key, value)
407
+ # Requires memcached 1.2.4
408
+ check_return_code(
409
+ Lib.memcached_prepend(@struct, key, value.to_s, IGNORED, IGNORED),
410
+ key
411
+ )
412
+ rescue => e
413
+ tries ||= 0
414
+ raise unless tries < options[:exception_retry_limit] && should_retry(e)
415
+ tries += 1
416
+ retry
417
+ end
418
+
419
+ # Reads a key's value from the server and yields it to a block. Replaces the key's value with the result of the block as long as the key hasn't been updated in the meantime, otherwise raises <b>Memcached::NotStored</b>. Accepts a String <tt>key</tt> and a block.
420
+ #
421
+ # Also accepts an optional <tt>ttl</tt> value.
422
+ #
423
+ # CAS stands for "compare and swap", and avoids the need for manual key mutexing. CAS support must be enabled in Memcached.new or a <b>Memcached::ClientError</b> will be raised. Note that CAS may be buggy in memcached itself.
424
+ # :retry_on_exceptions does not apply to this method
425
+ def cas(key, ttl=@default_ttl, marshal=true, flags=FLAGS)
426
+ raise ClientError, "CAS not enabled for this Memcached instance" unless options[:support_cas]
427
+
428
+ begin
429
+ value, flags, ret = Lib.memcached_get_rvalue(@struct, key)
430
+ check_return_code(ret, key)
431
+ rescue => e
432
+ tries_for_get ||= 0
433
+ raise unless tries_for_get < options[:exception_retry_limit] && should_retry(e)
434
+ tries_for_get += 1
435
+ retry
436
+ end
437
+
438
+ cas = @struct.result.cas
439
+
440
+ value = Marshal.load(value) if marshal
441
+ value = yield value
442
+ value = Marshal.dump(value) if marshal
443
+
444
+ begin
445
+ check_return_code(
446
+ Lib.memcached_cas(@struct, key, value, ttl, flags, cas),
447
+ key
448
+ )
449
+ rescue => e
450
+ tries_for_cas ||= 0
451
+ raise unless tries_for_cas < options[:exception_retry_limit] && should_retry(e)
452
+ tries_for_cas += 1
453
+ retry
454
+ end
455
+ end
456
+
457
+ alias :compare_and_swap :cas
458
+
459
+ ### Deleters
460
+
461
+ # Deletes a key/value pair from the server. Accepts a String <tt>key</tt>. Raises <b>Memcached::NotFound</b> if the key does not exist.
462
+ def delete(key)
463
+ check_return_code(
464
+ Lib.memcached_delete(@struct, key, IGNORED),
465
+ key
466
+ )
467
+ rescue => e
468
+ tries ||= 0
469
+ raise unless tries < options[:exception_retry_limit] && should_retry(e)
470
+ tries += 1
471
+ retry
472
+ end
473
+
474
+ # Flushes all key/value pairs from all the servers.
475
+ def flush
476
+ check_return_code(
477
+ Lib.memcached_flush(@struct, IGNORED)
478
+ )
479
+ rescue => e
480
+ tries ||= 0
481
+ raise unless tries < options[:exception_retry_limit] && should_retry(e)
482
+ tries += 1
483
+ retry
484
+ end
485
+
486
+ ### Getters
487
+
488
+ # Gets a key's value from the server. Accepts a String <tt>key</tt> or array of String <tt>keys</tt>.
489
+ #
490
+ # Also accepts a <tt>marshal</tt> value, which defaults to <tt>true</tt>. Set <tt>marshal</tt> to <tt>false</tt> if you want the <tt>value</tt> to be returned directly as a String. Otherwise it will be assumed to be a marshalled Ruby object and unmarshalled.
491
+ #
492
+ # If you pass a String key, and the key does not exist on the server, <b>Memcached::NotFound</b> will be raised. If you pass an array of keys, memcached's <tt>multiget</tt> mode will be used, and a hash of key/value pairs will be returned. The hash will contain only the keys that were found.
493
+ #
494
+ # The multiget behavior is subject to change in the future; however, for multiple lookups, it is much faster than normal mode.
495
+ #
496
+ # Note that when you rescue Memcached::NotFound exceptions, you should use a the block rescue syntax instead of the inline syntax. Block rescues are very fast, but inline rescues are very slow.
497
+ #
498
+ def get(keys, marshal=true)
499
+ if keys.is_a? Array
500
+ # Multi get
501
+ ret = Lib.memcached_mget(@struct, keys);
502
+ check_return_code(ret, keys)
503
+
504
+ hash = {}
505
+ keys.each do
506
+ value, key, flags, ret = Lib.memcached_fetch_rvalue(@struct)
507
+ break if ret == Lib::MEMCACHED_END
508
+ if ret != Lib::MEMCACHED_NOTFOUND
509
+ check_return_code(ret, key)
510
+ # Assign the value
511
+ hash[key] = (marshal ? Marshal.load(value) : value)
512
+ end
513
+ end
514
+ hash
515
+ else
516
+ # Single get
517
+ value, flags, ret = Lib.memcached_get_rvalue(@struct, keys)
518
+ check_return_code(ret, keys)
519
+ marshal ? Marshal.load(value) : value
520
+ end
521
+ rescue => e
522
+ tries ||= 0
523
+ raise unless tries < options[:exception_retry_limit] && should_retry(e)
524
+ tries += 1
525
+ retry
526
+ end
527
+
528
+ ### Information methods
529
+
530
+ # Return the server used by a particular key.
531
+ def server_by_key(key)
532
+ ret = Lib.memcached_server_by_key(@struct, key)
533
+ if ret.is_a?(Array)
534
+ check_return_code(ret.last)
535
+ inspect_server(ret.first)
536
+ else
537
+ check_return_code(ret)
538
+ end
539
+ end
540
+
541
+ # Return a Hash of statistics responses from the set of servers. Each value is an array with one entry for each server, in the same order the servers were defined.
542
+ def stats(subcommand = nil)
543
+ stats = Hash.new([])
544
+
545
+ stat_struct, ret = Lib.memcached_stat(@struct, subcommand)
546
+ check_return_code(ret)
547
+
548
+ keys, ret = Lib.memcached_stat_get_keys(@struct, stat_struct)
549
+ check_return_code(ret)
550
+
551
+ keys.each do |key|
552
+ server_structs.size.times do |index|
553
+
554
+ value, ret = Lib.memcached_stat_get_rvalue(
555
+ @struct,
556
+ Lib.memcached_select_stat_at(@struct, stat_struct, index),
557
+ key)
558
+ check_return_code(ret, key)
559
+
560
+ value = case value
561
+ when /^\d+\.\d+$/ then value.to_f
562
+ when /^\d+$/ then value.to_i
563
+ else value
564
+ end
565
+
566
+ stats[key.to_sym] += [value]
567
+ end
568
+ end
569
+
570
+ Lib.memcached_stat_free(@struct, stat_struct)
571
+ stats
572
+ rescue Memcached::SomeErrorsWereReported => _
573
+ e = _.class.new("Error getting stats")
574
+ e.set_backtrace(_.backtrace)
575
+ raise e
576
+ end
577
+
578
+ ### Operations helpers
579
+
580
+ private
581
+
582
+ # Checks the return code from Rlibmemcached against the exception list. Raises the corresponding exception if the return code is not Memcached::Success or Memcached::ActionQueued. Accepts an integer return code and an optional key, for exception messages.
583
+ def check_return_code(ret, key = nil) #:doc:
584
+ if ret == 0 # Memcached::Success
585
+ elsif ret == Lib::MEMCACHED_BUFFERED # Memcached::ActionQueued
586
+ elsif ret == Lib::MEMCACHED_NOTFOUND and @not_found
587
+ raise @not_found
588
+ elsif ret == Lib::MEMCACHED_NOTSTORED and @not_stored
589
+ raise @not_stored
590
+ else
591
+ message = "Key #{inspect_keys(key, (detect_failure if ret == Lib::MEMCACHED_SERVER_MARKED_DEAD)).inspect}"
592
+ if key.is_a?(String)
593
+ if ret == Lib::MEMCACHED_ERRNO
594
+ if (server = Lib.memcached_server_by_key(@struct, key)).is_a?(Array)
595
+ errno = server.first.cached_errno
596
+ message = "Errno #{errno}: #{ERRNO_HASH[errno].inspect}. #{message}"
597
+ end
598
+ elsif ret == Lib::MEMCACHED_SERVER_ERROR
599
+ if (server = Lib.memcached_server_by_key(@struct, key)).is_a?(Array)
600
+ message = "\"#{server.first.cached_server_error}\". #{message}."
601
+ end
602
+ end
603
+ end
604
+ raise EXCEPTIONS[ret], message
605
+ end
606
+ end
607
+
608
+ # Turn an array of keys into a hash of keys to servers.
609
+ def inspect_keys(keys, server = nil)
610
+ Hash[*Array(keys).map do |key|
611
+ [key, server || server_by_key(key)]
612
+ end.flatten]
613
+ end
614
+
615
+ # Find which server failed most recently.
616
+ # FIXME Is this still necessary with cached_errno?
617
+ def detect_failure
618
+ time = Time.now
619
+ server = server_structs.detect do |server|
620
+ server.next_retry > time
621
+ end
622
+ inspect_server(server) if server
623
+ end
624
+
625
+ # Set the behaviors on the struct from the current options.
626
+ def set_behaviors
627
+ BEHAVIORS.keys.each do |behavior|
628
+ set_behavior(behavior, options[behavior]) if options.key?(behavior)
629
+ end
630
+ # BUG Hash must be last due to the weird Libmemcached multi-behaviors
631
+ set_behavior(:hash, options[:hash])
632
+ end
633
+
634
+ # Set the SASL credentials from the current options. If credentials aren't provided, try to get them from the environment.
635
+ def set_credentials
636
+ if options[:credentials]
637
+ check_return_code(
638
+ Lib.memcached_set_sasl_auth_data(@struct, *options[:credentials])
639
+ )
640
+ end
641
+ end
642
+
643
+ def is_unix_socket?(server)
644
+ server.type == Lib::MEMCACHED_CONNECTION_UNIX_SOCKET
645
+ end
646
+
647
+ # Stringify an opaque server struct
648
+ def inspect_server(server)
649
+ strings = [server.hostname]
650
+ if !is_unix_socket?(server)
651
+ strings << ":#{server.port}"
652
+ strings << ":#{server.weight}" if options[:ketama_weighted]
653
+ end
654
+ strings.join
655
+ end
656
+ end