git » nmdb » commit d33c7a6

Implement CAS in libnmdb.

author Alberto Bertogli
2007-04-29 06:37:46 UTC
committer Alberto Bertogli
2007-04-29 06:37:46 UTC
parent 2c23a7bb908b3dbbe91b02cb687326a32bd0926c

Implement CAS in libnmdb.

libnmdb/libnmdb.3 +26 -6
libnmdb/libnmdb.c +82 -0
libnmdb/net-const.h +4 -0
libnmdb/nmdb.h +8 -0

diff --git a/libnmdb/libnmdb.3 b/libnmdb/libnmdb.3
index 27306d8..7426529 100644
--- a/libnmdb/libnmdb.3
+++ b/libnmdb/libnmdb.3
@@ -32,6 +32,15 @@ libnmdb - Library for interacting with a nmdb server
 .BI "             const unsigned char *" key ", size_t " ksize ");"
 .BI "int nmdb_cache_del(nmdb_t *" db ","
 .BI "             const unsigned char *" key ", size_t " ksize ");"
+.sp
+.BI "int nmdb_cas(nmdb_t *" db ","
+.BI "             const unsigned char *" key " , size_t " ksize ","
+.BI "             const unsigned char *" oldval ", size_t " ovsize ","
+.BI "             const unsigned char *" newval ", size_t " nvsize ")"
+.BI "int nmdb_cache_cas(nmdb_t *" db ","
+.BI "             const unsigned char *" key " , size_t " ksize ","
+.BI "             const unsigned char *" oldval ", size_t " ovsize ","
+.BI "             const unsigned char *" newval ", size_t " nvsize ")"
 .fi
 .SH DESCRIPTION
 
@@ -70,14 +79,19 @@ of the keys and values apply: keys can't exceed 64Kb, values can't exceed
 64Kb, and the size of a key + the size of it's associated value can't exceed
 64Kb.
 
-There are three kinds of operations:
+There are four kinds of operations:
 .IR set ,
-.I get
-and
+which sets pairs;
+.IR get ,
+which gets pairs;
 .IR del ,
-with their obvious meaning. The normal set and del operations return as soon
-as they've been queued on the server for asynchronous completion. Note that in
-this case no message is sent to the client when the operation completes.
+which removes pairs;
+and
+.IR cas ,
+which compares-and-sets values. The normal set and del operations return as
+soon as they've been queued on the server for asynchronous completion. Note
+that in this case no message is sent to the client when the operation
+completes.
 
 Each operation has variants, that make it behave in a different way. All three
 have "cache" variants, that only affect the cache and not the database; and
@@ -113,6 +127,12 @@ returns 1 if it was queued successfuly, or < 0 on failure. The cache and
 synchronous variant return 1 if the key was removed successfuly, 0 if the key
 was not in the database/cache, or < 0 on failure.
 
+.BR nmdb_cas ()
+is used to compare-and-swap a key's value. It takes an old value to compare
+with the one in the database, and if they match, it sets the key to the given
+new value. Returns 2 if the swap was performed, 1 if the values didn't match,
+0 if the key was not on in the database/cache, and < 0 on failure.
+
 .SH SEE ALSO
 
 .BR nmdb (1),
diff --git a/libnmdb/libnmdb.c b/libnmdb/libnmdb.c
index f581169..6e482e4 100644
--- a/libnmdb/libnmdb.c
+++ b/libnmdb/libnmdb.c
@@ -451,3 +451,85 @@ int nmdb_cache_del(nmdb_t *db, const unsigned char *key, size_t ksize)
 }
 
 
