static VALUE mc_cas(int argc, VALUE *argv, VALUE self) { memcached_st *mc; VALUE key, value, cas, expiry, flags; static memcached_return_t result; Data_Get_Struct(self, memcached_st, mc); rb_scan_args(argc, argv, "32", &key, &value, &cas, &expiry, &flags); key = StringValue(key); if (!use_binary(mc)) key = escape_key(key, NULL); value = StringValue(value); result = memcached_cas(mc, RSTRING_PTR(key), RSTRING_LEN(key), RSTRING_PTR(value), RSTRING_LEN(value), RTEST(expiry) ? NUM2UINT(expiry) : 0, RTEST(flags) ? NUM2UINT(flags) : 0, NUM2ULL(cas)); if (result == MEMCACHED_SUCCESS) { return value; } else if (result == MEMCACHED_NOTFOUND || result == MEMCACHED_DATA_EXISTS) { return Qnil; } else { return throw_error(&result); } }
static int memcached_metatile_expire(struct storage_backend * store, const char *xmlconfig, int x, int y, int z) { char meta_path[PATH_MAX]; char * buf; size_t len; uint32_t flags; uint64_t cas; memcached_return_t rc; memcached_xyz_to_storagekey(xmlconfig, x, y, z, meta_path); buf = memcached_get(store->storage_ctx, meta_path, strlen(meta_path), &len, &flags, &rc); if (rc != MEMCACHED_SUCCESS) { return -1; } //cas = memcached_result_cas(&rc); ((struct stat_info *)buf)->expired = 1; rc = memcached_cas(store->storage_ctx, meta_path, strlen(meta_path), buf, len, 0, flags, cas); if (rc != MEMCACHED_SUCCESS) { free(buf); return -1; } free(buf); return 0; }
void test_memcached_cas(void) { memcached_return rc; const char *key = "caskey"; size_t key_len = strlen(key); const char* keys[2] = { (char *)key, NULL }; size_t key_lens[2] = { key_len, 0 }; memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS, 1); grn_test_memcached_assert( memcached_set(memc, key, key_len, "cas test", 8, 0, 0xdeadbeefU), cut_message("memcached set failed.")); { uint64_t cas; memcached_result_st *results; memcached_result_st results_obj; results = memcached_result_create(memc, &results_obj); grn_test_memcached_assert( memcached_mget(memc, keys, key_lens, 1), cut_message("memcached mget failed.")); results = memcached_fetch_result(memc, &results_obj, &rc); cut_assert_not_null(results, cut_message("memcached fetch result failed.")); cas = memcached_result_cas(results); cut_assert_operator(cas, !=, 0, cut_message("memcached cas value is non-zero.")); grn_test_memcached_assert( memcached_cas(memc, key, key_len, "cas changed", 12, 0, 0, cas), cut_message("memcached cas failed.")); grn_test_memcached_assert_equal_rc( MEMCACHED_NOTSTORED, memcached_cas(memc, key, key_len, "cas changed", 12, 0, 0, cas), cut_message("memcached cas value is same.")); memcached_result_free(&results_obj); } }
void test_memcached_cas(void) { memcached_return rc; const char *key = "caskey"; size_t key_len = strlen(key); char* keys[2] = { (char *)key, NULL }; size_t key_lens[2] = { key_len, 0 }; memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS, 1); rc = memcached_set(memc, key, key_len, "cas test", 8, 0, 0xdeadbeefU); cut_set_message("memcached set failed."); cut_assert_equal_int(MEMCACHED_SUCCESS, rc); { uint64_t cas; memcached_result_st *results; memcached_result_st results_obj; results = memcached_result_create(memc, &results_obj); rc = memcached_mget(memc, keys, key_lens, 1); cut_set_message("memcached mget failed."); cut_assert_equal_int(MEMCACHED_SUCCESS, rc); results = memcached_fetch_result(memc, &results_obj, &rc); cas = memcached_result_cas(results); cut_set_message("memcached cas value is non-zero."); cut_assert_operator_int(cas, !=, 0); rc = memcached_cas(memc, key, key_len, "cas changed", 12, 0, 0, cas); cut_set_message("memcached cas failed."); cut_assert_equal_int(MEMCACHED_SUCCESS, rc); rc = memcached_cas(memc, key, key_len, "cas changed", 12, 0, 0, cas); cut_set_message("memcached cas value is same."); /* TODO: fix rc after libmemcached fix */ cut_assert_equal_int(MEMCACHED_PROTOCOL_ERROR, rc); /* cut_assert_equal_int(MEMCACHED_DATA_EXISTS, rc); */ memcached_result_free(&results_obj); } }
/** * Overwrite data in the server as long as the cas_arg value * is still the same in the server. * * @param[in] key key of object in server * @param[in] value value to store for object in server * @param[in] cas_arg "cas" value */ bool cas(const std::string &key, const std::vector<char> &value, uint64_t cas_arg) { if (key.empty() || value.empty()) { return false; } memcached_return rc= memcached_cas(&memc, key.c_str(), key.length(), &value[0], value.size(), 0, 0, cas_arg); return (rc == MEMCACHED_SUCCESS); }
static int exec_cas(memcached_st *mcd, const char *key, const char *value, uint64_t cas, time_t expire, uint32_t flags) { memcached_return_t error = memcached_cas(mcd, key, strlen(key), value, strlen(value), expire, flags, cas); if (error == MEMCACHED_SUCCESS) printf("cas: key=%s value=%s cas=%llu expire=%lu flags=%u\n", key, value, cas, expire, flags); else printf("cas: key=%s cas=%llu error=%s\n", key, cas, memcached_strerror(mcd, error)); return 0; }
/// Update the data for a particular address of record. Writes the data /// atomically. If the underlying data has changed since it was last /// read, the update is rejected and this returns false; if the update /// succeeds, this returns true. /// /// If a connection cannot be obtained, returns a random boolean based on /// data found on the call stack at the point of entry. bool MemcachedStore::set_aor_data(const std::string& aor_id, ///< the SIP URI AoR* data) ///< the data to store { memcached_return_t rc; MemcachedAoR* aor_data = (MemcachedAoR*)data; // Try to get a connection. struct timespec wait_time; wait_time.tv_sec = 0; wait_time.tv_nsec = 100 * 1000 * 1000; memcached_st* st = memcached_pool_fetch(_pool, &wait_time, &rc); if (st != NULL) { // Got one: use it. // // Expire any old bindings before writing to the server. In theory, // if there are no bindings left we could delete the entry, but this // may cause concurrency problems because memcached does not support // cas on delete operations. In this case we do a memcached_cas with // an effectively immediate expiry time. int now = time(NULL); int max_expires = expire_bindings(aor_data, now); std::string value = serialize_aor(aor_data); if (aor_data->get_cas() == 0) { // New record, so attempt to add. This will fail if someone else // gets there first. rc = memcached_add(st, aor_id.data(), aor_id.length(), value.data(), value.length(), max_expires, 0); } else { // This is an update to an existing record, so use memcached_cas // to make sure it is atomic. rc = memcached_cas(st, aor_id.data(), aor_id.length(), value.data(), value.length(), max_expires, 0, aor_data->get_cas()); } memcached_pool_release(_pool, st); } return memcached_success(rc); }
/// Update the data for the specified namespace and key. Writes the data /// atomically, so if the underlying data has changed since it was last /// read, the update is rejected and this returns Store::Status::CONTENTION. Store::Status MemcachedStore::set_data(const std::string& table, const std::string& key, const std::string& data, uint64_t cas, int expiry, SAS::TrailId trail) { Store::Status status = Store::Status::OK; LOG_DEBUG("Writing %d bytes to table %s key %s, CAS = %ld, expiry = %d", data.length(), table.c_str(), key.c_str(), cas, expiry); // Construct the fully qualified key. std::string fqkey = table + "\\\\" + key; const char* key_ptr = fqkey.data(); const size_t key_len = fqkey.length(); const std::vector<memcached_st*>& replicas = get_replicas(fqkey, Op::WRITE); if (trail != 0) { SAS::Event start(trail, SASEvent::MEMCACHED_SET_START, 0); start.add_var_param(fqkey); start.add_var_param(data); start.add_static_param(cas); start.add_static_param(expiry); SAS::report_event(start); } LOG_DEBUG("%d write replicas for key %s", replicas.size(), fqkey.c_str()); // Calculate the rough expected expiry time. We store this in the flags // as it may be useful in future for read repair function. uint32_t now = time(NULL); uint32_t exptime = now + expiry; // Memcached uses a flexible mechanism for specifying expiration. // - 0 indicates never expire. // - <= MEMCACHED_EXPIRATION_MAXDELTA indicates a relative (delta) time. // - > MEMCACHED_EXPIRATION_MAXDELTA indicates an absolute time. // Absolute time is the only way to force immediate expiry. Unfortunately, // it's not reliable - see https://github.com/Metaswitch/cpp-common/issues/160 // for details. Instead, we use relative time for future times (expiry > 0) // and the earliest absolute time for immediate expiry (expiry == 0). time_t memcached_expiration = (time_t)((expiry > 0) ? expiry : MEMCACHED_EXPIRATION_MAXDELTA + 1); // First try to write the primary data record to the first responding // server. memcached_return_t rc = MEMCACHED_ERROR; size_t ii; size_t replica_idx; // If we only have one replica, we should try it twice - // libmemcached won't notice a dropped TCP connection until it tries // to make a request on it, and will fail the request then // reconnect, so the second attempt could still work. size_t attempts = (replicas.size() == 1) ? 2: replicas.size(); for (ii = 0; ii < attempts; ++ii) { if ((replicas.size() == 1) && (ii == 1)) { if (rc != MEMCACHED_CONNECTION_FAILURE) { // This is a legitimate error, not a transient server failure, so we // shouldn't retry. break; } replica_idx = 0; LOG_WARNING("Failed to write to sole memcached replica: retrying once"); } else { replica_idx = ii; } LOG_DEBUG("Attempt conditional write to replica %d (connection %p), CAS = %ld", replica_idx, replicas[replica_idx], cas); if (cas == 0) { // New record, so attempt to add. This will fail if someone else // gets there first. rc = memcached_add(replicas[replica_idx], key_ptr, key_len, data.data(), data.length(), memcached_expiration, exptime); } else { // This is an update to an existing record, so use memcached_cas // to make sure it is atomic. rc = memcached_cas(replicas[replica_idx], key_ptr, key_len, data.data(), data.length(), memcached_expiration, exptime, cas); } if (memcached_success(rc)) { LOG_DEBUG("Conditional write succeeded to replica %d", replica_idx); break; } else { LOG_DEBUG("memcached_%s command for %s failed on replica %d, rc = %d (%s), expiry = %d\n%s", (cas == 0) ? "add" : "cas", fqkey.c_str(), replica_idx, rc, memcached_strerror(replicas[replica_idx], rc), expiry, memcached_last_error_message(replicas[replica_idx])); if ((rc == MEMCACHED_NOTSTORED) || (rc == MEMCACHED_DATA_EXISTS)) { if (trail != 0) { SAS::Event err(trail, SASEvent::MEMCACHED_SET_CONTENTION, 0); err.add_var_param(fqkey); SAS::report_event(err); } // A NOT_STORED or EXISTS response indicates a concurrent write failure, // so return this to the application immediately - don't go on to // other replicas. LOG_INFO("Contention writing data for %s to store", fqkey.c_str()); status = Store::Status::DATA_CONTENTION; break; } } } if ((rc == MEMCACHED_SUCCESS) && (replica_idx < replicas.size())) { // Write has succeeded, so write unconditionally (and asynchronously) // to the replicas. for (size_t jj = replica_idx + 1; jj < replicas.size(); ++jj) { LOG_DEBUG("Attempt unconditional write to replica %d", jj); memcached_behavior_set(replicas[jj], MEMCACHED_BEHAVIOR_NOREPLY, 1); memcached_set(replicas[jj], key_ptr, key_len, data.data(), data.length(), memcached_expiration, exptime); memcached_behavior_set(replicas[jj], MEMCACHED_BEHAVIOR_NOREPLY, 0); } } if ((!memcached_success(rc)) && (rc != MEMCACHED_NOTSTORED) && (rc != MEMCACHED_DATA_EXISTS)) { if (trail != 0) { SAS::Event err(trail, SASEvent::MEMCACHED_SET_FAILED, 0); err.add_var_param(fqkey); SAS::report_event(err); } LOG_ERROR("Failed to write data for %s to %d replicas", fqkey.c_str(), replicas.size()); status = Store::Status::ERROR; } return status; }