vinted-memcached 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (210) hide show
  1. checksums.yaml +7 -0
  2. data/BENCHMARKS +142 -0
  3. data/CHANGELOG +176 -0
  4. data/Gemfile +11 -0
  5. data/Gemfile.lock +45 -0
  6. data/LICENSE +184 -0
  7. data/Manifest +209 -0
  8. data/README.rdoc +124 -0
  9. data/Rakefile +134 -0
  10. data/TODO +1 -0
  11. data/ext/extconf-make.rb +25 -0
  12. data/ext/extconf.rb +78 -0
  13. data/ext/libmemcached-0.32/AUTHORS +7 -0
  14. data/ext/libmemcached-0.32/COPYING +32 -0
  15. data/ext/libmemcached-0.32/ChangeLog +303 -0
  16. data/ext/libmemcached-0.32/INSTALL +302 -0
  17. data/ext/libmemcached-0.32/Makefile.am +36 -0
  18. data/ext/libmemcached-0.32/Makefile.in +911 -0
  19. data/ext/libmemcached-0.32/NEWS +1 -0
  20. data/ext/libmemcached-0.32/README +33 -0
  21. data/ext/libmemcached-0.32/THANKS +14 -0
  22. data/ext/libmemcached-0.32/TODO +11 -0
  23. data/ext/libmemcached-0.32/aclocal.m4 +2108 -0
  24. data/ext/libmemcached-0.32/clients/Makefile.am +80 -0
  25. data/ext/libmemcached-0.32/clients/Makefile.in +773 -0
  26. data/ext/libmemcached-0.32/clients/client_options.h +32 -0
  27. data/ext/libmemcached-0.32/clients/execute.c +64 -0
  28. data/ext/libmemcached-0.32/clients/execute.h +5 -0
  29. data/ext/libmemcached-0.32/clients/generator.c +74 -0
  30. data/ext/libmemcached-0.32/clients/generator.h +20 -0
  31. data/ext/libmemcached-0.32/clients/memcat.c +178 -0
  32. data/ext/libmemcached-0.32/clients/memcp.c +251 -0
  33. data/ext/libmemcached-0.32/clients/memdump.c +170 -0
  34. data/ext/libmemcached-0.32/clients/memerror.c +80 -0
  35. data/ext/libmemcached-0.32/clients/memflush.c +143 -0
  36. data/ext/libmemcached-0.32/clients/memrm.c +160 -0
  37. data/ext/libmemcached-0.32/clients/memslap.c +441 -0
  38. data/ext/libmemcached-0.32/clients/memstat.c +326 -0
  39. data/ext/libmemcached-0.32/clients/utilities.c +207 -0
  40. data/ext/libmemcached-0.32/clients/utilities.h +41 -0
  41. data/ext/libmemcached-0.32/config/compile +143 -0
  42. data/ext/libmemcached-0.32/config/config.guess +1561 -0
  43. data/ext/libmemcached-0.32/config/config.rpath +666 -0
  44. data/ext/libmemcached-0.32/config/config.sub +1686 -0
  45. data/ext/libmemcached-0.32/config/depcomp +630 -0
  46. data/ext/libmemcached-0.32/config/install-sh +520 -0
  47. data/ext/libmemcached-0.32/config/ltmain.sh +9636 -0
  48. data/ext/libmemcached-0.32/config/missing +376 -0
  49. data/ext/libmemcached-0.32/config.h.in +254 -0
  50. data/ext/libmemcached-0.32/configure +23843 -0
  51. data/ext/libmemcached-0.32/configure.ac +120 -0
  52. data/ext/libmemcached-0.32/libmemcached/Makefile.am +111 -0
  53. data/ext/libmemcached-0.32/libmemcached/Makefile.in +1069 -0
  54. data/ext/libmemcached-0.32/libmemcached/byteorder.c +31 -0
  55. data/ext/libmemcached-0.32/libmemcached/common.h +189 -0
  56. data/ext/libmemcached-0.32/libmemcached/crc.c +86 -0
  57. data/ext/libmemcached-0.32/libmemcached/hsieh_hash.c +68 -0
  58. data/ext/libmemcached-0.32/libmemcached/jenkins_hash.c +213 -0
  59. data/ext/libmemcached-0.32/libmemcached/libmemcached.ver +1 -0
  60. data/ext/libmemcached-0.32/libmemcached/libmemcached_probes.d +30 -0
  61. data/ext/libmemcached-0.32/libmemcached/libmemcached_probes.h +82 -0
  62. data/ext/libmemcached-0.32/libmemcached/md5.c +354 -0
  63. data/ext/libmemcached-0.32/libmemcached/memcached/README.txt +7 -0
  64. data/ext/libmemcached-0.32/libmemcached/memcached/protocol_binary.h +385 -0
  65. data/ext/libmemcached-0.32/libmemcached/memcached.c +153 -0
  66. data/ext/libmemcached-0.32/libmemcached/memcached.h +305 -0
  67. data/ext/libmemcached-0.32/libmemcached/memcached.hpp +799 -0
  68. data/ext/libmemcached-0.32/libmemcached/memcached_allocators.c +72 -0
  69. data/ext/libmemcached-0.32/libmemcached/memcached_analyze.c +100 -0
  70. data/ext/libmemcached-0.32/libmemcached/memcached_auto.c +207 -0
  71. data/ext/libmemcached-0.32/libmemcached/memcached_behavior.c +290 -0
  72. data/ext/libmemcached-0.32/libmemcached/memcached_callback.c +175 -0
  73. data/ext/libmemcached-0.32/libmemcached/memcached_configure.h.in +23 -0
  74. data/ext/libmemcached-0.32/libmemcached/memcached_connect.c +371 -0
  75. data/ext/libmemcached-0.32/libmemcached/memcached_constants.h +146 -0
  76. data/ext/libmemcached-0.32/libmemcached/memcached_delete.c +0 -0
  77. data/ext/libmemcached-0.32/libmemcached/memcached_do.c +72 -0
  78. data/ext/libmemcached-0.32/libmemcached/memcached_dump.c +79 -0
  79. data/ext/libmemcached-0.32/libmemcached/memcached_exist.c +114 -0
  80. data/ext/libmemcached-0.32/libmemcached/memcached_exist.h +20 -0
  81. data/ext/libmemcached-0.32/libmemcached/memcached_fetch.c +102 -0
  82. data/ext/libmemcached-0.32/libmemcached/memcached_flush.c +89 -0
  83. data/ext/libmemcached-0.32/libmemcached/memcached_flush_buffers.c +23 -0
  84. data/ext/libmemcached-0.32/libmemcached/memcached_get.c +494 -0
  85. data/ext/libmemcached-0.32/libmemcached/memcached_get.h +87 -0
  86. data/ext/libmemcached-0.32/libmemcached/memcached_hash.c +252 -0
  87. data/ext/libmemcached-0.32/libmemcached/memcached_hosts.c +510 -0
  88. data/ext/libmemcached-0.32/libmemcached/memcached_internal.h +31 -0
  89. data/ext/libmemcached-0.32/libmemcached/memcached_io.c +594 -0
  90. data/ext/libmemcached-0.32/libmemcached/memcached_io.h +72 -0
  91. data/ext/libmemcached-0.32/libmemcached/memcached_key.c +28 -0
  92. data/ext/libmemcached-0.32/libmemcached/memcached_parse.c +74 -0
  93. data/ext/libmemcached-0.32/libmemcached/memcached_pool.h +38 -0
  94. data/ext/libmemcached-0.32/libmemcached/memcached_purge.c +76 -0
  95. data/ext/libmemcached-0.32/libmemcached/memcached_quit.c +75 -0
  96. data/ext/libmemcached-0.32/libmemcached/memcached_response.c +529 -0
  97. data/ext/libmemcached-0.32/libmemcached/memcached_result.c +57 -0
  98. data/ext/libmemcached-0.32/libmemcached/memcached_result.h +59 -0
  99. data/ext/libmemcached-0.32/libmemcached/memcached_sasl.c +225 -0
  100. data/ext/libmemcached-0.32/libmemcached/memcached_sasl.h +44 -0
  101. data/ext/libmemcached-0.32/libmemcached/memcached_server.c +159 -0
  102. data/ext/libmemcached-0.32/libmemcached/memcached_server.h +93 -0
  103. data/ext/libmemcached-0.32/libmemcached/memcached_stats.c +437 -0
  104. data/ext/libmemcached-0.32/libmemcached/memcached_storage.c +514 -0
  105. data/ext/libmemcached-0.32/libmemcached/memcached_storage.h +107 -0
  106. data/ext/libmemcached-0.32/libmemcached/memcached_strerror.c +92 -0
  107. data/ext/libmemcached-0.32/libmemcached/memcached_string.c +138 -0
  108. data/ext/libmemcached-0.32/libmemcached/memcached_string.h +53 -0
  109. data/ext/libmemcached-0.32/libmemcached/memcached_touch.c +60 -0
  110. data/ext/libmemcached-0.32/libmemcached/memcached_touch.h +31 -0
  111. data/ext/libmemcached-0.32/libmemcached/memcached_types.h +44 -0
  112. data/ext/libmemcached-0.32/libmemcached/memcached_util.h +15 -0
  113. data/ext/libmemcached-0.32/libmemcached/memcached_verbosity.c +36 -0
  114. data/ext/libmemcached-0.32/libmemcached/memcached_version.c +112 -0
  115. data/ext/libmemcached-0.32/libmemcached/memcached_watchpoint.h +38 -0
  116. data/ext/libmemcached-0.32/libmemcached/murmur_hash.c +76 -0
  117. data/ext/libmemcached-0.32/libmemcached/visibility.h +51 -0
  118. data/ext/libmemcached-0.32/libmemcachedutil/Makefile.am +11 -0
  119. data/ext/libmemcached-0.32/libmemcachedutil/Makefile.in +604 -0
  120. data/ext/libmemcached-0.32/libmemcachedutil/libmemcachedutil.ver +1 -0
  121. data/ext/libmemcached-0.32/libmemcachedutil/memcached_pool.c +170 -0
  122. data/ext/libmemcached-0.32/m4/ac_cxx_compile_stdcxx_0x.m4 +103 -0
  123. data/ext/libmemcached-0.32/m4/ac_cxx_header_stdcxx_98.m4 +67 -0
  124. data/ext/libmemcached-0.32/m4/acx_pthread.m4 +276 -0
  125. data/ext/libmemcached-0.32/m4/byteorder.m4 +40 -0
  126. data/ext/libmemcached-0.32/m4/deprecated.m4 +17 -0
  127. data/ext/libmemcached-0.32/m4/enable_utillib.m4 +16 -0
  128. data/ext/libmemcached-0.32/m4/extensions.m4 +94 -0
  129. data/ext/libmemcached-0.32/m4/hsieh.m4 +18 -0
  130. data/ext/libmemcached-0.32/m4/lib-prefix.m4 +221 -0
  131. data/ext/libmemcached-0.32/m4/libtool.m4 +7831 -0
  132. data/ext/libmemcached-0.32/m4/ltoptions.m4 +369 -0
  133. data/ext/libmemcached-0.32/m4/ltsugar.m4 +123 -0
  134. data/ext/libmemcached-0.32/m4/ltversion.m4 +23 -0
  135. data/ext/libmemcached-0.32/m4/lt~obsolete.m4 +98 -0
  136. data/ext/libmemcached-0.32/m4/memcached.m4 +30 -0
  137. data/ext/libmemcached-0.32/m4/pandora_64bit.m4 +55 -0
  138. data/ext/libmemcached-0.32/m4/pandora_canonical.m4 +151 -0
  139. data/ext/libmemcached-0.32/m4/pandora_check_compiler_version.m4 +37 -0
  140. data/ext/libmemcached-0.32/m4/pandora_check_cxx_standard.m4 +16 -0
  141. data/ext/libmemcached-0.32/m4/pandora_enable_dtrace.m4 +41 -0
  142. data/ext/libmemcached-0.32/m4/pandora_ensure_gcc_version.m4 +36 -0
  143. data/ext/libmemcached-0.32/m4/pandora_have_better_malloc.m4 +54 -0
  144. data/ext/libmemcached-0.32/m4/pandora_have_sasl.m4 +133 -0
  145. data/ext/libmemcached-0.32/m4/pandora_header_assert.m4 +23 -0
  146. data/ext/libmemcached-0.32/m4/pandora_libtool.m4 +15 -0
  147. data/ext/libmemcached-0.32/m4/pandora_optimize.m4 +79 -0
  148. data/ext/libmemcached-0.32/m4/pandora_shared_ptr.m4 +56 -0
  149. data/ext/libmemcached-0.32/m4/pandora_vc_build.m4 +32 -0
  150. data/ext/libmemcached-0.32/m4/pandora_warnings.m4 +262 -0
  151. data/ext/libmemcached-0.32/m4/pod2man.m4 +7 -0
  152. data/ext/libmemcached-0.32/m4/protocol_binary.m4 +23 -0
  153. data/ext/libmemcached-0.32/m4/setsockopt.m4 +57 -0
  154. data/ext/libmemcached-0.32/m4/visibility.m4 +52 -0
  155. data/ext/libmemcached-0.32/support/Makefile.am +4 -0
  156. data/ext/libmemcached-0.32/support/Makefile.in +487 -0
  157. data/ext/libmemcached-0.32/support/libmemcached-fc.spec.in +105 -0
  158. data/ext/libmemcached-0.32/support/libmemcached.pc.in +10 -0
  159. data/ext/libmemcached-0.32/support/libmemcached.spec +105 -0
  160. data/ext/libmemcached-0.32/support/libmemcached.spec.in +105 -0
  161. data/ext/libmemcached-0.32/support/set_benchmark.sh +5 -0
  162. data/ext/libmemcached-0.32/tests/Makefile.am +113 -0
  163. data/ext/libmemcached-0.32/tests/Makefile.in +762 -0
  164. data/ext/libmemcached-0.32/tests/atomsmasher.c +245 -0
  165. data/ext/libmemcached-0.32/tests/function.c +4904 -0
  166. data/ext/libmemcached-0.32/tests/ketama_test_cases.h +108 -0
  167. data/ext/libmemcached-0.32/tests/output.cmp +7 -0
  168. data/ext/libmemcached-0.32/tests/output.res +7 -0
  169. data/ext/libmemcached-0.32/tests/output2.res +46 -0
  170. data/ext/libmemcached-0.32/tests/plus.cpp +293 -0
  171. data/ext/libmemcached-0.32/tests/r/memcat.res +19 -0
  172. data/ext/libmemcached-0.32/tests/r/memcp.res +27 -0
  173. data/ext/libmemcached-0.32/tests/r/memrm.res +19 -0
  174. data/ext/libmemcached-0.32/tests/r/memslap.res +33 -0
  175. data/ext/libmemcached-0.32/tests/r/memstat.res +33 -0
  176. data/ext/libmemcached-0.32/tests/server.c +118 -0
  177. data/ext/libmemcached-0.32/tests/server.h +25 -0
  178. data/ext/libmemcached-0.32/tests/start.c +16 -0
  179. data/ext/libmemcached-0.32/tests/t/memcat.test +4 -0
  180. data/ext/libmemcached-0.32/tests/t/memcp.test +3 -0
  181. data/ext/libmemcached-0.32/tests/t/memrm.test +3 -0
  182. data/ext/libmemcached-0.32/tests/t/memslap.test +5 -0
  183. data/ext/libmemcached-0.32/tests/t/memstat.test +3 -0
  184. data/ext/libmemcached-0.32/tests/test.c +137 -0
  185. data/ext/libmemcached-0.32/tests/test.h +46 -0
  186. data/ext/libmemcached-0.32/tests/udp.c +76 -0
  187. data/ext/rlibmemcached.i +258 -0
  188. data/ext/rlibmemcached_wrap.c +13917 -0
  189. data/lib/memcached/auth.rb +16 -0
  190. data/lib/memcached/behaviors.rb +78 -0
  191. data/lib/memcached/exceptions.rb +84 -0
  192. data/lib/memcached/experimental.rb +48 -0
  193. data/lib/memcached/marshal_codec.rb +10 -0
  194. data/lib/memcached/memcached.rb +732 -0
  195. data/lib/memcached/rails.rb +250 -0
  196. data/lib/memcached.rb +33 -0
  197. data/memcached.gemspec +0 -0
  198. data/test/profile/benchmark.rb +280 -0
  199. data/test/profile/c_profiler.rb +14 -0
  200. data/test/profile/exercise.rb +185 -0
  201. data/test/profile/rb_profiler.rb +21 -0
  202. data/test/profile/valgrind.rb +10 -0
  203. data/test/setup.rb +30 -0
  204. data/test/teardown.rb +0 -0
  205. data/test/test_helper.rb +18 -0
  206. data/test/unit/binding_test.rb +8 -0
  207. data/test/unit/memcached_experimental_test.rb +272 -0
  208. data/test/unit/memcached_test.rb +1487 -0
  209. data/test/unit/rails_test.rb +330 -0
  210. metadata +336 -0
