PageRenderTime 53ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/src/providers/consumers/cache.c

https://gitlab.com/drwdal/magma
C | 403 lines | 240 code | 64 blank | 99 comment | 87 complexity | 260eaf31d048af7fc2bd6720318abc08 MD5 | raw file
  1. /**
  2. * @file /magma/providers/consumers/cache.c
  3. *
  4. * @brief Distributed cache interface.
  5. *
  6. * $Author$
  7. * $Date$
  8. * $Revision$
  9. *
  10. */
  11. #include "magma.h"
  12. pool_t *cache_pool = NULL;
  13. /**
  14. * @brief Initialize the memcached library and bind dynamically to the exported functions that are required.
  15. * @result true on success or false on failure.
  16. */
  17. bool_t lib_load_cache(void) {
  18. symbol_t cache[] = {
  19. M_BIND(memcached_add), M_BIND(memcached_append), M_BIND(memcached_behavior_set), M_BIND(memcached_cas), M_BIND(memcached_create),
  20. M_BIND(memcached_decrement), M_BIND(memcached_decrement_with_initial), M_BIND(memcached_delete), M_BIND(memcached_flush),
  21. M_BIND(memcached_free), M_BIND(memcached_get), M_BIND(memcached_increment), M_BIND(memcached_increment_with_initial),
  22. M_BIND(memcached_lib_version), M_BIND(memcached_prepend), M_BIND(memcached_replace), M_BIND(memcached_server_add_with_weight),
  23. M_BIND(memcached_set), M_BIND(memcached_strerror)
  24. };
  25. if (lib_symbols(sizeof(cache) / sizeof(symbol_t), cache) != 1) {
  26. return false;
  27. }
  28. return true;
  29. }
  30. /**
  31. * @brief Return the version string of the memcached library.
  32. * @return a pointer to a character string containing the memcached library version information.
  33. */
  34. const char * lib_version_cache(void) {
  35. return memcached_lib_version_d();
  36. }
  37. /*
  38. * @brief Create and initialize a pool of binary client connections to the memcached server(s).
  39. * @return false on failure or true on success.
  40. */
  41. bool_t cache_start(void) {
  42. memcached_st *object;
  43. memcached_return_t e;
  44. // Allocate a pool structure for the cache instances.
  45. if (!(cache_pool = pool_alloc(magma.iface.cache.pool.connections, magma.iface.cache.pool.timeout))) {
  46. log_critical("Could not allocate memory for the cache connection pool.");
  47. return false;
  48. }
  49. for (uint32_t i = 0; i < magma.iface.cache.pool.connections; i++) {
  50. // Create the context.
  51. if (!(object = memcached_create_d(NULL))) {
  52. log_critical("Could not initialize the cache connection structure.");
  53. return false;
  54. }
  55. // The increment/decrement key with an initial value functions only work if were using the binary protocol.
  56. else if ((e = memcached_behavior_set_d(object, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, 1)) != MEMCACHED_SUCCESS) {
  57. log_critical("Unable to configure the cache context to use the binary protocols. {%s}", memcached_strerror_d(object, e));
  58. memcached_free_d(object);
  59. return false;
  60. }
  61. // If the client can't connect to a particular server, it will be removed from the rotation (for ten minutes by default).
  62. else if ((e = memcached_behavior_set_d(object, MEMCACHED_BEHAVIOR_RETRY_TIMEOUT, magma.iface.cache.retry)) != MEMCACHED_SUCCESS) {
  63. log_critical("Unable to configure the cache context to use the binary protocols. {%s}", memcached_strerror_d(object, e));
  64. memcached_free_d(object);
  65. return false;
  66. }
  67. // If the TCP socket timeout.
  68. else if ((e = memcached_behavior_set_d(object, MEMCACHED_BEHAVIOR_SND_TIMEOUT, magma.iface.cache.timeout)) != MEMCACHED_SUCCESS ||
  69. (e = memcached_behavior_set_d(object, MEMCACHED_BEHAVIOR_RCV_TIMEOUT, magma.iface.cache.timeout)) != MEMCACHED_SUCCESS) {
  70. log_critical("Unable to configure the socket timeout. {%s}", memcached_strerror_d(object, e));
  71. memcached_free_d(object);
  72. return false;
  73. }
  74. // Add the cache servers.
  75. for (uint32_t j = 0; j < MAGMA_CACHE_INSTANCES; j++) {
  76. if (magma.iface.cache.host[j] && (e = memcached_server_add_with_weight_d(object, magma.iface.cache.host[j]->name, magma.iface.cache.host[j]->port,
  77. magma.iface.cache.host[j]->weight)) != MEMCACHED_SUCCESS) {
  78. log_critical("Unable to add the cache instance to our context. {%s}", memcached_strerror_d(object, e));
  79. memcached_free_d(object);
  80. return false;
  81. }
  82. }
  83. pool_set_obj(cache_pool, i, object);
  84. }
  85. return true;
  86. }
  87. /**
  88. * @brief Destroy the memcached client pool and all active connections.
  89. * @return This function returns no value.
  90. */
  91. void cache_stop(void) {
  92. memcached_st *object;
  93. // Destroy the objects.
  94. for (uint32_t i = 0; i < magma.iface.cache.pool.connections; i++) {
  95. if ((object = pool_get_obj(cache_pool, i))) memcached_free_d(object);
  96. }
  97. // Destroy the pool.
  98. pool_free(cache_pool);
  99. cache_pool = NULL;
  100. return;
  101. }
  102. /*
  103. * @brief Flush (wipe) the contents held by memcached.
  104. * @return This function returns no value.
  105. */
  106. void cache_flush(void) {
  107. uint32_t pool;
  108. memcached_return_t e;
  109. if ((pool_pull(cache_pool, &pool)) != PL_RESERVED) {
  110. return;
  111. }
  112. else if ((e = memcached_flush_d(pool_get_obj(cache_pool, pool), 0)) != MEMCACHED_SUCCESS) {
  113. log_info("Unable to flush distributed caching system. {%s}", memcached_strerror_d(pool_get_obj(cache_pool, pool), e));
  114. }
  115. pool_release(cache_pool, pool);
  116. return;
  117. }
  118. /**
  119. * @brief Retrieve data from memcached by key.
  120. * @param key a managed string containing a key to be passed to memcached.
  121. * @return NULL on failure, or a pointer to a managed string containing the cached data on success.
  122. */
  123. stringer_t * cache_get(stringer_t *key) {
  124. void *data;
  125. size_t length = 0;
  126. uint32_t flags = 0, pool;
  127. stringer_t *result;
  128. memcached_return_t error;
  129. if ((pool_pull(cache_pool, &pool)) != PL_RESERVED) {
  130. return NULL;
  131. }
  132. else if (!(data = memcached_get_d(pool_get_obj(cache_pool, pool), st_char_get(key), st_length_get(key), &length, &flags, &error))) {
  133. // Error check.
  134. if (error != MEMCACHED_NOTFOUND) {
  135. log_info("An error occurred while trying to fetch the %.*s object. {%s}", st_length_int(key), st_char_get(key), memcached_strerror_d(pool_get_obj(cache_pool, pool), error));
  136. }
  137. pool_release(cache_pool, pool);
  138. return NULL;
  139. }
  140. else if (!length) {
  141. log_info("A non-NULL pointer was returned, but the length was set to zero.");
  142. pool_release(cache_pool, pool);
  143. mm_free(data);
  144. return NULL;
  145. }
  146. pool_release(cache_pool, pool);
  147. result = st_import(data, length);
  148. mm_free(data);
  149. return result;
  150. }
  151. /**
  152. * @brief Retrieve a 64 bit value from memcached by key.
  153. * @param key a managed string containing a key to be passed to memcached.
  154. * @return NULL on failure, or the 64 bit value cached data on success.
  155. */
  156. uint64_t cache_get_u64(stringer_t *key) {
  157. void *data;
  158. size_t length = 0;
  159. uint64_t result = 0;
  160. uint32_t flags = 0, pool;
  161. memcached_return_t error;
  162. if ((pool_pull(cache_pool, &pool)) != PL_RESERVED) {
  163. return 0;
  164. }
  165. else if ((data = memcached_get_d(pool_get_obj(cache_pool, pool), st_char_get(key), st_length_get(key), &length, &flags, &error)) == NULL) {
  166. // Error check.
  167. if (error != MEMCACHED_NOTFOUND) {
  168. log_info("An error occurred while trying to fetch the %.*s object. {%s}", st_length_int(key), st_char_get(key), memcached_strerror_d(pool_get_obj(cache_pool, pool), error));
  169. }
  170. pool_release(cache_pool, pool);
  171. return 0;
  172. }
  173. else if (!length) {
  174. log_info("A non-NULL pointer was returned, but the length was set to zero.");
  175. pool_release(cache_pool, pool);
  176. mm_free(data);
  177. return 0;
  178. }
  179. // If the result is the correct number of bytes, store it in teh result.
  180. if (length == sizeof(uint64_t)) {
  181. result = *((uint64_t *)data);
  182. }
  183. pool_release(cache_pool, pool);
  184. mm_free(data);
  185. return result;
  186. }
  187. /**
  188. * @brief Set a value in memcached by key.
  189. * @param key a managed string containing a key to be passed to memcached.
  190. * @param object a managed string containing the new data to be associated with the supplied key.
  191. * @param expiration the expiration time of the cached data.
  192. * @return 0 on failure, or 1 on success.
  193. */
  194. int_t cache_set(stringer_t *key, stringer_t *object, time_t expiration) {
  195. uint32_t pool;
  196. memcached_return_t ret;
  197. if ((pool_pull(cache_pool, &pool)) != PL_RESERVED) {
  198. return 0;
  199. }
  200. else if ((ret = memcached_set_d(pool_get_obj(cache_pool, pool), st_char_get(key), st_length_get(key), st_char_get(object), st_length_get(object), expiration, 0)) != MEMCACHED_SUCCESS) {
  201. log_info("Unable to store the %.*s object. {%s}", st_length_int(key), st_char_get(key), memcached_strerror_d(pool_get_obj(cache_pool, pool), ret));
  202. pool_release(cache_pool, pool);
  203. return 0;
  204. }
  205. pool_release(cache_pool, pool);
  206. return 1;
  207. }
  208. /**
  209. * @brief Set a 64 bit value in memcached by key.
  210. * @param key a managed string containing a key to be passed to memcached.
  211. * @param value the new value to be associated with the supplied key.
  212. * @param expiration the expiration time of the cached data.
  213. * @return NULL on failure, or the retrieved unsigned 64 bit cached data on success.
  214. */
  215. int_t cache_set_u64(stringer_t *key, uint64_t value, time_t expiration) {
  216. uint32_t pool;
  217. memcached_return_t ret;
  218. if ((pool_pull(cache_pool, &pool)) != PL_RESERVED) {
  219. return 0;
  220. }
  221. else if ((ret = memcached_set_d(pool_get_obj(cache_pool, pool), st_char_get(key), st_length_get(key), (void *)&value, sizeof(uint64_t), expiration, 0)) != MEMCACHED_SUCCESS) {
  222. log_info("Unable to store the %.*s object. {%s}", st_length_int(key), st_char_get(key), memcached_strerror_d(pool_get_obj(cache_pool, pool), ret));
  223. pool_release(cache_pool, pool);
  224. return 0;
  225. }
  226. pool_release(cache_pool, pool);
  227. return 1;
  228. }
  229. /**
  230. * @brief Add a new key/value pair to the memcached server.
  231. * @param key a managed string with the name of the key to be added to the cache.
  232. * @param object a managed string containing the initial value of the new key.
  233. * @param expiration an expiration time, in seconds, for the newly cached data.
  234. */
  235. int_t cache_add(stringer_t *key, stringer_t *object, time_t expiration) {
  236. uint32_t pool;
  237. memcached_return_t ret;
  238. if ((pool_pull(cache_pool, &pool)) != PL_RESERVED) {
  239. return 0;
  240. }
  241. else if ((ret = memcached_add_d(pool_get_obj(cache_pool, pool), st_char_get(key), st_length_get(key), st_char_get(object), st_length_get(object), expiration, 0)) != MEMCACHED_SUCCESS) {
  242. log_info("Unable to store the %.*s object. {%s}", st_length_int(key), st_char_get(key), memcached_strerror_d(pool_get_obj(cache_pool, pool), ret));
  243. pool_release(cache_pool, pool);
  244. return 0;
  245. }
  246. pool_release(cache_pool, pool);
  247. return 1;
  248. }
  249. /**
  250. * @brief Add a new key/value pair to the memcached server, but suppress any error messages.
  251. * @param key a managed string with the name of the key to be added to the cache.
  252. * @param object a managed string containing the initial value of the new key.
  253. * @param expiration an expiration time, in seconds, for the newly cached data.
  254. */
  255. int_t cache_silent_add(stringer_t *key, stringer_t *object, time_t expiration) {
  256. uint32_t pool;
  257. memcached_return_t ret;
  258. if ((pool_pull(cache_pool, &pool)) != PL_RESERVED) {
  259. return 0;
  260. }
  261. else if ((ret = memcached_add_d(pool_get_obj(cache_pool, pool), st_char_get(key), st_length_get(key), st_char_get(object), st_length_get(object), expiration, 0)) != MEMCACHED_SUCCESS) {
  262. pool_release(cache_pool, pool);
  263. return 0;
  264. }
  265. pool_release(cache_pool, pool);
  266. return 1;
  267. }
  268. /**
  269. * @brief Append data to the value of a cached keye on a memcached server.
  270. * @param key a managed string with the name of the target memcached key.
  271. * @param object a managed string containing the value of the data being appended to the original key.
  272. * @param expiration an expiration time, in seconds, for the modified cached data.
  273. */
  274. int_t cache_append(stringer_t *key, stringer_t *object, time_t expiration) {
  275. uint32_t pool;
  276. memcached_return_t val;
  277. if ((pool_pull(cache_pool, &pool)) != PL_RESERVED) {
  278. return 0;
  279. }
  280. else if ((val = memcached_append_d(pool_get_obj(cache_pool, pool), st_char_get(key), st_length_get(key), st_char_get(object), st_length_get(object), expiration, 0)) != MEMCACHED_SUCCESS) {
  281. if (val == MEMCACHED_NOTSTORED && (val = memcached_add_d(pool_get_obj(cache_pool, pool), st_char_get(key), st_length_get(key), st_char_get(object), st_length_get(object), expiration, 0)) != MEMCACHED_SUCCESS) {
  282. log_info("Unable to append to the %.*s object. {%s}", st_length_int(key), st_char_get(key), memcached_strerror_d(pool_get_obj(cache_pool, pool), val));
  283. pool_release(cache_pool, pool);
  284. return 0;
  285. }
  286. else if (val != MEMCACHED_NOTSTORED) {
  287. log_info("Unable to append to the %.*s object. {%s}", st_length_int(key), st_char_get(key), memcached_strerror_d(pool_get_obj(cache_pool, pool), val));
  288. pool_release(cache_pool, pool);
  289. return 0;
  290. }
  291. }
  292. pool_release(cache_pool, pool);
  293. return 1;
  294. }
  295. /**
  296. * @brief Delete a key from memcached immediately.
  297. * @note memcached_delete()
  298. * @param key the name of the key to be deleted from the memcached server.
  299. * @result 0 on failure, or MEMCACHED_SUCCESS on success.
  300. */
  301. int_t cache_delete(stringer_t *key) {
  302. uint32_t pool;
  303. memcached_return_t val;
  304. if ((pool_pull(cache_pool, &pool)) != PL_RESERVED) {
  305. return 0;
  306. }
  307. else if ((val = memcached_delete_d(pool_get_obj(cache_pool, pool), st_char_get(key), st_length_get(key), 0)) != MEMCACHED_SUCCESS) {
  308. log_info("Unable to delete the %.*s object. {%s}", st_length_int(key), st_char_get(key), memcached_strerror_d(pool_get_obj(cache_pool, pool), val));
  309. pool_release(cache_pool, pool);
  310. return 0;
  311. }
  312. pool_release(cache_pool, pool);
  313. return 1;
  314. }
  315. /**
  316. * @brief Increment the value stored with a key by memcached by a specified offset.
  317. * @see memcached_increment_with_initial()
  318. * @param key a managed string containing the name of the memcached key to be adjusted.
  319. * @param offset the offset by which the specified key's value should be incremented.
  320. * @param initial the initial value to seed the key if it does not already exist.
  321. * @param expiration the time, in seconds, when the cached key will expire.
  322. * @result 0 on failure, or the new incremented value of the data associated with the key, on success.
  323. */
  324. uint64_t cache_increment(stringer_t *key, uint64_t offset, uint64_t initial, time_t expiration) {
  325. uint32_t pool;
  326. uint64_t output;
  327. memcached_return_t val;
  328. if ((pool_pull(cache_pool, &pool)) != PL_RESERVED) {
  329. return 0;
  330. }
  331. // Try incrementing. If we can't, try creating the key.
  332. else if ((val = memcached_increment_with_initial_d(pool_get_obj(cache_pool, pool), st_char_get(key), st_length_get(key), offset, initial, expiration, &output)) != MEMCACHED_SUCCESS) {
  333. log_pedantic("Unable to increment the %.*s object. {%s}", st_length_int(key), st_char_get(key), memcached_strerror_d(pool_get_obj(cache_pool, pool), val));
  334. pool_release(cache_pool, pool);
  335. return 0;
  336. }
  337. pool_release(cache_pool, pool);
  338. return output;
  339. }