Ejemplo n.º 1
0
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);
  }
}
Ejemplo n.º 2
0
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;
}
Ejemplo n.º 3
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);
  }
}
Ejemplo n.º 4
0
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);
  }
}
Ejemplo n.º 5
0
 /**
  * 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);
 }
Ejemplo n.º 6
0
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;
}
Ejemplo n.º 7
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);
  }
Ejemplo n.º 8
0
/// 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;
}