Skip to content

Commit 8e8a491

Browse files
avsejingenthr
authored andcommitted
CCBC-17 Implement ketama distribution algorithm
Use ketama consistent hashing algorithm for memcached nodes. It is conform to libketama. Change-Id: I7c4d5fbaa0eecb276f90d68ec3d412fe9aa7be73 Reviewed-on: http://review.couchbase.org/9718 Tested-by: Sergey Avseyev <sergey.avseyev@gmail.com> Reviewed-by: Matt Ingenthron <matt@couchbase.com> Tested-by: Matt Ingenthron <matt@couchbase.com>
1 parent 045e223 commit 8e8a491

19 files changed

+1914
-197
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
/regression
4747
/stamp-h1
4848
/testapp
49+
/testketama
4950
/vbucketkeygen
5051
/vbuckettool
5152
/vc100.pdb

Makefile.am

+11-2
Original file line numberDiff line numberDiff line change
@@ -71,14 +71,14 @@ vbucket_get_master.3vbucket: docs/vbucket.pod
7171
vbucket_get_replica.3vbucket: docs/vbucket.pod
7272
${POD2MAN} -c "$*" -r "" -s 3lib $< $@
7373

74-
74+
noinst_LTLIBRARIES = libketama.la
7575

7676
lib_LTLIBRARIES = libvbucket.la
7777

7878
pkgconfigdir = $(libdir)/pkgconfig
7979
pkgconfig_DATA = libvbucket.pc
8080

81-
check_PROGRAMS= testapp regression
81+
check_PROGRAMS= testapp regression testketama
8282
TESTS = ${check_PROGRAMS}
8383

8484
# Test application to test stuff from C
@@ -90,6 +90,13 @@ regression_SOURCES = tests/regression.c
9090
regression_DEPENDENCIES= libvbucket.la
9191
regression_LDADD= libvbucket.la
9292

93+
testketama_SOURCES = tests/testketama.c
94+
testketama_DEPENDENCIES= libvbucket.la libketama.la
95+
testketama_LDADD= libvbucket.la libketama.la
96+
97+
libketama_la_SOURCES = src/ketama.c
98+
libketama_la_CFLAGS = $(AM_CFLAGS) ${NO_WERROR}
99+
93100
libvbucket_la_SOURCES= \
94101
src/crc32.c \
95102
src/hash.h \
@@ -106,6 +113,8 @@ current=0
106113
revision=0
107114
age=0
108115
libvbucket_la_LDFLAGS= -version-info $(current):$(revision):$(age) -no-undefined
116+
libvbucket_la_LIBADD = libketama.la
117+
libvbucket_la_DEPENDENCIES = libketama.la
109118

110119
vbuckettool_SOURCES = src/vbuckettool.c
111120
vbuckettool_DEPENDENCIES= libvbucket.la

NMakefile

+6-3
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,15 @@ TARGETOS=WINNT
2020
INSTALL=c:\local
2121
INSTALLDIRS=$(INSTALL) $(INSTALL)\include $(INSTALL)\include\libvbucket $(INSTALL)\lib $(INSTALL)\bin
2222

23-
libvbucket.dll: src\cJSON.obj src\vbucket.obj src\crc32.obj
24-
$(link) $(dlllflags) -out:libvbucket.dll -version:1.0 src\cJSON.obj src\vbucket.obj src\crc32.obj
23+
libvbucket.dll: src\cJSON.obj src\vbucket.obj src\crc32.obj src\hash.obj
24+
$(link) $(dlllflags) -out:libvbucket.dll -version:1.0 src\cJSON.obj src\vbucket.obj src\crc32.obj src\ketama.obj
2525

2626
src\crc32.obj: src\crc32.c
2727
$(cc) $(cdebug) $(cflags) $(cvarsdll) -D_CRT_SECURE_NO_WARNINGS -Fosrc\crc32.obj -Iwin32 -Iinclude -DLIBISASL_INTERNAL src\crc32.c
2828

