Quellcode durchsuchen

memcached(sr): add support for setting of value expiration - $mctex(key)

- add support for setting of value expiration. As the memcache library
  don't provide functions to later change the expire value of an existing
  key we need to fetch the value and then store it again with the given
  time. This should be changed with a different (improved) library
- refactor functions a bit, use a helper method instead of duplicating
  the cache access functions
- update documentation
Henning Westerholt vor 16 Jahren
Ursprung
Commit
f3528b2e48

+ 22 - 8
modules_k/memcached/README

@@ -36,10 +36,11 @@ Henning Westerholt
 
    1.1. Storing and retrieving entries
    1.2. Using atomic operations
-   1.3. Set servers parameter
-   1.4. Set expire parameter
-   1.5. Set mode parameter
-   1.6. Set timeout parameter
+   1.3. Modifying expire time for existing entries
+   1.4. Set servers parameter
+   1.5. Set expire parameter
+   1.6. Set mode parameter
+   1.7. Set timeout parameter
 
 Chapter 1. Admin Guide
 
@@ -95,6 +96,15 @@ $mcinc(cnt) = 1; # increment by 1
 xlog("counter is now $mct(cnt)");
 $mcdec(cnt) = 1; # decrement by 1
 xlog("counter is now $mct(cnt)");
+...
+
+   Example 1.3. Modifying expire time for existing entries
+...
+$mct(test) = 1;
+xlog("stored value is $mct(test)");
+$mctex(test) = 10; # set expire time to 10 seconds
+# sleep 10 seconds
+xlog("stored value is $mct(test)"); # will return <null>
 ...
 
    This module is an addition to the existing htable functionality, not a
@@ -177,7 +187,7 @@ xlog("counter is now $mct(cnt)");
 
    Default value is "localhost:11211".
 
-   Example 1.3. Set servers parameter
+   Example 1.4. Set servers parameter
 ...
 modparam("memcached", "servers", "localhost:11211")
 ...
@@ -196,9 +206,12 @@ modparam("memcached", "servers", "localhost:11211")
    Items can also be deleted before there expire time when the available
    space in memory is exhausted.
 
+   Its possible to override this default value later on by setting a
+   different timeout for this key with the mctex pseudo-variable.
+
    Default value is "10800"s (3h).
 
-   Example 1.4. Set expire parameter
+   Example 1.5. Set expire parameter
 ...
 modparam("memcached", "expire", 10800)
 ...
@@ -213,7 +226,7 @@ modparam("memcached", "expire", 10800)
 
    Default value is "0" (overwrite).
 
-   Example 1.5. Set mode parameter
+   Example 1.6. Set mode parameter
 ...
 modparam("memcached", "mode", 0)
 ...
@@ -224,7 +237,7 @@ modparam("memcached", "mode", 0)
 
    Default value is "5000" (5s).
 
-   Example 1.6. Set timeout parameter
+   Example 1.7. Set timeout parameter
 ...
 modparam("memcached", "timeout", 10000)
 ...
@@ -236,6 +249,7 @@ modparam("memcached", "timeout", 10000)
      * $mct(key)
      * $mcinc(key)
      * $mcdec(key)
+     * $mctex(key)
 
    Exported pseudo-variables are documented at
    http://www.kamailio.org/dokuwiki/.

+ 19 - 0
modules_k/memcached/doc/memcached_admin.xml

@@ -44,6 +44,18 @@ $mcinc(cnt) = 1; # increment by 1
 xlog("counter is now $mct(cnt)");
 $mcdec(cnt) = 1; # decrement by 1
 xlog("counter is now $mct(cnt)");
+...
+			</programlisting>
+		</example>
+		<example>
+		<title>Modifying expire time for existing entries</title>
+			<programlisting format="linespecific">
+...
+$mct(test) = 1;
+xlog("stored value is $mct(test)");
+$mctex(test) = 10; # set expire time to 10 seconds
+# sleep 10 seconds
+xlog("stored value is $mct(test)"); # will return &lt;null&gt;
 ...
 			</programlisting>
 		</example>
@@ -170,6 +182,10 @@ modparam("memcached", "servers", "localhost:11211")
 			or on insertion of new entries when the cache is full. Items can also be deleted
 			before there expire time when the available space in memory is exhausted.
 		</para>
