1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3 * Copyright 2010 Couchbase, Inc
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 #include "config.h"
18
19 #include <memcached/engine.h>
20 #include <memcached/engine_testapp.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include <algorithm>
26 #include <cstdlib>
27 #include <iostream>
28 #include <map>
29 #include <sstream>
30 #include <string>
31 #include <vector>
32
33 #ifdef linux
34 /* /usr/include/netinet/in.h defines macros from ntohs() to _bswap_nn to
35 * optimize the conversion functions, but the prototypes generate warnings
36 * from gcc. The conversion methods isn't the bottleneck for my app, so
37 * just remove the warnings by undef'ing the optimization ..
38 */
39 #undef ntohs
40 #undef ntohl
41 #undef htons
42 #undef htonl
43 #endif
44
45 bool abort_msg(const char *expr, const char *msg, int line);
46
47 #define check(expr, msg) \
48 static_cast<void>((expr) ? 0 : abort_msg(#expr, msg, __LINE__))
49
50 protocol_binary_response_status last_status(static_cast<protocol_binary_response_status>(0));
51 char *last_key = NULL;
52 char *last_body = NULL;
53 std::map<std::string, std::string> vals;
54
55 struct test_harness testHarness;
56
abort_msg(const char *expr, const char *msg, int line)57 bool abort_msg(const char *expr, const char *msg, int line) {
58 fprintf(stderr, "%s:%d Test failed: `%s' (%s)\n",
59 __FILE__, line, msg, expr);
60 abort();
61 // UNREACHABLE
62 return false;
63 }
64
65 extern "C" {
rmdb(void)66 static void rmdb(void) {
67 #ifdef WIN32
68 _unlink("/tmp/test");
69 #else
70 unlink("/tmp/test");
71 #endif
72 }
73
teardown(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1)74 static bool teardown(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
75 (void)h; (void)h1;
76 atexit(rmdb);
77 vals.clear();
78 return true;
79 }
80 }
81
decayingSleep(useconds_t *sleepTime)82 static inline void decayingSleep(useconds_t *sleepTime) {
83 static const useconds_t maxSleepTime = 500000;
84 usleep(*sleepTime);
85 *sleepTime = std::min(*sleepTime << 1, maxSleepTime);
86 }
87
storeCasVb11(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1, const void *cookie, ENGINE_STORE_OPERATION op, const char *key, const char *value, size_t vlen, uint32_t flags, item **outitem, uint64_t casIn, uint16_t vb)88 static ENGINE_ERROR_CODE storeCasVb11(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1,
89 const void *cookie,
90 ENGINE_STORE_OPERATION op,
91 const char *key,
92 const char *value, size_t vlen,
93 uint32_t flags,
94 item **outitem, uint64_t casIn,
95 uint16_t vb) {
96 item *it = NULL;
97 uint64_t cas = 0;
98
99 ENGINE_ERROR_CODE rv = h1->allocate(h, cookie, &it,
100 key, strlen(key),
101 vlen, flags, 3600,
102 PROTOCOL_BINARY_RAW_BYTES);
103 check(rv == ENGINE_SUCCESS, "Allocation failed.");
104
105 item_info info;
106 info.nvalue = 1;
107 if (!h1->get_item_info(h, cookie, it, &info)) {
108 abort();
109 }
110
111 memcpy(info.value[0].iov_base, value, vlen);
112 h1->item_set_cas(h, cookie, it, casIn);
113
114 rv = h1->store(h, cookie, it, &cas, op, vb);
115
116 if (outitem) {
117 *outitem = it;
118 }
119
120 return rv;
121 }
122
123 extern "C" {
add_stats(const char *key, const uint16_t klen, const char *val, const uint32_t vlen, const void *cookie)124 static void add_stats(const char *key, const uint16_t klen,
125 const char *val, const uint32_t vlen,
126 const void *cookie) {
127 (void)cookie;
128 std::string k(key, klen);
129 std::string v(val, vlen);
130 vals[k] = v;
131 }
132 }
133
get_int_stat(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1, const char *statname, const char *statkey = NULL)134 static int get_int_stat(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1,
135 const char *statname, const char *statkey = NULL) {
136 vals.clear();
137 check(h1->get_stats(h, NULL, statkey, statkey == NULL ? 0 : strlen(statkey),
138 add_stats) == ENGINE_SUCCESS,
139 "Failed to get stats.");
140 std::string s = vals[statname];
141 return atoi(s.c_str());
142 }
143
verify_curr_items(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1, int exp, const char *msg)144 static void verify_curr_items(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1,
145 int exp, const char *msg) {
146 int curr_items = get_int_stat(h, h1, "curr_items");
147 if (curr_items != exp) {
148 std::cerr << "Expected "<< exp << " curr_items after " << msg
149 << ", got " << curr_items << std::endl;
150 abort();
151 }
152 }
153
wait_for_flusher_to_settle(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1)154 static void wait_for_flusher_to_settle(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
155 useconds_t sleepTime = 128;
156 while (get_int_stat(h, h1, "ep_queue_size") > 0) {
157 decayingSleep(&sleepTime);
158 }
159 }
160
env_int(const char *k, size_t rv)161 static size_t env_int(const char *k, size_t rv) {
162 char *x = getenv(k);
163 if (x) {
164 rv = static_cast<size_t>(atoi(x));
165 }
166 return rv;
167 }
168
169 extern "C" {
test_persistence(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1)170 static test_result test_persistence(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
171 size_t total = env_int("TEST_TOTAL_KEYS", 100000);
172 size_t size = env_int("TEST_VAL_SIZE", 20);
173
174 char key[24];
175 char *data;
176 data = static_cast<char *>(malloc(sizeof(char) * size));
177 cb_assert(data);
178
179 for (size_t i = 0; i < (sizeof(char) * size); ++i) {
180 data[i] = 0xff & rand();
181 }
182
183 for (size_t i = 0; i < total; ++i) {
184 item *it = NULL;
185 snprintf(key, sizeof(key), "k%d", static_cast<int>(i));
186
187 check(storeCasVb11(h, h1, NULL, OPERATION_SET, key, data,
188 size, 9713, &it, 0, 0) == ENGINE_SUCCESS,
189 "store failure");
190 }
191 free(data);
192 wait_for_flusher_to_settle(h, h1);
193
194 std::cout << total << " at " << size << " - "
195 << get_int_stat(h, h1, "ep_flush_duration_total")
196 << ", " << get_int_stat(h, h1, "ep_commit_time_total") << std::endl;
197
198 verify_curr_items(h, h1, total, "storing something");
199
200 return SUCCESS;
201 }
202 }
203
204 extern "C" MEMCACHED_PUBLIC_API
setup_suite(struct test_harness *th)205 bool setup_suite(struct test_harness *th) {
206 testHarness = *th;
207 return true;
208 }
209
210 extern "C" MEMCACHED_PUBLIC_API
get_tests(void)211 engine_test_t* get_tests(void) {
212
213 static engine_test_t tests[] = {
214 {"test persistence", test_persistence, NULL, teardown, NULL,
215 NULL, NULL},
216 {NULL, NULL, NULL, NULL, NULL, NULL, NULL}
217 };
218 return tests;
219 }
220