@@ -0,0 +1,732 @@
1
+ =begin rdoc
2
+ The Memcached client class.
3
+ =end
4
+ class Memcached
5
+ FLAGS = 0x0
6
+
7
+ DEFAULTS = {
8
+ :hash => :fnv1_32,
9
+ :no_block => false,
10
+ :noreply => 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 => 60,
19
+ :timeout => 0.25,
20
+ :rcv_timeout => nil,
21
+ :snd_timeout => nil,
22
+ :poll_max_retries => 1,
23
+ :poll_timeout => nil,
24
+ :connect_timeout => 0.25,
25
+ :prefix_key => '',
26
+ :prefix_delimiter => '',
27
+ :hash_with_prefix_key => true,
28
+ :default_ttl => 604800,
29
+ :default_weight => 8,
30
+ :sort_hosts => false,
31
+ :auto_eject_hosts => true,
32
+ :server_failure_limit => 2,
33
+ :verify_key => true,
34
+ :use_udp => false,
35
+ :binary_protocol => false,
36
+ :credentials => nil,
37
+ :experimental_features => false,
38
+ :codec => Memcached::MarshalCodec,
39
+ :exception_retry_limit => 5,
40
+ :exceptions_to_retry => [
41
+ Memcached::ServerIsMarkedDead,
42
+ Memcached::ATimeoutOccurred,
43
+ Memcached::ConnectionBindFailure,
44
+ Memcached::ConnectionFailure,
45
+ Memcached::ConnectionSocketCreateFailure,
46
+ Memcached::Failure,
47
+ Memcached::MemoryAllocationFailure,
48
+ Memcached::ReadFailure,
49
+ Memcached::ServerEnd,
50
+ Memcached::ServerError,
51
+ Memcached::SystemError,
52
+ Memcached::UnknownReadFailure,
53
+ Memcached::WriteFailure,
54
+ Memcached::SomeErrorsWereReported]
55
+ }
56
+
57
+ #:stopdoc:
58
+ IGNORED = 0
59
+ #:startdoc:
60
+
61
+ attr_reader :options # Return the options Hash used to configure this instance.
62
+
63
+ ###### Configuration
64
+
65
+ =begin rdoc
66
+ Create a new Memcached instance. Accepts string or array of server strings, as well an an optional configuration hash.
67
+
68
+ Memcached.new('localhost', ...) # A single server
69
+ Memcached.new(['web001:11212', 'web002:11212'], ...) # Two servers with custom ports
70
+ Memcached.new(['web001:11211:2', 'web002:11211:8'], ...) # Two servers with default ports and explicit weights
71
+
72
+ 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.
73
+
74
+ Valid option parameters are:
75
+
76
+ <tt>:prefix_key</tt>:: A string to prepend to every key, for namespacing. Max length is 127. Defaults to the empty string.
77
+ <tt>:prefix_delimiter</tt>:: A character to append to the prefix key. Defaults to the empty string.
78
+ <tt>:codec</tt>:: An object to use as the codec for encoded requests. Defaults to Memcached::MarshalCodec.
79
+ <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>, <tt>:murmur</tt>, and <tt>:none</tt>. <tt>:fnv1_32</tt> is fast and well known, and is the default. Use <tt>:md5</tt> for compatibility with other ketama clients. <tt>:none</tt> is for use when there is a single server, and performs no actual hashing.
80
+ <tt>:distribution</tt>:: Either <tt>:modula</tt>, <tt>:consistent_ketama</tt>, <tt>:consistent_wheel</tt>, or <tt>:ketama</tt>. Defaults to <tt>:ketama</tt>.
81
+ <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.
82
+ <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>.
83
+ <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>.
84
+ <tt>:exception_retry_limit</tt>:: How many times to retry before raising exceptions in <tt>:exceptions_to_retry</tt>. Defaults to <tt>5</tt>.
85
+ <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>.
86
+ <tt>:cache_lookups</tt>:: Whether to cache hostname lookups for the life of the instance. Defaults to <tt>true</tt>.
87
+ <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.
88
+ <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.
89
+ <tt>:no_block</tt>:: Whether to use pipelining for writes. Defaults to <tt>false</tt>.
90
+ <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. Client behavior is undefined unless <tt>:no_block</tt> is enabled. Defaults to <tt>false</tt>.
91
+ <tt>:noreply</tt>:: Ask server not to reply for storage commands. Client behavior is undefined unless <tt>:no_block</tt> and <tt>:buffer_requests</tt> are enabled. Defaults to <tt>false</tt>.
92
+ <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.
93
+ <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.
94
+ <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.
95
+ <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.
96
+ <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.
97
+ <tt>:hash_with_prefix_key</tt>:: Whether to include the prefix when calculating which server a key falls on. Defaults to <tt>true</tt>.
98
+ <tt>:use_udp</tt>:: Use the UDP protocol to reduce connection overhead. Defaults to false.
99
+ <tt>:binary_protocol</tt>:: Use the binary protocol. Defaults to false. Please note that using the binary protocol is usually <b>slower</b> than the ASCII protocol.
100
+ <tt>:sort_hosts</tt>:: Whether to force the server list to stay sorted. This defeats consistent hashing and is rarely useful.
101
+ <tt>:verify_key</tt>:: Validate keys before accepting them. Never disable this.
102
+ <tt>:poll_max_retries</tt>:: Maximum poll timeout retries before marking a flush failed on timeouts.
103
+
104
+ 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.
105
+
106
+ =end
107
+
108
+ def initialize(servers = nil, opts = {})
109
+ @struct = Lib.memcached_create(nil)
110
+
111
+ # Merge option defaults and discard meaningless keys
112
+ @options = DEFAULTS.merge(opts)
113
+ @options.each do |key,_|
114
+ unless DEFAULTS.keys.include? key
115
+ raise ArgumentError, "#{key.inspect} is not a valid configuration parameter."
116
+ end
117
+ end
118
+
119
+ # Marginally speed up settings access for hot paths
120
+ @default_ttl = options[:default_ttl]
121
+ @codec = options[:codec]
122
+ @support_cas = options[:support_cas]
123
+
124
+ if servers == nil || servers == []
125
+ if ENV.key?("MEMCACHE_SERVERS")
126
+ servers = ENV["MEMCACHE_SERVERS"].split(",").map do | s | s.strip end
127
+ else
128
+ servers = "127.0.0.1:11211"
129
+ end
130
+ end
131
+
132
+ if !options[:credentials] and ENV["MEMCACHE_USERNAME"] and ENV["MEMCACHE_PASSWORD"]
133
+ options[:credentials] = [ENV["MEMCACHE_USERNAME"], ENV["MEMCACHE_PASSWORD"]]
134
+ end
135
+
136
+ instance_eval { send(:extend, Experimental) } if options[:experimental_features]
137
+
138
+ # SASL requires binary protocol
139
+ options[:binary_protocol] = true if options[:credentials]
140
+
141
+ # UDP requires noreply
142
+ options[:noreply] = true if options[:use_udp]
143
+
144
+ # Buffering requires non-blocking
145
+ # FIXME This should all be wrapped up in a single :pipeline option.
146
+ options[:no_block] = true if options[:buffer_requests]
147
+
148
+ # Disallow weights without ketama
149
+ options.delete(:ketama_weighted) if options[:distribution] != :consistent_ketama
150
+
151
+ # Disallow :sort_hosts with consistent hashing
152
+ if options[:sort_hosts] and options[:distribution] == :consistent
153
+ raise ArgumentError, ":sort_hosts defeats :consistent hashing"
154
+ end
155
+
156
+ # Read timeouts
157
+ options[:rcv_timeout] ||= options[:timeout]
158
+ options[:poll_timeout] ||= options[:timeout]
159
+
160
+ # Write timeouts
161
+ options[:snd_timeout] ||= options[:timeout]
162
+
163
+ # Set the prefix key
164
+ set_prefix_key(options[:prefix_key])
165
+
166
+ # Set the behaviors and credentials on the struct
167
+ set_behaviors
168
+ set_credentials
169
+
170
+ # Freeze the hash
171
+ options.freeze
172
+
173
+ # Set the servers on the struct
174
+ set_servers(servers)
175
+
176
+ # Not found exceptions
177
+ if options[:show_backtraces]
178
+ # Raise classes for full context
179
+ @not_found = NotFound
180
+ @not_stored = NotStored
181
+ else
182
+ # Raise fast context-free exception instances
183
+ @not_found = NotFound.new
184
+ @not_found.no_backtrace = true
185
+ @not_stored = NotStored.new
186
+ @not_stored.no_backtrace = true
187
+ end
188
+ end
189
+
190
+ # Set the server list.
191
+ # FIXME Does not necessarily free any existing server structs.
192
+ def set_servers(servers)
193
+ Array(servers).each_with_index do |server, index|
194
+ # Socket
195
+ check_return_code(
196
+ if server.is_a?(String) and File.socket?(server)
197
+ args = [@struct, server, options[:default_weight].to_i]
198
+ Lib.memcached_server_add_unix_socket_with_weight(*args)
199
+ # Network
200
+ elsif server.is_a?(String) and server =~ /^[\w\d\.-]+(:\d{1,5}){0,2}$/
201
+ host, port, weight = server.split(":")
202
+ args = [@struct, host, port.to_i, (weight || options[:default_weight]).to_i]
203
+ if options[:use_udp]
204
+ Lib.memcached_server_add_udp_with_weight(*args)
205
+ else
206
+ Lib.memcached_server_add_with_weight(*args)
207
+ end
208
+ else
209
+ 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)."
210
+ end
211
+ )
212
+ end
213
+ end
214
+
215
+ # Return the array of server strings used to configure this instance.
216
+ def servers
217
+ server_structs.map do |server|
218
+ inspect_server(server)
219
+ end
220
+ end
221
+
222
+ # Set the prefix key.
223
+ def set_prefix_key(key)
224
+ check_return_code(
225
+ if key
226
+ key += options[:prefix_delimiter]
227
+ raise ArgumentError, "Max prefix key + prefix delimiter size is #{Lib::MEMCACHED_PREFIX_KEY_MAX_SIZE - 1}" unless
228
+ key.size < Lib::MEMCACHED_PREFIX_KEY_MAX_SIZE
229
+ Lib.memcached_callback_set(@struct, Lib::MEMCACHED_CALLBACK_PREFIX_KEY, key)
230
+ else
231
+ Lib.memcached_callback_set(@struct, Lib::MEMCACHED_CALLBACK_PREFIX_KEY, "")
232
+ end
233
+ )
234
+ end
235
+
236
+ # Return the current prefix key.
237
+ def prefix_key
238
+ if @struct.prefix_key.size > 0
239
+ @struct.prefix_key[0..-1 - options[:prefix_delimiter].size]
240
+ else
241
+ ""
242
+ end
243
+ end
244
+
245
+ # Safely copy this instance. Returns a Memcached instance.
246
+ #
247
+ # <tt>clone</tt> is useful for threading, since each thread must have its own unshared Memcached
248
+ # object.
249
+ #
250
+ def clone
251
+ memcached = super
252
+ struct = Lib.memcached_clone(nil, @struct)
253
+ memcached.instance_variable_set('@struct', struct)
254
+ memcached
255
+ end
256
+
257
+ # Reset the state of the libmemcached struct. This is useful for changing the server list at runtime.
258
+ def reset(current_servers = nil)
259
+ # Store state and teardown
260
+ current_servers ||= servers
261
+ prev_prefix_key = prefix_key
262
+ quit
263
+
264
+ # Create
265
+ # FIXME Duplicates logic with initialize()
266
+ @struct = Lib.memcached_create(nil)
267
+ set_prefix_key(prev_prefix_key)
268
+ set_behaviors
269
+ set_credentials
270
+ set_servers(current_servers)
271
+ end
272
+
273
+ # Disconnect from all currently connected servers
274
+ def quit
275
+ Lib.memcached_quit(@struct)
276
+ self
277
+ end
278
+
279
+ # Should retry the exception
280
+ def should_retry(e)
281
+ options[:exceptions_to_retry].each {|ex_class| return true if e.instance_of?(ex_class)}
282
+ false
283
+ end
284
+
285
+ #:stopdoc:
286
+ alias :dup :clone #:nodoc:
287
+ #:startdoc:
288
+
289
+ ### Configuration helpers
290
+
291
+ private
292
+
293
+ # Return an array of raw <tt>memcached_host_st</tt> structs for this instance.
294
+ def server_structs
295
+ array = []
296
+ if @struct.hosts
297
+ @struct.hosts.count.times do |i|
298
+ array << Lib.memcached_select_server_at(@struct, i)
299
+ end
300
+ end
301
+ array
302
+ end
303
+
304
+ ###### Operations
305
+
306
+ public
307
+
308
+ ### Setters
309
+
310
+ # Set a key/value pair. Accepts a String <tt>key</tt> and an arbitrary Ruby object. Overwrites any existing value on the server.
311
+ #
312
+ # Accepts an optional <tt>ttl</tt> value to specify the maximum lifetime of the key on the server, in seconds. <tt>ttl</tt> must be a <tt>FixNum</tt>. <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.
313
+ #
314
+ # Also accepts an <tt>encode</tt> value, which defaults to <tt>true</tt> and uses the default Marshal codec. Set <tt>encode</tt> to <tt>false</tt>, and pass a String as the <tt>value</tt>, if you want to set a raw byte array.
315
+ #
316
+ def set(key, value, ttl=@default_ttl, encode=true, flags=FLAGS)
317
+ value, flags = @codec.encode(key, value, flags) if encode
318
+ begin
319
+ check_return_code(
320
+ Lib.memcached_set(@struct, key, value, ttl, flags),
321
+ key
322
+ )
323
+ rescue => e
324
+ tries ||= 0
325
+ retry if e.instance_of?(ClientError) && !tries
326
+ raise unless tries < options[:exception_retry_limit] && should_retry(e)
327
+ tries += 1
328
+ retry
329
+ end
330
+ end
331
+
332
+ # 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>.
333
+ def add(key, value, ttl=@default_ttl, encode=true, flags=FLAGS)
334
+ value, flags = @codec.encode(key, value, flags) if encode
335
+ begin
336
+ check_return_code(
337
+ Lib.memcached_add(@struct, key, value, ttl, flags),
338
+ key
339
+ )
340
+ rescue => e
341
+ tries ||= 0
342
+ raise unless tries < options[:exception_retry_limit] && should_retry(e)
343
+ tries += 1
344
+ retry
345
+ end
346
+ end
347
+
348
+ # Increment a key's value. Accepts a String <tt>key</tt>. Raises <b>Memcached::NotFound</b> if the key does not exist.
349
+ #
350
+ # Also accepts an optional <tt>offset</tt> paramater, which defaults to 1. <tt>offset</tt> must be an integer.
351
+ #
352
+ # Note that the key must be initialized to an unencoded integer first, via <tt>set</tt>, <tt>add</tt>, or <tt>replace</tt> with <tt>encode</tt> set to <tt>false</tt>.
353
+ def increment(key, offset=1)
354
+ ret, value = Lib.memcached_increment(@struct, key, offset)
355
+ check_return_code(ret, key)
356
+ value
357
+ rescue => e
358
+ tries ||= 0
359
+ raise unless tries < options[:exception_retry_limit] && should_retry(e)
360
+ tries += 1
361
+ retry
362
+ end
363
+
364
+ # Decrement a key's value. The parameters and exception behavior are the same as <tt>increment</tt>.
365
+ def decrement(key, offset=1)
366
+ ret, value = Lib.memcached_decrement(@struct, key, offset)
367
+ check_return_code(ret, key)
368
+ value
369
+ rescue => e
370
+ tries ||= 0
371
+ raise unless tries < options[:exception_retry_limit] && should_retry(e)
372
+ tries += 1
373
+ retry
374
+ end
375
+
376
+ #:stopdoc:
377
+ alias :incr :increment
378
+ alias :decr :decrement
379
+ #:startdoc:
380
+
381
+ # 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>.
382
+ def replace(key, value, ttl=@default_ttl, encode=true, flags=FLAGS)
383
+ value, flags = @codec.encode(key, value, flags) if encode
384
+ begin
385
+ check_return_code(
386
+ Lib.memcached_replace(@struct, key, value, ttl, flags),
387
+ key
388
+ )
389
+ rescue => e
390
+ tries ||= 0
391
+ raise unless tries < options[:exception_retry_limit] && should_retry(e)
392
+ tries += 1
393
+ retry
394
+ end
395
+ end
396
+
397
+ # 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.
398
+ #
399
+ # Note that the key must be initialized to an unencoded string first, via <tt>set</tt>, <tt>add</tt>, or <tt>replace</tt> with <tt>encode</tt> set to <tt>false</tt>.
400
+ def append(key, value)
401
+ # Requires memcached 1.2.4
402
+ check_return_code(
403
+ Lib.memcached_append(@struct, key, value.to_s, IGNORED, IGNORED),
404
+ key
405
+ )
406
+ rescue => e
407
+ tries ||= 0
408
+ raise unless tries < options[:exception_retry_limit] && should_retry(e)
409
+ tries += 1
410
+ retry
411
+ end
412
+
413
+ # Prepends a string to a key's value. The parameters and exception behavior are the same as <tt>append</tt>.
414
+ def prepend(key, value)
415
+ # Requires memcached 1.2.4
416
+ check_return_code(
417
+ Lib.memcached_prepend(@struct, key, value.to_s, IGNORED, IGNORED),
418
+ key
419
+ )
420
+ rescue => e
421
+ tries ||= 0
422
+ raise unless tries < options[:exception_retry_limit] && should_retry(e)
423
+ tries += 1
424
+ retry
425
+ end
426
+
427
+ # 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.
428
+ #
429
+ # Also accepts an optional <tt>ttl</tt> value.
430
+ #
431
+ # 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.
432
+ # :retry_on_exceptions does not apply to this method
433
+ def cas(keys, ttl=@default_ttl, decode=true)
434
+ raise ClientError, "CAS not enabled for this Memcached instance" unless options[:support_cas]
435
+ tries ||= 0
436
+
437
+ if keys.is_a? Array
438
+ # Multi CAS
439
+ hash, flags_and_cas = multi_get(keys, decode)
440
+ unless hash.empty?
441
+ hash = yield hash
442
+ # Only CAS entries that were updated from the original hash
443
+ hash.delete_if {|k, _| !flags_and_cas.has_key?(k) }
444
+ hash = multi_cas(hash, ttl, flags_and_cas, decode, tries)
445
+ end
446
+ hash
447
+ else
448
+ # Single CAS
449
+ value, flags, cas = single_get(keys, decode)
450
+ value = yield value
451
+ single_cas(keys, value, ttl, flags, cas, decode)
452
+ end
453
+ rescue => e
454
+ raise unless tries < options[:exception_retry_limit] && should_retry(e)
455
+ tries += 1
456
+ retry
457
+ end
458
+
459
+ alias :compare_and_swap :cas
460
+
461
+ ### Deleters
462
+
463
+ # 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.
464
+ def delete(key)
465
+ check_return_code(
466
+ Lib.memcached_delete(@struct, key, IGNORED),
467
+ key
468
+ )
469
+ rescue => e
470
+ tries ||= 0
471
+ raise unless tries < options[:exception_retry_limit] && should_retry(e)
472
+ tries += 1
473
+ retry
474
+ end
475
+
476
+ # Flushes all key/value pairs from all the servers.
477
+ def flush
478
+ check_return_code(
479
+ Lib.memcached_flush(@struct, IGNORED)
480
+ )
481
+ rescue => e
482
+ tries ||= 0
483
+ raise unless tries < options[:exception_retry_limit] && should_retry(e)
484
+ tries += 1
485
+ retry
486
+ end
487
+
488
+ ### Getters
489
+
490
+ # Gets a key's value from the server. Accepts a String <tt>key</tt> or array of String <tt>keys</tt>.
491
+ #
492
+ # Also accepts a <tt>decode</tt> value, which defaults to <tt>true</tt>. Set <tt>decode</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 an encoded Ruby object and decoded.
493
+ #
494
+ # 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.
495
+ #
496
+ # The multiget behavior is subject to change in the future; however, for multiple lookups, it is much faster than normal mode.
497
+ #
498
+ # 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.
499
+ #
500
+ def get(keys, decode=true)
501
+ if keys.is_a? Array
502
+ multi_get(keys, decode).first
503
+ else
504
+ single_get(keys, decode).first
505
+ end
506
+ rescue => e
507
+ tries ||= 0
508
+ raise unless tries < options[:exception_retry_limit] && should_retry(e)
509
+ tries += 1
510
+ retry
511
+ end
512
+
513
+ # Check if a key exists on the server. It will return nil if the value is found, or raise
514
+ # <tt>Memcached::NotFound</tt> if the key does not exist.
515
+ def exist(key)
516
+ check_return_code(
517
+ Lib.memcached_exist(@struct, key),
518
+ key
519
+ )
520
+ end
521
+
522
+ # Gets a key's value from the previous server. Only useful with random distribution.
523
+ def get_from_last(key, decode=true)
524
+ raise ArgumentError, "get_from_last() is not useful unless :random distribution is enabled." unless options[:distribution] == :random
525
+ value, flags, ret = Lib.memcached_get_from_last_rvalue(@struct, key)
526
+ check_return_code(ret, key)
527
+ decode ? @codec.decode(key, value, flags) : value
528
+ end
529
+
530
+ ### Information methods
531
+
532
+ # Return the server used by a particular key.
533
+ def server_by_key(key)
534
+ ret = Lib.memcached_server_by_key(@struct, key)
535
+ if ret.is_a?(Array)
536
+ check_return_code(ret.last)
537
+ inspect_server(ret.first)
538
+ else
539
+ check_return_code(ret)
540
+ end
541
+ rescue ABadKeyWasProvidedOrCharactersOutOfRange
542
+ end
543
+
544
+ # 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.
545
+ def stats(subcommand = nil)
546
+ stats = Hash.new([])
547
+
548
+ stat_struct, ret = Lib.memcached_stat(@struct, subcommand)
549
+ check_return_code(ret)
550
+
551
+ keys, ret = Lib.memcached_stat_get_keys(@struct, stat_struct)
552
+ check_return_code(ret)
553
+
554
+ keys.each do |key|
555
+ server_structs.size.times do |index|
556
+
557
+ value, ret = Lib.memcached_stat_get_value(
558
+ @struct,
559
+ Lib.memcached_select_stat_at(@struct, stat_struct, index),
560
+ key)
561
+ check_return_code(ret, key)
562
+
563
+ value = case value
564
+ when /^\d+\.\d+$/ then value.to_f
565
+ when /^\d+$/ then value.to_i
566
+ else value
567
+ end
568
+
569
+ stats[key.to_sym] += [value]
570
+ end
571
+ end
572
+
573
+ Lib.memcached_stat_free(@struct, stat_struct)
574
+ stats
575
+ rescue Memcached::SomeErrorsWereReported => _
576
+ e = _.class.new("Error getting stats")
577
+ e.set_backtrace(_.backtrace)
578
+ raise e
579
+ end
580
+
581
+ ### Operations helpers
582
+
583
+ private
584
+
585
+ # 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.
586
+ def check_return_code(ret, key = nil) #:doc:
587
+ if ret == 0 # Lib::MEMCACHED_SUCCESS
588
+ elsif ret == 32 # Lib::MEMCACHED_BUFFERED
589
+ elsif ret == 16
590
+ raise @not_found # Lib::MEMCACHED_NOTFOUND
591
+ elsif ret == 14
592
+ raise @not_stored # Lib::MEMCACHED_NOTSTORED
593
+ else
594
+ reraise(key, ret)
595
+ end
596
+ rescue TypeError
597
+ # Reached if show_backtraces is true
598
+ reraise(key, ret)
599
+ end
600
+
601
+ def reraise(key, ret)
602
+ message = "Key #{inspect_keys(key, (detect_failure if ret == Lib::MEMCACHED_SERVER_MARKED_DEAD)).inspect}" if key
603
+ if key.is_a?(String)
604
+ if ret == Lib::MEMCACHED_ERRNO
605
+ if (server = Lib.memcached_server_by_key(@struct, key)).is_a?(Array)
606
+ errno = server.first.cached_errno
607
+ message = "Errno #{errno}: #{ERRNO_HASH[errno].inspect}. #{message}"
608
+ end
609
+ elsif ret == Lib::MEMCACHED_SERVER_ERROR
610
+ if (server = Lib.memcached_server_by_key(@struct, key)).is_a?(Array)
611
+ message = "\"#{server.first.cached_server_error}\". #{message}"
612
+ end
613
+ end
614
+ end
615
+ if EXCEPTIONS[ret]
616
+ raise EXCEPTIONS[ret], message
617
+ else
618
+ raise Memcached::Error, "Unknown return code: #{ret}"
619
+ end
620
+ end
621
+
622
+ # Turn an array of keys into a hash of keys to servers.
623
+ def inspect_keys(keys, server = nil)
624
+ Hash[*Array(keys).map do |key|
625
+ [key, server || server_by_key(key)]
626
+ end.flatten]
627
+ end
628
+
629
+ # Find which server failed most recently.
630
+ # FIXME Is this still necessary with cached_errno?
631
+ def detect_failure
632
+ time = Time.now
633
+ server = server_structs.detect do |server|
634
+ server.next_retry > time
635
+ end
636
+ inspect_server(server) if server
637
+ end
638
+
639
+ # Set the behaviors on the struct from the current options.
640
+ def set_behaviors
641
+ BEHAVIORS.keys.each do |behavior|
642
+ set_behavior(behavior, options[behavior]) if options.key?(behavior)
643
+ end
644
+ # BUG Hash must be last due to the weird Libmemcached multi-behaviors
645
+ set_behavior(:hash, options[:hash])
646
+ end
647
+
648
+ # Set the SASL credentials from the current options. If credentials aren't provided, try to get them from the environment.
649
+ def set_credentials
650
+ if options[:credentials]
651
+ check_return_code(
652
+ Lib.memcached_set_sasl_auth_data(@struct, *options[:credentials])
653
+ )
654
+ end
655
+ end
656
+
657
+ def is_unix_socket?(server)
658
+ server.type == Lib::MEMCACHED_CONNECTION_UNIX_SOCKET
659
+ end
660
+
661
+ # Stringify an opaque server struct
662
+ def inspect_server(server)
663
+ strings = [server.hostname]
664
+ if !is_unix_socket?(server)
665
+ strings << ":#{server.port}"
666
+ strings << ":#{server.weight}" if options[:ketama_weighted]
667
+ end
668
+ strings.join
669
+ end
670
+
671
+ def single_get(key, decode)
672
+ value, flags, ret = Lib.memcached_get_rvalue(@struct, key)
673
+ check_return_code(ret, key)
674
+ cas = @struct.result.cas if @support_cas
675
+ value = @codec.decode(key, value, flags) if decode
676
+ [value, flags, cas]
677
+ end
678
+
679
+ def multi_get(keys, decode)
680
+ ret = Lib.memcached_mget(@struct, keys)
681
+ check_return_code(ret, keys)
682
+
683
+ hash = {}
684
+ flags_and_cas = {} if @support_cas
685
+ value, key, flags, ret = Lib.memcached_fetch_rvalue(@struct)
686
+ while ret != 21 do # Lib::MEMCACHED_END
687
+ if ret == 0 # Lib::MEMCACHED_SUCCESS
688
+ flags_and_cas[key] = [flags, @struct.result.cas] if @support_cas
689
+ hash[key] = decode ? [value, flags] : value
690
+ elsif ret != 16 # Lib::MEMCACHED_NOTFOUND
691
+ check_return_code(ret, key)
692
+ end
693
+ value, key, flags, ret = Lib.memcached_fetch_rvalue(@struct)
694
+ end
695
+ if decode
696
+ hash.each do |key, value_and_flags|
697
+ hash[key] = @codec.decode(key, *value_and_flags)
698
+ end
699
+ end
700
+ [hash, flags_and_cas]
701
+ end
702
+
703
+ def multi_cas(hash, ttl, flags_and_cas, encode, tries)
704
+ result = {}
705
+ hash.each do |key, value|
706
+ raw_value = value
707
+ flags, cas = flags_and_cas[key]
708
+ value, flags = @codec.encode(key, value, flags) if encode
709
+ begin
710
+ ret = Lib.memcached_cas(@struct, key, value, ttl, flags, cas)
711
+ if ret == 0 # Lib::MEMCACHED_SUCCESS
712
+ result[key] = raw_value
713
+ elsif ret != 12 && ret != 16 # Lib::MEMCACHED_DATA_EXISTS, Lib::MEMCACHED_NOTFOUND
714
+ check_return_code(ret, key)
715
+ end
716
+ rescue => e
717
+ raise unless tries < options[:exception_retry_limit] && should_retry(e)
718
+ tries += 1
719
+ retry
720
+ end
721
+ end
722
+ result
723
+ end
724
+
725
+ def single_cas(key, value, ttl, flags, cas, encode)
726
+ value, flags = @codec.encode(key, value, flags) if encode
727
+ check_return_code(
728
+ Lib.memcached_cas(@struct, key, value, ttl, flags, cas),
729
+ key
730
+ )
731
+ end
732
+ end