client.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. import six
  2. try:
  3. import cPickle as pickle
  4. except ImportError:
  5. import pickle as pickle
  6. from bmemcached.protocol import Protocol
  7. _SOCKET_TIMEOUT = 3
  8. class Client(object):
  9. """
  10. This is intended to be a client class which implement standard cache interface that common libs do.
  11. :param servers: A list of servers with ip[:port] or unix socket.
  12. :type servers: list
  13. :param username: If your server requires SASL authentication, provide the username.
  14. :type username: six.string_types
  15. :param password: If your server requires SASL authentication, provide the password.
  16. :type password: six.string_types
  17. :param compression: This memcached client uses zlib compression by default,
  18. but you can change it to any Python module that provides
  19. `compress` and `decompress` functions, such as `bz2`.
  20. :type compression: Python module
  21. :param pickler: Use this to replace the object serialization mechanism.
  22. :type pickler: function
  23. :param unpickler: Use this to replace the object deserialization mechanism.
  24. :type unpickler: function
  25. :param socket_timeout: The timeout applied to memcached connections.
  26. :type socket_timeout: float
  27. """
  28. def __init__(self, servers=('127.0.0.1:11211',), username=None,
  29. password=None, compression=None,
  30. socket_timeout=_SOCKET_TIMEOUT,
  31. pickle_protocol=0,
  32. pickler=pickle.Pickler,
  33. unpickler=pickle.Unpickler):
  34. self.username = username
  35. self.password = password
  36. self.compression = compression
  37. self.socket_timeout = socket_timeout
  38. self.pickle_protocol = pickle_protocol
  39. self.pickler = pickler
  40. self.unpickler = unpickler
  41. self.set_servers(servers)
  42. @property
  43. def servers(self):
  44. for server in self._servers:
  45. yield server
  46. def set_servers(self, servers):
  47. """
  48. Iter to a list of servers and instantiate Protocol class.
  49. :param servers: A list of servers
  50. :type servers: list
  51. :return: Returns nothing
  52. :rtype: None
  53. """
  54. if isinstance(servers, six.string_types):
  55. servers = [servers]
  56. assert servers, "No memcached servers supplied"
  57. self._servers = [Protocol(
  58. server=server,
  59. username=self.username,
  60. password=self.password,
  61. compression=self.compression,
  62. socket_timeout=self.socket_timeout,
  63. pickle_protocol=self.pickle_protocol,
  64. pickler=self.pickler,
  65. unpickler=self.unpickler,
  66. ) for server in servers]
  67. def _set_retry_delay(self, value):
  68. for server in self._servers:
  69. server.set_retry_delay(value)
  70. def enable_retry_delay(self, enable):
  71. """
  72. Enable or disable delaying between reconnection attempts.
  73. The first reconnection attempt will always happen immediately, so intermittent network
  74. errors don't cause caching to turn off. The retry delay takes effect after the first
  75. reconnection fails.
  76. The reconnection delay is enabled by default for TCP connections, and disabled by
  77. default for Unix socket connections.
  78. """
  79. # The public API only allows enabling or disabling the delay, so it'll be easier to
  80. # add exponential falloff in the future. _set_retry_delay is exposed for tests.
  81. self._set_retry_delay(5 if enable else 0)
  82. def get(self, key, get_cas=False):
  83. """
  84. Get a key from server.
  85. :param key: Key's name
  86. :type key: six.string_types
  87. :param get_cas: If true, return (value, cas), where cas is the new CAS value.
  88. :type get_cas: boolean
  89. :return: Returns a key data from server.
  90. :rtype: object
  91. """
  92. for server in self.servers:
  93. value, cas = server.get(key)
  94. if value is not None:
  95. if get_cas:
  96. return value, cas
  97. else:
  98. return value
  99. def gets(self, key):
  100. """
  101. Get a key from server, returning the value and its CAS key.
  102. This method is for API compatibility with other implementations.
  103. :param key: Key's name
  104. :type key: six.string_types
  105. :return: Returns (key data, value), or (None, None) if the value is not in cache.
  106. :rtype: object
  107. """
  108. for server in self.servers:
  109. value, cas = server.get(key)
  110. if value is not None:
  111. return value, cas
  112. return None, None
  113. def get_multi(self, keys, get_cas=False):
  114. """
  115. Get multiple keys from server.
  116. :param keys: A list of keys to from server.
  117. :type keys: list
  118. :param get_cas: If get_cas is true, each value is (data, cas), with each result's CAS value.
  119. :type get_cas: boolean
  120. :return: A dict with all requested keys.
  121. :rtype: dict
  122. """
  123. d = {}
  124. if keys:
  125. for server in self.servers:
  126. results = server.get_multi(keys)
  127. if not get_cas:
  128. for key, (value, cas) in results.items():
  129. results[key] = value
  130. d.update(results)
  131. keys = [_ for _ in keys if _ not in d]
  132. if not keys:
  133. break
  134. return d
  135. def set(self, key, value, time=0, compress_level=-1):
  136. """
  137. Set a value for a key on server.
  138. :param key: Key's name
  139. :type key: str
  140. :param value: A value to be stored on server.
  141. :type value: object
  142. :param time: Time in seconds that your key will expire.
  143. :type time: int
  144. :param compress_level: How much to compress.
  145. 0 = no compression, 1 = fastest, 9 = slowest but best,
  146. -1 = default compression level.
  147. :type compress_level: int
  148. :return: True in case of success and False in case of failure
  149. :rtype: bool
  150. """
  151. returns = []
  152. for server in self.servers:
  153. returns.append(server.set(key, value, time, compress_level=compress_level))
  154. return any(returns)
  155. def cas(self, key, value, cas, time=0, compress_level=-1):
  156. """
  157. Set a value for a key on server if its CAS value matches cas.
  158. :param key: Key's name
  159. :type key: six.string_types
  160. :param value: A value to be stored on server.
  161. :type value: object
  162. :param time: Time in seconds that your key will expire.
  163. :type time: int
  164. :param compress_level: How much to compress.
  165. 0 = no compression, 1 = fastest, 9 = slowest but best,
  166. -1 = default compression level.
  167. :type compress_level: int
  168. :return: True in case of success and False in case of failure
  169. :rtype: bool
  170. """
  171. returns = []
  172. for server in self.servers:
  173. returns.append(server.cas(key, value, cas, time, compress_level=compress_level))
  174. return any(returns)
  175. def set_multi(self, mappings, time=0, compress_level=-1):
  176. """
  177. Set multiple keys with it's values on server.
  178. :param mappings: A dict with keys/values
  179. :type mappings: dict
  180. :param time: Time in seconds that your key will expire.
  181. :type time: int
  182. :param compress_level: How much to compress.
  183. 0 = no compression, 1 = fastest, 9 = slowest but best,
  184. -1 = default compression level.
  185. :type compress_level: int
  186. :return: True in case of success and False in case of failure
  187. :rtype: bool
  188. """
  189. returns = []
  190. if mappings:
  191. for server in self.servers:
  192. returns.append(server.set_multi(mappings, time, compress_level=compress_level))
  193. return all(returns)
  194. def add(self, key, value, time=0, compress_level=-1):
  195. """
  196. Add a key/value to server ony if it does not exist.
  197. :param key: Key's name
  198. :type key: six.string_types
  199. :param value: A value to be stored on server.
  200. :type value: object
  201. :param time: Time in seconds that your key will expire.
  202. :type time: int
  203. :param compress_level: How much to compress.
  204. 0 = no compression, 1 = fastest, 9 = slowest but best,
  205. -1 = default compression level.
  206. :type compress_level: int
  207. :return: True if key is added False if key already exists
  208. :rtype: bool
  209. """
  210. returns = []
  211. for server in self.servers:
  212. returns.append(server.add(key, value, time, compress_level=compress_level))
  213. return any(returns)
  214. def replace(self, key, value, time=0, compress_level=-1):
  215. """
  216. Replace a key/value to server ony if it does exist.
  217. :param key: Key's name
  218. :type key: six.string_types
  219. :param value: A value to be stored on server.
  220. :type value: object
  221. :param time: Time in seconds that your key will expire.
  222. :type time: int
  223. :param compress_level: How much to compress.
  224. 0 = no compression, 1 = fastest, 9 = slowest but best,
  225. -1 = default compression level.
  226. :type compress_level: int
  227. :return: True if key is replace False if key does not exists
  228. :rtype: bool
  229. """
  230. returns = []
  231. for server in self.servers:
  232. returns.append(server.replace(key, value, time, compress_level=compress_level))
  233. return any(returns)
  234. def delete(self, key, cas=0):
  235. """
  236. Delete a key/value from server. If key does not exist, it returns True.
  237. :param key: Key's name to be deleted
  238. :type key: six.string_types
  239. :return: True in case o success and False in case of failure.
  240. :rtype: bool
  241. """
  242. returns = []
  243. for server in self.servers:
  244. returns.append(server.delete(key, cas))
  245. return any(returns)
  246. def delete_multi(self, keys):
  247. returns = []
  248. for server in self.servers:
  249. returns.append(server.delete_multi(keys))
  250. return all(returns)
  251. def incr(self, key, value):
  252. """
  253. Increment a key, if it exists, returns it's actual value, if it don't, return 0.
  254. :param key: Key's name
  255. :type key: six.string_types
  256. :param value: Number to be incremented
  257. :type value: int
  258. :return: Actual value of the key on server
  259. :rtype: int
  260. """
  261. returns = []
  262. for server in self.servers:
  263. returns.append(server.incr(key, value))
  264. return returns[0]
  265. def decr(self, key, value):
  266. """
  267. Decrement a key, if it exists, returns it's actual value, if it don't, return 0.
  268. Minimum value of decrement return is 0.
  269. :param key: Key's name
  270. :type key: six.string_types
  271. :param value: Number to be decremented
  272. :type value: int
  273. :return: Actual value of the key on server
  274. :rtype: int
  275. """
  276. returns = []
  277. for server in self.servers:
  278. returns.append(server.decr(key, value))
  279. return returns[0]
  280. def flush_all(self, time=0):
  281. """
  282. Send a command to server flush|delete all keys.
  283. :param time: Time to wait until flush in seconds.
  284. :type time: int
  285. :return: True in case of success, False in case of failure
  286. :rtype: bool
  287. """
  288. returns = []
  289. for server in self.servers:
  290. returns.append(server.flush_all(time))
  291. return any(returns)
  292. def stats(self, key=None):
  293. """
  294. Return server stats.
  295. :param key: Optional if you want status from a key.
  296. :type key: six.string_types
  297. :return: A dict with server stats
  298. :rtype: dict
  299. """
  300. # TODO: Stats with key is not working.
  301. returns = {}
  302. for server in self.servers:
  303. returns[server.server] = server.stats(key)
  304. return returns
  305. def disconnect_all(self):
  306. """
  307. Disconnect all servers.
  308. :return: Nothing
  309. :rtype: None
  310. """
  311. for server in self.servers:
  312. server.disconnect()