static void get_callback(lcb_t instance, const void *cookie, lcb_error_t err, const lcb_get_resp_t *resp) { // If you can't read from active, read from replica. if (err != LCB_SUCCESS && flag == 0 ) { printf("REPLICA\t"); printf("Failed to read from active copy: %s\n", lcb_strerror(instance,err)); fprintf(stderr, "Reading from REPLICA. Failed to read from active copy: %s\n", lcb_strerror(instance,err)); lcb_get_replica_cmd_t rcmd = {0}; const lcb_get_replica_cmd_t *rcmdlist = &rcmd; rcmd.v.v1.key = resp->v.v0.key; rcmd.v.v1.nkey = (int)resp->v.v0.nkey; rcmd.v.v1.strategy = LCB_REPLICA_FIRST; err = lcb_get_replica(instance, NULL, 1, &rcmdlist); flag=1; lcb_wait(instance); // get_replica_callback is invoked here } else if (err != LCB_SUCCESS && flag == 1){ // If you fail to read from active and replica, just print the below message and the key. flag=0; printf("Failed to read from replica too: %s \n",lcb_strerror(instance,err)); fprintf(stderr, "Failed to read from replica too: %s\n", lcb_strerror(instance, err)); } else { // If you were able to get the key, print the key and value. printf("Retrieved key %.*s\t\t", (int)resp->v.v0.nkey, resp->v.v0.key); printf("Value is %.*s\n", (int)resp->v.v0.nbytes, resp->v.v0.bytes); fprintf(stderr, "Retrieved key %.*s\t value is %.*s\n", (int)resp->v.v0.nkey, resp->v.v0.key, (int)resp->v.v0.nbytes, resp->v.v0.bytes); flag=0; } }
/* * Obtain an object stored in Couchbase by given key. * * @since 1.0.0 * * @see http://couchbase.com/docs/couchbase-manual-2.0/couchbase-architecture-apis-memcached-protocol-additions.html#couchbase-architecture-apis-memcached-protocol-additions-getl * * @overload get(*keys, options = {}) * @param keys [String, Symbol, Array] One or several keys to fetch * @param options [Hash] Options for operation. * @option options [true, false] :extended (false) If set to +true+, the * operation will return a tuple +[value, flags, cas]+, otherwise (by * default) it returns just the value. * @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key. * Values larger than 30*24*60*60 seconds (30 days) are interpreted as * absolute times (from the epoch). * @option options [true, false] :quiet (self.quiet) If set to +true+, the * operation won't raise error for missing key, it will return +nil+. * Otherwise it will raise error in synchronous mode. In asynchronous * mode this option ignored. * @option options [Symbol] :format (nil) Explicitly choose the decoder * for this key (+:plain+, +:document+, +:marshal+). See * {Bucket#default_format}. * @option options [Fixnum, Boolean] :lock Lock the keys for time span. * If this parameter is +true+ the key(s) will be locked for default * timeout. Also you can use number to setup your own timeout in * seconds. If it will be lower that zero or exceed the maximum, the * server will use default value. You can determine actual default and * maximum values calling {Bucket#stats} without arguments and * inspecting keys "ep_getl_default_timeout" and "ep_getl_max_timeout" * correspondingly. See overloaded hash syntax to specify custom timeout * per each key. * @option options [true, false] :assemble_hash (false) Assemble Hash for * results. Hash assembled automatically if +:extended+ option is true * or in case of "get and touch" multimple keys. * @option options [true, false, :all, :first, Fixnum] :replica * (false) Read key from replica node. Options +:ttl+ and +:lock+ * are not compatible with +:replica+. Value +true+ is a synonym to * +:first+, which means sequentially iterate over all replicas * and return first successful response, skipping all failures. * It is also possible to query all replicas in parallel using * the +:all+ option, or pass a replica index, starting from zero. * * @yieldparam ret [Result] the result of operation in asynchronous mode * (valid attributes: +error+, +operation+, +key+, +value+, +flags+, * +cas+). * * @return [Object, Array, Hash] the value(s) (or tuples in extended mode) * associated with the key. * * @raise [Couchbase::Error::NotFound] if the key is missing in the * bucket. * * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect}) * * @raise [ArgumentError] when passing the block in synchronous mode * * @example Get single value in quiet mode (the default) * c.get("foo") #=> the associated value or nil * * @example Use alternative hash-like syntax * c["foo"] #=> the associated value or nil * * @example Get single value in verbose mode * c.get("missing-foo", :quiet => false) #=> raises Couchbase::NotFound * c.get("missing-foo", :quiet => true) #=> returns nil * * @example Get and touch single value. The key won't be accessible after 10 seconds * c.get("foo", :ttl => 10) * * @example Extended get * val, flags, cas = c.get("foo", :extended => true) * * @example Get multiple keys * c.get("foo", "bar", "baz") #=> [val1, val2, val3] * * @example Get multiple keys with assembing result into the Hash * c.get("foo", "bar", "baz", :assemble_hash => true) * #=> {"foo" => val1, "bar" => val2, "baz" => val3} * * @example Extended get multiple keys * c.get("foo", "bar", :extended => true) * #=> {"foo" => [val1, flags1, cas1], "bar" => [val2, flags2, cas2]} * * @example Asynchronous get * c.run do * c.get("foo", "bar", "baz") do |res| * ret.operation #=> :get * ret.success? #=> true * ret.key #=> "foo", "bar" or "baz" in separate calls * ret.value * ret.flags * ret.cas * end * end * * @example Get and lock key using default timeout * c.get("foo", :lock => true) * * @example Determine lock timeout parameters * c.stats.values_at("ep_getl_default_timeout", "ep_getl_max_timeout") * #=> [{"127.0.0.1:11210"=>"15"}, {"127.0.0.1:11210"=>"30"}] * * @example Get and lock key using custom timeout * c.get("foo", :lock => 3) * * @example Get and lock multiple keys using custom timeout * c.get("foo", "bar", :lock => 3) * * @overload get(keys, options = {}) * When the method receive hash map, it will behave like it receive list * of keys (+keys.keys+), but also touch each key setting expiry time to * the corresponding value. But unlike usual get this command always * return hash map +{key => value}+ or +{key => [value, flags, cas]}+. * * @param keys [Hash] Map key-ttl * @param options [Hash] Options for operation. (see options definition * above) * * @return [Hash] the values (or tuples in extended mode) associated with * the keys. * * @example Get and touch multiple keys * c.get("foo" => 10, "bar" => 20) #=> {"foo" => val1, "bar" => val2} * * @example Extended get and touch multiple keys * c.get({"foo" => 10, "bar" => 20}, :extended => true) * #=> {"foo" => [val1, flags1, cas1], "bar" => [val2, flags2, cas2]} * * @example Get and lock multiple keys for chosen period in seconds * c.get("foo" => 10, "bar" => 20, :lock => true) * #=> {"foo" => val1, "bar" => val2} */ VALUE cb_bucket_get(int argc, VALUE *argv, VALUE self) { struct cb_bucket_st *bucket = DATA_PTR(self); struct cb_context_st *ctx; VALUE rv, proc, exc; size_t ii; lcb_error_t err = LCB_SUCCESS; struct cb_params_st params; if (!cb_bucket_connected_bang(bucket, cb_sym_get)) { return Qnil; } memset(¶ms, 0, sizeof(struct cb_params_st)); rb_scan_args(argc, argv, "0*&", ¶ms.args, &proc); if (!bucket->async && proc != Qnil) { rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks"); } params.type = cb_cmd_get; params.bucket = bucket; params.cmd.get.keys_ary = rb_ary_new(); cb_params_build(¶ms); ctx = cb_context_alloc_common(bucket, proc, params.cmd.get.num); ctx->extended = params.cmd.get.extended; ctx->quiet = params.cmd.get.quiet; ctx->transcoder = params.cmd.get.transcoder; ctx->transcoder_opts = params.cmd.get.transcoder_opts; if (RTEST(params.cmd.get.replica)) { if (params.cmd.get.replica == cb_sym_all) { ctx->nqueries = lcb_get_num_replicas(bucket->handle); ctx->all_replicas = 1; } err = lcb_get_replica(bucket->handle, (const void *)ctx, params.cmd.get.num, params.cmd.get.ptr_gr); } else { err = lcb_get(bucket->handle, (const void *)ctx, params.cmd.get.num, params.cmd.get.ptr); } cb_params_destroy(¶ms); exc = cb_check_error(err, "failed to schedule get request", Qnil); if (exc != Qnil) { cb_context_free(ctx); rb_exc_raise(exc); } bucket->nbytes += params.npayload; if (bucket->async) { cb_maybe_do_loop(bucket); return Qnil; } else { if (ctx->nqueries > 0) { /* we have some operations pending */ lcb_wait(bucket->handle); } exc = ctx->exception; rv = ctx->rv; cb_context_free(ctx); if (exc != Qnil) { rb_exc_raise(exc); } exc = bucket->exception; if (exc != Qnil) { bucket->exception = Qnil; rb_exc_raise(exc); } if (params.cmd.get.gat || params.cmd.get.assemble_hash || (params.cmd.get.extended && (params.cmd.get.num > 1 || params.cmd.get.array))) { return rv; /* return as a hash {key => [value, flags, cas], ...} */ } if (params.cmd.get.num > 1 || params.cmd.get.array) { VALUE keys, ret; ret = rb_ary_new(); /* make sure ret is guarded so not invisible in a register * when stack scanning */ RB_GC_GUARD(ret); keys = params.cmd.get.keys_ary; for (ii = 0; ii < params.cmd.get.num; ++ii) { rb_ary_push(ret, rb_hash_aref(rv, rb_ary_entry(keys, ii))); } return ret; /* return as an array [value1, value2, ...] */ } else { VALUE vv = Qnil; rb_hash_foreach(rv, cb_first_value_i, (VALUE)&vv); return vv; } } }
PHP_COUCHBASE_LOCAL void php_couchbase_get_impl(INTERNAL_FUNCTION_PARAMETERS, int multi, int oo, int lock, int touch, int replica) { char *key, **keys; long *klens, klen = 0; int nkey = 0; long flag = 0; lcb_time_t exp = {0}; long expiry = 0; zval *res, *cas_token = NULL; int argflags; lcb_error_t retval; php_couchbase_res *couchbase_res; php_couchbase_ctx *ctx; zend_fcall_info fci = {0}; zend_fcall_info_cache fci_cache; argflags = oo ? PHP_COUCHBASE_ARG_F_OO : PHP_COUCHBASE_ARG_F_FUNCTIONAL; if (multi) { zval *akeys; zval **ppzval; zend_bool preserve_order; int i; if (lock) { PHP_COUCHBASE_GET_PARAMS_WITH_ZV(res, couchbase_res, argflags, "az|ll", &akeys, &cas_token, &flag, &expiry); } else if (touch) { PHP_COUCHBASE_GET_PARAMS_WITH_ZV(res, couchbase_res, argflags, "al|z", &akeys, &expiry, &cas_token); } else { PHP_COUCHBASE_GET_PARAMS_WITH_ZV(res, couchbase_res, argflags, "a|zl", &akeys, &cas_token, &flag); } nkey = zend_hash_num_elements(Z_ARRVAL_P(akeys)); keys = ecalloc(nkey, sizeof(char *)); klens = ecalloc(nkey, sizeof(long)); preserve_order = (flag & COUCHBASE_GET_PRESERVE_ORDER); array_init(return_value); for (i = 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)), i++) { if (zend_hash_get_current_data(Z_ARRVAL_P(akeys), (void **)&ppzval) == FAILURE) { nkey--; continue; } if (IS_ARRAY != Z_TYPE_PP(ppzval)) { convert_to_string_ex(ppzval); } if (!Z_STRLEN_PP(ppzval)) { nkey--; continue; } if (couchbase_res->prefix_key_len) { klens[i] = spprintf(&(keys[i]), 0, "%s_%s", couchbase_res->prefix_key, Z_STRVAL_PP(ppzval)); } else { keys[i] = Z_STRVAL_PP(ppzval); klens[i] = Z_STRLEN_PP(ppzval); } if (preserve_order) { add_assoc_null_ex(return_value, keys[i], klens[i] + 1); } } if (!nkey) { efree(keys); efree(klens); return; } if (cas_token && IS_ARRAY != Z_TYPE_P(cas_token)) { zval_dtor(cas_token); array_init(cas_token); } } else { if (lock) { PHP_COUCHBASE_GET_PARAMS_WITH_ZV(res, couchbase_res, argflags, "sz|l", &key, &klen, &cas_token, &expiry); } else if (touch) { PHP_COUCHBASE_GET_PARAMS_WITH_ZV(res, couchbase_res, argflags, "sl|z", &key, &klen, &expiry, &cas_token); } else { PHP_COUCHBASE_GET_PARAMS_WITH_ZV(res, couchbase_res, argflags, "s|f!z", &key, &klen, &fci, &fci_cache, &cas_token); } if (!klen) { return; } nkey = 1; if (couchbase_res->prefix_key_len) { klen = spprintf(&key, 0, "%s_%s", couchbase_res->prefix_key, key); } keys = &key; klens = &klen; if (cas_token) { zval_dtor(cas_token); ZVAL_NULL(cas_token); } } if (replica) { lcb_get_replica_cmd_t **commands = ecalloc(nkey, sizeof(lcb_get_replica_cmd_t *)); int ii; for (ii = 0; ii < nkey; ++ii) { lcb_get_replica_cmd_t *cmd = ecalloc(1, sizeof(lcb_get_replica_cmd_t)); commands[ii] = cmd; cmd->v.v0.key = keys[ii]; cmd->v.v0.nkey = klens[ii]; } ctx = ecalloc(1, sizeof(php_couchbase_ctx)); ctx->res = couchbase_res; ctx->rv = return_value; ctx->cas = cas_token; retval = lcb_get_replica(couchbase_res->handle, ctx, nkey, (const lcb_get_replica_cmd_t * const *)commands); for (ii = 0; ii < nkey; ++ii) { efree(commands[ii]); } efree(commands); } else { lcb_get_cmd_t **commands = ecalloc(nkey, sizeof(lcb_get_cmd_t *)); int ii; if (expiry) { exp = pcbc_check_expiry(expiry); } for (ii = 0; ii < nkey; ++ii) { lcb_get_cmd_t *cmd = ecalloc(1, sizeof(lcb_get_cmd_t)); commands[ii] = cmd; cmd->v.v0.key = keys[ii]; cmd->v.v0.nkey = klens[ii]; cmd->v.v0.lock = (int)lock; cmd->v.v0.exptime = exp; /* NB: this assumes sizeof(lcb_time_t) == sizeof(long) */ } ctx = ecalloc(1, sizeof(php_couchbase_ctx)); ctx->res = couchbase_res; ctx->rv = return_value; ctx->cas = cas_token; retval = lcb_get(couchbase_res->handle, ctx, nkey, (const lcb_get_cmd_t * const *)commands); for (ii = 0; ii < nkey; ++ii) { efree(commands[ii]); } efree(commands); } if (LCB_SUCCESS != retval) { if (couchbase_res->prefix_key_len) { int i; for (i = 0; i < nkey; i++) { efree(keys[i]); } } if (multi) { efree(keys); efree(klens); zval_dtor(return_value); } efree(ctx); couchbase_report_error(INTERNAL_FUNCTION_PARAM_PASSTHRU, oo, cb_lcb_exception, "Failed to schedule get request: %s", lcb_strerror(couchbase_res->handle, retval)); return; } couchbase_res->seqno += nkey; pcbc_start_loop(couchbase_res); if (ctx->res->rc != LCB_SUCCESS) { if (!multi) { RETVAL_FALSE; } if (ctx->res->rc == LCB_KEY_ENOENT) { if (fci.size) { zval *result; zval *zkey; zval *retval_ptr = NULL; zval **params[3]; int cbret; MAKE_STD_ZVAL(result); MAKE_STD_ZVAL(zkey); ZVAL_NULL(result); ZVAL_STRINGL(zkey, key, klen, 1); params[0] = &res; params[1] = &zkey; params[2] = &result; fci.retval_ptr_ptr = &retval_ptr; fci.param_count = 3; fci.params = params; cbret = zend_call_function(&fci, &fci_cache TSRMLS_CC); if (cbret == SUCCESS && fci.retval_ptr_ptr && *fci.retval_ptr_ptr) { if (Z_TYPE_P(retval_ptr) == IS_BOOL && Z_BVAL_P(retval_ptr)) { zval_ptr_dtor(fci.retval_ptr_ptr); zval_ptr_dtor(&zkey); efree(ctx); if (multi) { zval_dtor(return_value); } RETURN_ZVAL(result, 1, 1); } zval_ptr_dtor(fci.retval_ptr_ptr); } zval_ptr_dtor(&zkey); zval_ptr_dtor(&result); } } else { couchbase_report_error(INTERNAL_FUNCTION_PARAM_PASSTHRU, oo, cb_lcb_exception, "Failed to get a value from server: %s", lcb_strerror(couchbase_res->handle, ctx->res->rc)); } } efree(ctx); if (couchbase_res->prefix_key_len) { int i; for (i = 0; i < nkey; i++) { efree(keys[i]); } } if (multi) { efree(keys); efree(klens); } }