Ejemplo n.º 1
0
/*
  Set the value, then quit to make sure it is flushed.
  Come back in and test that add fails.
*/
static test_return_t add_test(memcached_st *memc)
{
  memcached_return_t rc;
  const char *key= "foo";
  const char *value= "when we sanitize";
  unsigned long long setting_value;

  setting_value= memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_NO_BLOCK);

  rc= memcached_set(memc, key, strlen(key),
                    value, strlen(value),
                    (time_t)0, (uint32_t)0);
  test_true(rc == MEMCACHED_SUCCESS || rc == MEMCACHED_BUFFERED);
  memcached_quit(memc);
  rc= memcached_add(memc, key, strlen(key),
                    value, strlen(value),
                    (time_t)0, (uint32_t)0);

  /* Too many broken OS'es have broken loopback in async, so we can't be sure of the result */
  if (setting_value)
  {
    test_true(rc == MEMCACHED_NOTSTORED || rc == MEMCACHED_STORED);
  }
  else
  {
    test_true(rc == MEMCACHED_NOTSTORED);
  }

  return 0;
}
Ejemplo n.º 2
0
/*doc Memcached add(key, value[, expiration])
Asks memcached to store the value identified by the key,
but only if the server *doesn't* already hold data for this key.
Returns true on success, false in case of a collision.
Otherwise raises an exception.
*/
IoObject *IoMemcached_add(IoMemcached *self, IoObject *locals, IoMessage *m)
{
	IoSeq    *key   = IoMessage_locals_seqArgAt_(m, locals, 0);
	IoObject *value = IoMessage_locals_quickValueArgAt_(m, locals, 1);

	time_t expiration = IoMessage_argCount(m) == 3 ? IoMessage_locals_intArgAt_(m, locals, 2) : 0;

	uint32_t flags;
	size_t size;
	char *cvalue = IoMemcached_serialize(self, locals, value, &size, &flags);

	memcached_return_t rc;
	rc = memcached_add(DATA(self)->mc,
		CSTRING(key), IOSEQ_LENGTH(key),
		cvalue, size,
		expiration, flags
	);

	free(cvalue);

	if(rc != MEMCACHED_SUCCESS && rc != MEMCACHED_NOTSTORED)
		IoState_error_(IOSTATE, m, memcached_strerror(DATA(self)->mc, rc));

	// MEMCACHED_NOTSTORED is a legitmate error in the case of a collision.
	if(rc == MEMCACHED_NOTSTORED)
		return IOSTATE->ioFalse;

	return IOSTATE->ioTrue; // MEMCACHED_SUCCESS
}
Ejemplo n.º 3
0
static bool HHVM_METHOD(Memcache, add, const String& key, const Variant& var,
                                       int flag /*= 0*/, int expire /*= 0*/) {
  if (key.empty()) {
    raise_warning("Key cannot be empty");
    return false;
  }

  auto data = Native::data<MemcacheData>(this_);

  if (!hasAvailableServers(data)) {
    return false;
  }

  std::vector<char> serialized = memcache_prepare_for_storage(data, var, flag);

  String serializedKey = memcache_prepare_key(key);
  memcached_return_t ret = memcached_add(&data->m_memcache,
                                        serializedKey.c_str(),
                                        serializedKey.length(),
                                        serialized.data(),
                                        serialized.size(),
                                        expire, flag);

  return (ret == MEMCACHED_SUCCESS);
}
Ejemplo n.º 4
0
void
test_memcached_replace(void)
{
  uint32_t flags;
  memcached_return rc;

  grn_test_memcached_assert_equal_rc(
    MEMCACHED_NOTSTORED,
    memcached_replace(memc, "key", 3, "value", 5, 0, 0xdeadbeefU),
    cut_message("memcached replace succeeded."));

  sleep(1);

  grn_test_memcached_assert(
    memcached_add(memc, "key", 3, "value", 5, 0, 0xdeadbeefU),
    cut_message("memcached add failed."));

  grn_test_memcached_assert(
    memcached_replace(memc, "key", 3, "new-value", 9, 0, 0xdeadbeefU),
    cut_message("memcached replace failed."));

  val1 = memcached_get(memc, "key", 3, &val1_len, &flags, &rc);
  grn_test_memcached_assert(rc, cut_message("memcached get failed."));
  cut_assert_equal_string("new-value", val1);
  cut_assert_equal_uint(0xdeadbeefU, flags);
}
Ejemplo n.º 5
0
void
test_memcached_replace(void)
{
  uint32_t flags;
  memcached_return rc;

  rc = memcached_replace(memc, "key", 3, "value", 5, 0, 0xdeadbeefU);
  cut_set_message("memcached replace succeeded.");
  /* TODO: fix rc after libmemcached fix */
  cut_assert_equal_int(MEMCACHED_PROTOCOL_ERROR, rc);

  sleep(1);

  rc = memcached_add(memc, "key", 3, "value", 5, 0, 0xdeadbeefU);
  cut_set_message("memcached add failed.");
  cut_assert_equal_int(MEMCACHED_SUCCESS, rc);

  rc = memcached_replace(memc, "key", 3, "new-value", 9, 0, 0xdeadbeefU);
  cut_set_message("memcached replace failed.");
  cut_assert_equal_int(MEMCACHED_SUCCESS, rc);

  val1 = memcached_get(memc, "key", 3, &val1_len, &flags, &rc);
  cut_set_message("memcached get failed.");
  cut_assert_equal_int(MEMCACHED_SUCCESS, rc);
  cut_assert_equal_string("new-value", val1);
  cut_assert_equal_uint(0xdeadbeefU, flags);
}
Ejemplo n.º 6
0
static mrb_value mrb_memcached_add(mrb_state *mrb, mrb_value self)
{
  mrb_value key, val;
  mrb_int expr = 0;
  mrb_memcached_data *data = DATA_PTR(self);
  memcached_return mrt;

  mrb_get_args(mrb, "oo|i", &key, &val, &expr);
  switch (mrb_type(key)) {
    case MRB_TT_STRING:
      break;
    case MRB_TT_SYMBOL:
      key = mrb_obj_as_string(mrb, key);
      break;
    default:
      mrb_raise(mrb, E_RUNTIME_ERROR, "memcached key type is string or symbol");
  }
  val = mrb_obj_as_string(mrb, val);

  mrt = memcached_add(data->mst, RSTRING_PTR(key), RSTRING_LEN(key), RSTRING_PTR(val), RSTRING_LEN(val), (time_t)expr, (uint32_t)0);
  if (mrt != MEMCACHED_SUCCESS && mrt != MEMCACHED_STORED && mrt != MEMCACHED_NOTSTORED) {
    // add failed
    return mrb_nil_value();
  }
  return mrb_fixnum_value(mrt);
}
Ejemplo n.º 7
0
static int php_memc_sess_lock(memcached_st *memc, const char *key)
{
	char *lock_key = NULL;
	int lock_key_len = 0;
	unsigned long attempts;
	long write_retry_attempts = 0;
	long lock_maxwait = MEMC_G(sess_lock_max_wait);
	long lock_wait = MEMC_G(sess_lock_wait);
	long lock_expire = MEMC_G(sess_lock_expire);
	time_t expiration;
	memcached_return status;
	/* set max timeout for session_start = max_execution_time.  (c) Andrei Darashenka, Richter & Poweleit GmbH */
	if (lock_maxwait <= 0) {
		lock_maxwait = zend_ini_long(ZEND_STRS("max_execution_time"), 0);
		if (lock_maxwait <= 0) {
			lock_maxwait = MEMC_SESS_LOCK_EXPIRATION;
		}
	}
	if (lock_wait == 0) {
		lock_wait = MEMC_SESS_DEFAULT_LOCK_WAIT;
	}
	if (lock_expire <= 0) {
		lock_expire = lock_maxwait;
	}
	expiration  = lock_expire + 1;
	attempts = (unsigned long)((1000000.0 / lock_wait) * lock_maxwait);

	/* Set the number of write retry attempts to the number of replicas times the number of attempts to remove a server */
	if (MEMC_G(sess_remove_failed_enabled)) {
		write_retry_attempts = MEMC_G(sess_number_of_replicas) * ( memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT) + 1);
	}

	lock_key_len = spprintf(&lock_key, 0, "lock.%s", key);
	do {
		status = memcached_add(memc, lock_key, lock_key_len, "1", sizeof("1")-1, expiration, 0);
		if (status == MEMCACHED_SUCCESS) {
			MEMC_G(sess_locked) = 1;
			MEMC_G(sess_lock_key) = lock_key;
			MEMC_G(sess_lock_key_len) = lock_key_len;
			return 0;
		} else if (status != MEMCACHED_NOTSTORED && status != MEMCACHED_DATA_EXISTS) {
			if (write_retry_attempts > 0) {
				write_retry_attempts--;
				continue;
			}
			php_error_docref(NULL, E_WARNING, "Write of lock failed");
			break;
		}

		if (lock_wait > 0) {
			usleep(lock_wait);
		}
	} while(--attempts > 0);

	efree(lock_key);
	return -1;
}
Ejemplo n.º 8
0
 /**
  * Add an object with the specified key and value to the server. This
  * function returns false if the object already exists on the server.
  *
  * @param[in] key key of object to add
  * @param[in] value of object to add
  * @return true on success; false otherwise
  */
 bool add(const std::string &key, const std::vector<char> &value)
 {
   if (key.empty() || value.empty())
   {
     return false;
   }
   memcached_return rc= memcached_add(&memc, key.c_str(), key.length(), 
                                      &value[0], value.size(), 0, 0);
   return (rc == MEMCACHED_SUCCESS);
 }