+static int do_cas(nmdb_t *db, const unsigned char *key, size_t ksize,
+		const unsigned char *oldval, size_t ovsize,
+		const unsigned char *newval, size_t nvsize,
+		int impact_db)
+{
+	ssize_t rv, t;
+	unsigned char *buf, *p;
+	size_t bsize;
+	uint32_t request, reply;
+	struct nmdb_srv *srv;
+
+	request = REQ_CACHE_CAS;
+	if (impact_db)
+		request = REQ_CAS;
+
+
+	/* Use the same buffer for the request and the reply.
+	 * Request: 4 bytes ver+id, 4 bytes request code, 4 bytes ksize, 4
+	 *		bytes ovsize, 4 bytes nvsize, ksize bytes key,
+	 *		ovsize bytes oldval, nvsize bytes newval.
+	 * Reply: 4 bytes id, 4 bytes reply code.
+	 */
+	bsize = 4 + 4 + 4 + 4 + 4 + ksize + ovsize + nvsize;
+	buf = malloc(bsize);
+	if (buf == NULL)
+		return -1;
+
+	* (uint32_t *) buf = htonl( (PROTO_VER << 28) | ID_CODE );
+	* ((uint32_t *) buf + 1) = htonl(request);
+	* ((uint32_t *) buf + 2) = htonl(ksize);
+	* ((uint32_t *) buf + 3) = htonl(ovsize);
+	* ((uint32_t *) buf + 4) = htonl(nvsize);
+	p = buf + 5 * 4;
+	memcpy(p, key, ksize);
+	p += ksize;
+	memcpy(p, oldval, ovsize);
+	p += ovsize;
+	memcpy(p, newval, nvsize);
+
+	srv = select_srv(db, key, ksize);
+	t = srv_send(db, srv, buf, bsize);
+	if (t <= 0) {
+		rv = -1;
+		goto exit;
+	}
+
+	reply = get_rep(db, srv, buf, bsize, NULL, NULL);
+
+	if (reply == REP_OK) {
+		rv = 2;
+		goto exit;
+	} else if (reply == REP_NOMATCH) {
+		rv = 1;
+		goto exit;
+	} else if (reply == REP_NOTIN) {
+		rv = 0;
+		goto exit;
+	}
+
+	/* REP_ERR or invalid response */
+	rv = -1;
+
+exit:
+	free(buf);
+	return rv;
+
+}
+
+int nmdb_cas(nmdb_t *db, const unsigned char *key, size_t ksize,
+		const unsigned char *oldval, size_t ovsize,
+		const unsigned char *newval, size_t nvsize)
+{
+	return do_cas(db, key, ksize, oldval, ovsize, newval, nvsize, 1);
+}
+
+int nmdb_cache_cas(nmdb_t *db, const unsigned char *key, size_t ksize,
+		const unsigned char *oldval, size_t ovsize,
+		const unsigned char *newval, size_t nvsize)
+{
+	return do_cas(db, key, ksize, oldval, ovsize, newval, nvsize, 0);
+}
+
diff --git a/libnmdb/net-const.h b/libnmdb/net-const.h
index a382490..127b9b9 100644
--- a/libnmdb/net-const.h
+++ b/libnmdb/net-const.h
@@ -23,6 +23,8 @@
 #define REQ_DEL_SYNC		0x106
 #define REQ_SET_ASYNC		0x107
 #define REQ_DEL_ASYNC		0x108
+#define REQ_CACHE_CAS		0x109
+#define REQ_CAS			0x110
 
 /* Network replies (different namespace from requests) */
 #define REP_ERR			0x800
@@ -30,6 +32,7 @@
 #define REP_CACHE_MISS		0x802
 #define REP_OK			0x803
 #define REP_NOTIN		0x804
+#define REP_NOMATCH		0x805
 
 /* Network error replies */
 #define ERR_VER			0x101	/* Version mismatch */
@@ -39,5 +42,6 @@
 #define ERR_MEM			0x105	/* Memory allocation error */
 #define ERR_DB			0x106	/* Database error */
 
+
 #endif
 
diff --git a/libnmdb/nmdb.h b/libnmdb/nmdb.h
index ea86356..7fc0994 100644
--- a/libnmdb/nmdb.h
+++ b/libnmdb/nmdb.h
@@ -38,5 +38,13 @@ int nmdb_del(nmdb_t *db, const unsigned char *key, size_t ksize);
 int nmdb_del_sync(nmdb_t *db, const unsigned char *key, size_t ksize);
 int nmdb_cache_del(nmdb_t *db, const unsigned char *key, size_t ksize);
 
+int nmdb_cas(nmdb_t *db, const unsigned char *key, size_t ksize,
+		const unsigned char *oldval, size_t ovsize,
+		const unsigned char *newval, size_t nvsize);
+int nmdb_cache_cas(nmdb_t *db, const unsigned char *key, size_t ksize,
+		const unsigned char *oldval, size_t ovsize,
+		const unsigned char *newval, size_t nvsize);
+
+
 #endif