From ad692a4f28f031ff02385f1985e45f0b17a39d87 Mon Sep 17 00:00:00 2001 From: Trond Norbye Date: Fri, 23 Nov 2012 16:30:11 +0100 Subject: [PATCH] PCBC-83 - Add persist_to and replicate_to This replace the observe parameter for the object oriented variants for add, set, replace, cas, setMulti and delete Change-Id: Idcba1502371de680c4212065d56cfb7272ca0460 Reviewed-on: http://review.couchbase.org/22712 Tested-by: Trond Norbye Reviewed-by: Matt Ingenthron --- apidecl.c | 62 +- apidecl.h | 8 +- config.m4 | 1 + config.w32 | 1 + exceptions.c | 19 + exceptions.h | 12 + internal.h | 1 + php_couchbase.h | 2 +- remove.c | 205 ++++-- simple_observe.c | 207 ++++++ simple_observe.h | 59 ++ store.c | 634 +++++++++++++++---- tests/AppendPrepend.inc | 11 + tests/Delete.inc | 16 +- tests/Get.inc | 7 +- tests/Replace.inc | 4 + tests/Store.inc | 75 ++- tests/Sync.inc | 260 ++++++++ tests/TEST_CLASSES | 1 + tests/phpt/Store/CasSingleOO.phpt | 14 + tests/phpt/Sync/IllegalNumberOfReplicas.phpt | 14 + tests/phpt/Sync/addOnePersist.phpt | 14 + tests/phpt/Sync/appendOnePersist.phpt | 14 + tests/phpt/Sync/casOnePersist.phpt | 14 + tests/phpt/Sync/prependOnePersist.phpt | 14 + tests/phpt/Sync/removeOnePersist.phpt | 14 + tests/phpt/Sync/replaceOnePersist.phpt | 14 + tests/phpt/Sync/setOnePersist.phpt | 14 + touch.c | 48 +- 29 files changed, 1483 insertions(+), 276 deletions(-) create mode 100644 simple_observe.c create mode 100644 simple_observe.h create mode 100644 tests/Sync.inc create mode 100644 tests/phpt/Store/CasSingleOO.phpt create mode 100644 tests/phpt/Sync/IllegalNumberOfReplicas.phpt create mode 100644 tests/phpt/Sync/addOnePersist.phpt create mode 100644 tests/phpt/Sync/appendOnePersist.phpt create mode 100644 tests/phpt/Sync/casOnePersist.phpt create mode 100644 tests/phpt/Sync/prependOnePersist.phpt create mode 100644 tests/phpt/Sync/removeOnePersist.phpt create mode 100644 tests/phpt/Sync/replaceOnePersist.phpt create mode 100644 tests/phpt/Sync/setOnePersist.phpt diff --git a/apidecl.c b/apidecl.c index 2c9d050..0a32b16 100644 --- a/apidecl.c +++ b/apidecl.c @@ -42,7 +42,6 @@ ZEND_ARG_INFO(0, resource) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) -ZEND_ARG_ARRAY_INFO(0, durability, 0) ZEND_END_ARG_INFO() COUCHBASE_ARG_PREFIX @@ -52,7 +51,6 @@ ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) ZEND_ARG_INFO(0, cas) -ZEND_ARG_ARRAY_INFO(0, durability, 0) ZEND_END_ARG_INFO() COUCHBASE_ARG_PREFIX @@ -60,7 +58,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_set_multi, 0, 0, 2) ZEND_ARG_INFO(0, resource) ZEND_ARG_ARRAY_INFO(0, values, 0) ZEND_ARG_INFO(0, expiration) -ZEND_ARG_ARRAY_INFO(0, durability, 0) ZEND_END_ARG_INFO() COUCHBASE_ARG_PREFIX @@ -70,7 +67,6 @@ ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) ZEND_ARG_INFO(0, cas) -ZEND_ARG_ARRAY_INFO(0, durability, 0) ZEND_END_ARG_INFO() COUCHBASE_ARG_PREFIX @@ -80,7 +76,6 @@ ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) ZEND_ARG_INFO(0, cas) -ZEND_ARG_ARRAY_INFO(0, durability, 0) ZEND_END_ARG_INFO() COUCHBASE_ARG_PREFIX @@ -90,7 +85,6 @@ ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) ZEND_ARG_INFO(0, cas) -ZEND_ARG_ARRAY_INFO(0, durability, 0) ZEND_END_ARG_INFO() COUCHBASE_ARG_PREFIX @@ -100,7 +94,6 @@ ZEND_ARG_INFO(0, cas) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) -ZEND_ARG_ARRAY_INFO(0, durability, 0) ZEND_END_ARG_INFO() COUCHBASE_ARG_PREFIX @@ -138,7 +131,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_touch, 0, 0, 3) ZEND_ARG_INFO(0, resource) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, expiry) -ZEND_ARG_ARRAY_INFO(0, durability, 0) ZEND_END_ARG_INFO() COUCHBASE_ARG_PREFIX @@ -146,7 +138,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_touch_multi, 0, 0, 3) ZEND_ARG_INFO(0, resource) ZEND_ARG_ARRAY_INFO(0, keys, 0) ZEND_ARG_INFO(0, expiry) -ZEND_ARG_ARRAY_INFO(0, durability, 0) ZEND_END_ARG_INFO() COUCHBASE_ARG_PREFIX @@ -185,7 +176,6 @@ ZEND_ARG_INFO(0, offset) ZEND_ARG_INFO(0, create) ZEND_ARG_INFO(0, expiration) ZEND_ARG_INFO(0, initial) -ZEND_ARG_ARRAY_INFO(0, durability, 0) ZEND_END_ARG_INFO() COUCHBASE_ARG_PREFIX @@ -196,7 +186,6 @@ ZEND_ARG_INFO(0, offset) ZEND_ARG_INFO(0, create) ZEND_ARG_INFO(0, expiration) ZEND_ARG_INFO(0, initial) -ZEND_ARG_ARRAY_INFO(0, durability, 0) ZEND_END_ARG_INFO() COUCHBASE_ARG_PREFIX @@ -204,7 +193,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_delete, 0, 0, 2) ZEND_ARG_INFO(0, resource) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, cas) -ZEND_ARG_ARRAY_INFO(0, durability, 0) ZEND_END_ARG_INFO() COUCHBASE_ARG_PREFIX @@ -318,7 +306,8 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_m_add, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) -ZEND_ARG_ARRAY_INFO(0, durability, 0) +ZEND_ARG_INFO(0, persist_to) +ZEND_ARG_INFO(0, replicate_to) ZEND_END_ARG_INFO() COUCHBASE_ARG_PREFIX @@ -327,14 +316,16 @@ ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) ZEND_ARG_INFO(0, cas) -ZEND_ARG_ARRAY_INFO(0, durability, 0) +ZEND_ARG_INFO(0, persist_to) +ZEND_ARG_INFO(0, replicate_to) ZEND_END_ARG_INFO() COUCHBASE_ARG_PREFIX ZEND_BEGIN_ARG_INFO_EX(arginfo_m_setmulti, 0, 0, 1) ZEND_ARG_ARRAY_INFO(0, values, 0) ZEND_ARG_INFO(0, expiration) -ZEND_ARG_ARRAY_INFO(0, durability, 0) +ZEND_ARG_INFO(0, persist_to) +ZEND_ARG_INFO(0, replicate_to) ZEND_END_ARG_INFO() COUCHBASE_ARG_PREFIX @@ -343,7 +334,8 @@ ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) ZEND_ARG_INFO(0, cas) -ZEND_ARG_ARRAY_INFO(0, durability, 0) +ZEND_ARG_INFO(0, persist_to) +ZEND_ARG_INFO(0, replicate_to) ZEND_END_ARG_INFO() COUCHBASE_ARG_PREFIX @@ -352,7 +344,8 @@ ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) ZEND_ARG_INFO(0, cas) -ZEND_ARG_ARRAY_INFO(0, durability, 0) +ZEND_ARG_INFO(0, persist_to) +ZEND_ARG_INFO(0, replicate_to) ZEND_END_ARG_INFO() COUCHBASE_ARG_PREFIX @@ -361,7 +354,8 @@ ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) ZEND_ARG_INFO(0, cas) -ZEND_ARG_ARRAY_INFO(0, durability, 0) +ZEND_ARG_INFO(0, persist_to) +ZEND_ARG_INFO(0, replicate_to) ZEND_END_ARG_INFO() COUCHBASE_ARG_PREFIX @@ -370,7 +364,6 @@ ZEND_ARG_INFO(0, cas) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) -ZEND_ARG_ARRAY_INFO(0, durability, 0) ZEND_END_ARG_INFO() COUCHBASE_ARG_PREFIX @@ -404,14 +397,12 @@ COUCHBASE_ARG_PREFIX ZEND_BEGIN_ARG_INFO_EX(arginfo_m_touch, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, expiry) -ZEND_ARG_ARRAY_INFO(0, durability, 0) ZEND_END_ARG_INFO() COUCHBASE_ARG_PREFIX ZEND_BEGIN_ARG_INFO_EX(arginfo_m_touchmulti, 0, 0, 2) ZEND_ARG_ARRAY_INFO(0, keys, 0) ZEND_ARG_INFO(0, expiry) -ZEND_ARG_ARRAY_INFO(0, durability, 0) ZEND_END_ARG_INFO() COUCHBASE_ARG_PREFIX @@ -446,7 +437,6 @@ ZEND_ARG_INFO(0, offset) ZEND_ARG_INFO(0, create) ZEND_ARG_INFO(0, expiration) ZEND_ARG_INFO(0, initial) -ZEND_ARG_ARRAY_INFO(0, durability, 0) ZEND_END_ARG_INFO() COUCHBASE_ARG_PREFIX @@ -456,14 +446,14 @@ ZEND_ARG_INFO(0, offset) ZEND_ARG_INFO(0, create) ZEND_ARG_INFO(0, expiration) ZEND_ARG_INFO(0, initial) -ZEND_ARG_ARRAY_INFO(0, durability, 0) ZEND_END_ARG_INFO() COUCHBASE_ARG_PREFIX ZEND_BEGIN_ARG_INFO_EX(arginfo_m_delete, 0, 0, 1) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, cas) -ZEND_ARG_ARRAY_INFO(0, durability, 0) +ZEND_ARG_INFO(0, persist_to) +ZEND_ARG_INFO(0, replicate_to) ZEND_END_ARG_INFO() COUCHBASE_ARG_PREFIX @@ -682,7 +672,7 @@ PHP_METHOD(couchbase, cas) */ PHP_METHOD(couchbase, add) { - php_couchbase_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, LCB_ADD, 0, 1); + php_couchbase_store_impl_oo(INTERNAL_FUNCTION_PARAM_PASSTHRU, LCB_ADD); } /* }}} */ @@ -690,7 +680,7 @@ PHP_METHOD(couchbase, add) */ PHP_METHOD(couchbase, set) { - php_couchbase_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, LCB_SET, 0, 1); + php_couchbase_store_impl_oo(INTERNAL_FUNCTION_PARAM_PASSTHRU, LCB_SET); } /* }}} */ @@ -698,7 +688,7 @@ PHP_METHOD(couchbase, set) */ PHP_METHOD(couchbase, setMulti) { - php_couchbase_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, LCB_SET, 1, 1); + php_couchbase_store_multi_impl_oo(INTERNAL_FUNCTION_PARAM_PASSTHRU); } /* }}} */ @@ -706,7 +696,7 @@ PHP_METHOD(couchbase, setMulti) */ PHP_METHOD(couchbase, prepend) { - php_couchbase_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, LCB_PREPEND, 0, 1); + php_couchbase_store_impl_oo(INTERNAL_FUNCTION_PARAM_PASSTHRU, LCB_PREPEND); } /* }}} */ @@ -714,7 +704,7 @@ PHP_METHOD(couchbase, prepend) */ PHP_METHOD(couchbase, append) { - php_couchbase_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, LCB_APPEND, 0, 1); + php_couchbase_store_impl_oo(INTERNAL_FUNCTION_PARAM_PASSTHRU, LCB_APPEND); } /* }}} */ @@ -722,7 +712,7 @@ PHP_METHOD(couchbase, append) */ PHP_METHOD(couchbase, replace) { - php_couchbase_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, LCB_REPLACE, 0, 1); + php_couchbase_store_impl_oo(INTERNAL_FUNCTION_PARAM_PASSTHRU, LCB_REPLACE); } /* }}} */ @@ -969,7 +959,7 @@ PHP_FUNCTION(couchbase_cas) */ PHP_FUNCTION(couchbase_add) { - php_couchbase_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, LCB_ADD, 0, 0); + php_couchbase_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, LCB_ADD, 0); } /* }}} */ @@ -977,7 +967,7 @@ PHP_FUNCTION(couchbase_add) */ PHP_FUNCTION(couchbase_set) { - php_couchbase_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, LCB_SET, 0, 0); + php_couchbase_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, LCB_SET, 0); } /* }}} */ @@ -985,7 +975,7 @@ PHP_FUNCTION(couchbase_set) */ PHP_FUNCTION(couchbase_set_multi) { - php_couchbase_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, LCB_SET, 1, 0); + php_couchbase_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, LCB_SET, 1); } /* }}} */ @@ -993,7 +983,7 @@ PHP_FUNCTION(couchbase_set_multi) */ PHP_FUNCTION(couchbase_prepend) { - php_couchbase_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, LCB_PREPEND, 0, 0); + php_couchbase_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, LCB_PREPEND, 0); } /* }}} */ @@ -1001,7 +991,7 @@ PHP_FUNCTION(couchbase_prepend) */ PHP_FUNCTION(couchbase_append) { - php_couchbase_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, LCB_APPEND, 0, 0); + php_couchbase_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, LCB_APPEND, 0); } /* }}} */ @@ -1009,7 +999,7 @@ PHP_FUNCTION(couchbase_append) */ PHP_FUNCTION(couchbase_replace) { - php_couchbase_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, LCB_REPLACE, 0, 0); + php_couchbase_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, LCB_REPLACE, 0); } /* }}} */ diff --git a/apidecl.h b/apidecl.h index fe0e527..03bc470 100644 --- a/apidecl.h +++ b/apidecl.h @@ -54,7 +54,13 @@ PHP_COUCHBASE_LOCAL void php_couchbase_fetch_impl(INTERNAL_FUNCTION_PARAMETERS, int multi, int oo); PHP_COUCHBASE_LOCAL -void php_couchbase_store_impl(INTERNAL_FUNCTION_PARAMETERS, lcb_storage_t op, int multi, int oo); +void php_couchbase_store_impl(INTERNAL_FUNCTION_PARAMETERS, lcb_storage_t op, int multi); + +PHP_COUCHBASE_LOCAL +void php_couchbase_store_multi_impl_oo(INTERNAL_FUNCTION_PARAMETERS); + +PHP_COUCHBASE_LOCAL +void php_couchbase_store_impl_oo(INTERNAL_FUNCTION_PARAMETERS, lcb_storage_t op); PHP_COUCHBASE_LOCAL void php_couchbase_remove_impl(INTERNAL_FUNCTION_PARAMETERS, int oo); diff --git a/config.m4 b/config.m4 index 0c8be99..074fbcf 100644 --- a/config.m4 +++ b/config.m4 @@ -187,6 +187,7 @@ if test "$PHP_COUCHBASE" != "no"; then views.c \ convert.c \ get.c \ + simple_observe.c \ store.c \ arithmetic.c \ remove.c \ diff --git a/config.w32 b/config.w32 index 5894309..a85d036 100644 --- a/config.w32 +++ b/config.w32 @@ -20,6 +20,7 @@ if (PHP_COUCHBASE != "no") { ADD_SOURCES(configure_module_dirname, "observe.c", "couchbase"); ADD_SOURCES(configure_module_dirname, "remove.c", "couchbase"); ADD_SOURCES(configure_module_dirname, "resmgr.c", "couchbase"); + ADD_SOURCES(configure_module_dirname, "simple_observe.c", "couchbase"); ADD_SOURCES(configure_module_dirname, "store.c", "couchbase"); ADD_SOURCES(configure_module_dirname, "timeout.c", "couchbase"); ADD_SOURCES(configure_module_dirname, "touch.c", "couchbase"); diff --git a/exceptions.c b/exceptions.c index ef7f415..f1f07c5 100644 --- a/exceptions.c +++ b/exceptions.c @@ -26,6 +26,9 @@ zend_class_entry *cb_exception; PHP_COUCHBASE_LOCAL zend_class_entry *cb_illegal_key_exception; +PHP_COUCHBASE_LOCAL +zend_class_entry *cb_no_such_key_exception; + PHP_COUCHBASE_LOCAL zend_class_entry *cb_auth_exception; @@ -35,6 +38,15 @@ zend_class_entry *cb_lcb_exception; PHP_COUCHBASE_LOCAL zend_class_entry *cb_server_exception; +PHP_COUCHBASE_LOCAL +zend_class_entry *cb_key_mutated_exception; + +PHP_COUCHBASE_LOCAL +zend_class_entry *cb_timeout_exception; + +PHP_COUCHBASE_LOCAL +zend_class_entry *cb_not_enough_nodes_exception; + #define setup(var, name, parent) \ do { \ zend_class_entry cbe; \ @@ -57,9 +69,16 @@ void init_couchbase_exceptions(TSRMLS_D) setup(cb_exception, "CouchbaseException", root); setup(cb_illegal_key_exception, "CouchbaseIllegalKeyException", cb_exception); + setup(cb_no_such_key_exception, "CouchbaseNoSuchKeyException", + cb_exception); setup(cb_auth_exception, "CouchbaseAuthenticationException", cb_exception); setup(cb_lcb_exception, "CouchbaseLibcouchbaseException", cb_exception); setup(cb_server_exception, "CouchbaseServerException", cb_exception); + setup(cb_key_mutated_exception, "CouchbaseKeyMutatedException", + cb_exception); + setup(cb_timeout_exception, "CouchbaseTimeoutException", cb_exception); + setup(cb_not_enough_nodes_exception, "CouchbaseNotEnoughNodesException", + cb_exception); } /* diff --git a/exceptions.h b/exceptions.h index d08aafc..48d7a49 100644 --- a/exceptions.h +++ b/exceptions.h @@ -30,6 +30,9 @@ extern zend_class_entry *cb_exception; PHP_COUCHBASE_LOCAL extern zend_class_entry *cb_illegal_key_exception; +PHP_COUCHBASE_LOCAL +extern zend_class_entry *cb_no_such_key_exception; + PHP_COUCHBASE_LOCAL extern zend_class_entry *cb_auth_exception; @@ -39,6 +42,15 @@ extern zend_class_entry *cb_lcb_exception; PHP_COUCHBASE_LOCAL extern zend_class_entry *cb_server_exception; +PHP_COUCHBASE_LOCAL +extern zend_class_entry *cb_key_mutated_exception; + +PHP_COUCHBASE_LOCAL +extern zend_class_entry *cb_timeout_exception; + +PHP_COUCHBASE_LOCAL +extern zend_class_entry *cb_not_enough_nodes_exception; + #endif /* MANAGEMENT_EXCEPTION_H */ /* diff --git a/internal.h b/internal.h index 664f86d..701bd8b 100644 --- a/internal.h +++ b/internal.h @@ -70,6 +70,7 @@ #include "management/cluster.h" #include "exceptions.h" #include "apidecl.h" +#include "simple_observe.h" #endif diff --git a/php_couchbase.h b/php_couchbase.h index ec39614..9c8f17b 100644 --- a/php_couchbase.h +++ b/php_couchbase.h @@ -224,7 +224,7 @@ PHP_FUNCTION(couchbase_set_timeout); #define PCBC_INIDEFL_OBS_INTERVAL "100000" #define PCBC_INIENT_OBS_TIMEOUT "couchbase.durability_default_timeout" -#define PCBC_INIDEFL_OBS_TIMEOUT "4000000" +#define PCBC_INIDEFL_OBS_TIMEOUT "40000000" #define PCBC_INIENT_SERIALIZER "couchbase.serializer" #define PCBC_INIDEFL_SERIALIZER "php" diff --git a/remove.c b/remove.c index 5d0715e..3a613b2 100644 --- a/remove.c +++ b/remove.c @@ -1,95 +1,164 @@ #include "internal.h" +struct remove_cookie { + lcb_error_t error; + uint64_t cas; +}; + /* {{{ static void php_couchbase_remove_callback(...) */ -static void -php_couchbase_remove_callback(lcb_t instance, - const void *cookie, - lcb_error_t error, - const lcb_remove_resp_t *resp) +static void php_couchbase_remove_callback(lcb_t instance, + const void *cookie, + lcb_error_t error, + const lcb_remove_resp_t *resp) { - php_couchbase_ctx *ctx = (php_couchbase_ctx *)cookie; - php_ignore_value(instance); - php_ignore_value(resp); + struct remove_cookie *rmc = (struct remove_cookie *)cookie; + rmc->error = error; + rmc->cas = resp->v.v0.cas; + if (resp->version != 0) { + rmc->error = LCB_ERROR; + } +} +/* }}} */ - if (--ctx->res->seqno == 0) { - pcbc_stop_loop(ctx->res); +static lcb_error_t do_remove(lcb_t instance, const void *key, uint16_t klen, + lcb_cas_t *cas) +{ + lcb_remove_cmd_t cmd; + const lcb_remove_cmd_t *const commands[] = { &cmd }; + struct remove_cookie rmc; + lcb_error_t retval; + + memset(&cmd, 0, sizeof(cmd)); + memset(&rmc, 0, sizeof(rmc)); + cmd.v.v0.key = key; + cmd.v.v0.nkey = klen; + cmd.v.v0.cas = *cas; + + lcb_behavior_set_syncmode(instance, LCB_SYNCHRONOUS); + retval = lcb_remove(instance, &rmc, 1, commands); + lcb_behavior_set_syncmode(instance, LCB_ASYNCHRONOUS); + + if (retval == LCB_SUCCESS && rmc.error == LCB_SUCCESS) { + *cas = rmc.cas; } - ctx->res->rc = error; + return (retval == LCB_SUCCESS) ? rmc.error : retval; } -/* }}} */ PHP_COUCHBASE_LOCAL void php_couchbase_remove_impl(INTERNAL_FUNCTION_PARAMETERS, int oo) /* {{{ */ { - zval *akc = NULL, *adurability = NULL; - char *key, *cas = NULL; - long klen = 0, cas_len = 0; - unsigned long long cas_v = 0; + char *key; + char *cas = NULL; + long klen = 0; + long cas_len = 0; + long replicate_to = 0; + long persist_to = 0; + lcb_cas_t cas_v = 0; php_couchbase_res *couchbase_res; - - int argflags = oo ? PHP_COUCHBASE_ARG_F_OO : PHP_COUCHBASE_ARG_F_FUNCTIONAL; - - PHP_COUCHBASE_GET_PARAMS(couchbase_res, argflags, - "s|sa", &key, &klen, &cas, &cas_len, &adurability); - { - lcb_error_t retval; - php_couchbase_ctx *ctx; - - ctx = ecalloc(1, sizeof(php_couchbase_ctx)); - ctx->res = couchbase_res; - - if (cas) { - cas_v = strtoull(cas, 0, 10); - } - - { - lcb_remove_cmd_t cmd; - lcb_remove_cmd_t *commands[] = { &cmd }; - memset(&cmd, 0, sizeof(cmd)); - cmd.v.v0.key = key; - cmd.v.v0.nkey = klen; - cmd.v.v0.cas = cas_v; - retval = lcb_remove(couchbase_res->handle, ctx, - 1, (const lcb_remove_cmd_t * const *)commands); - } - if (LCB_SUCCESS != retval) { - efree(ctx); - php_error_docref(NULL TSRMLS_CC, E_WARNING, - "Failed to schedule delete request: %s", lcb_strerror(couchbase_res->handle, retval)); + lcb_error_t retval; + php_couchbase_ctx *ctx; + char errmsg[256]; + + int arg = (oo) ? PHP_COUCHBASE_ARG_F_OO : PHP_COUCHBASE_ARG_F_FUNCTIONAL; + + PHP_COUCHBASE_GET_PARAMS(couchbase_res, arg, + "s|sll", &key, &klen, &cas, &cas_len, + &persist_to, &replicate_to); + + if (klen == 0) { + if (oo) { + zend_throw_exception(cb_illegal_key_exception, + "No key specified: Empty key", + 0 TSRMLS_CC); + return ; + } else { RETURN_FALSE; } + } - couchbase_res->seqno += 1; - pcbc_start_loop(couchbase_res); - if (LCB_SUCCESS == ctx->res->rc) { - if (oo) { - RETVAL_ZVAL(getThis(), 1, 0); - } else { - RETVAL_TRUE; - } - } else if (LCB_KEY_ENOENT == ctx->res->rc || /* skip missing key errors */ - LCB_KEY_EEXISTS == ctx->res->rc) { /* skip CAS mismatch */ + if (validate_simple_observe_clause(couchbase_res->handle, + persist_to, + replicate_to TSRMLS_CC) == -1) { + /* Exception already thrown */ + return; + } + + if (cas_len > 0) { + cas_v = (lcb_cas_t)strtoull(cas, 0, 10); + } + + retval = do_remove(couchbase_res->handle, key, klen, &cas_v); + couchbase_res->rc = retval; + + switch (retval) { + case LCB_SUCCESS: + Z_TYPE_P(return_value) = IS_STRING; + Z_STRLEN_P(return_value) = spprintf(&(Z_STRVAL_P(return_value)), 0, + "%llu", cas_v); + break; + case LCB_KEY_ENOENT: + RETURN_FALSE; + /* NOTREACHED */ + case LCB_KEY_EEXISTS: + if (oo) { + sprintf(errmsg, "Failed to remove the value from the server: %s", + lcb_strerror(couchbase_res->handle, retval)); + zend_throw_exception(cb_key_mutated_exception, errmsg, 0 TSRMLS_CC); + } else { RETVAL_FALSE; + } + return ; + default: + if (oo) { + sprintf(errmsg, "Failed to remove the value from the server: %s", + lcb_strerror(couchbase_res->handle, retval)); + zend_throw_exception(cb_lcb_exception, errmsg, 0 TSRMLS_CC); } else { php_error_docref(NULL TSRMLS_CC, E_WARNING, - "Failed to remove a value from server: %s", lcb_strerror(couchbase_res->handle, ctx->res->rc)); + "Failed to remove a value from server: %s", + lcb_strerror(couchbase_res->handle, retval)); RETVAL_FALSE; } + return ; + } - /* If we have a durability spec, after the commands have been issued (and callbacks returned), try to - * fulfill that spec by using polling observe internal: + if (retval == LCB_SUCCESS && (persist_to > 0 || replicate_to > 0)) { + /* + * If we have a durability spec, after the commands have been + * issued (and callbacks returned), try to fulfill that spec by + * using polling observe internal (please note that this is + * only possible from OO) */ - if (adurability != NULL) { - array_init(akc); - add_assoc_long(akc, key, cas_v); - - ctx->cas = akc; - - observe_polling_internal(ctx, adurability, 0); + struct observe_entry entry; + memset(&entry, 0, sizeof(entry)); + entry.key = key; + entry.nkey = klen; + entry.cas = cas_v; + + retval = simple_observe(couchbase_res->handle, &entry, 1, + persist_to, replicate_to); + couchbase_res->rc = retval; + + if (retval != LCB_SUCCESS) { + if (retval == LCB_ETIMEDOUT) { + zend_throw_exception(cb_timeout_exception, + "Timed out waiting for the objects to persist", + 0 TSRMLS_CC); + } else { + snprintf(errmsg, sizeof(errmsg), "observe failed for: %s", + klen, key, lcb_strerror(couchbase_res->handle, + retval)); + zend_throw_exception(cb_lcb_exception, errmsg, 0 TSRMLS_CC); + } + } else { + /* @todo checkfor timeout!!! */ + if (entry.mutated) { + zend_throw_exception(cb_key_mutated_exception, + "The document was mutated", + 0 TSRMLS_CC); + } } - - efree(ctx); } } /* }}} */ diff --git a/simple_observe.c b/simple_observe.c new file mode 100644 index 0000000..87dd016 --- /dev/null +++ b/simple_observe.c @@ -0,0 +1,207 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright 2012 Couchbase, Inc. | + +----------------------------------------------------------------------+ + | Licensed under the Apache License, Version 2.0 (the "License"); | + | you may not use this file except in compliance with the License. | + | You may obtain a copy of the License at | + | http://www.apache.org/licenses/LICENSE-2.0 | + | Unless required by applicable law or agreed to in writing, software | + | distributed under the License is distributed on an "AS IS" BASIS, | + | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | + | implied. See the License for the specific language governing | + | permissions and limitations under the License. | + +----------------------------------------------------------------------+ + | Author: Trond Norbye | + +----------------------------------------------------------------------+ +*/ +#include "internal.h" + +struct observe_cookie { + struct observe_entry *entries; + int num; + lcb_error_t error; +}; + +static void simple_observe_callback(lcb_t instance, + const void *cookie, + lcb_error_t error, + const lcb_observe_resp_t *resp) +{ + struct observe_cookie *c = (struct observe_cookie *)cookie; + struct observe_entry *ent = NULL; + int ii; + + if (resp->version != 0) { + c->error = LCB_EINVAL; + } + + if (c->error != LCB_SUCCESS) { + /* + * if we've gotten any incorrect version responses we'll just + * ditch all of them + */ + return ; + } + + /* Is this the terminating packet? */ + if (resp->v.v0.nkey == 0) { + return; + } + + /* let's find the correct entry */ + for (ii = 0; ii < c->num; ++ii) { + if ((c->entries[ii].nkey == resp->v.v0.nkey) && + memcmp(c->entries[ii].key, resp->v.v0.key, resp->v.v0.nkey) == 0) { + ent = &c->entries[ii]; + } + } + + if (ent == NULL) { + /* This shouldn't happen.. the server sent a key we didn't request! */ + c->error = LCB_ERROR; + return; + } + + ent->err = error; + if (error == LCB_SUCCESS) { + if (ent->cas != resp->v.v0.cas) { + /* + *It's only mutated if it is from the master (if not it may + * be data from the previous version of the key + */ + if (resp->v.v0.from_master) { + ent->mutated = 1; + } + } else { + if (resp->v.v0.ttp) { + ent->persisted++; + } + if (resp->v.v0.ttr) { + ent->replicated++; + } + } + } +} + +static int should_add(struct observe_entry *entries, + long persist_to, + long replicate_to) +{ + int ret = 0; + if (entries->mutated == 0) { + if (entries->persisted < persist_to) { + ret++; + } + if (entries->replicated < replicate_to) { + ret++; + } + + if (ret) { + /* reset the counters */ + entries->persisted = 0; + entries->replicated = 0; + } + } + + return ret; +} + +PHP_COUCHBASE_LOCAL +lcb_error_t simple_observe(lcb_t instance, + struct observe_entry *entries, + int nentries, + long persist_to, + long replicate_to) +{ + lcb_error_t err; + + lcb_observe_cmd_t *cmds = ecalloc(nentries, sizeof(lcb_observe_cmd_t)); + lcb_observe_cmd_t **commands = ecalloc(nentries, sizeof(lcb_observe_cmd_t *)); + int ii; + lcb_observe_callback org = lcb_set_observe_callback(instance, + simple_observe_callback); + struct observe_cookie cookie; + int xx; + int done = 0; + int numtries = 0; + int interval = INI_INT(PCBC_INIENT_OBS_INTERVAL); + int maxretry = INI_INT(PCBC_INIENT_OBS_TIMEOUT) / interval; + + + cookie.entries = entries; + cookie.num = nentries; + cookie.error = LCB_SUCCESS; + + lcb_behavior_set_syncmode(instance, LCB_SYNCHRONOUS); + + do { + /* set up the commands */ + for (xx = 0, ii = 0; ii < nentries; ++ii) { + cmds[ii].v.v0.key = entries[ii].key; + cmds[ii].v.v0.nkey = entries[ii].nkey; + if (should_add(&entries[ii], persist_to, replicate_to)) { + commands[xx++] = cmds + ii; + } + } + + if (xx > 0) { + if (numtries > 0) { + usleep(interval); + } + ++numtries; + err = lcb_observe(instance, &cookie, xx, + (const lcb_observe_cmd_t * const *)commands); + } else { + done = 1; + } + } while (!done && numtries < maxretry); + + efree(cmds); + efree(commands); + + lcb_behavior_set_syncmode(instance, LCB_ASYNCHRONOUS); + lcb_set_observe_callback(instance, org); + + if (!done) { + return LCB_ETIMEDOUT; + } + + return (err == LCB_SUCCESS) ? cookie.error : err; +} + +PHP_COUCHBASE_LOCAL +int validate_simple_observe_clause(lcb_t instance, + int persist, + int replicas TSRMLS_DC) +{ + int num_replicas = lcb_get_num_replicas(instance); + int num_nodes = lcb_get_num_nodes(instance); + + if ((persist > (num_replicas + 1)) || (replicas > num_replicas)) { + zend_throw_exception(cb_not_enough_nodes_exception, + "Not enough replicas to fulfill the request", + 0 TSRMLS_CC); + return -1; + } + + if ((persist > num_nodes) || ((replicas + 1) > num_nodes)) { + zend_throw_exception(cb_not_enough_nodes_exception, + "Not enough nodes to fulfill the request", + 0 TSRMLS_CC); + return -1; + } + + return 0; +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/simple_observe.h b/simple_observe.h new file mode 100644 index 0000000..7e61dca --- /dev/null +++ b/simple_observe.h @@ -0,0 +1,59 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright 2012 Couchbase, Inc. | + +----------------------------------------------------------------------+ + | Licensed under the Apache License, Version 2.0 (the "License"); | + | you may not use this file except in compliance with the License. | + | You may obtain a copy of the License at | + | http://www.apache.org/licenses/LICENSE-2.0 | + | Unless required by applicable law or agreed to in writing, software | + | distributed under the License is distributed on an "AS IS" BASIS, | + | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | + | implied. See the License for the specific language governing | + | permissions and limitations under the License. | + +----------------------------------------------------------------------+ + | Author: Trond Norbye | + +----------------------------------------------------------------------+ +*/ + +#ifndef COUCHBASE_INTERNAL_H +#error "Must include internal.h" +#endif + +#ifndef COUCHBASE_SIMPLE_OBSERVE_H +#define COUCHBASE_SIMPLE_OBSERVE_H + +struct observe_entry { + lcb_error_t err; + void *key; + int nkey; + uint64_t cas; + int mutated; + int persisted; + int replicated; +}; + +PHP_COUCHBASE_LOCAL +lcb_error_t simple_observe(lcb_t instance, + struct observe_entry *entries, + int nentries, + long persist_to, + long replicate_to); + +PHP_COUCHBASE_LOCAL +int validate_simple_observe_clause(lcb_t instance, + int persist, + int replicas TSRMLS_DC); + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/store.c b/store.c index 3415fc6..35c6eb1 100644 --- a/store.c +++ b/store.c @@ -2,12 +2,11 @@ /* {{{ static void php_couchbase_storage_callback(...) */ -static void -php_couchbase_store_callback(lcb_t instance, - const void *cookie, - lcb_storage_t operation, - lcb_error_t error, - const lcb_store_resp_t *resp) +static void php_couchbase_store_callback(lcb_t instance, + const void *cookie, + lcb_storage_t operation, + lcb_error_t error, + const lcb_store_resp_t *resp) { php_couchbase_ctx *ctx = (php_couchbase_ctx *)cookie; const void *key; @@ -38,7 +37,8 @@ php_couchbase_store_callback(lcb_t instance, MAKE_STD_ZVAL(rv); ZVAL_FALSE(rv); - zend_hash_update(Z_ARRVAL_P(ctx->rv), string_key, nkey + 1, (void **)&rv, sizeof(zval *), NULL); + zend_hash_update(Z_ARRVAL_P(ctx->rv), string_key, nkey + 1, + (void **)&rv, sizeof(zval *), NULL); efree(string_key); } return; @@ -53,7 +53,8 @@ php_couchbase_store_callback(lcb_t instance, MAKE_STD_ZVAL(rv); Z_TYPE_P(rv) = IS_STRING; Z_STRLEN_P(rv) = spprintf(&(Z_STRVAL_P(rv)), 0, "%llu", cas); - zend_hash_update(Z_ARRVAL_P(ctx->rv), string_key, nkey + 1, (void **)&rv, sizeof(zval *), NULL); + zend_hash_update(Z_ARRVAL_P(ctx->rv), string_key, nkey + 1, + (void **)&rv, sizeof(zval *), NULL); efree(string_key); } else { Z_TYPE_P(ctx->rv) = IS_STRING; @@ -63,9 +64,8 @@ php_couchbase_store_callback(lcb_t instance, /* }}} */ PHP_COUCHBASE_LOCAL -void php_couchbase_store_impl(INTERNAL_FUNCTION_PARAMETERS, lcb_storage_t op, int multi, int oo) /* {{{ */ +void php_couchbase_store_impl(INTERNAL_FUNCTION_PARAMETERS, lcb_storage_t op, int multi) /* {{{ */ { - zval *akc = NULL, *adurability = NULL; lcb_error_t retval; php_couchbase_res *couchbase_res; php_couchbase_ctx *ctx; @@ -82,12 +82,12 @@ void php_couchbase_store_impl(INTERNAL_FUNCTION_PARAMETERS, lcb_storage_t op, in zval *value; long klen = 0; PHP_COUCHBASE_GET_PARAMS(couchbase_res, - oo ? PHP_COUCHBASE_ARG_F_OO : PHP_COUCHBASE_ARG_F_FUNCTIONAL, - "sz|lsa", + PHP_COUCHBASE_ARG_F_FUNCTIONAL, + "sz|ls", &key, &klen, &value, &expire, - &cas, &cas_len, &adurability); + &cas, &cas_len); if (!klen) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to schedule set request: Empty key"); @@ -151,9 +151,9 @@ void php_couchbase_store_impl(INTERNAL_FUNCTION_PARAMETERS, lcb_storage_t op, in int key_type, nkey = 0; PHP_COUCHBASE_GET_PARAMS(couchbase_res, - oo ? PHP_COUCHBASE_ARG_F_OO : PHP_COUCHBASE_ARG_F_FUNCTIONAL, - "a|la", - &akeys, &expire, &adurability); + PHP_COUCHBASE_ARG_F_FUNCTIONAL, + "a|l", + &akeys, &expire); ctx = ecalloc(1, sizeof(php_couchbase_ctx)); ctx->res = couchbase_res; @@ -262,39 +262,6 @@ void php_couchbase_store_impl(INTERNAL_FUNCTION_PARAMETERS, lcb_storage_t op, in } } - /* If we have a durability spec, after the commands have been issued (and callbacks returned), try to - * fulfill that spec by using polling observe internal: - */ - if (adurability != NULL) { - array_init(akc); - - if (IS_ARRAY == Z_TYPE_P(return_value)) { /* multi */ - ulong curr_idx; - char *curr_key; - uint curr_key_len; - zval **curr_cas; - - for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(return_value)); - zend_hash_has_more_elements(Z_ARRVAL_P(return_value)) == SUCCESS; - zend_hash_move_forward(Z_ARRVAL_P(return_value))) { - zend_hash_get_current_key_ex(Z_ARRVAL_P(return_value), (char **)&curr_key, &curr_key_len, &curr_idx, 0, NULL); - zend_hash_get_current_data(Z_ARRVAL_P(return_value), (void **)&curr_cas); - if (Z_BVAL_PP(curr_cas)) { - add_assoc_long(akc, curr_key, Z_LVAL_PP(curr_cas)); - } - } - zend_hash_internal_pointer_reset(Z_ARRVAL_P(return_value)); - } else { /* not multi */ - if (Z_BVAL_P(return_value)) { /* it claims to have stored */ - add_assoc_long(akc, key, Z_LVAL_P(return_value)); - } - } - - ctx->cas = akc; - - observe_polling_internal(ctx, adurability, 0); - } - efree(ctx); } } @@ -311,89 +278,544 @@ void php_couchbase_cas_impl(INTERNAL_FUNCTION_PARAMETERS, int oo) /* {{{ */ char *key, *payload, *cas = NULL; long klen = 0, expire = 0, cas_len = 0; php_couchbase_res *couchbase_res; + long replicate_to = 0; + long persist_to = 0; + lcb_error_t retval; + php_couchbase_ctx *ctx; + lcb_store_cmd_t cmd; + const lcb_store_cmd_t *const commands[] = { &cmd }; + int argf = oo ? PHP_COUCHBASE_ARG_F_OO : PHP_COUCHBASE_ARG_F_FUNCTIONAL; + + PHP_COUCHBASE_GET_PARAMS(couchbase_res, argf, "ssz|lll", &cas, &cas_len, + &key, &klen, &value, &expire, &persist_to, + &replicate_to); + + if (validate_simple_observe_clause(couchbase_res->handle, + persist_to, + replicate_to TSRMLS_CC) == -1) { + /* Exception already thrown */ + return; + } - PHP_COUCHBASE_GET_PARAMS(couchbase_res, - oo ? PHP_COUCHBASE_ARG_F_OO : PHP_COUCHBASE_ARG_F_FUNCTIONAL, - "ssz|l", - &cas, &cas_len, - &key, &klen, - &value, &expire); + if (klen == 0) { + if (oo) { + zend_throw_exception(cb_illegal_key_exception, + "Failed to schedule set request: Empty key", + 0 TSRMLS_CC); + return; + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Failed to schedule set request: Empty key"); + RETURN_FALSE; + } + } - { - lcb_error_t retval; - php_couchbase_ctx *ctx; + if (cas_len == 0) { + if (oo) { + zend_throw_exception(cb_exception, + "Invalid cas specified", + 0 TSRMLS_CC); + return; + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Invalid cas specified"); + RETURN_FALSE; + } + } - ctx = ecalloc(1, sizeof(php_couchbase_ctx)); - ctx->res = couchbase_res; - ctx->rv = return_value; + ctx = ecalloc(1, sizeof(php_couchbase_ctx)); + ctx->res = couchbase_res; + ctx->rv = return_value; - if (expire) { - exp = pcbc_check_expiry(expire); + if (expire) { + exp = pcbc_check_expiry(expire); + } + + if (cas) { + cas_v = strtoull(cas, 0, 10); + } + + payload = php_couchbase_zval_to_payload(value, &payload_len, &flags, + couchbase_res->serializer, + couchbase_res->compressor + TSRMLS_CC); + if (payload == NULL) { + RETURN_FALSE; + } + + if (couchbase_res->prefix_key_len) { + klen = spprintf(&key, 0, "%s_%s", couchbase_res->prefix_key, key); + } + + memset(&cmd, 0, sizeof(cmd)); + cmd.v.v0.operation = LCB_SET; + cmd.v.v0.key = key; + cmd.v.v0.nkey = klen; + cmd.v.v0.bytes = payload; + cmd.v.v0.nbytes = payload_len; + cmd.v.v0.flags = flags; + cmd.v.v0.exptime = exp; + cmd.v.v0.cas = (uint64_t)cas_v; + + retval = lcb_store(couchbase_res->handle, ctx, 1, commands); + + efree(payload); + if (couchbase_res->prefix_key_len) { + efree(key); + } + + if (retval != LCB_SUCCESS) { + char errmsg[256]; + efree(ctx); + sprintf(errmsg, "Failed to schedule cas request: %s", + lcb_strerror(couchbase_res->handle, retval)); + + if (oo) { + zend_throw_exception(cb_lcb_exception, errmsg, 0 TSRMLS_CC); + return; + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, errmsg); + RETURN_FALSE; } + } - if (cas) { - cas_v = strtoull(cas, 0, 10); + ++couchbase_res->seqno; + pcbc_start_loop(couchbase_res); + zval_dtor(return_value); + + if (LCB_SUCCESS == ctx->res->rc) { + ZVAL_TRUE(return_value); + } else if (LCB_KEY_EEXISTS == ctx->res->rc) { + ZVAL_FALSE(return_value); + } else { + char errmsg[256]; + sprintf(errmsg, "Failed to store a value to server: %s", + lcb_strerror(couchbase_res->handle, ctx->res->rc)); + if (oo) { + zend_throw_exception(cb_lcb_exception, errmsg, 0 TSRMLS_CC); + } else { + ZVAL_FALSE(return_value); + php_error_docref(NULL TSRMLS_CC, E_WARNING, errmsg); } + } + efree(ctx); +} +/* }}} */ - if (!(payload = php_couchbase_zval_to_payload(value, &payload_len, &flags, couchbase_res->serializer, couchbase_res->compressor TSRMLS_CC))) { - RETURN_FALSE; +PHP_COUCHBASE_LOCAL +void php_couchbase_callbacks_store_init(lcb_t handle) +{ + lcb_set_store_callback(handle, php_couchbase_store_callback); +} + +PHP_COUCHBASE_LOCAL +void php_couchbase_store_impl_oo(INTERNAL_FUNCTION_PARAMETERS, lcb_storage_t op) +{ + lcb_error_t retval; + php_couchbase_res *couchbase_res; + php_couchbase_ctx *ctx; + time_t exp = {0}; + unsigned int flags = 0; + char *cas = NULL; + char *payload; + size_t payload_len = 0; + unsigned long long cas_v = 0; + long expire = 0; + long replicate_to = 0; + long persist_to = 0; + long cas_len = 0; + char *key = NULL; + zval *value; + long klen = 0; + lcb_store_cmd_t cmd; + const lcb_store_cmd_t *const commands[] = { &cmd }; + + if (op == LCB_ADD) { + PHP_COUCHBASE_GET_PARAMS(couchbase_res, PHP_COUCHBASE_ARG_F_OO, + "sz|lll", &key, &klen, &value, &expire, + &persist_to, &replicate_to); + } else { + PHP_COUCHBASE_GET_PARAMS(couchbase_res, PHP_COUCHBASE_ARG_F_OO, + "sz|lsll", &key, &klen, &value, &expire, + &cas, &cas_len, &persist_to, &replicate_to); + } + + if (validate_simple_observe_clause(couchbase_res->handle, + persist_to, + replicate_to TSRMLS_CC) == -1) { + /* Exception already thrown */ + return; + } + + if (!klen) { + zend_throw_exception(cb_illegal_key_exception, + "Failed to schedule set request: Empty key", + 0 TSRMLS_CC); + return ; + } + + payload = php_couchbase_zval_to_payload(value, &payload_len, &flags, + couchbase_res->serializer, + couchbase_res->compressor + TSRMLS_CC); + if (payload == NULL) { + /* ?? I guess we should throw an exception here? */ + RETURN_FALSE; + } + + if (couchbase_res->prefix_key_len) { + klen = spprintf(&key, 0, "%s_%s", couchbase_res->prefix_key, key); + } + + ctx = ecalloc(1, sizeof(php_couchbase_ctx)); + ctx->res = couchbase_res; + ctx->rv = return_value; + couchbase_res->seqno += 1; + + if (expire) { + exp = pcbc_check_expiry(expire); + } + + if (cas) { + cas_v = strtoull(cas, 0, 10); + } + + memset(&cmd, 0, sizeof(cmd)); + cmd.v.v0.operation = op; + cmd.v.v0.key = key; + cmd.v.v0.nkey = klen; + cmd.v.v0.bytes = payload; + cmd.v.v0.nbytes = payload_len; + cmd.v.v0.flags = flags; + cmd.v.v0.exptime = exp; + cmd.v.v0.cas = (uint64_t)cas_v; + + retval = lcb_store(couchbase_res->handle, ctx, 1, commands); + + efree(payload); + if (couchbase_res->prefix_key_len) { + efree(key); + } + + if (retval != LCB_SUCCESS) { + char errmsg[256]; + efree(ctx); + sprintf(errmsg, "Failed to schedule set request: %s", + lcb_strerror(couchbase_res->handle, retval)); + zend_throw_exception(cb_lcb_exception, errmsg, 0 TSRMLS_CC); + return ; + } + + pcbc_start_loop(couchbase_res); + if (LCB_SUCCESS != ctx->res->rc) { + RETVAL_FALSE; + switch (op) { + case LCB_ADD: + if (LCB_KEY_EEXISTS == ctx->res->rc) { + break; + } + case LCB_APPEND: + case LCB_PREPEND: + if (LCB_NOT_STORED == ctx->res->rc) { + break; + } + case LCB_REPLACE: + case LCB_SET: + if (LCB_KEY_ENOENT == ctx->res->rc) { + break; + } + if (cas && LCB_KEY_EEXISTS == ctx->res->rc) { + break; + } + default: { + char errmsg[256]; + sprintf(errmsg, "Failed to store value to server: %s", + lcb_strerror(couchbase_res->handle, retval)); + zend_throw_exception(cb_lcb_exception, errmsg, 0 TSRMLS_CC); } + break; + } + } else { + /* + * The item was stored successfully. Did the user want to wait until + * it was persisted/replicated? + */ + if (persist_to != 0 || replicate_to != 0) { + struct observe_entry entry; + memset(&entry, 0, sizeof(entry)); + entry.key = key; + entry.nkey = klen; + entry.cas = strtoull(Z_STRVAL_P(return_value), 0, 10); + + retval = simple_observe(couchbase_res->handle, &entry, 1, + persist_to, replicate_to); + couchbase_res->rc = retval; + if (retval != LCB_SUCCESS) { + if (retval == LCB_ETIMEDOUT) { + zend_throw_exception(cb_timeout_exception, + "Timed out waiting for the objects to persist", + 0 TSRMLS_CC); + } else { + char errmsg[512]; + snprintf(errmsg, sizeof(errmsg), "observe failed for: %s", + klen, key, lcb_strerror(couchbase_res->handle, + retval)); + zend_throw_exception(cb_lcb_exception, errmsg, 0 TSRMLS_CC); + } + } else { + /* @todo checkfor timeout!!! */ + if (entry.mutated) { + zend_throw_exception(cb_key_mutated_exception, + "The document was mutated", + 0 TSRMLS_CC); + } + } + } + } + efree(ctx); +} +/* }}} */ - if (couchbase_res->prefix_key_len) { - klen = spprintf(&key, 0, "%s_%s", couchbase_res->prefix_key, key); +static void release_entry_array(struct observe_entry *entries, int nent) +{ + int ii; + for (ii = 0; ii < nent; ++ii) { + efree(entries[ii].key); + } + efree(entries); +} + + +PHP_COUCHBASE_LOCAL +void php_couchbase_store_multi_impl_oo(INTERNAL_FUNCTION_PARAMETERS) +{ + lcb_error_t retval; + php_couchbase_res *couchbase_res; + php_couchbase_ctx *ctx; + time_t exp = {0}; + long expire = 0; + zval *akeys; + long persist_to = 0; + long replicate_to = 0; + struct observe_entry *entries; + int numkeys; + lcb_store_cmd_t *cmds; + lcb_store_cmd_t **commands; + int ii; + + PHP_COUCHBASE_GET_PARAMS(couchbase_res, PHP_COUCHBASE_ARG_F_OO, "a|lll", + &akeys, &expire, &persist_to, &replicate_to); + + if (validate_simple_observe_clause(couchbase_res->handle, + persist_to, + replicate_to TSRMLS_CC) == -1) { + /* Exception already thrown */ + return; + } + + numkeys = zend_hash_num_elements(Z_ARRVAL_P(akeys)); + if (numkeys == 0) { + zend_throw_exception(cb_illegal_key_exception, + "No items specified", + 0 TSRMLS_CC); + return ; + } + + entries = ecalloc(numkeys, sizeof(struct observe_entry)); + commands = ecalloc(numkeys, sizeof(lcb_store_cmd_t *)); + cmds = ecalloc(numkeys, sizeof(lcb_store_cmd_t)); + /* link the command pointers */ + for (ii = 0; ii < numkeys; ++ii) { + commands[ii] = cmds + ii; + } + + ctx = ecalloc(1, sizeof(php_couchbase_ctx)); + ctx->res = couchbase_res; + ctx->rv = return_value; + array_init(ctx->rv); + + if (expire) { + exp = pcbc_check_expiry(expire); + } + + for (ii = 0, zend_hash_internal_pointer_reset(Z_ARRVAL_P(akeys)); + zend_hash_has_more_elements(Z_ARRVAL_P(akeys)) == SUCCESS; + zend_hash_move_forward(Z_ARRVAL_P(akeys)), ++ii) { + char *key = NULL; + uint klen; + size_t payload_len = 0; + char *payload; + unsigned int flags = 0; + zval **ppzval; + + int key_type = zend_hash_get_current_key(Z_ARRVAL_P(akeys), + &key, NULL, 0); + + if (key_type != HASH_KEY_IS_STRING || (klen = strlen(key)) == 0) { + int xx; + for (xx = 0; xx < ii; ++xx) { + efree((void *)cmds[xx].v.v0.bytes); + } + efree(commands); + efree(cmds); + efree(ctx); + release_entry_array(entries, xx); + + zend_throw_exception(cb_illegal_key_exception, + "Invalid key specified (not a string)", + 0 TSRMLS_CC); + return ; } - { - lcb_store_cmd_t cmd; - lcb_store_cmd_t *commands[] = { &cmd }; - memset(&cmd, 0, sizeof(cmd)); - cmd.v.v0.operation = LCB_SET; - cmd.v.v0.key = key; - cmd.v.v0.nkey = klen; - cmd.v.v0.bytes = payload; - cmd.v.v0.nbytes = payload_len; - cmd.v.v0.flags = flags; - cmd.v.v0.exptime = exp; - cmd.v.v0.cas = (uint64_t)cas_v; + if (zend_hash_get_current_data(Z_ARRVAL_P(akeys), + (void **)&ppzval) == FAILURE) { + int xx; + for (xx = 0; xx < ii; ++xx) { + efree((void *)cmds[xx].v.v0.bytes); + } + efree(commands); + efree(cmds); + efree(ctx); + release_entry_array(entries, xx); - retval = lcb_store(couchbase_res->handle, ctx, - 1, (const lcb_store_cmd_t * const *)commands); + zend_throw_exception(cb_exception, + "Failed to get data for key", + 0 TSRMLS_CC); + return ; + } + + payload = php_couchbase_zval_to_payload(*ppzval, &payload_len, &flags, + couchbase_res->serializer, + couchbase_res->compressor + TSRMLS_CC); + + if (payload == NULL) { + /* Shouldn't we call an exception? */ + RETURN_FALSE; } - efree(payload); + if (couchbase_res->prefix_key_len) { + char *new_key; + klen = spprintf(&new_key, 0, "%s_%s", couchbase_res->prefix_key, key); + key = new_key; + } + + entries[ii].nkey = klen; + entries[ii].key = emalloc(klen); + memcpy(entries[ii].key, key, klen); + cmds[ii].v.v0.operation = LCB_SET; + cmds[ii].v.v0.key = entries[ii].key; + cmds[ii].v.v0.nkey = klen; + cmds[ii].v.v0.bytes = payload; + cmds[ii].v.v0.nbytes = payload_len; + cmds[ii].v.v0.flags = flags; + cmds[ii].v.v0.exptime = exp; + if (couchbase_res->prefix_key_len) { efree(key); } - if (LCB_SUCCESS != retval) { - efree(ctx); - php_error_docref(NULL TSRMLS_CC, E_WARNING, - "Failed to schedule cas request: %s", lcb_strerror(couchbase_res->handle, retval)); - RETURN_FALSE; + } + + retval = lcb_store(couchbase_res->handle, ctx, numkeys, + (const lcb_store_cmd_t * const *)commands); + couchbase_res->seqno += numkeys; + pcbc_start_loop(couchbase_res); + + /* + * Time to release the payloads... + */ + for (ii = 0; ii < numkeys; ++ii) { + efree((void *)cmds[ii].v.v0.bytes); + } + efree(cmds); + efree(commands); + + if (LCB_SUCCESS != retval) { + efree(ctx); + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Failed to schedule set request: %s", + lcb_strerror(couchbase_res->handle, retval)); + release_entry_array(entries, numkeys); + RETURN_FALSE; + } + + /* + * The item was stored successfully. Did the user want to wait until + * it was persisted/replicated? + */ + if (persist_to != 0 || replicate_to != 0) { + int ii = 0; + for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(return_value)); + zend_hash_has_more_elements(Z_ARRVAL_P(return_value)) == SUCCESS; + zend_hash_move_forward(Z_ARRVAL_P(return_value)), ++ii) { + zval **curr_cas; + zend_hash_get_current_data(Z_ARRVAL_P(return_value), + (void **)&curr_cas); + if (Z_STRLEN_PP(curr_cas)) { + entries[ii].cas = strtoull(Z_STRVAL_PP(curr_cas), 0, 10); + } else { + /* @todo what to do here? */ + fprintf(stderr, "wtf!\n"); + } } - ++couchbase_res->seqno; - pcbc_start_loop(couchbase_res); - zval_dtor(return_value); - if (LCB_SUCCESS == ctx->res->rc) { - ZVAL_TRUE(return_value); - } else if (LCB_KEY_EEXISTS == ctx->res->rc) { - ZVAL_FALSE(return_value); + retval = simple_observe(couchbase_res->handle, entries, numkeys, + persist_to, replicate_to); + couchbase_res->rc = retval; + + if (retval != LCB_SUCCESS) { + if (retval == LCB_ETIMEDOUT) { + zend_throw_exception(cb_timeout_exception, + "Timed out waiting for the objects to persist", + 0 TSRMLS_CC); + } else { + char errmsg[256]; + snprintf(errmsg, sizeof(errmsg), + "An error occured while waiting for the objects to persist: %s", + lcb_strerror(couchbase_res->handle, retval)); + zend_throw_exception(cb_lcb_exception, errmsg, 0 TSRMLS_CC); + } } else { - ZVAL_FALSE(return_value); - php_error_docref(NULL TSRMLS_CC, E_WARNING, - "Failed to store a value to server: %s", lcb_strerror(couchbase_res->handle, ctx->res->rc)); + int currsize = 4096; + char *errmsg = malloc(currsize); + int offset = sprintf(errmsg, "The following documents was mutated:"); + int errors = 0; + + for (ii = 0; ii < numkeys; ++ii) { + if (entries[ii].mutated) { + if ((offset + entries[ii].nkey + 3) > currsize) { + char *p = realloc(errmsg, currsize * 2); + if (p) { + currsize *= 2; + errmsg = p; + } + } + + if ((offset + entries[ii].nkey + 3) < currsize) { + offset += sprintf(errmsg + offset, " \""); + memcpy(errmsg + offset, entries[ii].key, + entries[ii].nkey); + offset += entries[ii].nkey; + offset += sprintf(errmsg + offset, "\""); + } + errors = 1; + } + } + + if (errors) { + zend_throw_exception(cb_key_mutated_exception, errmsg, + 0 TSRMLS_CC); + } + + free(errmsg); } - efree(ctx); } -} -/* }}} */ -PHP_COUCHBASE_LOCAL -void php_couchbase_callbacks_store_init(lcb_t handle) -{ - lcb_set_store_callback(handle, php_couchbase_store_callback); + release_entry_array(entries, numkeys); + efree(ctx); } +/* }}} */ /* * Local variables: diff --git a/tests/AppendPrepend.inc b/tests/AppendPrepend.inc index ccbca0e..3f21a95 100644 --- a/tests/AppendPrepend.inc +++ b/tests/AppendPrepend.inc @@ -24,6 +24,17 @@ class AppendPrepend extends CouchbaseTestCommon { $oo->append($key, "_suffix"); $this->assertEquals("prefix_$value" . "_suffix", $oo->get($key)); + + + # Ensure that it works if we try to wait for persistence + $key = $this->mk_key(); + $value = "foo"; + $oo = $this->oo; + $cas = $oo->add($key, $value); + $cas = $oo->prepend($key, "prefix_", 0, $cas, 1); + $cas = $oo->append($key, "_suffix", 0, $cas, 1); + $this->assertEquals("prefix_$value" . "_suffix", + $oo->get($key)); } function testAppendPrepend() { diff --git a/tests/Delete.inc b/tests/Delete.inc index 761b841..e3c7eee 100644 --- a/tests/Delete.inc +++ b/tests/Delete.inc @@ -47,13 +47,25 @@ class Delete extends CouchbaseTestCommon { $this->assertNotEmpty($cas2); - $this->assertFalse($oo->delete($key, $cas), - "delete Fails on stale cas"); + try { + $oo->delete($key, $cas); + $this->assertFalse(true, "Delete should fail for invalid cas"); + } catch (CouchbaseKeyMutatedException $e) {} $this->assertEquals($oo->get($key), "bar"); $oo->delete($key, $cas2); $this->assertNull($oo->get($key)); + + # Test that I can do a delete and wait for it to persist.. + # Unfortunately we have no idea how fast the server is, + # so we just have to ignore the timeout ;) + $cas = $oo->set($key, $value); + try { + $oo->delete($key, $cas, 1); + $this->assertNull($oo->get($key)); + } catch (CouchbaseTimeoutException $e) {} + } function testDelete() { diff --git a/tests/Get.inc b/tests/Get.inc index 91e12e5..1d7890e 100644 --- a/tests/Get.inc +++ b/tests/Get.inc @@ -310,7 +310,8 @@ class Get extends CouchbaseTestCommon { sleep(2); - $this->assertEquals($oo, $oo->delete($key)); // oddly, oo delete returns itself rather than true on success + $cas = $oo->delete($key); + $this->assertTrue(is_string($cas)); } function skipGetWithLockOO() { $this->_v11_feature(); } @@ -336,8 +337,8 @@ class Get extends CouchbaseTestCommon { $this->assertNotEquals($cas, $lockCAS); sleep(2); - - $this->assertTrue(couchbase_delete($h, $key)); + $cas = couchbase_delete($h, $key); + $this->assertTrue(is_string($cas)); } function skipGetWithLock() { $this->_v11_feature(); } } diff --git a/tests/Replace.inc b/tests/Replace.inc index 773d9e5..b9dd0d5 100644 --- a/tests/Replace.inc +++ b/tests/Replace.inc @@ -35,6 +35,10 @@ class Replace extends CouchbaseTestCommon { $this->assertNotEmpty($rv); $val = $oo->get($key); $this->assertEquals("bar", $val); + + # Test replace with persistence + $oo->replace($key, "foo", 0, 0, 1, 0); + } function testReplace() { diff --git a/tests/Store.inc b/tests/Store.inc index 9c03ac8..3c641d9 100644 --- a/tests/Store.inc +++ b/tests/Store.inc @@ -14,7 +14,7 @@ class Store extends CouchbaseTestCommon { * * @post * adding a single item works - * + * * @remark * Variants: OO, plain */ @@ -22,14 +22,14 @@ class Store extends CouchbaseTestCommon { $h = $this->handle; $key = $this->mk_key(); $val = uniqid('couchbase-value-'); - + $cas = couchbase_add($h, $key, $val); $valGet = couchbase_get($h, $key, NULL, $casGet); $this->assertEquals($valGet, $val); $this->assertEquals($casGet, $cas); } - + /** * @test * Add (OO) @@ -39,7 +39,7 @@ class Store extends CouchbaseTestCommon { * * @post * adding a single item works - * + * * @remark * Variants: OO, plain */ @@ -53,8 +53,12 @@ class Store extends CouchbaseTestCommon { $this->assertEquals($valGet, $val); $this->assertEquals($casGet, $cas); + + # test with persistence to disk + $key = $this->mk_key(); + $cas = $oo->add($key, "foobar", 0, 1); } - + /** * @test * Add on Key that Already Exists @@ -64,7 +68,7 @@ class Store extends CouchbaseTestCommon { * * @post * adding a key that already exists does not modify the prior value - * + * * @remark * Variants: OO, plain */ @@ -84,7 +88,7 @@ class Store extends CouchbaseTestCommon { $this->assertEquals($v, $val); $this->assertNotEquals($v, $val2); } - + /** * @test * Add on Key that Already Exists (OO) @@ -94,7 +98,7 @@ class Store extends CouchbaseTestCommon { * * @post * adding a key that already exists does not modify the prior value - * + * * @remark * Variants: OO, plain */ @@ -106,7 +110,7 @@ class Store extends CouchbaseTestCommon { $oo->add($key, $val); $val2 = uniqid('another-couchbase-value-'); - + $oo->add($key, $val2); $v = $oo->get( $key); @@ -117,14 +121,14 @@ class Store extends CouchbaseTestCommon { /** * @test - * Set + * Set * * @pre * connection established * * @post * setting a single item works - * + * * @remark * Variants: OO, plain */ @@ -132,7 +136,7 @@ class Store extends CouchbaseTestCommon { $h = $this->handle; $key = $this->mk_key(); $val = uniqid('couchbase-value-'); - + $cas = couchbase_set($h, $key, $val); $valGet = couchbase_get($h, $key, NULL, $casGet); @@ -149,7 +153,7 @@ class Store extends CouchbaseTestCommon { * * @post * setting a single item works - * + * * @remark * Variants: OO, plain */ @@ -163,8 +167,45 @@ class Store extends CouchbaseTestCommon { $this->assertEquals($valGet, $val); $this->assertEquals($casGet, $cas); + + # test with persistence to disk + $cas = $oo->set($key, "foobar", 0, $cas, 1, 0); + } + + /** + * @test + * Cas (OO) + * + * @pre + * connection established + * + * @post + * setting a single item works + * + * @remark + * Variants: OO, plain + */ + function testCasSingleOO() { + $oo = $this->oo; + try { + $oo->cas("1234", "", "foo"); + $this->assertEquals("", "Illegal key should have been thrown"); + } catch (CouchbaseIllegalKeyException $e) {} + + try { + $oo->cas("", "foo", "foo"); + $this->assertEquals("", "CouchbaseException should have been thrown"); + } catch (CouchbaseException $e) {} + + $key = $this->mk_key(); + $cas = $oo->set($key, "foo"); + + # Run the cas operation and wait for it to persist to disk + $oo->cas($cas, $key, "bar", 0, 1, 0); + $val = $oo->get($key); + $this->assertEquals($val, "bar"); } - + /** * @test * Set Multi @@ -174,7 +215,7 @@ class Store extends CouchbaseTestCommon { * * @post * setting several items at once works - * + * * @remark * Variants: OO, plain */ @@ -192,7 +233,7 @@ class Store extends CouchbaseTestCommon { $this->assertEquals($v, $keys[$k]); } } - + /** * @test * Set Multi (OO) @@ -202,7 +243,7 @@ class Store extends CouchbaseTestCommon { * * @post * setting several items at once works - * + * * @remark * Variants: OO, plain */ diff --git a/tests/Sync.inc b/tests/Sync.inc new file mode 100644 index 0000000..197cabe --- /dev/null +++ b/tests/Sync.inc @@ -0,0 +1,260 @@ +oo; + $key = $this->mk_key(); + try { + # persist on 100 nodes, 0 replicas + $cas = $oo->add($key, "foobar", 0, 100, 0); + $this->assertTrue(false, "We should get the exception"); + } catch (CouchbaseNotEnoughNodesException $e) { + + } + try { + # persist on 0 nodes, 100 replicas + $cas = $oo->add($key, "foobar", 0, 0, 100); + $this->assertTrue(false, "We should get the exception"); + } catch (CouchbaseNotEnoughNodesException $e) { + + } + try { + # persist on 100 nodes, 100 replicas + $cas = $oo->add($key, "foobar", 0, 100, 100); + $this->assertTrue(false, "We should get the exception"); + } catch (CouchbaseNotEnoughNodesException $e) { + + } + + try { + # persist on 100 nodes, 0 replicas + $cas = $oo->set($key, "foobar", 0, 0, 100, 0); + $this->assertTrue(false, "We should get the exception"); + } catch (CouchbaseNotEnoughNodesException $e) { + + } + try { + # persist on 0 nodes, 100 replicas + $cas = $oo->set($key, "foobar", 0, 0, 0, 100); + $this->assertTrue(false, "We should get the exception"); + } catch (CouchbaseNotEnoughNodesException $e) { + + } + try { + # persist on 100 nodes, 100 replicas + $cas = $oo->set($key, "foobar", 0, 0, 100, 100); + $this->assertTrue(false, "We should get the exception"); + } catch (CouchbaseNotEnoughNodesException $e) { + + } + + try { + # persist on 100 nodes, 0 replicas + $cas = $oo->replace($key, "foobar", 0, 0, 100, 0); + $this->assertTrue(false, "We should get the exception"); + } catch (CouchbaseNotEnoughNodesException $e) { + + } + try { + # persist on 0 nodes, 100 replicas + $cas = $oo->replace($key, "foobar", 0, 0, 0, 100); + $this->assertTrue(false, "We should get the exception"); + } catch (CouchbaseNotEnoughNodesException $e) { + + } + try { + # persist on 100 nodes, 100 replicas + $cas = $oo->replace($key, "foobar", 0, 0, 100, 100); + $this->assertTrue(false, "We should get the exception"); + } catch (CouchbaseNotEnoughNodesException $e) { + + } + + try { + # persist on 100 nodes, 0 replicas + $cas = $oo->append($key, "foobar", 0, 0, 100, 0); + $this->assertTrue(false, "We should get the exception"); + } catch (CouchbaseNotEnoughNodesException $e) { + + } + try { + # persist on 0 nodes, 100 replicas + $cas = $oo->append($key, "foobar", 0, 0, 0, 100); + $this->assertTrue(false, "We should get the exception"); + } catch (CouchbaseNotEnoughNodesException $e) { + + } + try { + # persist on 100 nodes, 100 replicas + $cas = $oo->append($key, "foobar", 0, 0, 100, 100); + $this->assertTrue(false, "We should get the exception"); + } catch (CouchbaseNotEnoughNodesException $e) { + + } + + try { + # persist on 100 nodes, 0 replicas + $cas = $oo->prepend($key, "foobar", 0, 0, 100, 0); + $this->assertTrue(false, "We should get the exception"); + } catch (CouchbaseNotEnoughNodesException $e) { + + } + try { + # persist on 0 nodes, 100 replicas + $cas = $oo->prepend($key, "foobar", 0, 0, 0, 100); + $this->assertTrue(false, "We should get the exception"); + } catch (CouchbaseNotEnoughNodesException $e) { + + } + try { + # persist on 100 nodes, 100 replicas + $cas = $oo->prepend($key, "foobar", 0, 0, 100, 100); + $this->assertTrue(false, "We should get the exception"); + } catch (CouchbaseNotEnoughNodesException $e) { + + } + + try { + # persist on 100 nodes, 0 replicas + $cas = $oo->delete($key, 0, 100, 0); + $this->assertTrue(false, "We should get the exception"); + } catch (CouchbaseNotEnoughNodesException $e) { + + } + try { + # persist on 0 nodes, 100 replicas + $cas = $oo->delete($key, 0, 0, 100); + $this->assertTrue(false, "We should get the exception"); + } catch (CouchbaseNotEnoughNodesException $e) { + + } + try { + # persist on 100 nodes, 100 replicas + $cas = $oo->delete($key, 0, 100, 100); + $this->assertTrue(false, "We should get the exception"); + } catch (CouchbaseNotEnoughNodesException $e) { + + } + } + + function testAddOnePersist() { + $oo = $this->oo; + $key = $this->mk_key(); + try { + # persist on 1 node + $cas = $oo->add($key, "foobar", 0, 1); + } catch (CouchbaseNotEnoughNodesException $e) { + $this->assertTrue(false, "We should at least have a single node"); + } catch (CouchbaseTimeoutException $e) { + # this could happen + } catch (CouchbaseException $e) { + $this->assertTrue(false, $e->getMessage()); + } + } + + function testAppendOnePersist() { + $oo = $this->oo; + $key = $this->mk_key(); + $cas = $oo->add($key, "foobar"); + + try { + # persist on 1 node + $cas2 = $oo->append($key, "foobar", 0, $cas, 1); + $this->assertNotEquals($cas, $cas2, "append should change cas"); + } catch (CouchbaseNotEnoughNodesException $e) { + $this->assertTrue(false, "We should at least have a single node"); + } catch (CouchbaseTimeoutException $e) { + # this could happen + } catch (CouchbaseException $e) { + $this->assertTrue(false, $e->getMessage()); + } + } + + function testCasOnePersist() { + $oo = $this->oo; + $key = $this->mk_key(); + $cas = $oo->add($key, "foobar"); + try { + # persist on 1 node + $oo->cas($cas, $key, "foobar", 0, 1); + } catch (CouchbaseNotEnoughNodesException $e) { + $this->assertTrue(false, "We should at least have a single node"); + } catch (CouchbaseTimeoutException $e) { + # this could happen + } catch (CouchbaseException $e) { + $this->assertTrue(false, $e->getMessage()); + } + } + + function testPrependOnePersist() { + $oo = $this->oo; + $key = $this->mk_key(); + $cas = $oo->add($key, "foobar"); + try { + # persist on 1 node + $cas2 = $oo->prepend($key, "foobar", 0, $cas, 1); + $this->assertNotEquals($cas, $cas2, "prepend should change cas"); + } catch (CouchbaseNotEnoughNodesException $e) { + $this->assertTrue(false, "We should at least have a single node"); + } catch (CouchbaseTimeoutException $e) { + # this could happen + } catch (CouchbaseException $e) { + $this->assertTrue(false, $e->getMessage()); + } + } + + function testRemoveOnePersist() { + $oo = $this->oo; + $key = $this->mk_key(); + $cas = $oo->add($key, "foobar"); + try { + # persist on 1 node + $cas2 = $oo->delete($key, $cas, 1); + $this->assertNotEquals($cas, $cas2, "remove should change cas"); + } catch (CouchbaseNotEnoughNodesException $e) { + $this->assertTrue(false, "We should at least have a single node"); + } catch (CouchbaseTimeoutException $e) { + # this could happen + } catch (CouchbaseException $e) { + $this->assertTrue(false, $e->getMessage()); + } + } + + function testReplaceOnePersist() { + $oo = $this->oo; + $key = $this->mk_key(); + $cas = $oo->add($key, "foobar"); + try { + # persist on 1 node + $cas2 = $oo->replace($key, "foobar", 0, $cas, 1); + $this->assertNotEquals($cas, $cas2, "replace should change cas"); + } catch (CouchbaseNotEnoughNodesException $e) { + $this->assertTrue(false, "We should at least have a single node"); + } catch (CouchbaseTimeoutException $e) { + # this could happen + } catch (CouchbaseException $e) { + $this->assertTrue(false, $e->getMessage()); + } + } + + function testSetOnePersist() { + $oo = $this->oo; + $key = $this->mk_key(); + $cas = $oo->add($key, "foobar"); + try { + # persist on 1 node + $cas2 = $oo->set($key, "foobar", 0, $cas, 1); + $this->assertNotEquals($cas, $cas2, "set should change cas"); + } catch (CouchbaseNotEnoughNodesException $e) { + $this->assertTrue(false, "We should at least have a single node"); + } catch (CouchbaseTimeoutException $e) { + # this could happen + } catch (CouchbaseException $e) { + $this->assertTrue(false, $e->getMessage()); + } + } + +} diff --git a/tests/TEST_CLASSES b/tests/TEST_CLASSES index 640a8d7..f3c16fb 100644 --- a/tests/TEST_CLASSES +++ b/tests/TEST_CLASSES @@ -25,5 +25,6 @@ Replace Serialization ServerStats Store +Sync ViewSimple ViewQueryStrings diff --git a/tests/phpt/Store/CasSingleOO.phpt b/tests/phpt/Store/CasSingleOO.phpt new file mode 100644 index 0000000..5cecbbc --- /dev/null +++ b/tests/phpt/Store/CasSingleOO.phpt @@ -0,0 +1,14 @@ +--TEST-- +Store - CasSingleOO + +--SKIPIF-- +v.v0.key; @@ -43,8 +42,6 @@ php_couchbase_touch_callback(lcb_t handle, } /* }}} */ - -#include "internal.h" PHP_COUCHBASE_LOCAL void php_couchbase_touch_impl(INTERNAL_FUNCTION_PARAMETERS, int multi, int oo) /* {{{ */ { @@ -56,7 +53,6 @@ void php_couchbase_touch_impl(INTERNAL_FUNCTION_PARAMETERS, int multi, int oo) / lcb_time_t exp = {0}; /* how long to set expiry. */ long expiry; /* used for parameter passing */ /* note that by server's behavior, anything longer than 30 days (60*60*24*30) is an epoch time to expire at */ - zval *adurability = NULL; php_couchbase_res *couchbase_res; php_couchbase_ctx *ctx; int argflags; @@ -74,7 +70,7 @@ void php_couchbase_touch_impl(INTERNAL_FUNCTION_PARAMETERS, int multi, int oo) / int i; PHP_COUCHBASE_GET_PARAMS(couchbase_res, argflags, - "al|a", &arr_keys, &expiry, &adurability); + "al", &arr_keys, &expiry); keycount = zend_hash_num_elements(Z_ARRVAL_P(arr_keys)); multi_keys = ecalloc(keycount, sizeof(char *)); @@ -118,7 +114,7 @@ void php_couchbase_touch_impl(INTERNAL_FUNCTION_PARAMETERS, int multi, int oo) / } else { /* single-valued */ PHP_COUCHBASE_GET_PARAMS(couchbase_res, argflags, - "sl|a", &single_key, &single_nkey, &expiry, &adurability); + "sl", &single_key, &single_nkey, &expiry); if (!single_nkey) { return; @@ -193,38 +189,6 @@ void php_couchbase_touch_impl(INTERNAL_FUNCTION_PARAMETERS, int multi, int oo) / lcb_strerror(couchbase_res->handle, ctx->res->rc)); } - /* If we have a durability spec, after the commands have been issued (and callbacks returned), try to - * fulfill that spec by using polling observe internal: - */ - if (adurability != NULL) { - zval *akc = NULL; - array_init(akc); - - if (IS_ARRAY == Z_TYPE_P(return_value)) { /* multi */ - ulong curr_idx; - char *curr_key; - uint curr_key_len; - zval **curr_cas; - - for (pcbc_ht_iter_init(return_value); pcbc_ht_iter_remaining(return_value); pcbc_ht_iter_next(return_value)) { - zend_hash_get_current_key_ex(Z_ARRVAL_P(return_value), (char **)&curr_key, &curr_key_len, &curr_idx, 0, NULL); - zend_hash_get_current_data(Z_ARRVAL_P(return_value), (void **)&curr_cas); - if (Z_BVAL_PP(curr_cas)) { - add_assoc_long(akc, curr_key, Z_LVAL_PP(curr_cas)); - } - } - pcbc_ht_iter_init(return_value); - } else { /* not multi */ - if (Z_BVAL_P(return_value)) { - add_assoc_long(akc, multi_keys[0], Z_LVAL_P(return_value)); - } - } - - ctx->cas = akc; - - observe_polling_internal(ctx, adurability, 0); - } - efree(ctx); if (couchbase_res->prefix_key_len) { int i;