void rate_limiter::update(const std::string &ip, int bytes)
{
  if (ptr)
  {
    time_t now = time(NULL);
    std::string key;
    state *sp;
    size_t length;
    uint32_t flags;
    memcached_return error;

    key = "cgimap:" + ip;

    retry:

    if (ptr &&
        (sp = (state *)memcached_get(ptr, key.data(), key.size(), &length, &flags, &error)) != NULL)
    {
      assert(length == sizeof(state));

      int64_t elapsed = now - sp->last_update;

      sp->last_update = now;

      if (elapsed * bytes_per_sec < sp->bytes_served)
      {
         sp->bytes_served = sp->bytes_served - elapsed * bytes_per_sec + bytes;
      }
      else
      {
         sp->bytes_served = bytes;
      }

      // should use CAS but it's a right pain so we'll wing it for now...
      memcached_replace(ptr, key.data(), key.size(), (char *)sp, sizeof(state), 0, 0);

      free(sp);
    }
    else
    {
      state s;

      s.last_update = now;
      s.bytes_served = bytes;

      if (memcached_add(ptr, key.data(), key.size(), (char *)&s, sizeof(state), 0, 0) == MEMCACHED_NOTSTORED)
      {
        goto retry;
      }
    }
  }

  return;
}
Ejemplo n.º 10
0
int ssMemcached::add(char *sKey, size_t nKeyLen, char *pValue, int pValueLen, int nTime) {
    uint32_t flag = 0;

    retCode_ = memcached_add(memc_, sKey, nKeyLen, pValue, pValueLen, nTime, flag);

    if (retCode_ == MEMCACHED_SUCCESS) {
        //fprintf(stderr,"Key stored successfully\n");
    }
    else {
        fprintf(stderr, "Couldn't store key: %s\n", memcached_strerror(memc_, retCode_));
        return 0;
    }
    return 1;
}
Ejemplo n.º 11
0
static int
exec_add(memcached_st *mcd, const char *key, const char *value,
		 time_t expire, uint32_t flags)
{
	memcached_return_t	error = memcached_add(mcd, key, strlen(key),
											  value, strlen(value),
											  expire, flags);
	if (error == MEMCACHED_SUCCESS)
		printf("add: key=%s value=%s expire=%lu flags=%u\n",
			   key, value, expire, flags);
	else
		printf("add: key=%s error=%s\n",
			   key, memcached_strerror(mcd, error));
	return 0;
}
Ejemplo n.º 12
0
void store_memcached(memcached_st *cache, const char *path,
		struct nipc_packet *packet) {
	char *key = key_for_memcached(path, packet->type);
	memcached_return_t memcached_response = memcached_add(cache, key,
			strlen(key), packet->data, packet->data_length, (time_t) 0,
			(uint32_t) 0);
	if (memcached_response == MEMCACHED_SUCCESS) {
		log_info(memcached_logger, "Se guardaron %d bytes para la clave %s",
				packet->data_length, key);
	} else {
		log_error(memcached_logger, "Error guardando datos en la clave %s: %s",
				key, memcached_strerror(cache, memcached_response));
	}
	free(key);
}
Ejemplo n.º 13
0
/**
 * @function memcached.add
 * 
 * ### Synopsis
 * 
 * var rc = memcached.add(handle, key, value);
 * var rc = memcached.add(handle, key, value, expiration);
 * var rc = memcached.add(handle, key, value, expiration, flags);
 * 
 * Store information in memcached indexed by key.  
 * 
 * If an object identified by key already exists on the server, an error occurs.
 * 
 * @param {object} handle - handle to memcached connection.
 * @param {string} key - key of data to set in memcached.
 * @param {string} value - value of data to set in memcached.
 * @param {int} expiration - length of time value is valid (after this it will be removed from memcached automatically).
 * @param {int} flags - user defined integer value stored along with the value.
 * @return {int} rc - result code; 0 if no error, otherwise the error code.
 */