+		<para>
+			Its possible to override this default value later on by setting a different
+			timeout for this key with the <emphasis>mctex</emphasis> pseudo-variable.
+		</para>
 		<para>
 		<emphasis>
 			Default value is <quote>10800</quote>s (3h).
@@ -240,6 +256,9 @@ modparam("memcached", "timeout", 10000)
 			<listitem><para>
 				<emphasis>$mcdec(key)</emphasis>
 			</para></listitem>
+			<listitem><para>
+				<emphasis>$mctex(key)</emphasis>
+			</para></listitem>
 		</itemizedlist>
 		<para>
 		Exported pseudo-variables are documented at &kamwikilink;.

+ 134 - 89
modules_k/memcached/mcd_var.c

@@ -34,25 +34,76 @@
 
 
 /*!
- * \brief Checks if the key is not too long, hash it with MD5 if necessary
- * \param in input string
+ * \brief Checks if the key is avaiable and not too long, hash it with MD5 if necessary
+ * \param msg SIP message
+ * \param param pseudo-variable input parameter
  * \param out output string
+ * \return 0 on success, negative on failure
  */
-static inline void pv_mcd_key_check(str * in, str * out) {
+static inline int pv_mcd_key_check(struct sip_msg *msg, pv_param_t *param, str * out) {
 
+	str tmp;
 	static char hash[32];
 
-	if (in->len < 250) {
-		out->s = in->s;
-		out->len = in->len;
+	if (msg == NULL || param == NULL) {
+		LM_ERR("bad parameters\n");
+		return -1;
+	}
+
+	if (pv_printf_s(msg, param->pvn.u.dname, &tmp) != 0)
+	{
+		LM_ERR("cannot get key name\n");
+		return -1;
+	}
+
+	if (tmp.len < 250) {
+		out->s = tmp.s;
+		out->len = tmp.len;
 	} else {
-		LM_DBG("key too long, hash it\n");
-		MD5StringArray (hash, in, 1);
+		LM_DBG("key too long (%d), hash it\n", tmp.len);
+		MD5StringArray (hash, &tmp, 1);
 		out->s = hash;
 		out->len = 32;
 	}
+	return 0;
 }
 
+/*!
+ * \brief Helper to get a cached value from memcached
+ * \param msg SIP message
+ * \param key value key
+ * \param mcd_req request
+ * \param mcd_res result
+ * \return null on success, negative on failure
+ */
+static int pv_get_mcd_value_helper(struct sip_msg *msg, str *key,
+		struct memcache_req **mcd_req, struct memcache_res **mcd_res) {
+
+	/* we don't use mc_aget here, because we're multi-process */
+	if ( (*mcd_req = mc_req_new()) == NULL) {
+		PKG_MEM_ERROR;
+		return -1;
+	}
+	LM_DBG("allocate new memcache request at %p\n", *mcd_req);
+
+	if ( (*mcd_res = mc_req_add(*mcd_req, key->s, key->len)) == NULL) {
+		PKG_MEM_ERROR;
+		return -1;
+	}
+	LM_DBG("allocate new memcache result at %p\n", *mcd_res);
+
+	mc_get(memcached_h, *mcd_req);
+	if (! ( (*mcd_res)->_flags & MCM_RES_FOUND)) {
+		LM_ERR("could not get result for key %.*s\n", key->len, key->s);
+		LM_DBG("free memcache request and result at %p\n", mcd_req);
+		mc_req_free(*mcd_req);
+		return -1;
+	}
+	LM_DBG("result: %.*s for key %.*s with flag %d\n", (*mcd_res)->bytes, (char*)(*mcd_res)->val,
+		key->len, key->s, (*mcd_res)->flags);
+
+	return 0;
+}
 
 /*!
  * \brief Get a cached value from memcached
@@ -61,45 +112,23 @@ static inline void pv_mcd_key_check(str * in, str * out) {
  * \param res result
  * \return null on success, negative on failure
  */
-int pv_get_mcd_value(struct sip_msg *msg,  pv_param_t *param, pv_value_t *res) {
+int pv_get_mcd_value(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) {
 
 	unsigned int res_int = 0;
-	str param_str, res_str, key_str;
-
+	str key, res_str;
 	struct memcache_req *mcd_req = NULL;
 	struct memcache_res *mcd_res = NULL;
 
-	if (msg==NULL || res==NULL)
-		return pv_get_null(msg, param, res);
-
-	if(pv_printf_s(msg, param->pvn.u.dname, &param_str)!=0)
-	{
-		LM_ERR("cannot get key name\n");
+	if (pv_mcd_key_check(msg, param, &key) < 0) {
 		return pv_get_null(msg, param, res);
 	}
 
-	/* we don't use mc_aget here, because we're multi-process */
-	if ( (mcd_req = mc_req_new()) == NULL) {
-		PKG_MEM_ERROR;
+	if (res==NULL)
 		return pv_get_null(msg, param, res);
-	}
-	LM_DBG("allocate new memcache request at %p\n", mcd_req);
-
-	pv_mcd_key_check(&param_str, &key_str);
 
-	if ( (mcd_res = mc_req_add(mcd_req, key_str.s, key_str.len)) == NULL) {
-		PKG_MEM_ERROR;
-		return pv_get_null(msg, param, res);
-	}
-	LM_DBG("allocate new memcache result at %p\n", mcd_res);
-
-	mc_get(memcached_h, mcd_req);
-	if (! (mcd_res->_flags & MCM_RES_FOUND)) {
-		LM_ERR("could not get result for key %.*s\n", key_str.len, key_str.s);
+	if (pv_get_mcd_value_helper(msg, &key, &mcd_req, &mcd_res) < 0) {
 		return pv_get_null(msg, param, res);
 	}
-	LM_DBG("result: %.*s for key %.*s with flag %d\n", mcd_res->bytes, (char*)mcd_res->val,
-		key_str.len, key_str.s, mcd_res->flags);
 
 	res_str.len = mcd_res->bytes;
 	res_str.s = mcd_res->val;
@@ -109,13 +138,13 @@ int pv_get_mcd_value(struct sip_msg *msg,  pv_param_t *param, pv_value_t *res) {
 	if(mcd_res->flags&VAR_VAL_STR) {
 		 if (pkg_str_dup(&(res->rs), &res_str) < 0) {
 			LM_ERR("could not copy string\n");
-			return pv_get_null(msg, param, res);
+			goto errout;
 		}
 		res->flags = PV_VAL_STR;
 	} else {
 		if (str2int(&res_str, &res_int) < 0) {
 			LM_ERR("could not convert string %.*s to integer value\n", res_str.len, res_str.s);
-			return pv_get_null(msg, param, res);
+			goto errout;
 		}
 		res->rs = res_str;
 		res->ri = res_int;
@@ -125,6 +154,11 @@ int pv_get_mcd_value(struct sip_msg *msg,  pv_param_t *param, pv_value_t *res) {
 	mc_req_free(mcd_req);
 
 	return 0;
+
+errout:
+	LM_DBG("free memcache request and result at %p\n", mcd_req);
+	mc_req_free(mcd_req);
+	return pv_get_null(msg, param, res);
 }
 
 
@@ -141,27 +175,17 @@ int pv_get_mcd_value(struct sip_msg *msg,  pv_param_t *param, pv_value_t *res) {
  int pv_set_mcd_value(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val) {
 
 	unsigned int val_flag = 0;
-	str param_str, val_str, key_str;
-
-	if (msg==NULL || param==NULL) {
-		LM_ERR("bad parameters\n");
-		return -1;
-	}
+	str val_str, key;
 
-	if(pv_printf_s(msg, param->pvn.u.dname, &param_str)!=0)
-	{
-		LM_ERR("cannot get key name\n");
+	if (pv_mcd_key_check(msg, param, &key) < 0)
 		return -1;
-	}
-
-	pv_mcd_key_check(&param_str, &key_str);
 
 	if (val == NULL) {
-		if (mc_delete(memcached_h, key_str.s, key_str.len, 0) != 0) {
+		if (mc_delete(memcached_h, key.s, key.len, 0) != 0) {
 			LM_ERR("could not delete key %.*s\n", param->pvn.u.isname.name.s.len,
 				param->pvn.u.isname.name.s.s);
 		}
-		LM_DBG("delete key %.*s\n", key_str.len, key_str.s);
+		LM_DBG("delete key %.*s\n", key.len, key.s);
 		return 0;
 	}
 
@@ -173,17 +197,17 @@ int pv_get_mcd_value(struct sip_msg *msg,  pv_param_t *param, pv_value_t *res) {
 	}
 
 	if (memcached_mode == 0) {
-		if (mc_set(memcached_h, key_str.s, key_str.len, val_str.s, val_str.len, memcached_expire, val_flag) != 0) {
-			LM_ERR("could not set value for key %.*s\n", key_str.len, key_str.s);
+		if (mc_set(memcached_h, key.s, key.len, val_str.s, val_str.len, memcached_expire, val_flag) != 0) {
+			LM_ERR("could not set value for key %.*s\n", key.len, key.s);
 			return -1;
 		}
 	} else {
-		if (mc_add(memcached_h, key_str.s, key_str.len, val_str.s, val_str.len, memcached_expire, val_flag) != 0) {
-			LM_ERR("could not add value for key %.*s\n", key_str.len, key_str.s);
+		if (mc_add(memcached_h, key.s, key.len, val_str.s, val_str.len, memcached_expire, val_flag) != 0) {
+			LM_ERR("could not add value for key %.*s\n", key.len, key.s);
 			return -1;
 		}
 	}
-	LM_DBG("set value %.*s for key %.*s with flag %d\n", val_str.len, val_str.s, key_str.len, key_str.s, val_flag);
+	LM_DBG("set value %.*s for key %.*s with flag %d\n", val_str.len, val_str.s, key.len, key.s, val_flag);
 
 	return 0;
 }
@@ -207,58 +231,35 @@ static int pv_mcd_atomic_helper(struct sip_msg* msg, pv_param_t *param, int op,
 		const unsigned int val)) {
 
 	unsigned int value = 0;
-	str param_str, key_str;
+	str key;
 	struct memcache_req *mcd_req = NULL;
 	struct memcache_res *mcd_res = NULL;
 	
-	if (msg==NULL || param==NULL) {
-		LM_ERR("bad parameters\n");
-		return -1;
-	}
-
-	if(pv_printf_s(msg, param->pvn.u.dname, &param_str)!=0)
-	{
-		LM_ERR("cannot get key name\n");
-		return -1;
-	}
-
-	pv_mcd_key_check(&param_str, &key_str);
-
 	if (! val->flags&PV_VAL_INT) {
-		LM_ERR("invalid right value %.*s for atomic operation, strings not allowed\n",
+		LM_ERR("invalid value %.*s for atomic operation, strings not allowed\n",
 			val->rs.len, val->rs.s);
 		return -1;
 	}
 
-	/* we don't use mc_aget here, because we're multi-process */
-	if ( (mcd_req = mc_req_new()) == NULL) {
-		PKG_MEM_ERROR;
+	if (pv_mcd_key_check(msg, param, &key) < 0)
 		return -1;
-	}
-	LM_DBG("allocate new memcache request at %p\n", mcd_req);
-
-	if ( (mcd_res = mc_req_add(mcd_req, key_str.s, key_str.len)) == NULL) {
-		PKG_MEM_ERROR;
-		return -1;
-	}
-	LM_DBG("allocate new memcache result at %p\n", mcd_res);
 
-	mc_get(memcached_h, mcd_req);
-	if (! (mcd_res->_flags & MCM_RES_FOUND)) {
-		LM_DBG("key %.*s not found for atomic operation\n", key_str.len, key_str.s);
+	if (pv_get_mcd_value_helper(msg, &key, &mcd_req, &mcd_res) < 0) {
 		return -1;
 	}
-	LM_DBG("atomic operation on result %.*s for %d with flag %d\n", mcd_res->bytes, (char*)mcd_res->val,
-			val->ri, mcd_res->flags);
 
 	if(mcd_res->flags&VAR_VAL_STR) {
-		LM_ERR("could not do atomic operations on string for key %.*s\n", key_str.len, key_str.s);
+		LM_ERR("could not do atomic operations on string for key %.*s\n", key.len, key.s);
+		LM_DBG("free memcache request and result at %p\n", mcd_req);
+		mc_req_free(mcd_req);
 		return -1;
 	}
+
+	LM_DBG("atomic operation on result %.*s for %d with flag %d\n", mcd_res->bytes, (char*)mcd_res->val, val->ri, mcd_res->flags);
 	LM_DBG("free memcache request and result at %p\n", mcd_req);
 	mc_req_free(mcd_req);
 
-	value = atomic_ops(memcached_h, key_str.s, key_str.len, val->ri);
+	value = atomic_ops(memcached_h, key.s, key.len, val->ri);
 	LM_DBG("value from atomic operation %d\n", value);
 
 	return 0;
@@ -291,6 +292,50 @@ int inline pv_dec_mcd_value(struct sip_msg* msg, pv_param_t *param, int op, pv_v
 }
 
 
+/*!
+ * \brief Set the expire value in the cache of memcached
+ * \note The memcache library don't provide functions to change the expiration
+ * time for a certain key after creation, so we need to do a get and set here.
+ * \param msg SIP message
+ * \param param parameter
+ * \param op not used
+ * \param val value
+ * \return 0 on success, -1 on failure
+ */
+int pv_set_mcd_expire(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val)
+{
+	str key;
+	struct memcache_req *mcd_req = NULL;
+	struct memcache_res *mcd_res = NULL;
+
+	if (! val->flags&PV_VAL_INT) {
+		LM_ERR("invalid value %.*s for expire time, strings not allowed\n",
+			val->rs.len, val->rs.s);
+		return -1;
+	}
+
+	if (pv_mcd_key_check(msg, param, &key) < 0)
+		return -1;
+
+	if (pv_get_mcd_value_helper(msg, &key, &mcd_req, &mcd_res) < 0) {
+		return -1;
+	}
+
+	LM_DBG("set expire time %d on result %.*s for %d with flag %d\n", val->ri, mcd_res->bytes, (char*)mcd_res->val, val->ri, mcd_res->flags);
+
+	if (mc_set(memcached_h, key.s, key.len, mcd_res->val, mcd_res->bytes, val->ri, mcd_res->flags) != 0) {
+		LM_ERR("could not set expire time %d for key %.*s\n", val->ri, key.len, key.s);
+		LM_DBG("free memcache request and result at %p\n", mcd_req);
+		mc_req_free(mcd_req);
+		return -1;
+	}
+	LM_DBG("free memcache request and result at %p\n", mcd_req);
+	mc_req_free(mcd_req);
+
+	return 0;
+}
+
+
 /*!
  * \brief Parse the pseudo-variable specification parameter
  * \param sp pseudo-variable specification

+ 13 - 0
modules_k/memcached/mcd_var.h

@@ -73,6 +73,19 @@ int pv_inc_mcd_value(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t
 int pv_dec_mcd_value(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val);
 
 
+/*!
+ * \brief Set the expire value in the cache of memcached
+ * \note The memcache library don't provide functions to change the expiration
+ * time for a certain key after creation, so we need to do a get and set here.
+ * \param msg SIP message
+ * \param param parameter
+ * \param op not used
+ * \param val value
+ * \return 0 on success, -1 on failure
+ */
+int pv_set_mcd_expire(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val);
+
+
 /*!
  * \brief Parse the pseudo-variable specification parameter
  * \param sp pseudo-variable specification

+ 3 - 1
modules_k/memcached/memcached.c

@@ -71,6 +71,8 @@ static pv_export_t mod_pvs[] = {
 		pv_parse_mcd_name, 0, 0, 0 },
 	{ {"mcdec", sizeof("mcdec")-1}, PVT_OTHER, pv_get_mcd_value, pv_dec_mcd_value,
 		pv_parse_mcd_name, 0, 0, 0 },
+	{ {"mctex", sizeof("mctex")-1}, PVT_OTHER, pv_get_null, pv_set_mcd_expire,
+		pv_parse_mcd_name, 0, 0, 0 },
 	{ {0, 0}, 0, 0, 0, 0, 0, 0, 0 }
 };
 
@@ -255,7 +257,7 @@ static int mod_init(void) {
 	LM_INFO("connected to server %s:%s\n", server, port);
 	pkg_free(server);
 
-	LM_INFO("memcached client version is %s\n", MEMCACHE_VER);
+	LM_INFO("memcached client version is %s, released on %d\n", mc_version(), mc_reldate());
 	return 0;
 }