29+
src\ketama.obj: src\ketama.c
30+
$(cc) $(cdebug) $(cflags) $(cvarsdll) -D_CRT_SECURE_NO_WARNINGS -Fosrc\ketama.obj -Iwin32 -Iinclude -DLIBISASL_INTERNAL src\ketama.c
31+
2932
src\cJSON.obj: src\cJSON.c
3033
$(cc) $(cdebug) $(cflags) $(cvarsdll) -D_CRT_SECURE_NO_WARNINGS -Fosrc\cJSON.obj -Iwin32 -Iinclude -DLIBISASL_INTERNAL src\cJSON.c
3134

@@ -42,5 +45,5 @@ install: $(INSTALLDIRS) libvbucket.dll
4245
$(INSTALL) $(INSTALL)\include $(INSTALL)\include\libvbucket $(INSTALL)\lib $(INSTALL)\bin:; -@mkdir $@
4346

4447
clean:
45-
-del libvbucket.dll libvbucket.exp libvbucket.lib src\cJSON.obj src\vbucket.obj src\crc32.obj vc100.pdb
48+
-del libvbucket.dll libvbucket.exp libvbucket.lib src\cJSON.obj src\vbucket.obj src\crc32.obj src\ketama.obj vc100.pdb
4649

README.markdown

+85-18
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,82 @@
11
libvbucket: a vbucket library for memcached
22
===========================================
33

4-
Config syntax
4+
Config Syntax
55
-------------
66

7+
libvbucket uses JSON for it's configuration format. The directory
8+
`tests/config` contains sample configurations for [vbucket][1] and
9+
[ketama][2] distribution algorithms taken from a live Couchbase
10+
cluster.
11+
12+
Envelope Section
13+
----------------
14+
15+
Configurations can have an optional envelope. If it is absent,
16+
libvbucket will treat the config stream as a VBucket section.
17+
18+
### name
19+
20+
The username which should be used for nodes. This will be used for both
21+
authentication in HTTP and over memcached binary protocol. Note that
22+
"default" is a special bucket which does not use HTTP auth or memcached
23+
protocol auth. Specifying "default" and empty string for password will
24+
configure the client to use the default bucket.
25+
26+
"name": "default",
27+
28+
### saslPassword
29+
30+
The password which should be used for nodes, which need authentication.
31+
32+
"saslPassword": "",
33+
34+
### nodeLocator
35+
36+
The distribution algorithm which will be used to map keys to nodes. If this
37+
field is absent the `vbucket` locator will be used. The possible values
38+
are `ketama` and `vbucket`.
39+
40+
"nodeLocator": "vbucket",
41+
42+
### vBucketServerMap
43+
44+
The section which describes the vbucket configuration. See the "VBucket
45+
Section" below.
46+
47+
### nodes
48+
49+
The list of objects which describe the nodes in the cluster. The
50+
parser will look for `couchApiBase`, `hostname` and `ports` fields in
51+
this entry.
52+
53+
* hostname
54+
55+
The hostname and port of the cluster's REST interface.
56+
57+
"hostname": "172.16.16.76:8091",
58+
59+
* couchApiBase
60+
61+
The endpoint for CouchDB REST interface. This endpoint should be used
62+
for Couch view execution.
63+
64+
"couchApiBase": "http://172.16.16.76:8091/default",
65+
66+
* ports
67+
68+
List of node ports. Currenty it contains `direct` and `proxy` port to
69+
connect to the node using memcached protocol. The proxy port could be
70+
used for vbucket unaware, legacy clients.
71+
72+
"ports": {
73+
"proxy": 11211,
74+
"direct": 11210
75+
},
76+
77+
VBucket Section
78+
---------------
79+
780
The configuration string is JSON.
881

982
Example:
@@ -23,19 +96,10 @@ Example:
2396

2497
### hashAlgorithm
2598

26-
The hash algorithm can be in upper or lower case. Libvbucket currently
27-
supports the following hash algorithms (via libhashkit):
28-
29-
* default - this is libhashkit's default of one_at_a_time
30-
* md5
31-
* crc - this is CRC32, a good general purpose hash for short strings
32-
* fnv1_64
33-
* fnv1a_64
34-
* fnv1_32
35-
* fnv1a_32
36-
* hsieh
37-
* murmur
38-
* jenkins
99+
The hash algorithm can be in upper or lower case. libvbucket currently
100+
uses a CRC32 hashing algorithm, a good general purpose hash for short
101+
strings. The hash algorithm will typically be ketama for memcached
102+
type buckets.
39103

40104
### numReplicas
41105

@@ -51,12 +115,15 @@ server config string in. This may change to be more structured later.
51115
### vBucketMap
52116

53117
This contains one entry per vBucket, and the number of entries must be
54-
a power of two. Each entry must be an array of numReplicas+1
118+
a power of two. Each entry must be an array of `numReplicas+1`
55119
zero-based indices into serverList, with the first entry indicating
56120
the master server for the bucket and the remaining entries specifying
57-
the replicas, in order. -1 indicates that no server is mapped for that
121+
the replicas, in order. `-1` indicates that no server is mapped for that
58122
particular master/replica of that particular vBucket.
59123

60-
In the future this will probably be extended to support "intermediate
61-
states" of vBuckets which are being migrated.
124+
Note there is also a vBucketMapForward which can be sent by the server
125+
in the case that changes are occurring. The vBucketMapForward indicates
126+
what the future state of the cluster layout will be.
62127

128+
[1]: https://github.com/membase/libvbucket/blob/master/tests/config/vbucket-eight-nodes
129+
[2]: https://github.com/membase/libvbucket/blob/master/tests/config/ketama-eight-nodes

include/libvbucket/vbucket.h

+37
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,15 @@ extern "C" {
5555
*/
5656
typedef struct vbucket_config_st* VBUCKET_CONFIG_HANDLE;
5757

58+
/**
59+
* Type of distribution used to map keys to servers. It is possible to
60+
* select algorithm using "locator" key in config.
61+
*/
62+
typedef enum {
63+
VBUCKET_DISTRIBUTION_VBUCKET = 0,
64+
VBUCKET_DISTRIBUTION_KETAMA = 1,
65+
} VBUCKET_DISTRIBUTION_TYPE;
66+
5867
/**
5968
* \addtogroup cfgcmp
6069
* @{
@@ -207,6 +216,16 @@ extern "C" {
207216
*/
208217
LIBVBUCKET_PUBLIC_API
209218
const char *vbucket_config_get_couch_api_base(VBUCKET_CONFIG_HANDLE vb, int i);
219+
220+
/**
221+
* Get the distribution type. Currently can be or "vbucket" (for
222+
* eventually persisted nodes) either "ketama" (for plain memcached
223+
* nodes).
224+
*
225+
* @return a member of VBUCKET_DISTRIBUTION_TYPE enum.
226+
*/
227+
LIBVBUCKET_PUBLIC_API
228+
VBUCKET_DISTRIBUTION_TYPE vbucket_config_get_distribution_type(VBUCKET_CONFIG_HANDLE vb);
210229
/**
211230
* @}
212231
*/
@@ -216,6 +235,24 @@ extern "C" {
216235
* @{
217236
*/
218237

238+
239+
/**
240+
* Map given key to server index. It is aware about current distribution
241+
* type.
242+
*
243+
* @param h the vbucket config
244+
* @param key pointer to the beginning of the key
245+
* @param nkey the size of the key
246+
* @param vbucket_id the vbucket identifier when vbucket distribution is
247+
* used or zero otherwise.
248+
* @param server_idx the server index
249+
*
250+
* @return zero on success
251+
*/
252+
LIBVBUCKET_PUBLIC_API
253+
int vbucket_map(VBUCKET_CONFIG_HANDLE h, const void *key, size_t nkey,
254+
int *vbucket_id, int *server_idx);
255+
219256
/**
220257
* Get the vbucket number for the given key.
221258
*

src/hash.h

+7-1
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,15 @@
1818
#ifndef LIBVBUCKET_HASH_H
1919
#define LIBVBUCKET_HASH_H 1
2020

21-
#include <inttypes.h>
21+
#include <stdint.h>
2222
#include <sys/types.h>
23+
#include <stdio.h>
2324

2425
uint32_t hash_crc32(const char *key, size_t key_length);
26+
uint32_t hash_ketama(const char *key, size_t key_length);
27+
void hash_md5(const char *key, size_t key_length, unsigned char *result);
28+
29+
void* hash_md5_update(void *ctx, const char *key, size_t key_length);
30+
void hash_md5_final(void *ctx, unsigned char *result);
2531

2632
#endif

src/ketama.c

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2+
3+
#include "hash.h"
4+
5+
/* Force md5 functions to be static. The compiler could show warnings but
6+
* it ok, we did it because we need to keep files in vendor/ directory
7+
* unmodified. */
8+
static void MD5Init();
9+
static void MD5Update();
10+
static void MD5Final();
11+
12+
/* This library uses the reference MD5 implementation from [RFC1321] */
13+
#define PROTOTYPES 1
14+
#include "rfc1321/md5c.c"
15+
#undef PROTOTYPES
16+
17+
void hash_md5(const char *key, size_t key_length, unsigned char *result)
18+
{
19+
MD5_CTX ctx;
20+
21+
MD5Init(&ctx);
22+
MD5Update(&ctx, (unsigned char *)key, key_length);
23+
MD5Final(result, &ctx);
24+
}
25+
26+
void* hash_md5_update(void *ctx, const char *key, size_t key_length)
27+
{
28+
if (ctx == NULL) {
29+
ctx = calloc(1, sizeof(MD5_CTX));
30+
MD5Init(ctx);
31+
}
32+
MD5Update(ctx, (unsigned char *)key, key_length);
33+
return ctx;
34+
}
35+
36+
void hash_md5_final(void *ctx, unsigned char *result)
37+
{
38+
if (ctx == NULL) {
39+
return;
40+
}
41+
MD5Final(result, ctx);
42+
free(ctx);
43+
}
44+
45+
uint32_t hash_ketama(const char *key, size_t key_length)
46+
{
47+
unsigned char digest[16];
48+
49+
hash_md5(key, key_length, digest);
50+
51+
return (uint32_t) ( (digest[3] << 24)
52+
|(digest[2] << 16)
53+
|(digest[1] << 8)
54+
| digest[0]);
55+
}

src/rfc1321/global.h

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/* GLOBAL.H - RSAREF types and constants
2+
*/
3+
4+
/* PROTOTYPES should be set to one if and only if the compiler supports
5+
function argument prototyping.
6+
The following makes PROTOTYPES default to 0 if it has not already
7+
been defined with C compiler flags.
8+
*/
9+
#ifndef PROTOTYPES
10+
#define PROTOTYPES 0
11+
#endif
12+
13+
#include <stdint.h>
14+
15+
/* POINTER defines a generic pointer type */
16+
typedef unsigned char *POINTER;
17+
18+
/* UINT2 defines a two byte word */
19+
typedef uint16_t UINT2;
20+
21+
/* UINT4 defines a four byte word */
22+
typedef uint32_t UINT4;
23+
24+
/* PROTO_LIST is defined depending on how PROTOTYPES is defined above.
25+
If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it
26+
returns an empty list.
27+
*/
28+
#if PROTOTYPES
29+
#define PROTO_LIST(list) list
30+
#else
31+
#define PROTO_LIST(list) ()
32+
#endif

0 commit comments

Comments
 (0)