JSVAL _memcached_add (JSARGS args) {
    HandleScope scope;
    M *handle = HANDLE(args[0]);
    String::Utf8Value key(args[1]);
    String::Utf8Value value(args[2]);
    time_t expiration = 0;
    if (args.Length() > 3) {
        expiration = args[3]->IntegerValue();
    }
    uint32_t flags = 0;
    if (args.Length() > 4) {
        flags = args[4]->IntegerValue();
    }

    return scope.Close(Integer::New(memcached_add(handle, *key, strlen(*key), *value, strlen(*value), expiration, flags)));
}
Ejemplo n.º 14
0
static void rush_add(int nr, char *buf, char seed, uint32_t key_len, uint32_t value_len, memcached_st *memc)
{
	int i;
	memcached_return_t rc;
	char *key, *value;

	for(i=0; i<nr; i++) {
		generate_key_value(i,buf,seed,&key,key_len,&value,value_len);

		rc=memcached_add(memc,key,key_len,value,value_len,(time_t)0,(uint32_t)0);
		if(memcached_failed(rc)) {
			printf("memcached_add() failed: %s\n",memcached_strerror(memc,rc));
		}
		PRINTF("i=%d, key_len=%ld, value_len=%ld\n",i,key_len,value_len);
	}
}
Ejemplo n.º 15
0
bool c_Memcache::t_add(CStrRef key, CVarRef var, int flag /*= 0*/,
                       int expire /*= 0*/) {
  if (key.empty()) {
    raise_warning("Key cannot be empty");
    return false;
  }

  String serialized = memcache_prepare_for_storage(var, flag);

  memcached_return_t ret = memcached_add(&m_memcache,
                                        key.c_str(), key.length(),
                                        serialized.c_str(),
                                        serialized.length(),
                                        expire, flag);

  return (ret == MEMCACHED_SUCCESS);
}
Ejemplo n.º 16
0
void Cache::add( const MC2String& key,
                 const void* data,
                 size_t dataLength,
                 time_t expiration /*= 0*/,
                 uint32_t flags /*= 0*/ ) {
   SysUtility::IgnorePipe ignorePipe;

   memcached_return rc = memcached_add( m_pimpl->memc.get(),
                                        key.c_str(),
                                        strlen( key.c_str() ),
                                        (const char*)data,
                                        dataLength,
                                        expiration,
                                        flags );

   throwOnFail( rc, m_pimpl->memc.get(), "Couldn't add a value" );
}
Ejemplo n.º 17
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);
  }
