[prev in list] [next in list] [prev in thread] [next in thread]
List: pecl-cvs
Subject: [PECL-CVS] cvs: pecl /memcache CREDITS memcache.c php_memcache.h /memcache/tests 018.phpt 019.phpt
From: "Mikael Johansson" <mikl () php ! net>
Date: 2005-10-29 14:24:50
Message-ID: cvsmikl1130595890 () cvsserver
[Download RAW message or body]
mikl Sat Oct 29 10:24:50 2005 EDT
Added files:
/pecl/memcache/tests 018.phpt 019.phpt 020.phpt 021.phpt 022.phpt
023.phpt 024.phpt 025.phpt 026.phpt 027.phpt
028.phpt
Modified files:
/pecl/memcache CREDITS memcache.c php_memcache.h
/pecl/memcache/tests connect.inc
Log:
Multiple servers and failover support
Automatic compress threshold
+3 new methods: memcache->addServer(), getExtendedStats(), setCompressThreshold()
["mikl-20051029102450.txt" (text/plain)]
http://cvs.php.net/diff.php/pecl/memcache/CREDITS?r1=1.1.1.1&r2=1.2&ty=u
Index: pecl/memcache/CREDITS
diff -u pecl/memcache/CREDITS:1.1.1.1 pecl/memcache/CREDITS:1.2
--- pecl/memcache/CREDITS:1.1.1.1 Wed Feb 11 11:06:39 2004
+++ pecl/memcache/CREDITS Sat Oct 29 10:24:49 2005
@@ -1 +1 @@
-Antony Dovgal
+Antony Dovgal, Mikael Johansson
http://cvs.php.net/diff.php/pecl/memcache/memcache.c?r1=1.29&r2=1.30&ty=u
Index: pecl/memcache/memcache.c
diff -u pecl/memcache/memcache.c:1.29 pecl/memcache/memcache.c:1.30
--- pecl/memcache/memcache.c:1.29 Tue Oct 18 05:37:16 2005
+++ pecl/memcache/memcache.c Sat Oct 29 10:24:49 2005
@@ -12,16 +12,12 @@
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
- | Author: Antony Dovgal <tony2001@phpclub.net> |
+ | Authors: Antony Dovgal <tony2001@phpclub.net> |
+ | Mikael Johansson <mikael AT synd DOT info> |
+----------------------------------------------------------------------+
*/
-/* $Id: memcache.c,v 1.29 2005/10/18 09:37:16 tony2001 Exp $ */
-
-/* TODO
- * - we should probably do a cleanup if some call failed,
- * because it can cause further calls to fail
- * */
+/* $Id: memcache.c,v 1.30 2005/10/29 14:24:49 mikl Exp $ */
#ifdef HAVE_CONFIG_H
#include "config.h"
@@ -38,15 +34,17 @@
#if HAVE_MEMCACHE
#include <zlib.h>
+#include <time.h>
#include "ext/standard/info.h"
#include "ext/standard/php_string.h"
#include "ext/standard/php_var.h"
#include "ext/standard/php_smart_str.h"
+#include "ext/standard/crc32.h"
#include "php_network.h"
#include "php_memcache.h"
/* True global resources - no need for thread safety here */
-static int le_memcache, le_pmemcache;
+static int le_memcache_pool, le_pmemcache;
static zend_class_entry *memcache_class_entry_ptr;
ZEND_DECLARE_MODULE_GLOBALS(memcache)
@@ -56,6 +54,7 @@
zend_function_entry memcache_functions[] = {
PHP_FE(memcache_connect, NULL)
PHP_FE(memcache_pconnect, NULL)
+ PHP_FE(memcache_add_server, NULL)
PHP_FE(memcache_get_version, NULL)
PHP_FE(memcache_add, NULL)
PHP_FE(memcache_set, NULL)
@@ -64,6 +63,8 @@
PHP_FE(memcache_delete, NULL)
PHP_FE(memcache_debug, NULL)
PHP_FE(memcache_get_stats, NULL)
+ PHP_FE(memcache_get_extended_stats, NULL)
+ PHP_FE(memcache_set_compress_threshold, NULL)
PHP_FE(memcache_increment, NULL)
PHP_FE(memcache_decrement, NULL)
PHP_FE(memcache_close, NULL)
@@ -74,17 +75,20 @@
static zend_function_entry php_memcache_class_functions[] = {
PHP_FALIAS(connect, memcache_connect, NULL)
PHP_FALIAS(pconnect, memcache_pconnect, NULL)
+ PHP_FALIAS(addserver, memcache_add_server, NULL)
PHP_FALIAS(getversion, memcache_get_version, NULL)
PHP_FALIAS(add, memcache_add, NULL)
PHP_FALIAS(set, memcache_set, NULL)
PHP_FALIAS(replace, memcache_replace, NULL)
PHP_FALIAS(get, memcache_get, NULL)
PHP_FALIAS(delete, memcache_delete, NULL)
- PHP_FALIAS(getstats, memcache_get_stats, NULL)
- PHP_FALIAS(increment, memcache_increment, NULL)
- PHP_FALIAS(decrement, memcache_decrement, NULL)
- PHP_FALIAS(close, memcache_close, NULL)
- PHP_FALIAS(flush, memcache_flush, NULL)
+ PHP_FALIAS(getstats, memcache_get_stats, NULL)
+ PHP_FALIAS(getextendedstats, memcache_get_extended_stats, NULL)
+ PHP_FALIAS(setcompressthreshold, memcache_set_compress_threshold, NULL)
+ PHP_FALIAS(increment, memcache_increment, NULL)
+ PHP_FALIAS(decrement, memcache_decrement, NULL)
+ PHP_FALIAS(close, memcache_close, NULL)
+ PHP_FALIAS(flush, memcache_flush, NULL)
{NULL, NULL, NULL}
};
@@ -104,7 +108,7 @@
PHP_RSHUTDOWN(memcache), /* Replace with NULL if there's nothing to do at request \
end */ PHP_MINFO(memcache),
#if ZEND_MODULE_API_NO >= 20010901
- "0.1", /* Replace with version number for your extension */
+ "1.3", /* Replace with version number for your extension */
#endif
STANDARD_MODULE_PROPERTIES
};
@@ -119,7 +123,7 @@
php_strtr(key, key_len, "\t\r\n ", "____", 4); \
#if ZEND_DEBUG
-
+
#define MMC_DEBUG(info) \
{\
mmc_debug info; \
@@ -131,18 +135,28 @@
{\
}\
-#endif
+#endif
+
-
/* }}} */
/* {{{ internal function protos */
-static void _mmc_server_list_dtor(zend_rsrc_list_entry * TSRMLS_DC);
+static void _mmc_pool_list_dtor(zend_rsrc_list_entry * TSRMLS_DC);
static void _mmc_pserver_list_dtor(zend_rsrc_list_entry * TSRMLS_DC);
+static mmc_pool_t *mmc_pool_new();
+static void mmc_pool_add(mmc_pool_t *, mmc_t *, unsigned int);
+static mmc_t *mmc_server_new(char *, int, unsigned short, int, int, int);
+static void mmc_server_free(mmc_t *);
+static void mmc_server_disconnect(mmc_t *);
+static void mmc_server_deactivate(mmc_t *);
+static int mmc_server_failure(mmc_t *);
+static mmc_t *mmc_server_find(mmc_pool_t *, char *, int TSRMLS_DC);
+static unsigned int mmc_hash(char *, int);
static int mmc_compress(char **, int *, char *, int TSRMLS_DC);
static int mmc_uncompress(char **, long *, char *, int);
-static int mmc_get_connection(zval *, mmc_t ** TSRMLS_DC);
-static mmc_t* mmc_open(const char *, int, short, long, int, char **, int * \
TSRMLS_DC); +static int mmc_get_pool(zval *, mmc_pool_t ** TSRMLS_DC);
+static int mmc_open(mmc_t *, int, char **, int * TSRMLS_DC);
+static mmc_t *mmc_find_persistent(char *, int, int, int, int TSRMLS_DC);
static int mmc_close(mmc_t * TSRMLS_DC);
static int mmc_readline(mmc_t * TSRMLS_DC);
static char * mmc_get_version(mmc_t * TSRMLS_DC);
@@ -150,13 +164,14 @@
static int mmc_sendcmd(mmc_t *, const char *, int TSRMLS_DC);
static int mmc_exec_storage_cmd(mmc_t *, char *, int, char *, int, int, int, char *, \
int TSRMLS_DC); static int mmc_parse_response(char *, char **, int, int *, int *);
-static int mmc_exec_retrieval_cmd(mmc_t *, char *, int, char *, int, int *, char **, \
int * TSRMLS_DC);
-static int mmc_exec_retrieval_cmd_multi(mmc_t *, zval *, zval ** TSRMLS_DC);
+static int mmc_exec_retrieval_cmd(mmc_pool_t *, zval *, zval ** TSRMLS_DC);
+static int mmc_exec_retrieval_cmd_multi(mmc_pool_t *, zval *, zval ** TSRMLS_DC);
+static int mmc_read_value(mmc_t *, char **, zval ** TSRMLS_DC);
static int mmc_delete(mmc_t *, char *, int, int TSRMLS_DC);
static int mmc_flush(mmc_t * TSRMLS_DC);
static void php_mmc_store (INTERNAL_FUNCTION_PARAMETERS, char *, int);
static int mmc_get_stats (mmc_t *, zval ** TSRMLS_DC);
-static int mmc_incr_decr (mmc_t *, int, char *, int, int TSRMLS_DC);
+static int mmc_incr_decr (mmc_t *, int, char *, int, int, long * TSRMLS_DC);
static void php_mmc_incr_decr (INTERNAL_FUNCTION_PARAMETERS, int);
static void php_mmc_connect (INTERNAL_FUNCTION_PARAMETERS, int);
/* }}} */
@@ -164,7 +179,7 @@
/* {{{ php_memcache_init_globals()
*/
static void php_memcache_init_globals(zend_memcache_globals *memcache_globals_p \
TSRMLS_DC)
-{
+{
MEMCACHE_G(debug_mode) = 0;
MEMCACHE_G(default_port) = MMC_DEFAULT_PORT;
MEMCACHE_G(num_persistent) = 0;
@@ -176,11 +191,11 @@
*/
PHP_MINIT_FUNCTION(memcache)
{
- zend_class_entry memcache_class_entry;
+ zend_class_entry memcache_class_entry;
INIT_CLASS_ENTRY(memcache_class_entry, "Memcache", php_memcache_class_functions);
- memcache_class_entry_ptr = zend_register_internal_class(&memcache_class_entry \
TSRMLS_CC); + memcache_class_entry_ptr = \
zend_register_internal_class(&memcache_class_entry TSRMLS_CC);
- le_memcache = zend_register_list_destructors_ex(_mmc_server_list_dtor, NULL, \
"memcache connection", module_number); + le_memcache_pool = \
zend_register_list_destructors_ex(NULL, _mmc_pool_list_dtor, "memcache connection", \
module_number); le_pmemcache = zend_register_list_destructors_ex(NULL, \
_mmc_pserver_list_dtor, "persistent memcache connection", module_number);
#ifdef ZTS
@@ -188,7 +203,7 @@
#else
php_memcache_init_globals(&memcache_globals TSRMLS_CC);
#endif
-
+
REGISTER_LONG_CONSTANT("MEMCACHE_COMPRESSED",MMC_COMPRESSED, CONST_CS | \
CONST_PERSISTENT);
return SUCCESS;
@@ -227,43 +242,149 @@
char buf[16];
sprintf(buf, "%ld", MEMCACHE_G(num_persistent));
-
+
php_info_print_table_start();
php_info_print_table_header(2, "memcache support", "enabled");
php_info_print_table_row(2, "Active persistent connections", buf);
- php_info_print_table_row(2, "Revision", "$Revision: 1.29 $");
+ php_info_print_table_row(2, "Revision", "$Revision: 1.30 $");
php_info_print_table_end();
}
/* }}} */
/* ------------------
- internal functions
+ internal functions
------------------ */
-static void _mmc_server_list_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */
+static void _mmc_pool_list_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */
{
- mmc_t *mmc = (mmc_t *)rsrc->ptr;
- mmc_close(mmc TSRMLS_CC);
- efree(mmc);
+ mmc_pool_t *pool = (mmc_pool_t *)rsrc->ptr;
+ int i;
+ for (i=0; i<pool->num_servers; i++) {
+ if (!pool->servers[i]->persistent) {
+ mmc_server_free(pool->servers[i]);
+ }
+ }
+
+ if (pool->num_servers) {
+ efree(pool->servers);
+ efree(pool->requests);
+ }
+
+ if (pool->num_buckets) {
+ efree(pool->buckets);
+ }
+
+ efree(pool);
}
/* }}} */
static void _mmc_pserver_list_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */
{
- mmc_t *mmc = (mmc_t *)rsrc->ptr;
- mmc_close(mmc TSRMLS_CC);
- free(mmc);
- MEMCACHE_G(num_persistent)--;
+ mmc_server_free((mmc_t *)rsrc->ptr);
+}
+/* }}} */
+
+static mmc_t *mmc_server_new(char *host, int host_len, unsigned short port, int \
persistent, int timeout, int retry_interval) /* {{{ */ +{
+ mmc_t *mmc;
+
+ if (persistent) {
+ mmc = malloc(sizeof(mmc_t));
+ mmc->host = malloc(host_len + 1);
+ }
+ else {
+ mmc = emalloc(sizeof(mmc_t));
+ mmc->host = emalloc(host_len + 1);
+ }
+
+ mmc->stream = NULL;
+ mmc->status = MMC_STATUS_DISCONNECTED;
+ memset(&(mmc->outbuf), 0, sizeof(smart_str));
+
+ strncpy(mmc->host, host, host_len);
+ mmc->host[host_len] = '\0';
+ mmc->port = port;
+
+ mmc->persistent = persistent;
+ if (persistent) {
+ MEMCACHE_G(num_persistent)++;
+ }
+
+ mmc->timeout = timeout;
+ mmc->retry_interval = retry_interval;
+
+ return mmc;
+}
+/* }}} */
+
+static void mmc_server_free(mmc_t *mmc) /* {{{ */
+{
+ if (mmc->persistent) {
+ if (mmc->stream != NULL) {
+ php_stream_pclose(mmc->stream);
+ }
+ free(mmc->host);
+ free(mmc);
+ MEMCACHE_G(num_persistent)--;
+ }
+ else {
+ if (mmc->stream != NULL) {
+ php_stream_close(mmc->stream);
+ }
+ efree(mmc->host);
+ efree(mmc);
+ }
+}
+/* }}} */
+
+static mmc_pool_t *mmc_pool_new() /* {{{ */
+{
+ mmc_pool_t *pool = emalloc(sizeof(mmc_pool_t));
+ pool->num_servers = 0;
+ pool->num_buckets = 0;
+ pool->compress_threshold = 0;
+ pool->min_compress_savings = MMC_DEFAULT_SAVINGS;
+ return pool;
+}
+/* }}} */
+
+static void mmc_pool_add(mmc_pool_t *pool, mmc_t *mmc, unsigned int weight) /* {{{ \
*/ +{
+ int i;
+
+ /* add server and a preallocated request pointer */
+ if (pool->num_servers) {
+ pool->servers = erealloc(pool->servers, sizeof(mmc_t *) * (pool->num_servers + \
1)); + pool->requests = erealloc(pool->requests, sizeof(mmc_t *) * \
(pool->num_servers + 1)); + }
+ else {
+ pool->servers = emalloc(sizeof(mmc_t *));
+ pool->requests = emalloc(sizeof(mmc_t *));
+ }
+
+ pool->servers[pool->num_servers] = mmc;
+ pool->num_servers++;
+
+ /* add weight number of buckets for this server */
+ if (pool->num_buckets)
+ pool->buckets = erealloc(pool->buckets, sizeof(mmc_t *) * (pool->num_buckets + \
weight)); + else
+ pool->buckets = emalloc(sizeof(mmc_t *));
+
+ for (i=0; i<weight; i++) {
+ pool->buckets[pool->num_buckets + i] = mmc;
+ }
+ pool->num_buckets += weight;
}
/* }}} */
-static int mmc_compress(char **result_data, int *result_len, char *data, int \
data_len TSRMLS_DC) /* {{{ */ +static int mmc_compress(char **result_data, int \
*result_len, char *data, int data_len TSRMLS_DC) /* {{{ */ {
int status, level = MEMCACHE_G(compression_level);
-
+
*result_len = data_len + (data_len / 1000) + 15 + 1; /* some magic from zlib.c */
*result_data = (char *) emalloc(*result_len);
-
+
if (!*result_data) {
return 0;
}
@@ -273,7 +394,7 @@
} else {
status = compress(*result_data, (unsigned long *)result_len, data, \
data_len); }
-
+
if (status == Z_OK) {
*result_data = erealloc(*result_data, *result_len + 1);
(*result_data)[*result_len] = '\0';
@@ -290,7 +411,7 @@
int status;
unsigned int factor=1, maxfactor=16;
char *tmp1=NULL;
-
+
do {
*result_len = (unsigned long)data_len * (1 << factor++);
*result_data = (char *) erealloc(tmp1, *result_len);
@@ -326,66 +447,72 @@
}
/* }}} */
-static int mmc_get_connection(zval *id, mmc_t **mmc TSRMLS_DC) /* {{{ */
+/**
+ * New style crc32 hash, compatible with other clients
+ */
+static unsigned int mmc_hash(char *key, int key_len) { /* {{{ */
+ unsigned int crc = ~0;
+ int i;
+
+ for (i=0; i<key_len; i++) {
+ CRC32(crc, key[i]);
+ }
+
+ crc = (~crc >> 16) & 0x7fff;
+ return crc ? crc : 1;
+}
+/* }}} */
+
+static int mmc_get_pool(zval *id, mmc_pool_t **pool TSRMLS_DC) /* {{{ */
{
- zval **connection;
- int resource_type;
+ zval **connection;
+ int resource_type;
if (Z_TYPE_P(id) != IS_OBJECT || zend_hash_find(Z_OBJPROP_P(id), "connection", \
sizeof("connection"), (void **)&connection) == FAILURE) { php_error_docref(NULL \
TSRMLS_CC, E_WARNING, "cannot find connection identifier"); return 0;
}
- *mmc = (mmc_t *) zend_list_find(Z_LVAL_PP(connection), &resource_type);
+ *pool = (mmc_pool_t *) zend_list_find(Z_LVAL_PP(connection), &resource_type);
- if (!*mmc || (resource_type != le_pmemcache && resource_type != le_memcache )) {
+ if (!*pool || resource_type != le_memcache_pool) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "connection identifier not found");
return 0;
}
- if (!(*mmc)->stream) {
- /* found previously closed connection */
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "using previously closed connection");
- return 0;
- }
-
return Z_LVAL_PP(connection);
}
/* }}} */
-static mmc_t* mmc_open(const char *host, int host_len, short port, long timeout, int \
persistent, char **error_string, int *errnum TSRMLS_DC) /* {{{ */ +static int \
_mmc_open(mmc_t *mmc, char **error_string, int *errnum TSRMLS_DC) /* {{{ */ {
- mmc_t *mmc;
- struct timeval tv;
- char *hostname = NULL, *hash_key = NULL, *errstr = NULL, *version;
- int hostname_len, err;
-
- tv.tv_sec = timeout;
+ struct timeval tv;
+ char *hostname = NULL, *hash_key = NULL, *errstr = NULL;
+ int hostname_len, err;
+
+ /* close open stream */
+ if (mmc->stream != NULL) {
+ mmc_server_disconnect(mmc);
+ }
+
+ tv.tv_sec = mmc->timeout;
tv.tv_usec = 0;
-
- hostname = emalloc(host_len + MAX_LENGTH_OF_LONG + 1 + 1);
- hostname_len = sprintf(hostname, "%s:%d", host, port);
- if (persistent) {
- mmc = malloc( sizeof(mmc_t) );
+ hostname = emalloc(strlen(mmc->host) + MAX_LENGTH_OF_LONG + 1 + 1);
+ hostname_len = sprintf(hostname, "%s:%d", mmc->host, mmc->port);
+ if (mmc->persistent) {
hash_key = emalloc(sizeof("mmc_open___") - 1 + hostname_len + 1);
sprintf(hash_key, "mmc_open___%s", hostname);
}
- else {
- mmc = emalloc( sizeof(mmc_t) );
- }
- mmc->stream = NULL;
- mmc->persistent = persistent ? 1 : 0;
-
-#if PHP_API_VERSION > 20020918
- mmc->stream = php_stream_xport_create( hostname, hostname_len,
+#if PHP_API_VERSION > 20020918
+ mmc->stream = php_stream_xport_create( hostname, hostname_len,
ENFORCE_SAFE_MODE | REPORT_ERRORS,
STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT,
hash_key, &tv, NULL, &errstr, &err);
#else
- if (persistent) {
+ if (mmc->persistent) {
switch(php_stream_from_persistent_id(hash_key, &(mmc->stream) TSRMLS_CC)) {
case PHP_STREAM_PERSISTENT_SUCCESS:
if (php_stream_eof(mmc->stream)) {
@@ -400,71 +527,158 @@
if (!mmc->stream) {
int socktype = SOCK_STREAM;
- mmc->stream = php_stream_sock_open_host(host, (unsigned short)port, socktype, &tv, \
hash_key); + mmc->stream = php_stream_sock_open_host(mmc->host, mmc->port, socktype, \
&tv, hash_key); }
-
+
#endif
-
- efree(hostname);
- if (hash_key) {
+ efree(hostname);
+ if (mmc->persistent) {
efree(hash_key);
}
-
+
if (!mmc->stream) {
- MMC_DEBUG(("mmc_open: can't open socket to host"));
- if (persistent) {
- free(mmc);
- }
- else {
- efree(mmc);
- }
- if (error_string && errstr) {
- *error_string = errstr;
+ MMC_DEBUG(("_mmc_open: can't open socket to host"));
+ mmc_server_deactivate(mmc);
+
+ if (errstr) {
+ if (error_string) {
+ *error_string = errstr;
+ }
+ else {
+ efree(errstr);
+ }
}
if (errnum) {
*errnum = err;
}
- return NULL;
+
+ return 0;
}
-
+
php_stream_set_option(mmc->stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &tv);
php_stream_set_option(mmc->stream, PHP_STREAM_OPTION_WRITE_BUFFER, \
PHP_STREAM_BUFFER_NONE, NULL);
-
- mmc->timeout = timeout;
-
- if ((version = mmc_get_version(mmc TSRMLS_CC)) == NULL) {
- MMC_DEBUG(("mmc_open: failed to get server's version"));
- if (persistent) {
+
+ mmc->status = MMC_STATUS_CONNECTED;
+ return 1;
+}
+/* }}} */
+
+static int mmc_open(mmc_t *mmc, int force_connect, char **error_string, int *errnum \
TSRMLS_DC) /* {{{ */ +{
+ switch (mmc->status) {
+ case MMC_STATUS_DISCONNECTED:
+ return _mmc_open(mmc, error_string, errnum TSRMLS_CC);
+
+ case MMC_STATUS_CONNECTED:
+ return 1;
+
+ case MMC_STATUS_UNKNOWN:
+ /* check connection if needed */
+ if (force_connect) {
+ char *version;
+ if ((version = mmc_get_version(mmc TSRMLS_CC)) == NULL && !_mmc_open(mmc, \
error_string, errnum TSRMLS_CC)) { + break;
+ }
+ efree(version);
+ mmc->status = MMC_STATUS_CONNECTED;
+ }
+ return 1;
+
+ case MMC_STATUS_FAILED:
+ /* retry failed server, possibly stale cache should be flushed if connect ok
+ * TODO: use client callback on successful reconnect to allow user to specify \
behaviour + */
+ if (mmc->retry <= (long)time(NULL)) {
+ if (_mmc_open(mmc, error_string, errnum TSRMLS_CC) /*&& mmc_flush(mmc TSRMLS_CC) \
> 0*/) { + return 1;
+ }
+ mmc_server_deactivate(mmc);
+ }
+ break;
+ }
+ return 0;
+}
+/* }}} */
+
+static void mmc_server_disconnect(mmc_t *mmc) /* {{{ */
+{
+ if (mmc->stream != NULL) {
+ if (mmc->persistent) {
php_stream_pclose(mmc->stream);
- free(mmc);
}
else {
php_stream_close(mmc->stream);
- efree(mmc);
}
- return NULL;
+ mmc->stream = NULL;
}
- efree(version);
- return mmc;
+ mmc->status = MMC_STATUS_DISCONNECTED;
}
/* }}} */
-static int mmc_close(mmc_t *mmc TSRMLS_DC) /* {{{ */
+static void mmc_server_deactivate(mmc_t *mmc) /* {{{ */
{
- MMC_DEBUG(("mmc_close: closing connection to server"));
+ mmc_server_disconnect(mmc);
+ mmc->status = MMC_STATUS_FAILED;
+ mmc->retry = (long)time(NULL) + mmc->retry_interval;
+}
+/* }}} */
- if (mmc == NULL) {
- return 0;
+/**
+ * Indicate a server failure
+ * @return bool True if the server was actually deactivated
+ */
+static int mmc_server_failure(mmc_t *mmc) /* {{{ */
+{
+ switch (mmc->status) {
+ case MMC_STATUS_DISCONNECTED:
+ return 0;
+
+ /* attempt reconnect of sockets in unknown state */
+ case MMC_STATUS_UNKNOWN:
+ mmc->status = MMC_STATUS_DISCONNECTED;
+ return 0;
}
+ mmc_server_deactivate(mmc);
+ return 1;
+}
+/* }}} */
+
+static mmc_t *mmc_server_find(mmc_pool_t *pool, char *key, int key_len TSRMLS_DC) /* \
{{{ */ +{
+ mmc_t *mmc;
- if (!mmc->persistent && mmc->stream) {
- mmc_sendcmd(mmc,"quit", 4 TSRMLS_CC);
- php_stream_close(mmc->stream);
+ if (pool->num_servers > 1) {
+ unsigned int hash = mmc_hash(key, key_len), i;
+ mmc = pool->buckets[hash % pool->num_buckets];
+
+ /* perform failover if needed */
+ for (i=0; !mmc_open(mmc, 0, NULL, NULL) && (i<20 || i<pool->num_buckets); i++) {
+ MMC_DEBUG(("mmc_server_find: failed to connect to server '%s:%d' status %d, \
trying next", mmc->host, mmc->port, mmc->status)); + char *next_key = \
emalloc(key_len + MAX_LENGTH_OF_LONG + 1); + int next_len = sprintf(next_key, \
"%d%s", i+1, key); +
+ hash += mmc_hash(next_key, next_len);
+ mmc = pool->buckets[hash % pool->num_buckets];
+
+ efree(next_key);
+ }
+ }
+ else {
+ mmc = pool->servers[0];
+ mmc_open(mmc, 0, NULL, NULL);
}
- mmc->stream = NULL;
-
+ return mmc->status != MMC_STATUS_FAILED ? mmc : NULL;
+}
+/* }}} */
+
+static int mmc_close(mmc_t *mmc TSRMLS_DC) /* {{{ */
+{
+ MMC_DEBUG(("mmc_close: closing connection to server"));
+ if (!mmc->persistent) {
+ mmc_server_disconnect(mmc);
+ }
return 1;
}
/* }}} */
@@ -474,11 +688,10 @@
char *buf;
if (mmc->stream == NULL) {
- php_error_docref(NULL TSRMLS_CC, E_NOTICE, "cannot read data from already closed \
socket"); MMC_DEBUG(("mmc_readline: socket is already closed"));
return -1;
}
-
+
buf = php_stream_gets(mmc->stream, mmc->inbuf, MMC_BUF_SIZE);
if (buf) {
MMC_DEBUG(("mmc_readline: read data:"));
@@ -488,7 +701,6 @@
return strlen(buf);
}
else {
- php_error_docref(NULL TSRMLS_CC, E_NOTICE, "failed to read the server's \
response"); MMC_DEBUG(("mmc_readline: cannot read a line from the server"));
return -1;
}
@@ -499,7 +711,7 @@
{
char *version_str;
int len;
-
+
if (mmc_sendcmd(mmc, "version", sizeof("version") - 1 TSRMLS_CC) < 0) {
return NULL;
}
@@ -512,8 +724,8 @@
version_str = estrndup(mmc->inbuf + sizeof("VERSION ") - 1, len - sizeof("VERSION \
") - 1 - sizeof("\r\n") - 1); return version_str;
}
-
- MMC_DEBUG(("mmc_get_version: data is not valid version string"));
+
+ MMC_DEBUG(("mmc_get_version: data is not valid version string"));
return NULL;
}
/* }}} */
@@ -521,7 +733,7 @@
static int mmc_str_left(char *haystack, char *needle, int haystack_len, int \
needle_len) /* {{{ */ {
char *found;
-
+
found = php_memnstr(haystack, needle, needle_len, haystack + haystack_len);
if ((found - haystack) == 0) {
return 1;
@@ -548,9 +760,8 @@
command[command_len] = '\0';
if (php_stream_write(mmc->stream, command, command_len) != command_len) {
- efree(command);
- php_error_docref(NULL TSRMLS_CC, E_NOTICE, "failed to send command to the \
server"); MMC_DEBUG(("mmc_sendcmd: write failed"));
+ efree(command);
return -1;
}
efree(command);
@@ -567,42 +778,41 @@
real_command = emalloc(
command_len
- + 1 /* space */
- + key_len
- + 1 /* space */
- + MAX_LENGTH_OF_LONG
+ + 1 /* space */
+ + key_len
+ + 1 /* space */
+ + MAX_LENGTH_OF_LONG
+ 1 /* space */
- + MAX_LENGTH_OF_LONG
+ + MAX_LENGTH_OF_LONG
+ 1 /* space */
- + MAX_LENGTH_OF_LONG
+ + MAX_LENGTH_OF_LONG
+ sizeof("\r\n") - 1
+ data_len
+ sizeof("\r\n") - 1
+ 1
);
- MMC_PREPARE_KEY(key, key_len);
-
size = sprintf(real_command, "%s %s %d %d %d\r\n", command, key, flags, expire, \
data_len);
memcpy(real_command + size, data, data_len);
memcpy(real_command + size + data_len, "\r\n", sizeof("\r\n") - 1);
size = size + data_len + sizeof("\r\n") - 1;
real_command[size] = '\0';
-
+
MMC_DEBUG(("mmc_exec_storage_cmd: store cmd is '%s'", real_command));
MMC_DEBUG(("mmc_exec_storage_cmd: trying to store '%s', %d bytes", data, \
data_len));
-
+
/* send command & data */
if (php_stream_write(mmc->stream, real_command, size) != size) {
- php_error_docref(NULL TSRMLS_CC, E_NOTICE, "failed to send command and data to the \
server"); + MMC_DEBUG(("failed to send command and data to the server"));
efree(real_command);
return -1;
}
efree(real_command);
-
+
/* get server's response */
if ((response_buf_size = mmc_readline(mmc TSRMLS_CC)) < 0) {
+ MMC_DEBUG(("failed to read the server's response"));
return -1;
}
@@ -612,8 +822,13 @@
if(mmc_str_left(mmc->inbuf,"STORED", response_buf_size, sizeof("STORED") - 1)) {
return 1;
}
-
- php_error_docref(NULL TSRMLS_CC, E_NOTICE, "an error occured while trying to store \
the item on the server"); +
+ /* not stored, return FALSE */
+ if(mmc_str_left(mmc->inbuf,"NOT_STORED", response_buf_size, sizeof("NOT_STORED") - \
1)) { + return 0;
+ }
+
+ MMC_DEBUG(("an error occured while trying to store the item on the server"));
return -1;
}
/* }}} */
@@ -628,7 +843,7 @@
}
MMC_DEBUG(("mmc_parse_response: got response '%s'", response));
-
+
for (i = 0; i < response_len; i++) {
if (response[i] == ' ') {
spaces[n] = i;
@@ -647,12 +862,12 @@
if (item_name != NULL) {
int item_name_len = spaces[1] - spaces[0] - 1;
-
+
*item_name = emalloc(item_name_len + 1);
memcpy(*item_name, response + spaces[0] + 1, item_name_len);
(*item_name)[item_name_len] = '\0';
}
-
+
*flags = atoi(response + spaces[1]);
*value_len = atoi(response + spaces[2]);
@@ -664,267 +879,217 @@
MMC_DEBUG(("mmc_parse_response: 2nd space is at %d position", spaces[2]));
MMC_DEBUG(("mmc_parse_response: flags = %d", *flags));
MMC_DEBUG(("mmc_parse_response: value_len = %d ", *value_len));
-
+
return 1;
}
-
/* }}} */
-static int mmc_exec_retrieval_cmd(mmc_t *mmc, char *command, int command_len, char \
*key, int key_len, int *flags, char **data, int *data_len TSRMLS_DC) /* {{{ */ \
+static int mmc_exec_retrieval_cmd(mmc_pool_t *pool, zval *key, zval **return_value \
TSRMLS_DC) /* {{{ */ {
- char *real_command, *tmp;
- int size, response_buf_size;
-
- real_command = emalloc(
- command_len
- + 1 /* space */
- + key_len
- + 1
- );
+ mmc_t *mmc;
+ char *request, *tmp_key;
+ int result = -1, request_len, response_len;
- MMC_PREPARE_KEY(key, key_len);
+ MMC_DEBUG(("mmc_exec_retrieval_cmd: key '%s'", Z_STRVAL_P(key)));
- size = sprintf(real_command, "%s %s", command, key);
+ convert_to_string(key);
+ MMC_PREPARE_KEY(Z_STRVAL_P(key), Z_STRLEN_P(key));
- real_command [size] = '\0';
+ /* get + ' ' + key + \0 */
+ request = emalloc(sizeof("get") + Z_STRLEN_P(key) + 1);
+ request_len = sprintf(request, "get %s", Z_STRVAL_P(key));
- MMC_DEBUG(("mmc_exec_retrieval_cmd: trying to get '%s'", key));
-
- /* gimme! =) */
- if (mmc_sendcmd(mmc, real_command, size TSRMLS_CC) < 0) {
- efree(real_command);
- return -1;
- }
- efree(real_command);
-
- /* get server's response */
- if ((response_buf_size = mmc_readline(mmc TSRMLS_CC)) < 0){
- return -1;
- }
+ while (result < 0 && (mmc = mmc_server_find(pool, Z_STRVAL_P(key), Z_STRLEN_P(key) \
TSRMLS_CC)) != NULL) { + MMC_DEBUG(("mmc_exec_retrieval_cmd: found server '%s:%d' \
for key '%s'", mmc->host, mmc->port, Z_STRVAL_P(key)));
- MMC_DEBUG(("mmc_exec_retrieval_cmd: got response '%s'", mmc->inbuf));
-
- /* what's this? */
+ /* send command and read value */
+ if ((result = mmc_sendcmd(mmc, request, request_len TSRMLS_CC)) > 0 &&
+ (result = mmc_read_value(mmc, &tmp_key, return_value)) >= 0) {
- if (mmc_str_left(mmc->inbuf,"END", response_buf_size, sizeof("END") - 1)) {
- /* not found */
- return -1;
- }
-
- if(mmc_str_left(mmc->inbuf,"VALUE", response_buf_size, sizeof("VALUE") - 1) <= 0) {
- php_error_docref(NULL TSRMLS_CC, E_NOTICE, "got invalid server's response \
header");
- return -1;
- }
-
- tmp = estrdup(mmc->inbuf);
- if (mmc_parse_response(tmp, NULL, response_buf_size, flags, data_len) < 0) {
- php_error_docref(NULL TSRMLS_CC, E_NOTICE, "got invalid server's response");
- efree(tmp);
- return -1;
+ /* not found */
+ if (result == 0) {
+ ZVAL_FALSE(*return_value);
+ }
+ /* read "END" */
+ else if ((response_len = mmc_readline(mmc TSRMLS_CC)) < 0 || \
!mmc_str_left(mmc->inbuf, "END", response_len, sizeof("END")-1)) { + result = -1;
+ }
+ }
+
+ if (result < 0 && mmc_server_failure(mmc)) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "marked server '%s:%d' as failed", \
mmc->host, mmc->port); + }
}
- efree(tmp);
- MMC_DEBUG(("mmc_exec_retrieval_cmd: data len is %d bytes", *data_len));
-
- {
- int data_to_be_read = *data_len + 2;
- int offset = 0;
+ efree(request);
+ return result;
+}
+/* }}} */
+
+static int mmc_exec_retrieval_cmd_multi(mmc_pool_t *pool, zval *keys, zval \
**return_value TSRMLS_DC) /* {{{ */ +{
+ mmc_t *mmc;
+ HashPosition pos;
+ zval **key, *value;
+ char *result_key;
+ int i = 0, j, num_requests, result, result_status;
+
+ array_init(*return_value);
+
+ /* until no retrival errors or all servers have failed */
+ do {
+ result_status = num_requests = 0;
+ zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(keys), &pos);
+
+ /* first pass to build requests for each server */
+ while (zend_hash_get_current_data_ex(Z_ARRVAL_P(keys), (void **) &key, &pos) == \
SUCCESS) { + if (Z_TYPE_PP(key) != IS_STRING) {
+ SEPARATE_ZVAL(key);
+ convert_to_string(*key);
+ }
+
+ MMC_PREPARE_KEY(Z_STRVAL_PP(key), Z_STRLEN_PP(key));
+
+ /* schedule key if first round or if missing from result */
+ if ((!i || !zend_hash_exists(Z_ARRVAL_PP(return_value), Z_STRVAL_PP(key), \
Z_STRLEN_PP(key))) && + (mmc = mmc_server_find(pool, Z_STRVAL_PP(key), \
Z_STRLEN_PP(key))) != NULL) { + if (!(mmc->outbuf.len)) {
+ smart_str_appendl(&(mmc->outbuf), "get", sizeof("get")-1);
+ pool->requests[num_requests++] = mmc;
+ }
+
+ smart_str_appendl(&(mmc->outbuf), " ", 1);
+ smart_str_appendl(&(mmc->outbuf), Z_STRVAL_PP(key), Z_STRLEN_PP(key));
+ MMC_DEBUG(("mmc_exec_retrieval_cmd_multi: scheduled key '%s' for '%s:%d' request \
length '%d'", Z_STRVAL_PP(key), mmc->host, mmc->port, mmc->outbuf.len)); + }
- *data = emalloc(data_to_be_read + 1);
-
- while (data_to_be_read > 0) {
- size = php_stream_read(mmc->stream, *data + offset, data_to_be_read);
- if (size == 0) break;
- offset += size, data_to_be_read -= size;
+ zend_hash_move_forward_ex(Z_ARRVAL_P(keys), &pos);
}
-
- if (data_to_be_read > 0) {
- php_error_docref(NULL TSRMLS_CC, E_NOTICE, "incomplete data block (expected %d, \
got %d)", (*data_len + 2), offset);
- efree(*data);
- return -1;
+
+ /* second pass to send requests in parallel */
+ for (j=0; j<num_requests; j++) {
+ smart_str_0(&(pool->requests[j]->outbuf));
+
+ if ((result = mmc_sendcmd(pool->requests[j], pool->requests[j]->outbuf.c, \
pool->requests[j]->outbuf.len TSRMLS_CC)) < 0) { + if \
(mmc_server_failure(pool->requests[j])) { + php_error_docref(NULL TSRMLS_CC, \
E_NOTICE, "marked server '%s:%d' as failed", pool->requests[j]->host, \
pool->requests[j]->port); + }
+ result_status = result;
+ }
}
-
- (*data) [*data_len] = '\0';
- MMC_DEBUG(("mmc_exec_retrieval_cmd: data '%s'", *data));
- }
- /* read "END" */
- if ((response_buf_size = mmc_readline(mmc TSRMLS_CC)) < 0) {
- efree(*data);
- return -1;
- }
+ /* third pass to read responses */
+ for (j=0; j<num_requests; j++) {
+ if (pool->requests[j]->status != MMC_STATUS_FAILED) {
+ for (value = NULL; (result = mmc_read_value(pool->requests[j], &result_key, \
&value)) > 0; value = NULL) { + add_assoc_zval(*return_value, result_key, value);
+ }
- if(mmc_str_left(mmc->inbuf,"END", response_buf_size, sizeof("END") - 1) < 0) {
- php_error_docref(NULL TSRMLS_CC, E_NOTICE, "got invalid data end delimiter");
- efree(*data);
- return -1;
- }
-
- return 1;
+ /* check for server failure */
+ if (result < 0) {
+ if (mmc_server_failure(pool->requests[j])) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "marked server '%s:%d' as failed", \
pool->requests[j]->host, pool->requests[j]->port); + }
+ result_status = result;
+ }
+ }
+
+ smart_str_free(&(pool->requests[j]->outbuf));
+ }
+ } while (result_status < 0 && i++ < 20);
+
+ return result_status;
}
/* }}} */
-static int mmc_exec_retrieval_cmd_multi(mmc_t *mmc, zval *keys, zval **result \
TSRMLS_DC) /* {{{ */ +static int mmc_read_value(mmc_t *mmc, char **key, zval **value \
TSRMLS_DC) /* {{{ */ {
- HashPosition pos;
- zval **tmp_zval, *tmp_serialize;
- int flags, data_len;
- char *data = NULL;
- char *real_command = NULL, *tmp_c, *tmp_name;
- int size, response_buf_size;
- smart_str implstr = {0};
-
- zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(keys), &pos);
-
- while (zend_hash_get_current_data_ex(Z_ARRVAL_P(keys), (void **) &tmp_zval, &pos) \
== SUCCESS) {
- if (Z_TYPE_PP(tmp_zval) != IS_STRING) {
- SEPARATE_ZVAL(tmp_zval);
- convert_to_string(*tmp_zval);
- }
-
- MMC_PREPARE_KEY(Z_STRVAL_PP(tmp_zval), Z_STRLEN_PP(tmp_zval));
-
- smart_str_appendl(&implstr, Z_STRVAL_PP(tmp_zval), Z_STRLEN_PP(tmp_zval));
- smart_str_appendl(&implstr, " ", 1);
- zend_hash_move_forward_ex(Z_ARRVAL_P(keys), &pos);
- }
- smart_str_0(&implstr);
-
- real_command = emalloc(
- sizeof("get") - 1
- + 1 /* space */
- + implstr.len
- + 1
- );
+ int response_len, flags, data_len, i, size;
+ char *data;
- if (!real_command) {
- smart_str_free(&implstr);
+ /* read "VALUE <key> <flags> <bytes>\r\n" header line */
+ if ((response_len = mmc_readline(mmc TSRMLS_CC)) < 0) {
+ MMC_DEBUG(("failed to read the server's response"));
return -1;
}
-
- size = sprintf(real_command, "get %s", implstr.c);
- real_command [size] = '\0';
- MMC_DEBUG(("mmc_exec_retrieval_cmd_multi: trying to get '%s'", implstr.c));
-
- smart_str_free(&implstr);
+ /* reached the end of the data */
+ if (mmc_str_left(mmc->inbuf, "END", response_len, sizeof("END") - 1)) {
+ return 0;
+ }
- if (mmc_sendcmd(mmc, real_command, size TSRMLS_CC) < 0) {
- efree(real_command);
+ if (mmc_parse_response(mmc->inbuf, key, response_len, &flags, &data_len) < 0) {
return -1;
}
- efree(real_command);
- array_init(*result);
- while ((response_buf_size = mmc_readline(mmc TSRMLS_CC)) > 0) {
+ MMC_DEBUG(("mmc_read_value: data len is %d bytes", data_len));
- tmp_c = estrdup(mmc->inbuf);
- if(mmc_str_left(mmc->inbuf,"END", response_buf_size, sizeof("END") - 1)) {
- /* reached the end of the data */
- efree(tmp_c);
- return 1;
- }
- else if (mmc_parse_response(tmp_c, &tmp_name, response_buf_size, &flags, \
&data_len) < 0) {
- efree(tmp_name);
- efree(tmp_c);
+ /* data_len + \r\n + \0 */
+ data = emalloc(data_len + 3);
+
+ for (i=0; i<data_len+2; i+=size) {
+ if ((size = php_stream_read(mmc->stream, data + i, data_len + 2 - i)) == 0) {
+ MMC_DEBUG(("incomplete data block (expected %d, got %d)", (data_len + 2), i));
+ efree(data);
+ efree(*key);
return -1;
}
- efree(tmp_c);
+ }
- MMC_DEBUG(("mmc_exec_retrieval_cmd: data len is %d bytes", data_len));
-
- {
- int data_to_be_read = data_len + 2;
- int offset = 0;
-
- data = emalloc(data_to_be_read + 1);
-
- while (data_to_be_read > 0) {
- size = php_stream_read(mmc->stream, data + offset, data_to_be_read);
- if (size == 0) break;
- offset += size, data_to_be_read -= size;
- }
+ data[data_len] = '\0';
+ if (!data) {
+ if (*value == NULL) {
+ MAKE_STD_ZVAL(*value);
+ }
+ ZVAL_EMPTY_STRING(*value);
+ efree(data);
+ efree(*key);
+ return 1;
+ }
- if (data_to_be_read > 0) {
- php_error_docref(NULL TSRMLS_CC, E_NOTICE, "incomplete data block (expected %d, \
got %d)", (data_len + 2), offset);
- efree(tmp_name);
- efree(data);
- return -1;
- }
+ if (flags & MMC_COMPRESSED) {
+ char *result_data;
+ long result_len = 0;
- data [data_len] = '\0';
- MMC_DEBUG(("mmc_exec_retrieval_cmd: data '%s'", data));
+ if (!mmc_uncompress(&result_data, &result_len, data, data_len)) {
+ MMC_DEBUG(("Error when uncompressing data"));
+ efree(data);
+ efree(*key);
+ return -1;
}
- if (!data) {
- add_assoc_bool(*result, tmp_name, 0);
- efree(tmp_name);
- continue;
- }
-
- if (flags & MMC_COMPRESSED) {
- const char *tmp;
- long result_len = 0;
- char *result_data;
-
- if (!mmc_uncompress(&result_data, &result_len, data, data_len)) {
- add_assoc_bool(*result, tmp_name, 0);
- efree(tmp_name);
- efree(data);
- continue;
- }
-
- tmp = result_data;
- if (flags & MMC_SERIALIZED) {
- php_unserialize_data_t var_hash;
-
- MAKE_STD_ZVAL(tmp_serialize);
- PHP_VAR_UNSERIALIZE_INIT(var_hash);
- if (!php_var_unserialize(&tmp_serialize, (const unsigned char **) &tmp, tmp + \
result_len, &var_hash TSRMLS_CC)) {
- PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
-
- php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Error at offset %d of %d bytes", \
tmp - data, data_len);
- efree(data);
- efree(result_data);
- add_assoc_bool(*result, tmp_name, 0);
- }
- else {
- efree(data);
- efree(result_data);
- PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
- add_assoc_zval(*result, tmp_name, tmp_serialize);
- }
- }
- else {
- efree(data);
- add_assoc_stringl(*result, tmp_name, result_data, result_len, 1);
- }
- }
- else if (flags & MMC_SERIALIZED) {
- const char *tmp = data;
- php_unserialize_data_t var_hash;
-
- MAKE_STD_ZVAL(tmp_serialize);
- PHP_VAR_UNSERIALIZE_INIT(var_hash);
- if (!php_var_unserialize(&tmp_serialize, (const unsigned char **) &tmp, tmp + \
data_len, &var_hash TSRMLS_CC)) {
- PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
- php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Error at offset %d of %d bytes", tmp \
- data, data_len);
- efree(data);
- add_assoc_bool(*result, tmp_name, 0);
- }
- else {
- efree(data);
- PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
- add_assoc_zval(*result, tmp_name, tmp_serialize);
- }
- }
- else {
- add_assoc_stringl(*result, tmp_name, data, data_len, 1);
+ efree(data);
+ data = result_data;
+ data_len = result_len;
+ }
+
+ MMC_DEBUG(("mmc_read_value: data '%s'", data));
+
+ if (*value == NULL) {
+ MAKE_STD_ZVAL(*value);
+ }
+
+ if (flags & MMC_SERIALIZED) {
+ const char *tmp = data;
+ php_unserialize_data_t var_hash;
+ PHP_VAR_UNSERIALIZE_INIT(var_hash);
+
+ if (!php_var_unserialize(value, (const unsigned char **)&tmp, tmp + data_len, \
&var_hash TSRMLS_CC)) { + MMC_DEBUG(("Error at offset %d of %d bytes", tmp - data, \
data_len)); + PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
efree(data);
+ efree(*key);
+ return -1;
}
- efree(tmp_name);
- }
+
+ PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
+ efree(data);
+ }
+ else {
+ ZVAL_STRINGL(*value, data, data_len, 0);
+ }
+
return 1;
}
/* }}} */
@@ -933,50 +1098,49 @@
{
char *real_command;
int size, response_buf_size;
-
+
real_command = emalloc(
sizeof("delete") - 1
- + 1 /* space */
+ + 1 /* space */
+ key_len
- + 1 /* space */
+ + 1 /* space */
+ MAX_LENGTH_OF_LONG
+ 1
);
- MMC_PREPARE_KEY(key, key_len);
-
size = sprintf(real_command, "delete %s %d", key, time);
-
+
real_command [size] = '\0';
MMC_DEBUG(("mmc_delete: trying to delete '%s'", key));
-
+
/* drop it! =) */
if (mmc_sendcmd(mmc, real_command, size TSRMLS_CC) < 0) {
efree(real_command);
return -1;
}
efree(real_command);
-
+
/* get server's response */
if ((response_buf_size = mmc_readline(mmc TSRMLS_CC)) < 0){
+ MMC_DEBUG(("failed to read the server's response"));
return -1;
}
-
+
MMC_DEBUG(("mmc_delete: server's response is '%s'", mmc->inbuf));
/* ok? */
if(mmc_str_left(mmc->inbuf,"DELETED", response_buf_size, sizeof("DELETED") - 1)) {
return 1;
}
-
+
if(mmc_str_left(mmc->inbuf,"NOT_FOUND", response_buf_size, sizeof("NOT_FOUND") - \
1)) { /* return 0, if such wasn't found */
return 0;
}
-
- php_error_docref(NULL TSRMLS_CC, E_NOTICE, "failed to delete item");
-
+
+ MMC_DEBUG(("failed to delete item"));
+
/* hmm.. */
return -1;
}
@@ -985,27 +1149,27 @@
static int mmc_flush(mmc_t *mmc TSRMLS_DC) /* {{{ */
{
int response_buf_size;
-
+
MMC_DEBUG(("mmc_flush: flushing the cache"));
-
+
if (mmc_sendcmd(mmc, "flush_all", sizeof("flush_all") - 1 TSRMLS_CC) < 0) {
return -1;
}
-
+
/* get server's response */
if ((response_buf_size = mmc_readline(mmc TSRMLS_CC)) < 0){
return -1;
}
-
+
MMC_DEBUG(("mmc_flush: server's response is '%s'", mmc->inbuf));
/* ok? */
if(mmc_str_left(mmc->inbuf,"OK", response_buf_size, sizeof("OK") - 1)) {
return 1;
}
-
- php_error_docref(NULL TSRMLS_CC, E_NOTICE, "failed to flush server's cache");
-
+
+ MMC_DEBUG(("failed to flush server's cache"));
+
/* hmm.. */
return -1;
}
@@ -1013,7 +1177,7 @@
static int mmc_get_stats (mmc_t *mmc, zval **stats TSRMLS_DC) /* {{{ */
{
- int response_buf_size, stats_tmp_len, space_len, i = 0;
+ int response_buf_size, stats_tmp_len, space_len;
char *stats_tmp, *space_tmp = NULL;
char *key, *val;
@@ -1040,7 +1204,6 @@
}
efree(stats_tmp);
- i++;
}
else if (mmc_str_left(mmc->inbuf, "END", response_buf_size, sizeof("END") - 1)) {
/* END of stats*/
@@ -1048,28 +1211,26 @@
}
else {
/* unknown error */
- break;
+ return -1;
}
}
- if (i == 0) {
- efree(*stats);
+ if (response_buf_size < 0) {
+ return -1;
}
return 1;
}
/* }}} */
-static int mmc_incr_decr (mmc_t *mmc, int cmd, char *key, int key_len, int value \
TSRMLS_DC) /* {{{ */ +static int mmc_incr_decr (mmc_t *mmc, int cmd, char *key, int \
key_len, int value, long *number TSRMLS_DC) /* {{{ */ {
char *command, *command_name;
int cmd_len, response_buf_size;
-
+
/* 4 is for strlen("incr") or strlen("decr"), doesn't matter */
- command = emalloc(4 + key_len + MAX_LENGTH_OF_LONG + 1);
+ command = emalloc(4 + key_len + MAX_LENGTH_OF_LONG + 1);
- MMC_PREPARE_KEY(key, key_len);
-
if (cmd > 0) {
command_name = emalloc(sizeof("incr"));
sprintf(command_name, "incr");
@@ -1080,7 +1241,7 @@
sprintf(command_name, "decr");
cmd_len = sprintf(command, "decr %s %d", key, value);
}
-
+
if (mmc_sendcmd(mmc, command, cmd_len TSRMLS_CC) < 0) {
efree(command);
efree(command_name);
@@ -1088,45 +1249,46 @@
}
efree(command);
- if ((response_buf_size = mmc_readline(mmc TSRMLS_CC)) > 0) {
- MMC_DEBUG(("mmc_incr_decr: server's answer is: '%s'", mmc->inbuf));
- if (mmc_str_left(mmc->inbuf, "NOT_FOUND", response_buf_size, sizeof("NOT_FOUND") - \
1)) {
- php_error_docref(NULL TSRMLS_CC, E_NOTICE, "failed to %sement variable - item \
with such key not found", command_name);
- efree(command_name);
- return -1;
- }
- else if (mmc_str_left(mmc->inbuf, "ERROR", response_buf_size, sizeof("ERROR") - \
1)) {
- php_error_docref(NULL TSRMLS_CC, E_NOTICE, "failed to %sement variable - an error \
occured", command_name);
- efree(command_name);
- return -1;
- }
- else if (mmc_str_left(mmc->inbuf, "CLIENT_ERROR", response_buf_size, \
sizeof("CLIENT_ERROR") - 1)) {
- php_error_docref(NULL TSRMLS_CC, E_NOTICE, "failed to %sement variable - client \
error occured", command_name);
- efree(command_name);
- return -1;
- }
- else if (mmc_str_left(mmc->inbuf, "SERVER_ERROR", response_buf_size, \
sizeof("SERVER_ERROR") - 1)) {
- php_error_docref(NULL TSRMLS_CC, E_NOTICE, "failed to %sement variable - server \
error occured", command_name);
- efree(command_name);
- return -1;
- }
- else {
- efree(command_name);
- return atoi(mmc->inbuf);
- }
+ if ((response_buf_size = mmc_readline(mmc TSRMLS_CC)) < 0) {
+ MMC_DEBUG(("failed to read the server's response"));
+ efree(command_name);
+ return -1;
}
- efree(command_name);
- return -1;
+ MMC_DEBUG(("mmc_incr_decr: server's answer is: '%s'", mmc->inbuf));
+ if (mmc_str_left(mmc->inbuf, "NOT_FOUND", response_buf_size, sizeof("NOT_FOUND") - \
1)) { + MMC_DEBUG(("failed to %sement variable - item with such key not found", \
command_name)); + efree(command_name);
+ return 0;
+ }
+ else if (mmc_str_left(mmc->inbuf, "ERROR", response_buf_size, sizeof("ERROR") - 1)) \
{ + MMC_DEBUG(("failed to %sement variable - an error occured", command_name));
+ efree(command_name);
+ return -1;
+ }
+ else if (mmc_str_left(mmc->inbuf, "CLIENT_ERROR", response_buf_size, \
sizeof("CLIENT_ERROR") - 1)) { + MMC_DEBUG(("failed to %sement variable - client \
error occured", command_name)); + efree(command_name);
+ return -1;
+ }
+ else if (mmc_str_left(mmc->inbuf, "SERVER_ERROR", response_buf_size, \
sizeof("SERVER_ERROR") - 1)) { + MMC_DEBUG(("failed to %sement variable - server \
error occured", command_name)); + efree(command_name);
+ return -1;
+ }
+ efree(command_name);
+ *number = (long)atol(mmc->inbuf);
+ return 1;
}
/* }}} */
static void php_mmc_store (INTERNAL_FUNCTION_PARAMETERS, char *command, int \
command_len) /* {{{ */ {
mmc_t *mmc;
- int inx, data_len = 0, real_expire = 0, real_flags = 0, key_len;
- char *data = NULL, *real_key;
+ mmc_pool_t *pool;
+ int result = -1, value_len, data_len, real_expire = 0, real_flags = 0, key_len;
+ char *value, *data, *real_key;
int ac = ZEND_NUM_ARGS();
zval *key, *var, *flags, *expire, *mmc_object = getThis();
@@ -1165,11 +1327,12 @@
}
}
- if ((inx = mmc_get_connection(mmc_object, &mmc TSRMLS_CC)) == 0) {
+ if (!mmc_get_pool(mmc_object, &pool TSRMLS_CC) || !pool->num_servers) {
RETURN_FALSE;
}
convert_to_string(key);
+ MMC_PREPARE_KEY(Z_STRVAL_P(key), Z_STRLEN_P(key));
if (Z_STRLEN_P(key) > MMC_KEY_MAX_SIZE) {
real_key = estrndup(Z_STRVAL_P(key), MMC_KEY_MAX_SIZE);
@@ -1179,57 +1342,69 @@
real_key = estrdup(Z_STRVAL_P(key));
key_len = Z_STRLEN_P(key);
}
-
+
switch (Z_TYPE_P(var)) {
case IS_STRING:
+ value = Z_STRVAL_P(var);
+ value_len = Z_STRLEN_P(var);
+ break;
+
case IS_LONG:
case IS_DOUBLE:
case IS_BOOL:
convert_to_string(var);
- if (real_flags & MMC_COMPRESSED) {
- if (!mmc_compress(&data, &data_len, Z_STRVAL_P(var), Z_STRLEN_P(var) TSRMLS_CC)) \
{
- RETURN_FALSE;
- }
- }
- else {
- data = Z_STRVAL_P(var);
- data_len = Z_STRLEN_P(var);
- }
+ value = Z_STRVAL_P(var);
+ value_len = Z_STRLEN_P(var);
break;
+
default:
PHP_VAR_SERIALIZE_INIT(var_hash);
php_var_serialize(&buf, &var, &var_hash TSRMLS_CC);
PHP_VAR_SERIALIZE_DESTROY(var_hash);
-
+
if (!buf.c) {
- /* you're trying to save null or smthing went really wrong */
+ /* you're trying to save null or something went really wrong */
RETURN_FALSE;
}
-
- real_flags |= MMC_SERIALIZED;
- if (real_flags & MMC_COMPRESSED) {
- if (!mmc_compress(&data, &data_len, buf.c, buf.len TSRMLS_CC)) {
- RETURN_FALSE;
- }
- }
- else {
- data = buf.c;
- data_len = buf.len;
- }
+ value = buf.c;
+ value_len = buf.len;
+ real_flags |= MMC_SERIALIZED;
break;
}
- if (mmc_exec_storage_cmd(mmc, command, command_len, real_key, key_len, real_flags, \
real_expire, data, data_len TSRMLS_CC) > 0) {
- if (real_flags & MMC_SERIALIZED) {
- smart_str_free(&buf);
+ /* autocompress large values */
+ if (pool->compress_threshold && value_len >= pool->compress_threshold) {
+ real_flags |= MMC_COMPRESSED;
+ }
+
+ if (real_flags & MMC_COMPRESSED) {
+ if (!mmc_compress(&data, &data_len, value, value_len TSRMLS_CC)) {
+ RETURN_FALSE;
}
- if (real_flags & MMC_COMPRESSED) {
+
+ MMC_DEBUG(("php_mmc_store: compressed '%s' from %d bytes to %d", real_key, \
value_len, data_len)); +
+ /* was enough space was saved to motivate uncompress processing on get() */
+ if (data_len >= value_len * (1 - pool->min_compress_savings)) {
efree(data);
+ data = value;
+ data_len = value_len;
+ real_flags &= ~MMC_COMPRESSED;
+ MMC_DEBUG(("php_mmc_store: compression saving were less that '%f', clearing \
compressed flag", pool->min_compress_savings)); + }
+ }
+ else {
+ data = value;
+ data_len = value_len;
+ }
+
+ while (result < 0 && (mmc = mmc_server_find(pool, real_key, key_len)) != NULL) {
+ if ((result = mmc_exec_storage_cmd(mmc, command, command_len, real_key, key_len, \
real_flags, real_expire, data, data_len TSRMLS_CC)) < 0 && mmc_server_failure(mmc)) { \
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "marked server '%s:%d' as failed", \
mmc->host, mmc->port); }
- efree(real_key);
- RETURN_TRUE;
}
+
if (real_flags & MMC_SERIALIZED) {
smart_str_free(&buf);
}
@@ -1237,6 +1412,13 @@
efree(data);
}
efree(real_key);
+
+ if (result > 0) {
+ RETURN_TRUE;
+ }
+ if (result < 0) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to %s '%s'", command, \
Z_STRVAL_P(key)); + }
RETURN_FALSE;
}
/* }}} */
@@ -1244,15 +1426,17 @@
static void php_mmc_incr_decr(INTERNAL_FUNCTION_PARAMETERS, int cmd) /* {{{ */
{
mmc_t *mmc;
- int inx, result, real_value = 1;
+ mmc_pool_t *pool;
+ int result = -1, real_value = 1;
int ac = ZEND_NUM_ARGS();
+ long number;
zval *value, *key, *mmc_object = getThis();
if (mmc_object == NULL) {
if (ac < 2 || ac > 3 || zend_get_parameters(ht, ac, &mmc_object, &key, &value) == \
FAILURE) { WRONG_PARAM_COUNT;
}
-
+
if (ac > 2) {
convert_to_long(value);
real_value = Z_LVAL_P(value);
@@ -1262,24 +1446,37 @@
if (ac < 1 || ac > 2 || zend_get_parameters(ht, ac, &key, &value) == FAILURE) {
WRONG_PARAM_COUNT;
}
-
+
if (ac > 1) {
convert_to_long(value);
real_value = Z_LVAL_P(value);
}
}
- if ((inx = mmc_get_connection(mmc_object, &mmc TSRMLS_CC)) == 0) {
+ if (!mmc_get_pool(mmc_object, &pool TSRMLS_CC) || !pool->num_servers) {
RETURN_FALSE;
}
convert_to_string(key);
+ MMC_PREPARE_KEY(Z_STRVAL_P(key), Z_STRLEN_P(key));
- if ((result = mmc_incr_decr(mmc, cmd, Z_STRVAL_P(key), Z_STRLEN_P(key), real_value \
TSRMLS_CC)) < 0) {
- RETURN_FALSE;
+ while (result < 0 && (mmc = mmc_server_find(pool, Z_STRVAL_P(key), \
Z_STRLEN_P(key))) != NULL) { + if ((result = mmc_incr_decr(mmc, cmd, \
Z_STRVAL_P(key), Z_STRLEN_P(key), real_value, &number TSRMLS_CC)) < 0 && \
mmc_server_failure(mmc)) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "marked \
server '%s:%d' as failed", mmc->host, mmc->port); + }
}
- RETURN_LONG(result);
+ if (result > 0) {
+ RETURN_LONG(number);
+ }
+
+ if (result < 0) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "an error occured while \
incrementing/decrementing '%s'", Z_STRVAL_P(key)); + }
+ else {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "no such key '%s'", Z_STRVAL_P(key));
+ }
+ RETURN_FALSE;
}
/* }}} */
@@ -1287,160 +1484,69 @@
{
zval **host, **port, **timeout, *mmc_object = getThis();
mmc_t *mmc = NULL;
- int timeout_sec = MMC_DEFAULT_TIMEOUT, real_port, hash_key_len, errnum = 0;
+ mmc_pool_t *pool;
+ int real_timeout = MMC_DEFAULT_TIMEOUT, real_port, errnum = 0;
int ac = ZEND_NUM_ARGS();
- char *hash_key = NULL, *version, *error_string = NULL;
+ char *error_string = NULL;
if (ac < 1 || ac > 3 || zend_get_parameters_ex(ac, &host, &port, &timeout) == \
FAILURE) { WRONG_PARAM_COUNT;
}
convert_to_string_ex(host);
-
- real_port = MEMCACHE_G(default_port);
-
+
if (ac > 1) {
convert_to_long_ex(port);
real_port = Z_LVAL_PP(port);
}
+ else {
+ real_port = MEMCACHE_G(default_port);
+ }
- if (ac == 3) {
+ if (ac > 2) {
convert_to_long_ex(timeout);
- timeout_sec = Z_LVAL_PP(timeout);
+ real_timeout = Z_LVAL_PP(timeout);
}
+ /* initialize and connect server struct */
if (persistent) {
- list_entry *le;
-
- MMC_DEBUG(("php_mmc_connect: seeking for persistent connection"));
-
- hash_key = emalloc(sizeof("mmc_connect___") - 1 + Z_STRLEN_PP(host) + \
MAX_LENGTH_OF_LONG + 1);
- hash_key_len = sprintf(hash_key, "mmc_connect___%s:%d", Z_STRVAL_PP(host), \
real_port);
- if (zend_hash_find(&EG(persistent_list), hash_key, hash_key_len+1, (void **) &le) \
== FAILURE) {
- list_entry new_le;
-
- MMC_DEBUG(("php_mmc_connect: connection wasn't found in the hash"));
-
- if ((mmc = mmc_open(Z_STRVAL_PP(host), Z_STRLEN_PP(host), real_port, timeout_sec, \
persistent, &error_string, &errnum TSRMLS_CC)) == NULL) {
- MMC_DEBUG(("php_mmc_connect: connection failed"));
- efree(hash_key);
- goto connect_done;
- }
-
- mmc->id = zend_list_insert(mmc,le_pmemcache);
-
- new_le.type = le_pmemcache;
- new_le.ptr = mmc;
-
- if (zend_hash_update(&EG(persistent_list), hash_key, hash_key_len+1, (void *) \
&new_le, sizeof(list_entry), NULL)==FAILURE) {
- efree(hash_key);
- goto connect_done;
- }
- efree(hash_key);
- MEMCACHE_G(num_persistent)++;
- }
- else {
- MMC_DEBUG(("php_mmc_connect: connection found in the hash"));
-
- if (le->type == le_pmemcache && le->ptr != NULL) {
-
- if ((version = mmc_get_version(le->ptr TSRMLS_CC)) != NULL) {
- mmc = (mmc_t *)le->ptr;
-
- MMC_DEBUG(("memcache_connect: all right"));
- mmc->id = zend_list_insert(mmc,le_pmemcache);
-
- /* Ok, server is alive and working */
- efree(version);
- efree(hash_key);
- goto connect_done;
- }
- else {
- list_entry new_le;
-
- MMC_DEBUG(("php_mmc_connect: server has gone away, reconnecting.."));
-
- /* Daemon has gone away, reconnecting */
- MEMCACHE_G(num_persistent)--;
- if ((mmc = mmc_open(Z_STRVAL_PP(host), Z_STRLEN_PP(host), real_port, \
timeout_sec, persistent, &error_string, &errnum TSRMLS_CC)) == NULL) \
{
- MMC_DEBUG(("php_mmc_connect: reconnect failed"));
- zend_hash_del(&EG(persistent_list), hash_key, hash_key_len+1);
- efree(hash_key);
- goto connect_done;
- }
-
- mmc->id = zend_list_insert(mmc,le_pmemcache);
-
- new_le.type = le_pmemcache;
- new_le.ptr = mmc;
-
- if (zend_hash_update(&EG(persistent_list), hash_key, hash_key_len+1, (void *) \
&new_le, sizeof(list_entry), NULL)==FAILURE) {
- efree(hash_key);
- goto connect_done;
- }
- efree(hash_key);
- MEMCACHE_G(num_persistent)++;
- }
- }
- else {
- list_entry new_le;
-
- MMC_DEBUG(("php_mmc_connect: smthing was wrong, reconnecting.."));
- if ((mmc = mmc_open(Z_STRVAL_PP(host), Z_STRLEN_PP(host), real_port, \
timeout_sec, persistent, &error_string, &errnum TSRMLS_CC)) == NULL) \
{
- MMC_DEBUG(("php_mmc_connect: reconnect failed"));
- zend_hash_del(&EG(persistent_list), hash_key, hash_key_len+1);
- efree(hash_key);
- goto connect_done;
- }
-
- mmc->id = zend_list_insert(mmc,le_pmemcache);
-
- new_le.type = le_pmemcache;
- new_le.ptr = mmc;
-
- if (zend_hash_update(&EG(persistent_list), hash_key, hash_key_len+1, (void *) \
&new_le, sizeof(list_entry), NULL)==FAILURE) {
- efree(hash_key);
- goto connect_done;
- }
- efree(hash_key);
- MEMCACHE_G(num_persistent)++;
- }
- }
+ mmc = mmc_find_persistent(Z_STRVAL_PP(host), Z_STRLEN_PP(host), real_port, \
real_timeout, MMC_DEFAULT_RETRY TSRMLS_CC); }
else {
MMC_DEBUG(("php_mmc_connect: creating regular connection"));
- mmc = mmc_open(Z_STRVAL_PP(host), Z_STRLEN_PP(host), real_port, timeout_sec, \
persistent, &error_string, &errnum TSRMLS_CC);
- if (mmc != NULL) {
- mmc->id = zend_list_insert(mmc,le_memcache);
- }
+ mmc = mmc_server_new(Z_STRVAL_PP(host), Z_STRLEN_PP(host), real_port, 0, \
real_timeout, MMC_DEFAULT_RETRY); }
-connect_done:
-
- if (mmc == NULL) {
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't connect to %s:%d, %s \
(%d)",Z_STRVAL_PP(host), real_port, error_string ? error_string : "Unknown error", \
errnum); + if (!mmc_open(mmc, 1, &error_string, &errnum)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't connect to %s:%d, %s (%d)", \
Z_STRVAL_PP(host), real_port, error_string ? error_string : "Unknown error", errnum); \
if (error_string) { efree(error_string);
}
RETURN_FALSE;
}
+ /* initialize pool */
+ MMC_DEBUG(("php_mmc_connect: initializing server pool"));
+ pool = mmc_pool_new();
+ pool->id = zend_list_insert(pool, le_memcache_pool);
+ mmc_pool_add(pool, mmc, 1);
+
if (mmc_object == NULL) {
object_init_ex(return_value, memcache_class_entry_ptr);
- add_property_resource(return_value, "connection",mmc->id);
+ add_property_resource(return_value, "connection", pool->id);
}
else {
- add_property_resource(mmc_object, "connection",mmc->id);
+ add_property_resource(mmc_object, "connection", pool->id);
RETURN_TRUE;
- }
+ }
}
/* }}} */
-/* ----------------
- module functions
+/* ----------------
+ module functions
---------------- */
-/* {{{ proto object memcache_connect( string host [, int port [, int timeout ] ])
+/* {{{ proto object memcache_connect( string host [, int port [, int timeout ] ])
Connects to server and returns Memcache object */
PHP_FUNCTION(memcache_connect)
{
@@ -1448,7 +1554,7 @@
}
/* }}} */
-/* {{{ proto object memcache_pconnect( string host [, int port [, int timeout ] ])
+/* {{{ proto object memcache_pconnect( string host [, int port [, int timeout ] ])
Connects to server and returns Memcache object */
PHP_FUNCTION(memcache_pconnect)
{
@@ -1456,34 +1562,190 @@
}
/* }}} */
-/* {{{ proto string memcache_get_version( object memcache )
+/* {{{ proto bool memcache_add_server( string host [, int port [ bool persistent [, \
unsigned int weight [, int timeout [, int retry_interval ] ] ] ] ]) + Adds a \
connection to the pool. The order in which this function is called is significant */ \
+PHP_FUNCTION(memcache_add_server) +{
+ zval **connection, **host, **port, **persistent, **weight, **timeout, \
**retry_interval, *mmc_object = getThis(); + mmc_pool_t *pool;
+ mmc_t *mmc;
+ int real_port, real_persistent = 1, real_weight = 1, real_timeout = \
MMC_DEFAULT_TIMEOUT, real_retry_interval = MMC_DEFAULT_RETRY; + int resource_type, ac \
= ZEND_NUM_ARGS(); +
+ if (mmc_object == NULL) {
+ if (zend_get_parameters(ht, 1, &mmc_object) == FAILURE) {
+ WRONG_PARAM_COUNT;
+ }
+ }
+
+ if (ac < 1 || ac > 6 || zend_get_parameters_ex(ac, &host, &port, &persistent, \
&weight, &timeout, &retry_interval) == FAILURE) { + WRONG_PARAM_COUNT;
+ }
+
+ convert_to_string_ex(host);
+
+ if (ac > 1) {
+ convert_to_long_ex(port);
+ real_port = Z_LVAL_PP(port);
+ }
+ else {
+ real_port = MEMCACHE_G(default_port);
+ }
+
+ if (ac > 2) {
+ convert_to_boolean_ex(persistent);
+ real_persistent = Z_LVAL_PP(persistent);
+ }
+
+ if (ac > 3) {
+ convert_to_long_ex(weight);
+ real_weight = Z_LVAL_PP(weight);
+ }
+
+ if (ac > 4) {
+ convert_to_long_ex(timeout);
+ real_timeout = Z_LVAL_PP(timeout);
+ }
+
+ if (ac > 5) {
+ convert_to_long_ex(retry_interval);
+ real_retry_interval = Z_LVAL_PP(retry_interval);
+ }
+
+ if (Z_TYPE_P(mmc_object) != IS_OBJECT) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied argument is not a valid \
memcache link resource"); + RETURN_FALSE;
+ }
+
+ if (real_weight < 0) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "weight must be a positive integer");
+ RETURN_FALSE;
+ }
+
+ /* lazy initialization of server struct */
+ if (real_persistent) {
+ mmc = mmc_find_persistent(Z_STRVAL_PP(host), Z_STRLEN_PP(host), real_port, \
real_timeout, real_retry_interval TSRMLS_CC); + }
+ else {
+ MMC_DEBUG(("memcache_add_server: initializing regular struct"));
+ mmc = mmc_server_new(Z_STRVAL_PP(host), Z_STRLEN_PP(host), real_port, 0, \
real_timeout, real_retry_interval); + }
+
+ /* initialize pool if need be */
+ if (zend_hash_find(Z_OBJPROP_P(mmc_object), "connection", sizeof("connection"), \
(void **)&connection) == FAILURE) { + MMC_DEBUG(("mmc_add_connection: initialized \
and appended server to empty pool")); +
+ pool = mmc_pool_new();
+ pool->id = zend_list_insert(pool, le_memcache_pool);
+
+ add_property_resource(mmc_object, "connection", pool->id);
+ }
+ else {
+ MMC_DEBUG(("memcache_add_server: appended server to pool"));
+
+ pool = (mmc_pool_t *) zend_list_find(Z_LVAL_PP(connection), &resource_type);
+ if (!pool || resource_type != le_memcache_pool) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "connection identifier not found");
+ RETURN_FALSE;
+ }
+ }
+
+ mmc_pool_add(pool, mmc, real_weight);
+ RETURN_TRUE;
+}
+/* }}} */
+
+static mmc_t *mmc_find_persistent(char *host, int host_len, int port, int timeout, \
int retry_interval TSRMLS_DC) /* {{{ */ +{
+ mmc_t *mmc;
+ list_entry *le;
+ char *hash_key;
+ int hash_key_len;
+
+ MMC_DEBUG(("mmc_find_persistent: seeking for persistent connection"));
+ hash_key = emalloc(sizeof("mmc_connect___") - 1 + host_len + MAX_LENGTH_OF_LONG + \
1); + hash_key_len = sprintf(hash_key, "mmc_connect___%s:%d", host, port);
+
+ if (zend_hash_find(&EG(persistent_list), hash_key, hash_key_len+1, (void **) &le) \
== FAILURE) { + list_entry new_le;
+ MMC_DEBUG(("mmc_find_persistent: connection wasn't found in the hash"));
+
+ mmc = mmc_server_new(host, host_len, port, 1, timeout, retry_interval);
+ new_le.type = le_pmemcache;
+ new_le.ptr = mmc;
+
+ /* register new persistent connection */
+ if (zend_hash_update(&EG(persistent_list), hash_key, hash_key_len+1, (void *) \
&new_le, sizeof(list_entry), NULL) == FAILURE) { + mmc_server_free(mmc);
+ mmc = NULL;
+ }
+ }
+ else if (le->type != le_pmemcache || le->ptr == NULL) {
+ list_entry new_le;
+ MMC_DEBUG(("mmc_find_persistent: something was wrong, reconnecting.."));
+ zend_hash_del(&EG(persistent_list), hash_key, hash_key_len+1);
+
+ mmc = mmc_server_new(host, host_len, port, 1, timeout, retry_interval);
+ new_le.type = le_pmemcache;
+ new_le.ptr = mmc;
+
+ /* register new persistent connection */
+ if (zend_hash_update(&EG(persistent_list), hash_key, hash_key_len+1, (void *) \
&new_le, sizeof(list_entry), NULL) == FAILURE) { + mmc_server_free(mmc);
+ mmc = NULL;
+ }
+ }
+ else {
+ MMC_DEBUG(("mmc_find_persistent: connection found in the hash"));
+ mmc = (mmc_t *)le->ptr;
+ mmc->timeout = timeout;
+ mmc->retry_interval = retry_interval;
+
+ /* attempt to reconnect this node before failover in case connection has gone away \
*/ + if (mmc->status == MMC_STATUS_CONNECTED) {
+ mmc->status = MMC_STATUS_UNKNOWN;
+ }
+ }
+
+ efree(hash_key);
+ return mmc;
+}
+/* }}} */
+
+/* {{{ proto string memcache_get_version( object memcache )
Returns server's version */
PHP_FUNCTION(memcache_get_version)
{
- mmc_t *mmc;
- int inx;
+ mmc_pool_t *pool;
char *version;
+ int i;
zval *mmc_object = getThis();
-
+
if (mmc_object == NULL) {
if (zend_get_parameters(ht, 1, &mmc_object) == FAILURE) {
WRONG_PARAM_COUNT;
}
}
- if ((inx = mmc_get_connection(mmc_object, &mmc TSRMLS_CC)) == 0) {
+ if (!mmc_get_pool(mmc_object, &pool TSRMLS_CC)) {
RETURN_FALSE;
}
- if ( (version = mmc_get_version(mmc TSRMLS_CC)) ) {
- RETURN_STRING(version, 0);
+ for (i=0; i<pool->num_servers; i++) {
+ if (mmc_open(pool->servers[i], 1, NULL, NULL) && (version = \
mmc_get_version(pool->servers[i] TSRMLS_CC)) != NULL) { + RETURN_STRING(version, \
0); + }
+ else if (mmc_server_failure(pool->servers[i])) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "marked server '%s:%d' as failed", \
pool->servers[i]->host, pool->servers[i]->port); + }
}
- php_error_docref(NULL TSRMLS_CC, E_NOTICE, "failed to get server's version");
+
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to get server's version");
RETURN_FALSE;
}
/* }}} */
-/* {{{ proto bool memcache_add( object memcache, string key, mixed var [, int expire \
] ) +/* {{{ proto bool memcache_add( object memcache, string key, mixed var [, int \
expire ] ) Adds new item. Item with such key should not exist. */
PHP_FUNCTION(memcache_add)
{
@@ -1491,7 +1753,7 @@
}
/* }}} */
-/* {{{ proto bool memcache_set( object memcache, string key, mixed var [, int expire \
] ) +/* {{{ proto bool memcache_set( object memcache, string key, mixed var [, int \
expire ] ) Sets the value of an item. Item may exist or not */
PHP_FUNCTION(memcache_set)
{
@@ -1499,7 +1761,7 @@
}
/* }}} */
-/* {{{ proto bool memcache_replace( object memcache, string key, mixed var [, int \
expire ] ) +/* {{{ proto bool memcache_replace( object memcache, string key, mixed \
var [, int expire ] ) Replaces existing item. Returns false if item doesn't exist */
PHP_FUNCTION(memcache_replace)
{
@@ -1507,16 +1769,11 @@
}
/* }}} */
-/* {{{ proto mixed memcache_get( object memcache, string key )
+/* {{{ proto mixed memcache_get( object memcache, string key )
Returns value of existing item or false */
PHP_FUNCTION(memcache_get)
{
- mmc_t *mmc;
- long result_len = 0;
- int inx, flags = 0, data_len = 0;
- char *data = NULL, *result_data = NULL;
- const char *tmp = NULL;
- php_unserialize_data_t var_hash;
+ mmc_pool_t *pool;
zval *key, *mmc_object = getThis();
if (mmc_object == NULL) {
@@ -1530,80 +1787,32 @@
}
}
- if ((inx = mmc_get_connection(mmc_object, &mmc TSRMLS_CC)) == 0) {
+ if (!mmc_get_pool(mmc_object, &pool TSRMLS_CC) || !pool->num_servers) {
RETURN_FALSE;
}
if (Z_TYPE_P(key) != IS_ARRAY) {
- convert_to_string(key);
-
- if (mmc_exec_retrieval_cmd(mmc, "get", sizeof("get") - 1, Z_STRVAL_P(key), \
Z_STRLEN_P(key), &flags, &data, &data_len TSRMLS_CC) > 0) {
-
- if (!data) {
- RETURN_EMPTY_STRING();
- }
-
- if (flags & MMC_COMPRESSED) {
- if (!mmc_uncompress(&result_data, &result_len, data, data_len)) {
- RETURN_FALSE;
- }
-
- tmp = result_data;
-
- if (flags & MMC_SERIALIZED) {
- PHP_VAR_UNSERIALIZE_INIT(var_hash);
- if (!php_var_unserialize(&return_value, (const unsigned char **) &tmp, tmp + \
result_len, &var_hash TSRMLS_CC)) {
- PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
- zval_dtor(return_value);
- php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Error at offset %d of %d bytes", \
tmp - data, data_len);
- efree(data);
- efree(result_data);
- RETURN_FALSE;
- }
- efree(data);
- efree(result_data);
- PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
- }
- else {
- efree(data);
- RETURN_STRINGL(result_data, result_len, 0);
- }
- }
- else if (flags & MMC_SERIALIZED) {
- tmp = data;
- PHP_VAR_UNSERIALIZE_INIT(var_hash);
- if (!php_var_unserialize(&return_value, (const unsigned char **) &tmp, tmp + \
data_len, &var_hash TSRMLS_CC)) {
- PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
- zval_dtor(return_value);
- php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Error at offset %d of %d bytes", \
tmp - data, data_len);
- efree(data);
- RETURN_FALSE;
- }
- efree(data);
- PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
- }
- else {
- RETURN_STRINGL(data, data_len, 0);
- }
- }
- else {
+ if (mmc_exec_retrieval_cmd(pool, key, &return_value TSRMLS_CC) < 0) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to fetch value from server");
RETURN_FALSE;
- }
+ }
}
else {
- if (mmc_exec_retrieval_cmd_multi(mmc, key, &return_value TSRMLS_CC) < 0) {
- RETURN_FALSE;
+ if (mmc_exec_retrieval_cmd_multi(pool, key, &return_value TSRMLS_CC) < 0) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to fetch value from server");
+ RETURN_FALSE;
}
}
}
/* }}} */
-/* {{{ proto bool memcache_delete( object memcache, string key, [ int expire ])
+/* {{{ proto bool memcache_delete( object memcache, string key, [ int expire ])
Deletes existing item */
PHP_FUNCTION(memcache_delete)
{
mmc_t *mmc;
- int inx, result, real_time = 0;
+ mmc_pool_t *pool;
+ int result = -1, real_time = 0;
int ac = ZEND_NUM_ARGS();
zval *key, *time, *mmc_object = getThis();
@@ -1611,7 +1820,7 @@
if (ac < 2 || ac > 3 || zend_get_parameters(ht, ac, &mmc_object, &key, &time) == \
FAILURE) { WRONG_PARAM_COUNT;
}
-
+
if (ac > 2) {
convert_to_long(time);
real_time = Z_LVAL_P(time);
@@ -1628,38 +1837,40 @@
}
}
- if ((inx = mmc_get_connection(mmc_object, &mmc TSRMLS_CC)) == 0) {
+ if (!mmc_get_pool(mmc_object, &pool TSRMLS_CC) || !pool->num_servers) {
RETURN_FALSE;
}
convert_to_string(key);
-
- result = mmc_delete(mmc, Z_STRVAL_P(key), Z_STRLEN_P(key), real_time TSRMLS_CC);
-
+ MMC_PREPARE_KEY(Z_STRVAL_P(key), Z_STRLEN_P(key));
+
+ while (result < 0 && (mmc = mmc_server_find(pool, Z_STRVAL_P(key), \
Z_STRLEN_P(key))) != NULL) { + if ((result = mmc_delete(mmc, Z_STRVAL_P(key), \
Z_STRLEN_P(key), real_time TSRMLS_CC)) < 0 && mmc_server_failure(mmc)) { \
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "marked server '%s:%d' as failed", \
mmc->host, mmc->port); + }
+ }
+
if (result > 0) {
RETURN_TRUE;
}
- else if (result == 0) {
- RETURN_FALSE;
- }
- else {
- php_error_docref(NULL TSRMLS_CC, E_NOTICE, "error while deleting item");
+ if (result < 0) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "error while deleting item");
}
RETURN_FALSE;
}
/* }}} */
-/* {{{ proto bool memcache_debug( bool onoff )
+/* {{{ proto bool memcache_debug( bool onoff )
Turns on/off internal debugging */
PHP_FUNCTION(memcache_debug)
{
#if ZEND_DEBUG
zval **onoff;
-
+
if (zend_get_parameters_ex(1, &onoff) == FAILURE) {
WRONG_PARAM_COUNT;
}
-
+
convert_to_long_ex(onoff);
if (Z_LVAL_PP(onoff) != 0) {
MEMCACHE_G(debug_mode) = 1;
@@ -1667,12 +1878,12 @@
else {
MEMCACHE_G(debug_mode) = 0;
}
-
+
RETURN_TRUE;
#else
RETURN_FALSE;
#endif
-
+
}
/* }}} */
@@ -1680,8 +1891,8 @@
Returns server's statistics */
PHP_FUNCTION(memcache_get_stats)
{
- mmc_t *mmc;
- int inx;
+ mmc_pool_t *pool;
+ int i, failures = 0;
zval *mmc_object = getThis();
if (mmc_object == NULL) {
@@ -1690,18 +1901,114 @@
}
}
- if ((inx = mmc_get_connection(mmc_object, &mmc TSRMLS_CC)) == 0) {
+ if (!mmc_get_pool(mmc_object, &pool TSRMLS_CC)) {
+ RETURN_FALSE;
+ }
+
+ for (i=0; i<pool->num_servers; i++) {
+ if (!mmc_open(pool->servers[i], 1, NULL, NULL) || mmc_get_stats(pool->servers[i], \
&return_value TSRMLS_CC) < 0) { + if (mmc_server_failure(pool->servers[i])) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "marked server '%s:%d' as failed", \
pool->servers[i]->host, pool->servers[i]->port); + }
+ failures++;
+ }
+ else {
+ break;
+ }
+ }
+
+ if (failures >= pool->num_servers) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to get statistics from \
server"); + RETURN_FALSE;
+ }
+}
+/* }}} */
+
+/* {{{ proto array memcache_get_extended_stats( object memcache )
+ Returns statistics for each server in the pool */
+PHP_FUNCTION(memcache_get_extended_stats)
+{
+ mmc_pool_t *pool;
+ char *hostname;
+ int i, hostname_len;
+ zval *mmc_object = getThis(), *stats;
+
+ if (mmc_object == NULL) {
+ if (zend_get_parameters(ht, 1, &mmc_object) == FAILURE) {
+ WRONG_PARAM_COUNT;
+ }
+ }
+
+ if (!mmc_get_pool(mmc_object, &pool TSRMLS_CC)) {
+ RETURN_FALSE;
+ }
+
+ array_init(return_value);
+ for (i=0; i<pool->num_servers; i++) {
+ MAKE_STD_ZVAL(stats);
+
+ hostname = emalloc(strlen(pool->servers[i]->host) + MAX_LENGTH_OF_LONG + 1 + 1);
+ hostname_len = sprintf(hostname, "%s:%d", pool->servers[i]->host, \
pool->servers[i]->port); +
+ if (!mmc_open(pool->servers[i], 1, NULL, NULL) || mmc_get_stats(pool->servers[i], \
&stats TSRMLS_CC) < 0) { + if (mmc_server_failure(pool->servers[i])) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "marked server '%s:%d' as failed", \
pool->servers[i]->host, pool->servers[i]->port); + }
+ ZVAL_FALSE(stats);
+ }
+
+ add_assoc_zval(return_value, hostname, stats);
+ efree(hostname);
+ }
+}
+/* }}} */
+
+/* {{{ proto array memcache_set_compress_threshold( object memcache, int threshold[ \
, float min_savings ] ) + Set automatic compress threshold */
+PHP_FUNCTION(memcache_set_compress_threshold)
+{
+ mmc_pool_t *pool;
+ int ac = ZEND_NUM_ARGS();
+ zval **threshold, **savings, *mmc_object = getThis();
+
+ if (mmc_object == NULL) {
+ if (zend_get_parameters(ht, 1, &mmc_object) == FAILURE) {
+ WRONG_PARAM_COUNT;
+ }
+ }
+
+ if (!mmc_get_pool(mmc_object, &pool TSRMLS_CC)) {
RETURN_FALSE;
}
- if (mmc_get_stats(mmc, &return_value TSRMLS_CC) < 0) {
- php_error_docref(NULL TSRMLS_CC, E_NOTICE, "failed to get server's statistics");
+ if (ac < 1 || ac > 2 || zend_get_parameters_ex(ac, &threshold, &savings) == \
FAILURE) { + WRONG_PARAM_COUNT;
+ }
+
+ convert_to_long_ex(threshold);
+ if (Z_LVAL_PP(threshold) < 0) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "threshold must be a positive \
integer"); RETURN_FALSE;
}
+ pool->compress_threshold = Z_LVAL_PP(threshold);
+
+ if (ac > 1) {
+ convert_to_double_ex(savings);
+ if (Z_DVAL_PP(savings) < 0 || Z_DVAL_PP(savings) > 1) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "min_savings must be a float in the \
0..1 range"); + RETURN_FALSE;
+ }
+ pool->min_compress_savings = Z_DVAL_PP(savings);
+ }
+ else {
+ pool->min_compress_savings = MMC_DEFAULT_SAVINGS;
+ }
+
+ RETURN_TRUE;
}
/* }}} */
-/* {{{ proto int memcache_increment( object memcache, string key [, int value ] )
+/* {{{ proto int memcache_increment( object memcache, string key [, int value ] )
Increments existing variable */
PHP_FUNCTION(memcache_increment)
{
@@ -1709,7 +2016,7 @@
}
/* }}} */
-/* {{{ proto int memcache_decrement( object memcache, string key [, int value ] )
+/* {{{ proto int memcache_decrement( object memcache, string key [, int value ] )
Decrements existing variable */
PHP_FUNCTION(memcache_decrement)
{
@@ -1717,57 +2024,73 @@
}
/* }}} */
-/* {{{ proto bool memcache_close( object memcache )
+/* {{{ proto bool memcache_close( object memcache )
Closes connection to memcached */
PHP_FUNCTION(memcache_close)
{
- mmc_t *mmc;
- int inx;
+ mmc_pool_t *pool;
+ int i, failures = 0;
zval *mmc_object = getThis();
-
+
if (mmc_object == NULL) {
if (zend_get_parameters(ht, 1, &mmc_object) == FAILURE) {
WRONG_PARAM_COUNT;
}
}
- if ((inx = mmc_get_connection(mmc_object, &mmc TSRMLS_CC)) == 0) {
+ if (!mmc_get_pool(mmc_object, &pool TSRMLS_CC)) {
RETURN_FALSE;
}
- if (mmc->persistent) {
- RETURN_TRUE;
+ for (i=0; i<pool->num_servers; i++) {
+ if (mmc_close(pool->servers[i] TSRMLS_CC) < 0) {
+ if (mmc_server_failure(pool->servers[i])) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "marked server '%s:%d' as failed", \
pool->servers[i]->host, pool->servers[i]->port); + }
+ failures++;
+ }
}
-
- if (mmc_close(mmc TSRMLS_CC) > 0) {
- RETURN_TRUE;
+
+ if (failures && failures >= pool->num_servers) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to close connection to \
servers"); + RETURN_FALSE;
}
- RETURN_FALSE;
+ RETURN_TRUE;
}
/* }}} */
-/* {{{ proto bool memcache_flush( object memcache )
+/* {{{ proto bool memcache_flush( object memcache )
Flushes cache */
PHP_FUNCTION(memcache_flush)
{
- mmc_t *mmc;
- int inx;
+ mmc_pool_t *pool;
+ int i, failures = 0;
zval *mmc_object = getThis();
-
+
if (mmc_object == NULL) {
if (zend_get_parameters(ht, 1, &mmc_object) == FAILURE) {
WRONG_PARAM_COUNT;
}
}
- if ((inx = mmc_get_connection(mmc_object, &mmc TSRMLS_CC)) == 0) {
+ if (!mmc_get_pool(mmc_object, &pool TSRMLS_CC)) {
RETURN_FALSE;
}
- if (mmc_flush(mmc TSRMLS_CC) > 0) {
- RETURN_TRUE;
+ for (i=0; i<pool->num_servers; i++) {
+ if (!mmc_open(pool->servers[i], 1, NULL, NULL) || mmc_flush(pool->servers[i] \
TSRMLS_CC) < 0) { + if (mmc_server_failure(pool->servers[i])) {
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "marked server '%s:%d' as failed", \
pool->servers[i]->host, pool->servers[i]->port); + }
+ failures++;
+ }
}
- RETURN_FALSE;
+
+ if (failures && failures >= pool->num_servers) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to flush servers");
+ RETURN_FALSE;
+ }
+ RETURN_TRUE;
}
/* }}} */
http://cvs.php.net/diff.php/pecl/memcache/php_memcache.h?r1=1.8&r2=1.9&ty=u
Index: pecl/memcache/php_memcache.h
diff -u pecl/memcache/php_memcache.h:1.8 pecl/memcache/php_memcache.h:1.9
--- pecl/memcache/php_memcache.h:1.8 Wed Apr 7 08:58:05 2004
+++ pecl/memcache/php_memcache.h Sat Oct 29 10:24:49 2005
@@ -16,7 +16,7 @@
+----------------------------------------------------------------------+
*/
-/* $Id: php_memcache.h,v 1.8 2004/04/07 12:58:05 tony2001 Exp $ */
+/* $Id: php_memcache.h,v 1.9 2005/10/29 14:24:49 mikl Exp $ */
#ifndef PHP_MEMCACHE_H
#define PHP_MEMCACHE_H
@@ -42,6 +42,7 @@
PHP_FUNCTION(memcache_connect);
PHP_FUNCTION(memcache_pconnect);
+PHP_FUNCTION(memcache_add_server);
PHP_FUNCTION(memcache_get_version);
PHP_FUNCTION(memcache_add);
PHP_FUNCTION(memcache_set);
@@ -50,6 +51,8 @@
PHP_FUNCTION(memcache_delete);
PHP_FUNCTION(memcache_debug);
PHP_FUNCTION(memcache_get_stats);
+PHP_FUNCTION(memcache_get_extended_stats);
+PHP_FUNCTION(memcache_set_compress_threshold);
PHP_FUNCTION(memcache_increment);
PHP_FUNCTION(memcache_decrement);
PHP_FUNCTION(memcache_close);
@@ -61,15 +64,38 @@
#define MMC_DEFAULT_TIMEOUT 1 /* seconds */
#define MMC_KEY_MAX_SIZE 250 /* stoled from memcached sources =) */
#define MMC_DEFAULT_PORT 11211
+#define MMC_DEFAULT_RETRY 15 /* retry failed server after x seconds */
+#define MMC_DEFAULT_SAVINGS 0.2 /* minimum 20% savings for compression to be used \
*/ +
+#define MMC_STATUS_DISCONNECTED 1
+#define MMC_STATUS_CONNECTED 2
+#define MMC_STATUS_UNKNOWN 3
+#define MMC_STATUS_FAILED 4
typedef struct mmc {
- int id;
php_stream *stream;
char inbuf[MMC_BUF_SIZE];
+ smart_str outbuf;
+ char *host;
+ unsigned short port;
long timeout;
+ long retry;
+ unsigned int retry_interval;
int persistent;
+ int status;
} mmc_t;
+typedef struct mmc_pool {
+ int id;
+ mmc_t **servers;
+ int num_servers;
+ mmc_t **buckets;
+ int num_buckets;
+ mmc_t **requests;
+ int compress_threshold;
+ double min_compress_savings;
+} mmc_pool_t;
+
/* our globals */
ZEND_BEGIN_MODULE_GLOBALS(memcache)
long debug_mode;
http://cvs.php.net/diff.php/pecl/memcache/tests/connect.inc?r1=1.1&r2=1.2&ty=u
Index: pecl/memcache/tests/connect.inc
diff -u pecl/memcache/tests/connect.inc:1.1 pecl/memcache/tests/connect.inc:1.2
--- pecl/memcache/tests/connect.inc:1.1 Wed Mar 17 04:34:56 2004
+++ pecl/memcache/tests/connect.inc Sat Oct 29 10:24:50 2005
@@ -7,6 +7,13 @@
$host = "localhost";
$port = 11211;
+// Start an extra server and uncomment to enable load-balancing and failover tests
+//$host2 = "localhost";
+//$port2 = 11212;
+
+$nonExistingHost = "localhost";
+$nonExistingPort = 11213;
+
$memcache = memcache_connect($host, $port);
if (!$memcache) {
http://cvs.php.net/co.php/pecl/memcache/tests/018.phpt?r=1.1&p=1
Index: pecl/memcache/tests/018.phpt
+++ pecl/memcache/tests/018.phpt
--TEST--
memcache_set() & memcache_add()
--SKIPIF--
<?php if(!extension_loaded("memcache")) print "skip"; ?>
--FILE--
<?php
include 'connect.inc';
$var = 'test';
error_reporting(E_ALL);
$result1 = memcache_set($memcache, 'non_existing_test_key', $var, false, 1);
$result2 = memcache_add($memcache, 'non_existing_test_key', $var, false, 1);
var_dump($result1);
var_dump($result2);
?>
--EXPECT--
bool(true)
bool(false)
http://cvs.php.net/co.php/pecl/memcache/tests/019.phpt?r=1.1&p=1
Index: pecl/memcache/tests/019.phpt
+++ pecl/memcache/tests/019.phpt
--TEST--
memcache->addServer()
--SKIPIF--
<?php include 'connect.inc'; if(!extension_loaded("memcache") || !isset($host2)) \
print "skip"; ?>
--FILE--
<?php
include 'connect.inc';
$var = 'test';
$memcache = new Memcache();
$memcache->addServer($host, $port);
$memcache->addServer($host2, $port2, true, 1, 1, 15);
$result1 = $memcache->set('non_existing_test_key', $var, false, 1);
$result2 = $memcache->get('non_existing_test_key');
var_dump($result1);
var_dump($result2);
?>
--EXPECT--
bool(true)
string(4) "test"
http://cvs.php.net/co.php/pecl/memcache/tests/020.phpt?r=1.1&p=1
Index: pecl/memcache/tests/020.phpt
+++ pecl/memcache/tests/020.phpt
--TEST--
memcache->set()/memcache->get() with multiple keys and load balancing
--SKIPIF--
<?php include 'connect.inc'; if(!extension_loaded("memcache") || !isset($host2)) \
print "skip"; ?>
--FILE--
<?php
include 'connect.inc';
$var1 = 'test1';
$var2 = 'test2';
$memcache = new Memcache();
$memcache->addServer($host, $port);
$memcache->addServer($host2, $port2);
$memcache1 = memcache_connect($host, $port, 1);
$memcache2 = memcache_pconnect($host2, $port2, 1);
$result1 = $memcache->set('load_test_key1', $var1, false, 1);
$result2 = $memcache->set('load_test_key2', $var2, false, 1);
$result3 = $memcache->get(array('load_test_key1','load_test_key2'));
var_dump($result1);
var_dump($result2);
var_dump($result3);
$result4 = $memcache1->get('load_test_key1');
$result5 = $memcache1->get('load_test_key2');
$result6 = $memcache2->get('load_test_key1');
$result7 = $memcache2->get('load_test_key2');
var_dump($result4);
var_dump($result5);
var_dump($result6);
var_dump($result7);
?>
--EXPECT--
bool(true)
bool(true)
array(2) {
["load_test_key1"]=>
string(5) "test1"
["load_test_key2"]=>
string(5) "test2"
}
bool(false)
string(5) "test2"
string(5) "test1"
bool(false)
http://cvs.php.net/co.php/pecl/memcache/tests/021.phpt?r=1.1&p=1
Index: pecl/memcache/tests/021.phpt
+++ pecl/memcache/tests/021.phpt
--TEST--
memcache->set()/memcache->get() with failover
--SKIPIF--
<?php if(!extension_loaded("memcache")) print "skip"; ?>
--FILE--
<?php
include 'connect.inc';
$var1 = 'test1';
$var2 = 'test2';
$memcache = new Memcache();
$memcache->addServer($host, $port);
$memcache->addServer($nonExistingHost, $nonExistingPort);
$result1 = $memcache->set('load_test_key1', $var1, false, 1);
$result2 = $memcache->set('load_test_key2', $var2, false, 1);
$result3 = $memcache->get('load_test_key1');
$result4 = $memcache->get(array('load_test_key1','load_test_key2'));
var_dump($result1);
var_dump($result2);
var_dump($result3);
var_dump($result4);
$memcache = new Memcache();
$memcache->addServer($nonExistingHost, $nonExistingPort);
$result5 = @$memcache->set('load_test_key1', $var1, false, 1);
$result6 = @$memcache->get('load_test_key1');
var_dump($result5);
var_dump($result6);
?>
--EXPECT--
bool(true)
bool(true)
string(5) "test1"
array(2) {
["load_test_key1"]=>
string(5) "test1"
["load_test_key2"]=>
string(5) "test2"
}
bool(false)
bool(false)
http://cvs.php.net/co.php/pecl/memcache/tests/022.phpt?r=1.1&p=1
Index: pecl/memcache/tests/022.phpt
+++ pecl/memcache/tests/022.phpt
--TEST--
memcache->getExtendedStats()
--SKIPIF--
<?php include 'connect.inc'; if(!extension_loaded("memcache") || !isset($host2)) \
print "skip"; ?>
--FILE--
<?php
include 'connect.inc';
$memcache = new Memcache();
$memcache->addServer($nonExistingHost, $nonExistingPort);
$memcache->addServer($host, $port);
$memcache->addServer($host2, $port2);
$result1 = @$memcache->getStats();
$result2 = @$memcache->getExtendedStats();
var_dump(count($result1));
var_dump(count($result2));
var_dump(count($result2["$host:$port"]));
var_dump(count($result2["$host2:$port2"]));
var_dump($result2["$nonExistingHost:$nonExistingPort"]);
?>
--EXPECT--
int(19)
int(3)
int(19)
int(19)
bool(false)
http://cvs.php.net/co.php/pecl/memcache/tests/023.phpt?r=1.1&p=1
Index: pecl/memcache/tests/023.phpt
+++ pecl/memcache/tests/023.phpt
--TEST--
memcache->delete() with load balancing
--SKIPIF--
<?php include 'connect.inc'; if(!extension_loaded("memcache") || !isset($host2)) \
print "skip"; ?>
--FILE--
<?php
include 'connect.inc';
$var1 = 'test1';
$var2 = 'test2';
$memcache = new Memcache();
$memcache->addServer($host, $port);
$memcache->addServer($host2, $port2);
$memcache1 = memcache_connect($host, $port);
$memcache2 = memcache_connect($host2, $port2);
$result1 = $memcache->set('delete_test_key1', $var1, false, 1);
$result2 = $memcache->set('delete_test_key2', $var2, false, 1);
$result3 = $memcache->get('delete_test_key1');
$result4 = $memcache->get('delete_test_key2');
var_dump($result1);
var_dump($result2);
var_dump($result3);
var_dump($result4);
$result5 = $memcache1->get('delete_test_key1');
$result6 = $memcache1->get('delete_test_key2');
$result7 = $memcache2->get('delete_test_key1');
$result8 = $memcache2->get('delete_test_key2');
var_dump($result5);
var_dump($result6);
var_dump($result7);
var_dump($result8);
$result9 = $memcache->delete('delete_test_key1');
$result10 = $memcache->get('delete_test_key1');
$result11 = $memcache2->get('delete_test_key1');
var_dump($result9);
var_dump($result10);
var_dump($result11);
$result12 = $memcache->delete('delete_test_key2');
$result13 = $memcache->get('delete_test_key2');
$result14 = $memcache1->get('delete_test_key2');
var_dump($result12);
var_dump($result13);
var_dump($result14);
?>
--EXPECT--
bool(true)
bool(true)
string(5) "test1"
string(5) "test2"
string(5) "test1"
bool(false)
bool(false)
string(5) "test2"
bool(true)
bool(false)
bool(false)
bool(true)
bool(false)
bool(false)
http://cvs.php.net/co.php/pecl/memcache/tests/024.phpt?r=1.1&p=1
Index: pecl/memcache/tests/024.phpt
+++ pecl/memcache/tests/024.phpt
--TEST--
memcache->close(), memcache->get()
--SKIPIF--
<?php include 'connect.inc'; if(!extension_loaded("memcache") || !isset($host2)) \
print "skip"; ?>
--FILE--
<?php
include 'connect.inc';
$memcache->addServer($host2, $port2);
$memcache->addServer($nonExistingHost, $nonExistingPort);
$result1 = $memcache->close();
var_dump($result1);
$memcache = new Memcache();
$result2 = $memcache->connect($host, $port);
$result3 = $memcache->set('non_existing_test_key', 'test', false, 1);
$result4 = $memcache->close();
// This additional get() will transparently reconnect
$result5 = $memcache->get('non_existing_test_key');
var_dump($result2);
var_dump($result3);
var_dump($result4);
var_dump($result5);
?>
--EXPECT--
bool(true)
bool(true)
bool(true)
bool(true)
string(4) "test"
http://cvs.php.net/co.php/pecl/memcache/tests/025.phpt?r=1.1&p=1
Index: pecl/memcache/tests/025.phpt
+++ pecl/memcache/tests/025.phpt
--TEST--
memcache->increment() with load balancing
--SKIPIF--
<?php include 'connect.inc'; if(!extension_loaded("memcache") || !isset($host2)) \
print "skip"; ?>
--FILE--
<?php
include 'connect.inc';
$var1 = 10;
$var2 = 20;
$memcache = new Memcache();
$memcache->addServer($host, $port);
$memcache->addServer($host2, $port2);
$memcache1 = memcache_connect($host, $port);
$memcache2 = memcache_connect($host2, $port2);
$result1 = $memcache->set('increment_test_key1', $var1, false, 1);
$result2 = $memcache->set('increment_test_key2', $var2, false, 1);
$result3 = $memcache->increment('increment_test_key1');
$result4 = $memcache->increment('increment_test_key2');
var_dump($result1);
var_dump($result2);
var_dump($result3);
var_dump($result4);
$result5 = $memcache1->get('increment_test_key1');
$result6 = $memcache1->get('increment_test_key2');
$result7 = $memcache2->get('increment_test_key1');
$result8 = $memcache2->get('increment_test_key2');
var_dump($result5);
var_dump($result6);
var_dump($result7);
var_dump($result8);
?>
--EXPECT--
bool(true)
bool(true)
int(11)
int(21)
bool(false)
string(2) "21"
string(2) "11"
bool(false)
http://cvs.php.net/co.php/pecl/memcache/tests/026.phpt?r=1.1&p=1
Index: pecl/memcache/tests/026.phpt
+++ pecl/memcache/tests/026.phpt
--TEST--
memcache->delete() with load balancing
--SKIPIF--
<?php include 'connect.inc'; if(!extension_loaded("memcache") || !isset($host2)) \
print "skip"; ?>
--FILE--
<?php
include 'connect.inc';
$var = 'test';
$memcache->addServer($host2, $port2);
$result1 = $memcache->set('delete_fail_key1', $var, false, 1);
$result2 = $memcache->delete('delete_fail_key1');
$result3 = $memcache->delete('delete_fail_key2');
var_dump($result1);
var_dump($result2);
var_dump($result3);
$memcache = new Memcache();
$memcache->addServer($nonExistingHost, $nonExistingPort);
$result4 = @$memcache->delete('delete_fail_key1');
var_dump($result4);
?>
--EXPECT--
bool(true)
bool(true)
bool(false)
bool(false)
http://cvs.php.net/co.php/pecl/memcache/tests/027.phpt?r=1.1&p=1
Index: pecl/memcache/tests/027.phpt
+++ pecl/memcache/tests/027.phpt
--TEST--
memcache->setCompressThreshold()
--SKIPIF--
<?php if(!extension_loaded("memcache")) print "skip"; ?>
--FILE--
<?php
include 'connect.inc';
$var = str_repeat('abc', 5000);
$memcache->setCompressThreshold(10000);
$result1 = $memcache->set('non_existing_test_key', $var, 0, 1);
$result2 = $memcache->get('non_existing_test_key');
var_dump($result1);
var_dump(strlen($result2));
$memcache->setCompressThreshold(10000, 0);
$result3 = $memcache->set('non_existing_test_key', $var, 0, 1);
$memcache->setCompressThreshold(10000, 1);
$result4 = $memcache->set('non_existing_test_key', $var, 0, 1);
var_dump($result3);
var_dump($result4);
$result5 = $memcache->set('non_existing_test_key', 'abc', MEMCACHE_COMPRESSED, 1);
$result6 = $memcache->get('non_existing_test_key');
var_dump($result5);
var_dump($result6);
?>
--EXPECT--
bool(true)
int(15000)
bool(true)
bool(true)
bool(true)
string(3) "abc"
http://cvs.php.net/co.php/pecl/memcache/tests/028.phpt?r=1.1&p=1
Index: pecl/memcache/tests/028.phpt
+++ pecl/memcache/tests/028.phpt
--TEST--
memcache->flush()
--SKIPIF--
<?php if(!extension_loaded("memcache")) print "skip"; ?>
--FILE--
<?php
// This test must be run last or some concurrency problems will occur
// since the "flush_all" seems to be done async and therefore will
// affect subsequent calls to set() done with a second or so.
include 'connect.inc';
$memcache->addServer($nonExistingHost, $nonExistingPort);
$result1 = @$memcache->flush();
var_dump($result1);
$memcache2 = new Memcache();
$memcache2->addServer($nonExistingHost, $nonExistingPort);
$result2 = @$memcache2->flush();
var_dump($result2);
?>
--EXPECT--
bool(true)
bool(false)
--
PECL CVS Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic