33 #define PREFIX_HASH_SIZE 256
36 static int num_prefixes = 0;
37 static int total_prefix_size = 0;
39 void stats_prefix_init() {
40 memset(prefix_stats, 0,
sizeof(prefix_stats));
47 void stats_prefix_clear() {
50 for (i = 0; i < PREFIX_HASH_SIZE; i++) {
52 for (cur = prefix_stats[i]; cur != NULL; cur = next) {
57 prefix_stats[
i] = NULL;
60 total_prefix_size = 0;
68 static PREFIX_STATS *stats_prefix_find(
const char *key,
const size_t nkey) {
76 for (length = 0; length < nkey && key[length] !=
'\0'; length++) {
77 if (key[length] ==
settings.prefix_delimiter) {
87 hashval = hash(key, length, 0) % PREFIX_HASH_SIZE;
89 for (pfs = prefix_stats[hashval]; NULL != pfs; pfs = pfs->next) {
90 if (strncmp(pfs->prefix, key, length) == 0)
96 perror(
"Can't allocate space for stats structure: calloc");
100 pfs->prefix = malloc(length + 1);
101 if (NULL == pfs->prefix) {
102 perror(
"Can't allocate space for copy of prefix: malloc");
107 strncpy(pfs->prefix, key, length);
108 pfs->prefix[length] =
'\0';
109 pfs->prefix_len = length;
111 pfs->next = prefix_stats[hashval];
112 prefix_stats[hashval] = pfs;
115 total_prefix_size += length;
123 void stats_prefix_record_get(
const char *key,
const size_t nkey,
const bool is_hit) {
127 pfs = stats_prefix_find(key, nkey);
140 void stats_prefix_record_delete(
const char *key,
const size_t nkey) {
144 pfs = stats_prefix_find(key, nkey);
154 void stats_prefix_record_set(
const char *key,
const size_t nkey) {
158 pfs = stats_prefix_find(key, nkey);
169 char *stats_prefix_dump(
int *length) {
170 const char *format =
"PREFIX %s get %llu hit %llu set %llu del %llu\r\n";
174 size_t size = 0, written = 0, total_written = 0;
183 size = strlen(format) + total_prefix_size +
184 num_prefixes * (strlen(format) - 2
189 perror(
"Can't allocate stats response: malloc");
195 for (i = 0; i < PREFIX_HASH_SIZE; i++) {
196 for (pfs = prefix_stats[i]; NULL != pfs; pfs = pfs->next) {
197 written = snprintf(buf + pos,
size-pos, format,
198 pfs->prefix, pfs->num_gets, pfs->num_hits,
199 pfs->num_sets, pfs->num_deletes);
201 total_written += written;
202 assert(total_written <
size);
207 memcpy(buf + pos,
"END\r\n", 6);
223 static char *current_test =
"";
224 static int test_count = 0;
225 static int fail_count = 0;
227 static void fail(
char *what) { printf(
"\tFAIL: %s\n", what); fflush(stdout); fail_count++; }
228 static void test_equals_int(
char *what,
int a,
int b) { test_count++;
if (a != b) fail(what); }
229 static void test_equals_ptr(
char *what,
void *a,
void *b) { test_count++;
if (a != b) fail(what); }
230 static void test_equals_str(
char *what,
const char *a,
const char *b) { test_count++;
if (strcmp(a, b)) fail(what); }
231 static void test_equals_ull(
char *what, uint64_t a, uint64_t b) { test_count++;
if (a != b) fail(what); }
232 static void test_notequals_ptr(
char *what,
void *a,
void *b) { test_count++;
if (a == b) fail(what); }
233 static void test_notnull_ptr(
char *what,
void *a) { test_count++;
if (NULL == a) fail(what); }
235 static void test_prefix_find() {
238 pfs1 = stats_prefix_find(
"abc");
239 test_notnull_ptr(
"initial prefix find", pfs1);
240 test_equals_ull(
"request counts", 0ULL,
241 pfs1->num_gets + pfs1->num_sets + pfs1->num_deletes + pfs1->num_hits);
242 pfs2 = stats_prefix_find(
"abc");
243 test_equals_ptr(
"find of same prefix", pfs1, pfs2);
244 pfs2 = stats_prefix_find(
"abc:");
245 test_equals_ptr(
"find of same prefix, ignoring delimiter", pfs1, pfs2);
246 pfs2 = stats_prefix_find(
"abc:d");
247 test_equals_ptr(
"find of same prefix, ignoring extra chars", pfs1, pfs2);
248 pfs2 = stats_prefix_find(
"xyz123");
249 test_notequals_ptr(
"find of different prefix", pfs1, pfs2);
250 pfs2 = stats_prefix_find(
"ab:");
251 test_notequals_ptr(
"find of shorter prefix", pfs1, pfs2);
254 static void test_prefix_record_get() {
257 stats_prefix_record_get(
"abc:123", 0);
258 pfs = stats_prefix_find(
"abc:123");
259 test_equals_ull(
"get count after get #1", 1, pfs->num_gets);
260 test_equals_ull(
"hit count after get #1", 0, pfs->num_hits);
261 stats_prefix_record_get(
"abc:456", 0);
262 test_equals_ull(
"get count after get #2", 2, pfs->num_gets);
263 test_equals_ull(
"hit count after get #2", 0, pfs->num_hits);
264 stats_prefix_record_get(
"abc:456", 1);
265 test_equals_ull(
"get count after get #3", 3, pfs->num_gets);
266 test_equals_ull(
"hit count after get #3", 1, pfs->num_hits);
267 stats_prefix_record_get(
"def:", 1);
268 test_equals_ull(
"get count after get #4", 3, pfs->num_gets);
269 test_equals_ull(
"hit count after get #4", 1, pfs->num_hits);
272 static void test_prefix_record_delete() {
275 stats_prefix_record_delete(
"abc:123");
276 pfs = stats_prefix_find(
"abc:123");
277 test_equals_ull(
"get count after delete #1", 0, pfs->num_gets);
278 test_equals_ull(
"hit count after delete #1", 0, pfs->num_hits);
279 test_equals_ull(
"delete count after delete #1", 1, pfs->num_deletes);
280 test_equals_ull(
"set count after delete #1", 0, pfs->num_sets);
281 stats_prefix_record_delete(
"def:");
282 test_equals_ull(
"delete count after delete #2", 1, pfs->num_deletes);
285 static void test_prefix_record_set() {
288 stats_prefix_record_set(
"abc:123");
289 pfs = stats_prefix_find(
"abc:123");
290 test_equals_ull(
"get count after set #1", 0, pfs->num_gets);
291 test_equals_ull(
"hit count after set #1", 0, pfs->num_hits);
292 test_equals_ull(
"delete count after set #1", 0, pfs->num_deletes);
293 test_equals_ull(
"set count after set #1", 1, pfs->num_sets);
294 stats_prefix_record_delete(
"def:");
295 test_equals_ull(
"set count after set #2", 1, pfs->num_sets);
298 static void test_prefix_dump() {
299 int hashval = hash(
"abc", 3, 0) % PREFIX_HASH_SIZE;
305 test_equals_str(
"empty stats",
"END\r\n", stats_prefix_dump(&length));
306 test_equals_int(
"empty stats length", 5, length);
307 stats_prefix_record_set(
"abc:123");
308 expected =
"PREFIX abc get 0 hit 0 set 1 del 0\r\nEND\r\n";
309 test_equals_str(
"stats after set", expected, stats_prefix_dump(&length));
310 test_equals_int(
"stats length after set", strlen(expected), length);
311 stats_prefix_record_get(
"abc:123", 0);
312 expected =
"PREFIX abc get 1 hit 0 set 1 del 0\r\nEND\r\n";
313 test_equals_str(
"stats after get #1", expected, stats_prefix_dump(&length));
314 test_equals_int(
"stats length after get #1", strlen(expected), length);
315 stats_prefix_record_get(
"abc:123", 1);
316 expected =
"PREFIX abc get 2 hit 1 set 1 del 0\r\nEND\r\n";
317 test_equals_str(
"stats after get #2", expected, stats_prefix_dump(&length));
318 test_equals_int(
"stats length after get #2", strlen(expected), length);
319 stats_prefix_record_delete(
"abc:123");
320 expected =
"PREFIX abc get 2 hit 1 set 1 del 1\r\nEND\r\n";
321 test_equals_str(
"stats after del #1", expected, stats_prefix_dump(&length));
322 test_equals_int(
"stats length after del #1", strlen(expected), length);
325 stats_prefix_record_delete(
"def:123");
326 expected =
"PREFIX abc get 2 hit 1 set 1 del 1\r\n"
327 "PREFIX def get 0 hit 0 set 0 del 1\r\n"
329 test_equals_str(
"stats after del #2", expected, stats_prefix_dump(&length));
330 test_equals_int(
"stats length after del #2", strlen(expected), length);
333 for (keynum = 0; keynum < PREFIX_HASH_SIZE * 100; keynum++) {
334 snprintf(tmp,
sizeof(tmp),
"%d", keynum);
335 if (hashval == hash(tmp, strlen(tmp), 0) % PREFIX_HASH_SIZE) {
339 stats_prefix_record_set(tmp);
340 snprintf(tmp,
sizeof(tmp),
341 "PREFIX %d get 0 hit 0 set 1 del 0\r\n"
342 "PREFIX abc get 2 hit 1 set 1 del 1\r\n"
343 "PREFIX def get 0 hit 0 set 0 del 1\r\n"
345 test_equals_str(
"stats with two stats in one bucket",
346 tmp, stats_prefix_dump(&length));
347 test_equals_int(
"stats length with two stats in one bucket",
348 strlen(tmp), length);
351 static void run_test(
char *what,
void (*func)(
void)) {
353 test_count = fail_count = 0;
357 stats_prefix_clear();
359 printf(
"\t%d / %d pass\n", (test_count - fail_count), test_count);
363 void mt_stats_lock() { }
364 void mt_stats_unlock() { }
366 main(
int argc,
char **argv) {
369 run_test(
"stats_prefix_find", test_prefix_find);
370 run_test(
"stats_prefix_record_get", test_prefix_record_get);
371 run_test(
"stats_prefix_record_delete", test_prefix_record_delete);
372 run_test(
"stats_prefix_record_set", test_prefix_record_set);
373 run_test(
"stats_prefix_dump", test_prefix_dump);