static
zend_bool s_lock_session(memcached_st *memc, zend_string *sid)
{
	memcached_return rc;
	char *lock_key;
	size_t lock_key_len;
	time_t expiration;
	zend_long wait_time, retries;
	php_memcached_user_data *user_data = memcached_get_user_data(memc);

	lock_key_len = spprintf(&lock_key, 0, "lock.%s", sid->val);
	expiration   = s_lock_expiration();

	wait_time = MEMC_SESS_INI(lock_wait_min);
	retries   = MEMC_SESS_INI(lock_retries);

	do {
		rc = memcached_add(memc, lock_key, lock_key_len, "1", sizeof ("1") - 1, expiration, 0);

		switch (rc) {

			case MEMCACHED_SUCCESS:
				user_data->lock_key  = zend_string_init(lock_key, lock_key_len, user_data->is_persistent);
				user_data->is_locked = 1;
			break;

			case MEMCACHED_NOTSTORED:
			case MEMCACHED_DATA_EXISTS:
				if (retries > 0) {
					usleep(wait_time * 1000);
					wait_time = MIN(MEMC_SESS_INI(lock_wait_max), wait_time * 2);
				}
			break;

			default:
				php_error_docref(NULL, E_WARNING, "Failed to write session lock: %s", memcached_strerror (memc, rc));
				break;
		}
	} while (!user_data->is_locked && retries-- > 0);

	efree(lock_key);
	return user_data->is_locked;
}
Ejemplo n.º 19
0
Memcache_Res CMemcache:: Memcac_Prepend(string strKey, const char* pValue, size_t val_size, time_t expiration)
{
	Memcache_Res mRes 	= MEMCACHED_SUCCESS;

	if(strKey.empty() || NULL == pValue)
		return static_cast<Memcache_Res>(MEMCACHED_INVALID_ARGUMENTS);

	// lock and prepend
	pthread_mutex_lock(&m_hmc_mutex);
	if(NULL == m_hMc)
		return MEMCACHED_SERVER_ERROR;
	
	mRes = memcached_prepend(m_hMc, strKey.c_str(), strKey.length(), pValue, val_size, expiration, 0);
	if(MEMCACHED_NOTSTORED == mRes)
		mRes = memcached_add(m_hMc, strKey.c_str(), strKey.length(), pValue, val_size, expiration, 0);
	pthread_mutex_unlock(&m_hmc_mutex);

	return mRes;
}
Ejemplo n.º 20
0
VALUE mc_add(int argc, VALUE *argv, VALUE self) {
  memcached_st *mc;
  VALUE key, value, expiry, flags;
  static memcached_return_t result;

  Data_Get_Struct(self, memcached_st, mc);
  rb_scan_args(argc, argv, "22", &key, &value, &expiry, &flags);

  key = StringValue(key);
  if (!use_binary(mc)) key = escape_key(key, NULL);
  value = StringValue(value);

  result = memcached_add(mc, RSTRING_PTR(key), RSTRING_LEN(key), RSTRING_PTR(value), RSTRING_LEN(value),
                         RTEST(expiry) ? NUM2UINT(expiry) : 0,
                         RTEST(flags)  ? NUM2UINT(flags)  : 0);

  if (result == MEMCACHED_SUCCESS) {
    return value;
  } else if(result == MEMCACHED_NOTSTORED) {
    return Qnil;
  } else {
    return throw_error(&result);
  }
}
Ejemplo n.º 21
0
static int
exec_simple_bench(memcached_st *mcd, double scale)
{
	char	kbuf[256];
	char	vbuf[1024 * 1024];
	int		klen, vlen;
	int		i, n, count;
	int		num_gets = 0;
	int		num_adds = 0;
	long	total = 0;
	long	interval;
	struct timeval tv1, tv2;

	if (scale < 100.0)
		scale = 100.0;

	n = (int)(scale * 64);
	count = (int)(scale * 512);

	srand(count);

	gettimeofday(&tv1, NULL);

	memcached_flush(mcd, 0);

	while (count-- > 0)
	{
		memcached_return_t	error;
		uint32_t	flags;
		char	   *value;
		size_t		value_length;

		i = rand() % n;

		klen = sprintf(kbuf, "mcdbench_key%06u", i);

		value = memcached_get(mcd, kbuf, klen, &value_length, &flags, &error);
		if (value == NULL)
		{
			if (error != MEMCACHED_NOTFOUND)
			{
				printf("key=%s error=%s\n", kbuf, memcached_strerror(mcd, error));
				continue;
			}
			make_pseudo_value(i, vbuf, &vlen);
			error = memcached_add(mcd, kbuf, klen, vbuf, vlen, 0, 0);
			if (error != MEMCACHED_SUCCESS)
			{
				printf("key=%s error=%s\n", kbuf, memcached_strerror(mcd, error));
				continue;
			}
			num_adds++;
			total += vlen;
		}
		else
		{
			num_gets++;
			total += value_length;
			free(value);
		}
	}
	gettimeofday(&tv2, NULL);

	interval = (tv2.tv_sec - tv1.tv_sec) * 1000000 + (tv2.tv_usec - tv1.tv_usec);

	printf("time: %.2f  adds: %u  gets: %u  transfer: %lubytes\n",
		   ((double)interval) / 1000000.0,
		   num_adds, num_gets, total);
	return 0;
}
Ejemplo n.º 22
0
int pr_memcache_kadd(pr_memcache_t *mcache, module *m, const char *key,
    size_t keysz, void *value, size_t valuesz, time_t expires, uint32_t flags) {
  memcached_return res;

  /* XXX Should we allow null values to be added, thus allowing use of keys
   * as sentinels?
   */
  if (mcache == NULL ||
      m == NULL ||
      key == NULL ||
      value == NULL) {
    errno = EINVAL;
    return -1;
  }

  mcache_set_module_namespace(mcache, m);
  res = memcached_add(mcache->mc, key, keysz, value, valuesz, expires, flags); 
  mcache_set_module_namespace(mcache, NULL);

  switch (res) {
    case MEMCACHED_SUCCESS:
      return 0;

    case MEMCACHED_ERRNO:
      if (errno != EINPROGRESS) {
        int xerrno = errno;

        pr_trace_msg(trace_channel, 3,
          "error adding key (%lu bytes), value (%lu bytes): system error: %s",
          (unsigned long) keysz, (unsigned long) valuesz, strerror(xerrno));

        errno = xerrno;

      } else {
        /* We know that we're not using nonblocking IO; this value usually
         * means that libmemcached could not connect to the configured
         * memcached servers.  So set the value to something more
         * indicative, and fall through.
         */
        res = MEMCACHED_CONNECTION_FAILURE;
      }
      break;

    case MEMCACHED_SERVER_MARKED_DEAD:
    case MEMCACHED_CONNECTION_FAILURE: {
      memcached_server_instance_st server;

      server = memcached_server_get_last_disconnect(mcache->mc);
      if (server != NULL) {
        pr_trace_msg(trace_channel, 3,
          "unable to connect to %s:%d", memcached_server_name(server),
          memcached_server_port(server));
      }

      break;
    }

    default:
      pr_trace_msg(trace_channel, 2,
        "error adding key (%lu bytes), value (%lu bytes): %s",
        (unsigned long) keysz, (unsigned long) valuesz,
        memcached_strerror(mcache->mc, res));
      errno = EPERM;
      break;
  }

  return -1;
}
Ejemplo n.º 23
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;
}
Ejemplo n.º 24
0
int main(int argc, char *argv[])
{
  memcached_st *memc;
  memcached_return_t rc;
  memcached_server_st *servers;

  int return_code= 0;

  options_parse(argc, argv);
  initialize_sockets();

  memc= memcached_create(NULL);
  process_hash_option(memc, opt_hash);

  if (!opt_servers)
  {
    char *temp;

    if ((temp= getenv("MEMCACHED_SERVERS")))
    {
      opt_servers= strdup(temp);
    }
    else
    {
      fprintf(stderr, "No Servers provided\n");
      exit(1);
    }
  }

  if (opt_servers)
    servers= memcached_servers_parse(opt_servers);
  else
    servers= memcached_servers_parse(argv[--argc]);

  memcached_server_push(memc, servers);
  memcached_server_list_free(servers);
  memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL,
                         (uint64_t)opt_binary);
  if (!initialize_sasl(memc, opt_username, opt_passwd))
  {
    memcached_free(memc);
    return 1;
  }

  while (optind < argc)
  {
    struct stat sbuf;
    int fd;
    char *ptr;
    ssize_t read_length;
    char *file_buffer_ptr;

    fd= open(argv[optind], O_RDONLY);
    if (fd < 0)
    {
      fprintf(stderr, "memcp: %s: %s\n", argv[optind], strerror(errno));
      optind++;
      continue;
    }

    (void)fstat(fd, &sbuf);

    ptr= rindex(argv[optind], '/');
    if (ptr)
      ptr++;
    else
      ptr= argv[optind];

    if (opt_verbose)
    {
      static const char *opstr[] = { "set", "add", "replace" };
      printf("op: %s\nsource file: %s\nlength: %zu\n"
	     "key: %s\nflags: %x\nexpires: %llu\n",
	     opstr[opt_method - OPT_SET], argv[optind], (size_t)sbuf.st_size,
	     ptr, opt_flags, (unsigned long long)opt_expires);
    }

    if ((file_buffer_ptr= (char *)malloc(sizeof(char) * (size_t)sbuf.st_size)) == NULL)
    {
      fprintf(stderr, "malloc: %s\n", strerror(errno));
      exit(1);
    }

    if ((read_length= read(fd, file_buffer_ptr, (size_t)sbuf.st_size)) == -1)
    {
      fprintf(stderr, "read: %s\n", strerror(errno));
      exit(1);
    }

    if (read_length != sbuf.st_size)
    {
      fprintf(stderr, "Failure reading from file\n");
      exit(1);
    }

    if (opt_method == OPT_ADD)
      rc= memcached_add(memc, ptr, strlen(ptr),
                        file_buffer_ptr, (size_t)sbuf.st_size,
			opt_expires, opt_flags);
    else if (opt_method == OPT_REPLACE)
      rc= memcached_replace(memc, ptr, strlen(ptr),
			    file_buffer_ptr, (size_t)sbuf.st_size,
			    opt_expires, opt_flags);
    else
      rc= memcached_set(memc, ptr, strlen(ptr),
                        file_buffer_ptr, (size_t)sbuf.st_size,
                        opt_expires, opt_flags);

    if (rc != MEMCACHED_SUCCESS)
    {
      fprintf(stderr, "memcp: %s: memcache error %s",
	      ptr, memcached_strerror(memc, rc));
      if (memc->cached_errno)
	fprintf(stderr, " system error %s", strerror(memc->cached_errno));
      fprintf(stderr, "\n");

      return_code= -1;
    }

    free(file_buffer_ptr);
    close(fd);
    optind++;
  }

  memcached_free(memc);

  if (opt_servers)
    free(opt_servers);
  if (opt_hash)
    free(opt_hash);
  shutdown_sasl();

  return return_code;
}