1 /* -*- MODE: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
3 * Copyright 2010 Couchbase, Inc
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 // Usage: (to run just a single test case)
19 // make engine_tests EP_TEST_NUM=3
27 #include <condition_variable>
38 #include <unordered_map>
39 #include <unordered_set>
43 #include "ep_test_apis.h"
45 #include "ep_testsuite_common.h"
47 #include <libcouchstore/couch_db.h>
48 #include <memcached/engine.h>
49 #include <memcached/engine_testapp.h>
50 #include <platform/cb_malloc.h>
51 #include <platform/dirutils.h>
52 #include <JSON_checker.h>
53 #include <memcached/types.h>
54 #include <string_utilities.h>
55 #include <xattr/blob.h>
56 #include <xattr/utils.h>
59 /* /usr/include/netinet/in.h defines macros from ntohs() to _bswap_nn to
60 * optimize the conversion functions, but the prototypes generate warnings
61 * from gcc. The conversion methods isn't the bottleneck for my app, so
62 * just remove the warnings by undef'ing the optimization ..
70 // ptr_fun don't like the extern "C" thing for unlock cookie.. cast it
72 typedef void (*UNLOCK_COOKIE_T)(const void *cookie);
74 #define MULTI_DISPATCHER_CONFIG \
75 "ht_size=129;ht_locks=3;chk_remover_stime=1;chk_period=60"
79 ThreadData(ENGINE_HANDLE *eh, ENGINE_HANDLE_V1 *ehv1,
80 int e=0) : h(eh), h1(ehv1), extra(e) {}
87 static void check_observe_seqno(bool failover, uint8_t format_type, uint16_t vb_id,
88 uint64_t vb_uuid, uint64_t last_persisted_seqno,
89 uint64_t current_seqno, uint64_t failover_vbuuid = 0,
90 uint64_t failover_seqno = 0) {
91 uint8_t recv_format_type;
93 uint64_t recv_vb_uuid;
94 uint64_t recv_last_persisted_seqno;
95 uint64_t recv_current_seqno;
96 uint64_t recv_failover_vbuuid;
97 uint64_t recv_failover_seqno;
99 memcpy(&recv_format_type, last_body.data(), sizeof(uint8_t));
100 checkeq(format_type, recv_format_type, "Wrong format type in result");
101 memcpy(&recv_vb_id, last_body.data() + 1, sizeof(uint16_t));
102 checkeq(vb_id, ntohs(recv_vb_id), "Wrong vbucket id in result");
103 memcpy(&recv_vb_uuid, last_body.data() + 3, sizeof(uint64_t));
104 checkeq(vb_uuid, ntohll(recv_vb_uuid), "Wrong vbucket uuid in result");
105 memcpy(&recv_last_persisted_seqno, last_body.data() + 11, sizeof(uint64_t));
106 checkeq(last_persisted_seqno, ntohll(recv_last_persisted_seqno),
107 "Wrong persisted seqno in result");
108 memcpy(&recv_current_seqno, last_body.data() + 19, sizeof(uint64_t));
109 checkeq(current_seqno, ntohll(recv_current_seqno), "Wrong current seqno in result");
112 memcpy(&recv_failover_vbuuid, last_body.data() + 27, sizeof(uint64_t));
113 checkeq(failover_vbuuid, ntohll(recv_failover_vbuuid),
114 "Wrong failover uuid in result");
115 memcpy(&recv_failover_seqno, last_body.data() + 35, sizeof(uint64_t));
116 checkeq(failover_seqno, ntohll(recv_failover_seqno),
117 "Wrong failover seqno in result");
121 static enum test_result test_replace_with_eviction(ENGINE_HANDLE *h,
122 ENGINE_HANDLE_V1 *h1) {
124 checkeq(ENGINE_SUCCESS,
125 store(h, h1, NULL, OPERATION_SET,"key", "somevalue", &i),
126 "Failed to set value.");
127 h1->release(h, NULL, i);
128 wait_for_flusher_to_settle(h, h1);
129 evict_key(h, h1, "key");
130 int numBgFetched = get_int_stat(h, h1, "ep_bg_fetched");
132 checkeq(ENGINE_SUCCESS,
133 store(h, h1, NULL, OPERATION_REPLACE,"key", "somevalue1", &i),
134 "Failed to replace existing value.");
136 checkeq(ENGINE_SUCCESS,
137 h1->get_stats(h, NULL, NULL, 0, add_stats),
138 "Failed to get stats.");
139 std::string eviction_policy = vals.find("ep_item_eviction_policy")->second;
140 if (eviction_policy == "full_eviction") {
144 checkeq(numBgFetched,
145 get_int_stat(h, h1, "ep_bg_fetched"),
146 "Bg fetched value didn't match");
148 h1->release(h, NULL, i);
149 check_key_value(h, h1, "key", "somevalue1", 10);
153 static enum test_result test_wrong_vb_mutation(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1,
154 ENGINE_STORE_OPERATION op) {
156 int numNotMyVBucket = get_int_stat(h, h1, "ep_num_not_my_vbuckets");
158 if (op == OPERATION_ADD) {
159 // Add operation with cas != 0 doesn't make sense
162 checkeq(ENGINE_NOT_MY_VBUCKET,
163 store(h, h1, NULL, op, "key", "somevalue", &i, cas, 1),
164 "Expected not_my_vbucket");
165 h1->release(h, NULL, i);
166 wait_for_stat_change(h, h1, "ep_num_not_my_vbuckets", numNotMyVBucket);
170 static enum test_result test_pending_vb_mutation(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1,
171 ENGINE_STORE_OPERATION op) {
172 const void *cookie = testHarness.create_cookie();
173 testHarness.set_ewouldblock_handling(cookie, false);
175 check(set_vbucket_state(h, h1, 1, vbucket_state_pending),
176 "Failed to set vbucket state.");
177 check(verify_vbucket_state(h, h1, 1, vbucket_state_pending),
178 "Bucket state was not set to pending.");
180 if (op == OPERATION_ADD) {
181 // Add operation with cas != 0 doesn't make sense..
184 checkeq(ENGINE_EWOULDBLOCK,
185 store(h, h1, cookie, op, "key", "somevalue", &i, cas, 1),
186 "Expected ewouldblock");
187 h1->release(h, NULL, i);
188 testHarness.destroy_cookie(cookie);
192 static enum test_result test_replica_vb_mutation(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1,
193 ENGINE_STORE_OPERATION op) {
195 check(set_vbucket_state(h, h1, 1, vbucket_state_replica),
196 "Failed to set vbucket state.");
197 check(verify_vbucket_state(h, h1, 1, vbucket_state_replica),
198 "Bucket state was not set to replica.");
199 int numNotMyVBucket = get_int_stat(h, h1, "ep_num_not_my_vbuckets");
202 if (op == OPERATION_ADD) {
203 // performing add with a CAS != 0 doesn't make sense...
206 checkeq(ENGINE_NOT_MY_VBUCKET,
207 store(h, h1, NULL, op, "key", "somevalue", &i, cas, 1),
208 "Expected not my vbucket");
209 wait_for_stat_change(h, h1, "ep_num_not_my_vbuckets", numNotMyVBucket);
210 h1->release(h, NULL, i);
215 // ----------------------------------------------------------------------
216 // The actual tests are below.
217 // ----------------------------------------------------------------------
220 static int checkCurrItemsAfterShutdown(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1,
221 int numItems2Load, bool shutdownForce) {
222 if (!isWarmupEnabled(h, h1)) {
226 std::vector<std::string> keys;
227 for (int index = 0; index < numItems2Load; ++index) {
229 s << "keys_2_load-" << index;
230 std::string key(s.str());
234 checkeq(0, get_int_stat(h, h1, "ep_total_persisted"),
235 "Expected ep_total_persisted equals 0");
236 checkeq(0, get_int_stat(h, h1, "curr_items"),
237 "Expected curr_items equals 0");
239 // stop flusher before loading new items
240 protocol_binary_request_header *pkt = createPacket(PROTOCOL_BINARY_CMD_STOP_PERSISTENCE);
241 checkeq(ENGINE_SUCCESS,
242 h1->unknown_command(h, NULL, pkt, add_response, testHarness.doc_namespace),
243 "CMD_STOP_PERSISTENCE failed!");
244 checkeq(PROTOCOL_BINARY_RESPONSE_SUCCESS,
246 "Failed to stop persistence!");
249 std::vector<std::string>::iterator itr;
250 for (itr = keys.begin(); itr != keys.end(); ++itr) {
252 checkeq(ENGINE_SUCCESS,
253 store(h, h1, NULL, OPERATION_SET, itr->c_str(), "oracle", &i, 0, 0),
254 "Failed to store a value");
255 h1->release(h, NULL, i);
258 checkeq(0, get_int_stat(h, h1, "ep_total_persisted"),
259 "Incorrect ep_total_persisted, expected 0");
260 std::stringstream ss;
261 ss << "Incorrect curr_items, expected " << numItems2Load;
262 std::string errmsg(ss.str());
263 checkeq(numItems2Load, get_int_stat(h, h1, "curr_items"),
266 // resume flusher before shutdown + warmup
267 pkt = createPacket(PROTOCOL_BINARY_CMD_START_PERSISTENCE);
268 checkeq(ENGINE_SUCCESS, h1->unknown_command(h, NULL, pkt, add_response, testHarness.doc_namespace),
269 "CMD_START_PERSISTENCE failed!");
270 checkeq(PROTOCOL_BINARY_RESPONSE_SUCCESS, last_status.load(),
271 "Failed to start persistence!");
274 // shutdown engine force and restart
275 testHarness.reload_engine(&h, &h1,
276 testHarness.engine_path,
277 testHarness.get_current_testcase()->cfg,
278 true, shutdownForce);
279 wait_for_warmup_complete(h, h1);
280 return get_int_stat(h, h1, "curr_items");
283 static enum test_result test_flush_shutdown_force(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
284 if (!isWarmupEnabled(h, h1)) {
288 int numItems2load = 3000;
289 bool shutdownForce = true;
290 int currItems = checkCurrItemsAfterShutdown(h, h1, numItems2load, shutdownForce);
291 check (currItems <= numItems2load,
292 "Number of curr items should be <= 3000, unless previous "
293 "shutdown force had to wait for the flusher");
297 static enum test_result test_flush_shutdown_noforce(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
298 if (!isWarmupEnabled(h, h1)) {
302 int numItems2load = 3000;
303 bool shutdownForce = false;
304 int currItems = checkCurrItemsAfterShutdown(h, h1, numItems2load, shutdownForce);
305 check (currItems == numItems2load,
306 "Number of curr items should be equal to 3000, unless previous "
307 "shutdown did not wait for the flusher");
311 static enum test_result test_flush_restart(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
312 if (!isWarmupEnabled(h, h1)) {
317 // First try to delete something we know to not be there.
318 checkeq(ENGINE_KEY_ENOENT, del(h, h1, "key", 0, 0),
319 "Failed to fail initial delete.");
320 checkeq(ENGINE_SUCCESS,
321 store(h, h1, NULL, OPERATION_SET, "key", "somevalue", &i),
323 h1->release(h, NULL, i);
324 check_key_value(h, h1, "key", "somevalue", 9);
326 // Restart once to ensure written to disk.
327 testHarness.reload_engine(&h, &h1,
328 testHarness.engine_path,
329 testHarness.get_current_testcase()->cfg,
331 wait_for_warmup_complete(h, h1);
333 // Read value from disk.
334 check_key_value(h, h1, "key", "somevalue", 9);
337 set_degraded_mode(h, h1, NULL, true);
338 checkeq(ENGINE_SUCCESS, h1->flush(h, NULL),
340 set_degraded_mode(h, h1, NULL, false);
342 checkeq(ENGINE_SUCCESS,
343 store(h, h1, NULL, OPERATION_SET, "key2", "somevalue", &i),
344 "Failed post-flush set.");
345 h1->release(h, NULL, i);
346 check_key_value(h, h1, "key2", "somevalue", 9);
348 // Restart again, ensure written to disk.
349 testHarness.reload_engine(&h, &h1,
350 testHarness.engine_path,
351 testHarness.get_current_testcase()->cfg,
353 wait_for_warmup_complete(h, h1);
355 checkeq(ENGINE_SUCCESS,
356 store(h, h1, NULL, OPERATION_SET, "key3", "somevalue", &i),
357 "Failed post-flush, post-restart set.");
358 h1->release(h, NULL, i);
359 check_key_value(h, h1, "key3", "somevalue", 9);
361 // Read value again, should not be there.
362 checkeq(ENGINE_KEY_ENOENT, verify_key(h, h1, "key"),
363 "Expected missing key");
367 static enum test_result test_shutdown_snapshot_range(ENGINE_HANDLE *h,
368 ENGINE_HANDLE_V1 *h1) {
369 if (!isWarmupEnabled(h, h1)) {
373 const int num_items = 100;
374 for (int j = 0; j < num_items; ++j) {
376 std::stringstream ss;
378 checkeq(ENGINE_SUCCESS,
379 store(h, h1, NULL, OPERATION_SET, ss.str().c_str(), "data", &i),
380 "Failed to store a value");
381 h1->release(h, NULL, i);
384 wait_for_flusher_to_settle(h, h1);
385 int end = get_int_stat(h, h1, "vb_0:high_seqno", "vbucket-seqno");
387 /* change vb state to replica before restarting (as it happens in graceful
389 check(set_vbucket_state(h, h1, 0, vbucket_state_replica),
390 "Failed set vbucket 0 to replica state.");
392 /* trigger persist vb state task */
393 check(set_param(h, h1, protocol_binary_engine_param_flush,
394 "vb_state_persist_run", "0"),
395 "Failed to trigger vb state persist");
397 /* restart the engine */
398 testHarness.reload_engine(&h, &h1,
399 testHarness.engine_path,
400 testHarness.get_current_testcase()->cfg,
402 wait_for_warmup_complete(h, h1);
404 /* Check if snapshot range is persisted correctly */
405 checkeq(end, get_int_stat(h, h1, "vb_0:last_persisted_snap_start",
407 "Wrong snapshot start persisted");
408 checkeq(end, get_int_stat(h, h1, "vb_0:last_persisted_snap_end",
410 "Wrong snapshot end persisted");
415 static enum test_result test_flush_multiv_restart(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
416 if (!isWarmupEnabled(h, h1)) {
421 check(set_vbucket_state(h, h1, 2, vbucket_state_active),
422 "Failed to set vbucket state.");
423 checkeq(ENGINE_SUCCESS,
424 store(h, h1, NULL, OPERATION_SET, "key", "somevalue", &i),
426 h1->release(h, NULL, i);
427 checkeq(ENGINE_SUCCESS,
428 store(h, h1, NULL, OPERATION_SET, "key2", "somevalue", &i, 0, 2),
429 "Failed set in vb2.");
430 h1->release(h, NULL, i);
432 // Restart once to ensure written to disk.
433 testHarness.reload_engine(&h, &h1,
434 testHarness.engine_path,
435 testHarness.get_current_testcase()->cfg,
437 wait_for_warmup_complete(h, h1);
439 // Read value from disk.
440 check_key_value(h, h1, "key", "somevalue", 9);
443 set_degraded_mode(h, h1, NULL, true);
444 checkeq(ENGINE_SUCCESS, h1->flush(h, NULL),
446 set_degraded_mode(h, h1, NULL, false);
448 // Restart again, ensure written to disk.
449 testHarness.reload_engine(&h, &h1,
450 testHarness.engine_path,
451 testHarness.get_current_testcase()->cfg,
453 wait_for_warmup_complete(h, h1);
455 // Read value again, should not be there.
456 checkeq(ENGINE_KEY_ENOENT, verify_key(h, h1, "key"), "Expected missing key");
457 check(verify_vbucket_missing(h, h1, 2), "Bucket 2 came back.");
461 static enum test_result test_restart(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
462 if (!isWarmupEnabled(h, h1)) {
467 static const char val[] = "somevalue";
468 ENGINE_ERROR_CODE ret = store(h, h1, NULL, OPERATION_SET, "key", val, &i);
469 checkeq(ENGINE_SUCCESS, ret, "Failed set.");
470 h1->release(h, NULL, i);
472 testHarness.reload_engine(&h, &h1,
473 testHarness.engine_path,
474 testHarness.get_current_testcase()->cfg,
476 wait_for_warmup_complete(h, h1);
477 check_key_value(h, h1, "key", val, strlen(val));
481 static enum test_result test_restart_session_stats(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
482 const void* cookie = createTapConn(h, h1, "tap_client_thread");
483 testHarness.unlock_cookie(cookie);
484 testHarness.destroy_cookie(cookie);
486 testHarness.reload_engine(&h, &h1,
487 testHarness.engine_path,
488 testHarness.get_current_testcase()->cfg,
490 wait_for_warmup_complete(h, h1);
491 cookie = createTapConn(h, h1, "tap_client_thread");
493 checkeq(ENGINE_SUCCESS, h1->get_stats(h, NULL, "tap", 3, add_stats),
494 "Failed to get stats.");
495 std::string val = vals["eq_tapq:tap_client_thread:backfill_completed"];
496 checkeq(0, strcmp(val.c_str(), "true"), "Don't expect the backfill upon restart");
497 testHarness.unlock_cookie(cookie);
498 testHarness.destroy_cookie(cookie);
502 static enum test_result test_specialKeys(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
504 ENGINE_ERROR_CODE ret;
506 // Simplified Chinese "Couchbase"
507 static const char key0[] = "沙发数据库";
508 static const char val0[] = "some Chinese value";
509 check((ret = store(h, h1, NULL, OPERATION_SET, key0, val0, &i)) == ENGINE_SUCCESS,
510 "Failed set Chinese key");
511 check_key_value(h, h1, key0, val0, strlen(val0));
512 h1->release(h, NULL, i);
513 // Traditional Chinese "Couchbase"
514 static const char key1[] = "沙發數據庫";
515 static const char val1[] = "some Traditional Chinese value";
516 check((ret = store(h, h1, NULL, OPERATION_SET, key1, val1, &i)) == ENGINE_SUCCESS,
517 "Failed set Traditional Chinese key");
518 h1->release(h, NULL, i);
519 // Korean "couch potato"
520 static const char key2[] = "쇼파감자";
521 static const char val2[] = "some Korean value";
522 check((ret = store(h, h1, NULL, OPERATION_SET, key2, val2, &i)) == ENGINE_SUCCESS,
523 "Failed set Korean key");
524 h1->release(h, NULL, i);
525 // Russian "couch potato"
526 static const char key3[] = "лодырь, лентяй";
527 static const char val3[] = "some Russian value";
528 check((ret = store(h, h1, NULL, OPERATION_SET, key3, val3, &i)) == ENGINE_SUCCESS,
529 "Failed set Russian key");
530 h1->release(h, NULL, i);
531 // Japanese "couch potato"
532 static const char key4[] = "カウチポテト";
533 static const char val4[] = "some Japanese value";
534 check((ret = store(h, h1, NULL, OPERATION_SET, key4, val4, &i)) == ENGINE_SUCCESS,
535 "Failed set Japanese key");
536 h1->release(h, NULL, i);
537 // Indian char key, and no idea what it is
538 static const char key5[] = "हरियानवी";
539 static const char val5[] = "some Indian value";
540 check((ret = store(h, h1, NULL, OPERATION_SET, key5, val5, &i)) == ENGINE_SUCCESS,
541 "Failed set Indian key");
542 h1->release(h, NULL, i);
543 // Portuguese translation "couch potato"
544 static const char key6[] = "sedentário";
545 static const char val6[] = "some Portuguese value";
546 check((ret = store(h, h1, NULL, OPERATION_SET, key6, val6, &i)) == ENGINE_SUCCESS,
547 "Failed set Portuguese key");
548 h1->release(h, NULL, i);
549 // Arabic translation "couch potato"
550 static const char key7[] = "الحافلةالبطاطة";
551 static const char val7[] = "some Arabic value";
552 check((ret = store(h, h1, NULL, OPERATION_SET, key7, val7, &i)) == ENGINE_SUCCESS,
553 "Failed set Arabic key");
554 h1->release(h, NULL, i);
556 if (isWarmupEnabled(h, h1)) {
557 // Check that after warmup the keys are still present.
558 testHarness.reload_engine(&h, &h1,
559 testHarness.engine_path,
560 testHarness.get_current_testcase()->cfg,
562 wait_for_warmup_complete(h, h1);
563 check_key_value(h, h1, key0, val0, strlen(val0));
564 check_key_value(h, h1, key1, val1, strlen(val1));
565 check_key_value(h, h1, key2, val2, strlen(val2));
566 check_key_value(h, h1, key3, val3, strlen(val3));
567 check_key_value(h, h1, key4, val4, strlen(val4));
568 check_key_value(h, h1, key5, val5, strlen(val5));
569 check_key_value(h, h1, key6, val6, strlen(val6));
570 check_key_value(h, h1, key7, val7, strlen(val7));
575 static enum test_result test_binKeys(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
577 ENGINE_ERROR_CODE ret;
579 // binary key with char values beyond 0x7F
580 static const char key0[] = "\xe0\xed\xf1\x6f\x7f\xf8\xfa";
581 static const char val0[] = "some value val8";
582 check((ret = store(h, h1, NULL, OPERATION_SET, key0, val0, &i)) == ENGINE_SUCCESS,
583 "Failed set binary key0");
584 check_key_value(h, h1, key0, val0, strlen(val0));
585 h1->release(h, NULL, i);
586 // binary keys with char values beyond 0x7F
587 static const char key1[] = "\xf1\xfd\xfe\xff\xf0\xf8\xef";
588 static const char val1[] = "some value val9";
589 check((ret = store(h, h1, NULL, OPERATION_SET, key1, val1, &i)) == ENGINE_SUCCESS,
590 "Failed set binary key1");
591 check_key_value(h, h1, key1, val1, strlen(val1));
592 h1->release(h, NULL, i);
593 // binary keys with special utf-8 BOM (Byte Order Mark) values 0xBB 0xBF 0xEF
594 static const char key2[] = "\xff\xfe\xbb\xbf\xef";
595 static const char val2[] = "some utf-8 bom value";
596 check((ret = store(h, h1, NULL, OPERATION_SET, key2, val2, &i)) == ENGINE_SUCCESS,
597 "Failed set binary utf-8 bom key");
598 check_key_value(h, h1, key2, val2, strlen(val2));
599 h1->release(h, NULL, i);
600 // binary keys with special utf-16BE BOM values "U+FEFF"
601 static const char key3[] = "U+\xfe\xff\xefU+\xff\xfe";
602 static const char val3[] = "some utf-16 bom value";
603 check((ret = store(h, h1, NULL, OPERATION_SET, key3, val3, &i)) == ENGINE_SUCCESS,
604 "Failed set binary utf-16 bom key");
605 check_key_value(h, h1, key3, val3, strlen(val3));
606 h1->release(h, NULL, i);
608 if (isWarmupEnabled(h, h1)) {
609 testHarness.reload_engine(&h, &h1,
610 testHarness.engine_path,
611 testHarness.get_current_testcase()->cfg,
613 wait_for_warmup_complete(h, h1);
614 check_key_value(h, h1, key0, val0, strlen(val0));
615 check_key_value(h, h1, key1, val1, strlen(val1));
616 check_key_value(h, h1, key2, val2, strlen(val2));
617 check_key_value(h, h1, key3, val3, strlen(val3));
622 static enum test_result test_restart_bin_val(ENGINE_HANDLE *h,
623 ENGINE_HANDLE_V1 *h1) {
624 if (!isWarmupEnabled(h, h1)) {
628 char binaryData[] = "abcdefg\0gfedcba";
629 cb_assert(sizeof(binaryData) != strlen(binaryData));
632 checkeq(ENGINE_SUCCESS,
633 storeCasVb11(h, h1, NULL, OPERATION_SET, "key",
634 binaryData, sizeof(binaryData), 82758, &i, 0, 0),
636 h1->release(h, NULL, i);
638 testHarness.reload_engine(&h, &h1,
639 testHarness.engine_path,
640 testHarness.get_current_testcase()->cfg,
642 wait_for_warmup_complete(h, h1);
644 check_key_value(h, h1, "key", binaryData, sizeof(binaryData));
648 static enum test_result test_wrong_vb_get(ENGINE_HANDLE *h,
649 ENGINE_HANDLE_V1 *h1) {
650 int numNotMyVBucket = get_int_stat(h, h1, "ep_num_not_my_vbuckets");
651 checkeq(ENGINE_NOT_MY_VBUCKET, verify_key(h, h1, "key", 1),
652 "Expected wrong bucket.");
653 wait_for_stat_change(h, h1, "ep_num_not_my_vbuckets", numNotMyVBucket);
657 static enum test_result test_vb_get_pending(ENGINE_HANDLE *h,
658 ENGINE_HANDLE_V1 *h1) {
659 check(set_vbucket_state(h, h1, 1, vbucket_state_pending),
660 "Failed to set vbucket state.");
661 const void *cookie = testHarness.create_cookie();
662 testHarness.set_ewouldblock_handling(cookie, false);
665 checkeq(ENGINE_EWOULDBLOCK,
666 get(h, h1, cookie, &i, "key", 1),
667 "Expected woodblock.");
668 h1->release(h, NULL, i);
670 testHarness.destroy_cookie(cookie);
674 static enum test_result test_vb_get_replica(ENGINE_HANDLE *h,
675 ENGINE_HANDLE_V1 *h1) {
676 check(set_vbucket_state(h, h1, 1, vbucket_state_replica),
677 "Failed to set vbucket state.");
678 int numNotMyVBucket = get_int_stat(h, h1, "ep_num_not_my_vbuckets");
679 checkeq(ENGINE_NOT_MY_VBUCKET,
680 verify_key(h, h1, "key", 1),
681 "Expected not my bucket.");
682 wait_for_stat_change(h, h1, "ep_num_not_my_vbuckets", numNotMyVBucket);
686 static enum test_result test_wrong_vb_set(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
687 return test_wrong_vb_mutation(h, h1, OPERATION_SET);
690 static enum test_result test_wrong_vb_cas(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
691 return test_wrong_vb_mutation(h, h1, OPERATION_CAS);
694 static enum test_result test_wrong_vb_add(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
695 return test_wrong_vb_mutation(h, h1, OPERATION_ADD);
698 static enum test_result test_wrong_vb_replace(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
699 return test_wrong_vb_mutation(h, h1, OPERATION_REPLACE);
702 static enum test_result test_wrong_vb_del(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
703 int numNotMyVBucket = get_int_stat(h, h1, "ep_num_not_my_vbuckets");
704 checkeq(ENGINE_NOT_MY_VBUCKET, del(h, h1, "key", 0, 1),
705 "Expected wrong bucket.");
706 wait_for_stat_change(h, h1, "ep_num_not_my_vbuckets", numNotMyVBucket);
710 /* Returns a string in the format "%Y-%m-%d %H:%M:%S" of the specified
713 std::string make_time_string(std::chrono::system_clock::time_point time_point) {
714 time_t tt = std::chrono::system_clock::to_time_t(time_point);
716 // Windows' gmtime() is already thread-safe.
717 struct tm* split = gmtime(&tt);
719 struct tm local_storage;
720 struct tm* split = gmtime_r(&tt, &local_storage);
723 strftime(timeStr, 20, "%Y-%m-%d %H:%M:%S", split);
727 static enum test_result test_expiry_pager_settings(ENGINE_HANDLE *h,
728 ENGINE_HANDLE_V1 *h1) {
730 cb_assert(!get_bool_stat(h, h1, "ep_exp_pager_enabled"));
731 checkeq(3600, get_int_stat(h, h1, "ep_exp_pager_stime"),
732 "Expiry pager sleep time not expected");
733 set_param(h, h1, protocol_binary_engine_param_flush,
734 "exp_pager_stime", "1");
735 checkeq(1, get_int_stat(h, h1, "ep_exp_pager_stime"),
736 "Expiry pager sleep time not updated");
737 cb_assert(!get_bool_stat(h, h1, "ep_exp_pager_enabled"));
739 checkeq(0, get_int_stat(h, h1, "ep_num_expiry_pager_runs"),
740 "Expiry pager run count is not zero");
742 set_param(h, h1, protocol_binary_engine_param_flush,
743 "exp_pager_enabled", "true");
744 checkeq(1, get_int_stat(h, h1, "ep_exp_pager_stime"),
745 "Expiry pager sleep time not updated");
746 wait_for_stat_to_be_gte(h, h1, "ep_num_expiry_pager_runs", 1);
749 testHarness.reload_engine(&h, &h1,
750 testHarness.engine_path,
751 testHarness.get_current_testcase()->cfg,
753 wait_for_warmup_complete(h, h1);
754 cb_assert(!get_bool_stat(h, h1, "ep_exp_pager_enabled"));
756 // Enable expiry pager again
757 set_param(h, h1, protocol_binary_engine_param_flush,
758 "exp_pager_enabled", "true");
760 checkeq(get_int_stat(h, h1, "ep_exp_pager_initial_run_time"), -1,
761 "Task time should be disable upon warmup");
764 // Update exp_pager_initial_run_time and ensure the update is successful
765 set_param(h, h1, protocol_binary_engine_param_flush,
766 "exp_pager_initial_run_time", "3");
767 std::string expected_time = "03:00";
768 std::string str = get_str_stat(h, h1, "ep_expiry_pager_task_time");
769 err_msg.assign("Updated time incorrect, expect: " +
770 expected_time + ", actual: " + str.substr(11, 5));
771 checkeq(0, str.substr(11, 5).compare(expected_time), err_msg.c_str());
773 // Update exp_pager_stime by 30 minutes and ensure that the update is successful
774 const std::chrono::minutes update_by{30};
775 std::string targetTaskTime1{make_time_string(std::chrono::system_clock::now() +
778 set_param(h, h1, protocol_binary_engine_param_flush, "exp_pager_stime",
779 std::to_string(update_by.count() * 60).c_str());
780 str = get_str_stat(h, h1, "ep_expiry_pager_task_time");
782 std::string targetTaskTime2{make_time_string(std::chrono::system_clock::now() +
785 // ep_expiry_pager_task_time should fall within the range of
786 // targetTaskTime1 and targetTaskTime2
787 err_msg.assign("Unexpected task time range, expect: " +
788 targetTaskTime1 + " <= " + str + " <= " + targetTaskTime2);
789 check(targetTaskTime1 <= str, err_msg.c_str());
790 check(str <= targetTaskTime2, err_msg.c_str());
795 static enum test_result test_expiry_with_xattr(ENGINE_HANDLE* h,
796 ENGINE_HANDLE_V1* h1) {
797 const char* key = "test_expiry";
798 cb::xattr::Blob blob;
801 blob.set(to_const_byte_buffer("user"),
802 to_const_byte_buffer("{\"author\":\"bubba\"}"));
803 blob.set(to_const_byte_buffer("_sync"),
804 to_const_byte_buffer("{\"cas\":\"0xdeadbeefcafefeed\"}"));
805 blob.set(to_const_byte_buffer("meta"),
806 to_const_byte_buffer("{\"content-type\":\"text\"}"));
808 auto xattr_value = blob.finalize();
810 //Now, append user data to the xattrs and store the data
811 std::string value_data("test_expiry_value");
812 std::vector<char> data;
813 std::copy(xattr_value.buf, xattr_value.buf + xattr_value.len,
814 std::back_inserter(data));
815 std::copy(value_data.c_str(), value_data.c_str() + value_data.length(),
816 std::back_inserter(data));
818 const void* cookie = testHarness.create_cookie();
821 checkeq(ENGINE_SUCCESS,
822 storeCasVb11(h, h1, cookie, OPERATION_SET, key,
823 reinterpret_cast<char*>(data.data()),
824 data.size(), 9258, &itm, 0, 0, 10,
825 PROTOCOL_BINARY_DATATYPE_XATTR),
826 "Failed to store xattr document");
827 h1->release(h, nullptr, itm);
829 if (isPersistentBucket(h, h1)) {
830 wait_for_flusher_to_settle(h, h1);
833 testHarness.time_travel(11);
836 get_meta(h, h1, "test_expiry", true, GetMetaVersion::V2, cookie),
837 "Get meta command failed");
838 auto prev_revseqno = last_meta.revSeqno;
840 checkeq(static_cast<uint8_t>(PROTOCOL_BINARY_DATATYPE_XATTR),
841 last_datatype.load(), "Datatype is not XATTR");
843 checkeq(ENGINE_SUCCESS,
844 get(h, h1, cookie, &itm, key, 0,
845 DocStateFilter::AliveOrDeleted),
846 "Unable to get a deleted item");
849 get_meta(h, h1, "test_expiry", false, GetMetaVersion::V1, cookie),
850 "Get meta command failed");
852 checkeq(last_meta.revSeqno, prev_revseqno + 1,
853 "rev seqno must have incremented by 1");
855 /* Retrieve the item info and create a new blob out of the data */
857 checkeq(true, h1->get_item_info(h, cookie, itm, &info),
858 "Unable to retrieve item info");
860 cb::byte_buffer value_buf{static_cast<uint8_t*>(info.value[0].iov_base),
861 info.value[0].iov_len};
863 cb::xattr::Blob new_blob(value_buf);
865 /* Only system extended attributes need to be present at this point.
866 * Thus, check the blob length with the system size.
868 const auto systemsize = new_blob.finalize().len;
870 checkeq(systemsize, new_blob.get_system_size(),
871 "The size of the blob doesn't match the size of system attributes");
873 const std::string& cas_str{"{\"cas\":\"0xdeadbeefcafefeed\"}"};
874 const std::string& sync_str = to_string(blob.get(to_const_byte_buffer("_sync")));
876 checkeq(cas_str, sync_str , "system xattr is invalid");
878 h1->release(h, nullptr, itm);
879 testHarness.destroy_cookie(cookie);
884 static enum test_result test_expiry(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
885 const char *key = "test_expiry";
886 const char *data = "some test data here.";
890 ENGINE_ERROR_CODE rv;
891 rv = allocate(h, h1, NULL, &it, key, strlen(data), 0, 2,
892 PROTOCOL_BINARY_RAW_BYTES, 0);
893 checkeq(ENGINE_SUCCESS, rv, "Allocation failed.");
896 if (!h1->get_item_info(h, NULL, it, &info)) {
899 memcpy(info.value[0].iov_base, data, strlen(data));
902 rv = h1->store(h, NULL, it, &cas, OPERATION_SET, DocumentState::Alive);
903 checkeq(ENGINE_SUCCESS, rv, "Set failed.");
904 check_key_value(h, h1, key, data, strlen(data));
905 h1->release(h, NULL, it);
907 testHarness.time_travel(5);
908 checkeq(ENGINE_KEY_ENOENT,
909 get(h, h1, NULL, &it, key, 0),
910 "Item didn't expire");
912 int expired_access = get_int_stat(h, h1, "ep_expired_access");
913 int expired_pager = get_int_stat(h, h1, "ep_expired_pager");
914 int active_expired = get_int_stat(h, h1, "vb_active_expired");
915 checkeq(0, expired_pager, "Expected zero expired item by pager");
916 checkeq(1, expired_access, "Expected an expired item on access");
917 checkeq(1, active_expired, "Expected an expired active item");
918 checkeq(ENGINE_SUCCESS, store(h, h1, NULL, OPERATION_SET, key, data, &it),
920 h1->release(h, NULL, it);
922 // When run under full eviction, the total item stats are set from the
923 // flusher. So we need to wait for it to finish before checking the
924 // total number of items.
925 wait_for_flusher_to_settle(h, h1);
927 std::stringstream ss;
928 ss << "curr_items stat should be still 1 after ";
929 ss << "overwriting the key that was expired, but not purged yet";
930 checkeq(1, get_int_stat(h, h1, "curr_items"), ss.str().c_str());
935 static enum test_result test_expiry_loader(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
936 if (!isWarmupEnabled(h, h1)) {
939 const char *key = "test_expiry_loader";
940 const char *data = "some test data here.";
944 ENGINE_ERROR_CODE rv;
945 rv = allocate(h, h1, NULL, &it, key, strlen(data), 0, 2,
946 PROTOCOL_BINARY_RAW_BYTES, 0);
947 checkeq(ENGINE_SUCCESS, rv, "Allocation failed.");
950 if (!h1->get_item_info(h, NULL, it, &info)) {
953 memcpy(info.value[0].iov_base, data, strlen(data));
956 rv = h1->store(h, NULL, it, &cas, OPERATION_SET, DocumentState::Alive);
957 checkeq(ENGINE_SUCCESS, rv, "Set failed.");
958 check_key_value(h, h1, key, data, strlen(data));
959 h1->release(h, NULL, it);
961 testHarness.time_travel(3);
963 checkeq(ENGINE_KEY_ENOENT,
964 get(h, h1, NULL, &it, key, 0),
965 "Item didn't expire");
967 // Restart the engine to ensure the above expired item is not loaded
968 testHarness.reload_engine(&h, &h1,
969 testHarness.engine_path,
970 testHarness.get_current_testcase()->cfg,
972 wait_for_warmup_complete(h, h1);
973 cb_assert(0 == get_int_stat(h, h1, "ep_warmup_value_count", "warmup"));
978 static enum test_result test_expiration_on_compaction(ENGINE_HANDLE *h,
979 ENGINE_HANDLE_V1 *h1) {
980 if (get_bool_stat(h, h1, "ep_exp_pager_enabled")) {
981 set_param(h, h1, protocol_binary_engine_param_flush,
982 "exp_pager_enabled", "false");
985 checkeq(1, get_int_stat(h, h1, "vb_0:persistence:num_visits",
986 "checkpoint"), "Cursor moved before item load");
988 for (int i = 0; i < 50; i++) {
990 std::stringstream ss;
992 checkeq(ENGINE_SUCCESS,
993 store(h, h1, NULL, OPERATION_SET, ss.str().c_str(),
994 "somevalue", &itm, 0, 0, 10,
995 PROTOCOL_BINARY_RAW_BYTES),
997 h1->release(h, NULL, itm);
1000 wait_for_flusher_to_settle(h, h1);
1001 checkeq(50, get_int_stat(h, h1, "curr_items"),
1002 "Unexpected number of items on database");
1003 check(1 < get_int_stat(h, h1, "vb_0:persistence:num_visits", "checkpoint"),
1004 "Cursor not moved even after flusher runs");
1006 testHarness.time_travel(15);
1008 // Compaction on VBucket
1009 compact_db(h, h1, 0, 0, 0, 0, 0);
1010 wait_for_stat_to_be(h, h1, "ep_pending_compactions", 0);
1012 checkeq(50, get_int_stat(h, h1, "ep_expired_compactor"),
1013 "Unexpected expirations by compactor");
1018 static enum test_result test_expiration_on_warmup(ENGINE_HANDLE *h,
1019 ENGINE_HANDLE_V1 *h1) {
1020 if (!isWarmupEnabled(h, h1)) {
1024 set_param(h, h1, protocol_binary_engine_param_flush,
1025 "exp_pager_enabled", "false");
1026 int pager_runs = get_int_stat(h, h1, "ep_num_expiry_pager_runs");
1028 const char *key = "KEY";
1029 const char *data = "VALUE";
1033 ENGINE_ERROR_CODE rv;
1034 rv = allocate(h, h1, NULL, &it, key, strlen(data), 0, 10,
1035 PROTOCOL_BINARY_RAW_BYTES, 0);
1036 checkeq(ENGINE_SUCCESS, rv, "Allocation failed.");
1039 if (!h1->get_item_info(h, NULL, it, &info)) {
1042 memcpy(info.value[0].iov_base, data, strlen(data));
1045 rv = h1->store(h, NULL, it, &cas, OPERATION_SET, DocumentState::Alive);
1046 checkeq(ENGINE_SUCCESS, rv, "Set failed.");
1047 check_key_value(h, h1, key, data, strlen(data));
1048 h1->release(h, NULL, it);
1049 wait_for_flusher_to_settle(h, h1);
1051 checkeq(1, get_int_stat(h, h1, "curr_items"), "Failed store item");
1052 testHarness.time_travel(15);
1054 checkeq(pager_runs, get_int_stat(h, h1, "ep_num_expiry_pager_runs"),
1055 "Expiry pager shouldn't have run during this time");
1057 // Restart the engine to ensure the above item is expired
1058 testHarness.reload_engine(&h, &h1,
1059 testHarness.engine_path,
1060 testHarness.get_current_testcase()->cfg,
1062 wait_for_warmup_complete(h, h1);
1063 check(get_bool_stat(h, h1, "ep_exp_pager_enabled"),
1064 "Expiry pager should be enabled on warmup");
1066 // Wait for the expiry pager to run and expire our item.
1067 wait_for_stat_to_be_gte(h, h1, "ep_expired_pager", 1, nullptr, /*secs*/10);
1069 // Note: previously we checked that curr_items was zero here (immediately
1070 // after waiting for ep_expired_pager == 1), however we cannot assume that
1071 // - items are actually expired asynchronously.
1072 // See EPStore::deleteExpiredItem - for non-temporary, expired items we
1073 // call processSoftDelete (soft-marking the item as deleted in the
1074 // hashtable), and then call queueDirty to queue a deletion, and then
1075 // increment the expired stat. Only when that delete is actually persisted
1076 // and the deleted callback is invoked -
1077 // PeristenceCallback::callback(int&) - is curr_items finally decremented.
1078 // Therefore we need to wait for the flusher to settle (i.e. delete
1079 // callback to be called) for the curr_items stat to be accurate.
1080 wait_for_flusher_to_settle(h, h1);
1082 checkeq(0, get_int_stat(h, h1, "curr_items"),
1083 "The item should have been expired.");
1089 static enum test_result test_bug3454(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1090 if (!isWarmupEnabled(h, h1)) {
1094 const char *key = "test_expiry_duplicate_warmup";
1095 const char *data = "some test data here.";
1099 ENGINE_ERROR_CODE rv;
1100 rv = allocate(h, h1, NULL, &it, key, strlen(data), 0, 5,
1101 PROTOCOL_BINARY_RAW_BYTES, 0);
1102 checkeq(ENGINE_SUCCESS, rv, "Allocation failed.");
1105 if (!h1->get_item_info(h, NULL, it, &info)) {
1108 memcpy(info.value[0].iov_base, data, strlen(data));
1111 rv = h1->store(h, NULL, it, &cas, OPERATION_SET, DocumentState::Alive);
1112 checkeq(ENGINE_SUCCESS, rv, "Set failed.");
1113 check_key_value(h, h1, key, data, strlen(data));
1114 h1->release(h, NULL, it);
1115 wait_for_flusher_to_settle(h, h1);
1117 // Advance the ep_engine time by 10 sec for the above item to be expired.
1118 testHarness.time_travel(10);
1119 checkeq(ENGINE_KEY_ENOENT,
1120 get(h, h1, NULL, &it, key, 0),
1121 "Item didn't expire");
1123 rv = allocate(h, h1, NULL, &it, key, strlen(data), 0, 0,
1124 PROTOCOL_BINARY_RAW_BYTES, 0);
1125 checkeq(ENGINE_SUCCESS, rv, "Allocation failed.");
1127 if (!h1->get_item_info(h, NULL, it, &info)) {
1130 memcpy(info.value[0].iov_base, data, strlen(data));
1133 // Add a new item with the same key.
1134 rv = h1->store(h, NULL, it, &cas, OPERATION_ADD, DocumentState::Alive);
1135 checkeq(ENGINE_SUCCESS, rv, "Add failed.");
1136 check_key_value(h, h1, key, data, strlen(data));
1137 h1->release(h, NULL, it);
1138 wait_for_flusher_to_settle(h, h1);
1140 checkeq(ENGINE_SUCCESS,
1141 get(h, h1, NULL, &it, key, 0),
1142 "Item shouldn't expire");
1143 h1->release(h, NULL, it);
1145 // Restart the engine to ensure the above unexpired new item is loaded
1146 testHarness.reload_engine(&h, &h1,
1147 testHarness.engine_path,
1148 testHarness.get_current_testcase()->cfg,
1150 wait_for_warmup_complete(h, h1);
1151 cb_assert(1 == get_int_stat(h, h1, "ep_warmup_value_count", "warmup"));
1152 cb_assert(0 == get_int_stat(h, h1, "ep_warmup_dups", "warmup"));
1157 static enum test_result test_bug3522(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1158 if (!isWarmupEnabled(h, h1)) {
1162 const char *key = "test_expiry_no_items_warmup";
1163 const char *data = "some test data here.";
1167 ENGINE_ERROR_CODE rv;
1168 rv = allocate(h, h1, NULL, &it, key, strlen(data), 0, 0,
1169 PROTOCOL_BINARY_RAW_BYTES, 0);
1170 checkeq(ENGINE_SUCCESS, rv, "Allocation failed.");
1173 if (!h1->get_item_info(h, NULL, it, &info)) {
1176 memcpy(info.value[0].iov_base, data, strlen(data));
1179 rv = h1->store(h, NULL, it, &cas, OPERATION_SET, DocumentState::Alive);
1180 checkeq(ENGINE_SUCCESS, rv, "Set failed.");
1181 check_key_value(h, h1, key, data, strlen(data));
1182 h1->release(h, NULL, it);
1183 wait_for_flusher_to_settle(h, h1);
1185 // Add a new item with the same key and 2 sec of expiration.
1186 const char *new_data = "new data here.";
1187 rv = allocate(h, h1, NULL, &it, key, strlen(new_data), 0, 2,
1188 PROTOCOL_BINARY_RAW_BYTES, 0);
1189 checkeq(ENGINE_SUCCESS, rv, "Allocation failed.");
1191 if (!h1->get_item_info(h, NULL, it, &info)) {
1194 memcpy(info.value[0].iov_base, new_data, strlen(new_data));
1196 int pager_runs = get_int_stat(h, h1, "ep_num_expiry_pager_runs");
1198 rv = h1->store(h, NULL, it, &cas, OPERATION_SET, DocumentState::Alive);
1199 checkeq(ENGINE_SUCCESS, rv, "Set failed.");
1200 check_key_value(h, h1, key, new_data, strlen(new_data));
1201 h1->release(h, NULL, it);
1202 testHarness.time_travel(3);
1203 wait_for_stat_change(h, h1, "ep_num_expiry_pager_runs", pager_runs);
1204 wait_for_flusher_to_settle(h, h1);
1206 // Restart the engine.
1207 testHarness.reload_engine(&h, &h1,
1208 testHarness.engine_path,
1209 testHarness.get_current_testcase()->cfg,
1211 wait_for_warmup_complete(h, h1);
1212 // TODO: modify this for a better test case
1213 cb_assert(0 == get_int_stat(h, h1, "ep_warmup_dups", "warmup"));
1218 static enum test_result test_get_replica_active_state(ENGINE_HANDLE *h,
1219 ENGINE_HANDLE_V1 *h1) {
1220 protocol_binary_request_header *pkt;
1221 pkt = prepare_get_replica(h, h1, vbucket_state_active);
1222 checkeq(ENGINE_SUCCESS,
1223 h1->unknown_command(h, NULL, pkt, add_response, testHarness.doc_namespace),
1224 "Get Replica Failed");
1225 checkeq(PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET, last_status.load(),
1226 "Expected PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET response.");
1232 static enum test_result test_get_replica_pending_state(ENGINE_HANDLE *h,
1233 ENGINE_HANDLE_V1 *h1) {
1234 protocol_binary_request_header *pkt;
1236 const void *cookie = testHarness.create_cookie();
1237 testHarness.set_ewouldblock_handling(cookie, false);
1238 pkt = prepare_get_replica(h, h1, vbucket_state_pending);
1239 checkeq(ENGINE_EWOULDBLOCK,
1240 h1->unknown_command(h, cookie, pkt, add_response, testHarness.doc_namespace),
1241 "Should have returned error for pending state");
1242 testHarness.destroy_cookie(cookie);
1247 static enum test_result test_get_replica_dead_state(ENGINE_HANDLE *h,
1248 ENGINE_HANDLE_V1 *h1) {
1249 protocol_binary_request_header *pkt;
1250 pkt = prepare_get_replica(h, h1, vbucket_state_dead);
1251 checkeq(ENGINE_SUCCESS,
1252 h1->unknown_command(h, NULL, pkt, add_response, testHarness.doc_namespace),
1253 "Get Replica Failed");
1254 checkeq(PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET, last_status.load(),
1255 "Expected PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET response.");
1261 static enum test_result test_get_replica(ENGINE_HANDLE *h,
1262 ENGINE_HANDLE_V1 *h1) {
1263 protocol_binary_request_header *pkt;
1264 pkt = prepare_get_replica(h, h1, vbucket_state_replica);
1265 checkeq(ENGINE_SUCCESS,
1266 h1->unknown_command(h, NULL, pkt, add_response, testHarness.doc_namespace),
1267 "Get Replica Failed");
1268 checkeq(PROTOCOL_BINARY_RESPONSE_SUCCESS, last_status.load(),
1269 "Expected PROTOCOL_BINARY_RESPONSE_SUCCESS response.");
1270 checkeq(std::string("replicadata"), last_body,
1271 "Should have returned identical value");
1277 static enum test_result test_get_replica_non_resident(ENGINE_HANDLE *h,
1278 ENGINE_HANDLE_V1 *h1) {
1281 checkeq(ENGINE_SUCCESS,
1282 store(h, h1, NULL, OPERATION_SET, "key", "value", &i, 0, 0),
1284 h1->release(h, NULL, i);
1285 wait_for_flusher_to_settle(h, h1);
1286 wait_for_stat_to_be(h, h1, "ep_total_persisted", 1);
1288 evict_key(h, h1, "key", 0, "Ejected.");
1289 check(set_vbucket_state(h, h1, 0, vbucket_state_replica),
1290 "Failed to set vbucket to replica");
1292 get_replica(h, h1, "key", 0);
1293 checkeq(PROTOCOL_BINARY_RESPONSE_SUCCESS, last_status.load(),
1294 "Expected success");
1299 static enum test_result test_get_replica_invalid_key(ENGINE_HANDLE *h,
1300 ENGINE_HANDLE_V1 *h1) {
1301 protocol_binary_request_header *pkt;
1302 bool makeinvalidkey = true;
1303 pkt = prepare_get_replica(h, h1, vbucket_state_replica, makeinvalidkey);
1304 checkeq(ENGINE_SUCCESS,
1305 h1->unknown_command(h, NULL, pkt, add_response, testHarness.doc_namespace),
1306 "Get Replica Failed");
1307 checkeq(PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET, last_status.load(),
1308 "Expected PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET response.");
1313 static enum test_result test_vb_del_pending(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1314 const void *cookie = testHarness.create_cookie();
1315 testHarness.set_ewouldblock_handling(cookie, false);
1316 check(set_vbucket_state(h, h1, 1, vbucket_state_pending),
1317 "Failed to set vbucket state.");
1318 checkeq(ENGINE_EWOULDBLOCK, del(h, h1, "key", 0, 1, cookie),
1319 "Expected woodblock.");
1320 testHarness.destroy_cookie(cookie);
1324 static enum test_result test_vb_del_replica(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1325 check(set_vbucket_state(h, h1, 1, vbucket_state_replica),
1326 "Failed to set vbucket state.");
1327 int numNotMyVBucket = get_int_stat(h, h1, "ep_num_not_my_vbuckets");
1328 checkeq(ENGINE_NOT_MY_VBUCKET, del(h, h1, "key", 0, 1),
1329 "Expected not my vbucket.");
1330 wait_for_stat_change(h, h1, "ep_num_not_my_vbuckets", numNotMyVBucket);
1334 static enum test_result test_vbucket_get_miss(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1335 return verify_vbucket_missing(h, h1, 1) ? SUCCESS : FAIL;
1338 static enum test_result test_vbucket_get(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1339 return verify_vbucket_state(h, h1, 0, vbucket_state_active) ? SUCCESS : FAIL;
1342 static enum test_result test_vbucket_create(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1343 if (!verify_vbucket_missing(h, h1, 1)) {
1344 fprintf(stderr, "vbucket wasn't missing.\n");
1348 if (!set_vbucket_state(h, h1, 1, vbucket_state_active)) {
1349 fprintf(stderr, "set state failed.\n");
1353 return verify_vbucket_state(h, h1, 1, vbucket_state_active) ? SUCCESS : FAIL;
1356 static enum test_result test_takeover_stats_race_with_vb_create_TAP(
1357 ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1358 check(set_vbucket_state(h, h1, 1, vbucket_state_active),
1359 "Failed to set vbucket state information");
1362 get_int_stat(h, h1, "on_disk_deletes", "tap-vbtakeover 1"),
1363 "Invalid number of on-disk deletes");
1368 static enum test_result test_takeover_stats_race_with_vb_create_DCP(
1369 ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1370 check(set_vbucket_state(h, h1, 1, vbucket_state_active),
1371 "Failed to set vbucket state information");
1374 get_int_stat(h, h1, "on_disk_deletes", "dcp-vbtakeover 1"),
1375 "Invalid number of on-disk deletes");
1380 static enum test_result test_takeover_stats_num_persisted_deletes(
1381 ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1383 std::string key("key");
1384 checkeq(ENGINE_SUCCESS,
1385 store(h, h1, NULL, OPERATION_SET, key.c_str(), "data", nullptr),
1386 "Failed to store an item");
1388 /* delete the item */
1389 checkeq(ENGINE_SUCCESS, del(h, h1, key.c_str(), 0, 0),
1390 "Failed to delete the item");
1392 /* wait for persistence */
1393 wait_for_flusher_to_settle(h, h1);
1395 /* check if persisted deletes stats is got correctly */
1397 get_int_stat(h, h1, "on_disk_deletes", "dcp-vbtakeover 0"),
1398 "Invalid number of on-disk deletes");
1403 static enum test_result test_vbucket_compact(ENGINE_HANDLE *h,
1404 ENGINE_HANDLE_V1 *h1) {
1405 const char *key = "Carss";
1406 const char *value = "pollute";
1407 if (!verify_vbucket_missing(h, h1, 0)) {
1408 fprintf(stderr, "vbucket wasn't missing.\n");
1412 if (!set_vbucket_state(h, h1, 0, vbucket_state_active)) {
1413 fprintf(stderr, "set state failed.\n");
1417 check(verify_vbucket_state(h, h1, 0, vbucket_state_active),
1418 "VBucket state not active");
1420 // Set two keys - one to be expired and other to remain...
1422 checkeq(ENGINE_SUCCESS,
1423 store(h, h1, NULL, OPERATION_SET, key, value, &itm),
1425 h1->release(h, NULL, itm);
1427 check_key_value(h, h1, key, value, strlen(value));
1429 // Set a non-expiring key...
1430 checkeq(ENGINE_SUCCESS,
1431 store(h, h1, NULL, OPERATION_SET, "trees", "cleanse", &itm),
1433 h1->release(h, NULL, itm);
1435 check_key_value(h, h1, "trees", "cleanse", strlen("cleanse"));
1437 touch(h, h1, key, 0, 11);
1438 checkeq(PROTOCOL_BINARY_RESPONSE_SUCCESS, last_status.load(),
1441 testHarness.time_travel(12);
1442 wait_for_flusher_to_settle(h, h1);
1444 // Store a dummy item since we do not purge the item with highest seqno
1445 checkeq(ENGINE_SUCCESS,
1446 store(h, h1, NULL, OPERATION_SET, "dummykey", "dummyvalue", &itm,
1449 h1->release(h, NULL, itm);
1451 wait_for_flusher_to_settle(h, h1);
1453 checkeq(0, get_int_stat(h, h1, "vb_0:purge_seqno", "vbucket-seqno"),
1454 "purge_seqno not found to be zero before compaction");
1456 // Compaction on VBucket
1457 compact_db(h, h1, 0, 0, 2, 3, 1);
1459 wait_for_stat_to_be(h, h1, "ep_pending_compactions", 0);
1461 // the key tree and its value should be intact...
1462 checkeq(ENGINE_SUCCESS, verify_key(h, h1, "trees"),
1463 "key trees should be found.");
1464 // the key Carrs should have disappeared...
1465 ENGINE_ERROR_CODE val = verify_key(h, h1, "Carss");
1466 checkeq(ENGINE_KEY_ENOENT, val, "Key Carss has not expired.");
1468 checkeq(4, get_int_stat(h, h1, "vb_0:purge_seqno", "vbucket-seqno"),
1469 "purge_seqno didn't match expected value");
1474 static enum test_result test_compaction_config(ENGINE_HANDLE *h,
1475 ENGINE_HANDLE_V1 *h1) {
1478 get_int_stat(h, h1, "ep_compaction_write_queue_cap"),
1479 "Expected compaction queue cap to be 10000");
1480 set_param(h, h1, protocol_binary_engine_param_flush,
1481 "compaction_write_queue_cap", "100000");
1482 checkeq(100000, get_int_stat(h, h1, "ep_compaction_write_queue_cap"),
1483 "Expected compaction queue cap to be 100000");
1487 struct comp_thread_ctx {
1489 ENGINE_HANDLE_V1 *h1;
1491 uint16_t db_file_id;
1495 static void compaction_thread(void *arg) {
1496 struct comp_thread_ctx *ctx = static_cast<comp_thread_ctx *>(arg);
1497 compact_db(ctx->h, ctx->h1, ctx->vbid, ctx->db_file_id, 0, 0, 0);
1501 static enum test_result test_multiple_vb_compactions(ENGINE_HANDLE *h,
1502 ENGINE_HANDLE_V1 *h1) {
1503 for (uint16_t i = 0; i < 4; ++i) {
1504 if (!set_vbucket_state(h, h1, i, vbucket_state_active)) {
1505 fprintf(stderr, "set state failed for vbucket %d.\n", i);
1508 check(verify_vbucket_state(h, h1, i, vbucket_state_active),
1509 "VBucket state not active");
1512 std::vector<std::string> keys;
1513 for (int j = 0; j < 20000; ++j) {
1514 std::stringstream ss;
1516 std::string key(ss.str());
1517 keys.push_back(key);
1521 std::vector<std::string>::iterator it;
1522 for (it = keys.begin(); it != keys.end(); ++it) {
1523 uint16_t vbid = count % 4;
1525 checkeq(ENGINE_SUCCESS,
1526 store(h, h1, NULL, OPERATION_SET, it->c_str(), it->c_str(), &i, 0, vbid),
1527 "Failed to store a value");
1528 h1->release(h, NULL, i);
1532 // Compact multiple vbuckets.
1533 const int n_threads = 4;
1534 cb_thread_t threads[n_threads];
1535 struct comp_thread_ctx ctx[n_threads];
1537 const int num_shards = get_int_stat(h, h1, "ep_workload:num_shards",
1540 for (int i = 0; i < n_threads; i++) {
1543 ctx[i].vbid = static_cast<uint16_t>(i);
1544 ctx[i].db_file_id = ctx[i].vbid % num_shards;
1545 int r = cb_create_thread(&threads[i], compaction_thread, &ctx[i], 0);
1549 for (int i = 0; i < n_threads; i++) {
1550 int r = cb_join_thread(threads[i]);
1554 wait_for_stat_to_be(h, h1, "ep_pending_compactions", 0);
1559 static enum test_result
1560 test_multi_vb_compactions_with_workload(ENGINE_HANDLE *h,
1561 ENGINE_HANDLE_V1 *h1) {
1562 for (uint16_t i = 0; i < 4; ++i) {
1563 if (!set_vbucket_state(h, h1, i, vbucket_state_active)) {
1564 fprintf(stderr, "set state failed for vbucket %d.\n", i);
1567 check(verify_vbucket_state(h, h1, i, vbucket_state_active),
1568 "VBucket state not active");
1571 std::vector<std::string> keys;
1572 for (int j = 0; j < 10000; ++j) {
1573 std::stringstream ss;
1575 std::string key(ss.str());
1576 keys.push_back(key);
1580 std::vector<std::string>::iterator it;
1581 for (it = keys.begin(); it != keys.end(); ++it) {
1582 uint16_t vbid = count % 4;
1584 checkeq(ENGINE_SUCCESS,
1585 store(h, h1, NULL, OPERATION_SET, it->c_str(), it->c_str(),
1587 "Failed to store a value");
1588 h1->release(h, NULL, i);
1591 wait_for_flusher_to_settle(h, h1);
1593 for (int i = 0; i < 2; ++i) {
1595 for (it = keys.begin(); it != keys.end(); ++it) {
1596 uint16_t vbid = count % 4;
1598 checkeq(ENGINE_SUCCESS,
1599 get(h, h1, NULL, &i, it->c_str(), vbid),
1600 "Unable to get stored item");
1601 h1->release(h, NULL, i);
1605 wait_for_stat_to_be(h, h1, "ep_workload_pattern", std::string{"read_heavy"});
1607 // Compact multiple vbuckets.
1608 const int n_threads = 4;
1609 cb_thread_t threads[n_threads];
1610 struct comp_thread_ctx ctx[n_threads];
1612 for (int i = 0; i < n_threads; i++) {
1615 ctx[i].vbid = static_cast<uint16_t>(i);
1616 int r = cb_create_thread(&threads[i], compaction_thread, &ctx[i], 0);
1620 for (int i = 0; i < n_threads; i++) {
1621 int r = cb_join_thread(threads[i]);
1625 wait_for_stat_to_be(h, h1, "ep_pending_compactions", 0);
1630 static enum test_result vbucket_destroy(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1,
1631 const char* value = NULL) {
1632 check(set_vbucket_state(h, h1, 1, vbucket_state_active),
1633 "Failed to set vbucket state.");
1635 vbucketDelete(h, h1, 2, value);
1636 checkeq(PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET,
1638 "Expected failure deleting non-existent bucket.");
1640 check(set_vbucket_state(h, h1, 1, vbucket_state_dead),
1641 "Failed set set vbucket 1 state.");
1643 vbucketDelete(h, h1, 1, value);
1644 checkeq(PROTOCOL_BINARY_RESPONSE_SUCCESS, last_status.load(),
1645 "Expected failure deleting non-existent bucket.");
1647 check(verify_vbucket_missing(h, h1, 1),
1648 "vbucket 0 was not missing after deleting it.");
1653 static enum test_result test_vbucket_destroy_stats(ENGINE_HANDLE *h,
1654 ENGINE_HANDLE_V1 *h1) {
1656 int cacheSize = get_int_stat(h, h1, "ep_total_cache_size");
1657 int overhead = get_int_stat(h, h1, "ep_overhead");
1658 int nonResident = get_int_stat(h, h1, "ep_num_non_resident");
1660 check(set_vbucket_state(h, h1, 1, vbucket_state_active),
1661 "Failed to set vbucket state.");
1663 std::vector<std::string> keys;
1664 for (int j = 0; j < 2000; ++j) {
1665 std::stringstream ss;
1667 std::string key(ss.str());
1668 keys.push_back(key);
1671 int itemsRemoved = get_int_stat(h, h1, "ep_items_rm_from_checkpoints");
1672 std::vector<std::string>::iterator it;
1673 for (it = keys.begin(); it != keys.end(); ++it) {
1675 checkeq(ENGINE_SUCCESS,
1676 store(h, h1, NULL, OPERATION_SET, it->c_str(), it->c_str(),
1678 "Failed to store a value");
1679 h1->release(h, NULL, i);
1681 wait_for_flusher_to_settle(h, h1);
1682 testHarness.time_travel(65);
1683 wait_for_stat_change(h, h1, "ep_items_rm_from_checkpoints", itemsRemoved);
1685 check(set_vbucket_state(h, h1, 1, vbucket_state_dead),
1686 "Failed set set vbucket 1 state.");
1688 int vbucketDel = get_int_stat(h, h1, "ep_vbucket_del");
1689 vbucketDelete(h, h1, 1);
1690 checkeq(PROTOCOL_BINARY_RESPONSE_SUCCESS,
1692 "Expected failure deleting non-existent bucket.");
1694 check(verify_vbucket_missing(h, h1, 1),
1695 "vbucket 1 was not missing after deleting it.");
1697 wait_for_stat_change(h, h1, "ep_vbucket_del", vbucketDel);
1699 wait_for_stat_to_be(h, h1, "ep_total_cache_size", cacheSize);
1700 wait_for_stat_to_be(h, h1, "ep_overhead", overhead);
1701 wait_for_stat_to_be(h, h1, "ep_num_non_resident", nonResident);
1706 static enum test_result vbucket_destroy_restart(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1,
1707 const char* value = NULL) {
1708 if (!isWarmupEnabled(h, h1)) {
1712 check(set_vbucket_state(h, h1, 1, vbucket_state_active),
1713 "Failed to set vbucket state.");
1715 // Store a value so the restart will try to resurrect it.
1717 checkeq(ENGINE_SUCCESS,
1718 store(h, h1, NULL, OPERATION_SET, "key", "somevalue", &i, 0, 1),
1719 "Failed to set a value");
1720 check_key_value(h, h1, "key", "somevalue", 9, 1);
1721 h1->release(h, NULL, i);
1723 // Reload to get a flush forced.
1724 testHarness.reload_engine(&h, &h1,
1725 testHarness.engine_path,
1726 testHarness.get_current_testcase()->cfg,
1728 wait_for_warmup_complete(h, h1);
1730 check(verify_vbucket_state(h, h1, 1, vbucket_state_active),
1731 "Bucket state was what it was initially, after restart.");
1732 check(set_vbucket_state(h, h1, 1, vbucket_state_active),
1733 "Failed to set vbucket state.");
1734 check_key_value(h, h1, "key", "somevalue", 9, 1);
1736 check(set_vbucket_state(h, h1, 1, vbucket_state_dead),
1737 "Failed set set vbucket 1 state.");
1739 vbucketDelete(h, h1, 1, value);
1740 checkeq(PROTOCOL_BINARY_RESPONSE_SUCCESS, last_status.load(),
1741 "Expected failure deleting non-existent bucket.");
1743 check(verify_vbucket_missing(h, h1, 1),
1744 "vbucket 1 was not missing after deleting it.");
1746 testHarness.reload_engine(&h, &h1,
1747 testHarness.engine_path,
1748 testHarness.get_current_testcase()->cfg,
1750 wait_for_warmup_complete(h, h1);
1752 if (verify_vbucket_state(h, h1, 1, vbucket_state_pending, true)) {
1753 std::cerr << "Bucket came up in pending state after delete." << std::endl;
1757 check(verify_vbucket_missing(h, h1, 1),
1758 "vbucket 1 was not missing after restart.");
1763 static enum test_result test_async_vbucket_destroy(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1764 return vbucket_destroy(h, h1);
1767 static enum test_result test_sync_vbucket_destroy(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1768 return vbucket_destroy(h, h1, "async=0");
1771 static enum test_result test_async_vbucket_destroy_restart(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1772 return vbucket_destroy_restart(h, h1);
1775 static enum test_result test_sync_vbucket_destroy_restart(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1776 return vbucket_destroy_restart(h, h1, "async=0");
1779 static enum test_result test_vb_set_pending(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1780 return test_pending_vb_mutation(h, h1, OPERATION_SET);
1783 static enum test_result test_vb_add_pending(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1784 return test_pending_vb_mutation(h, h1, OPERATION_ADD);
1787 static enum test_result test_vb_cas_pending(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1788 return test_pending_vb_mutation(h, h1, OPERATION_CAS);
1791 static enum test_result test_vb_set_replica(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1792 return test_replica_vb_mutation(h, h1, OPERATION_SET);
1795 static enum test_result test_vb_replace_replica(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1796 return test_replica_vb_mutation(h, h1, OPERATION_REPLACE);
1799 static enum test_result test_vb_replace_pending(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1800 return test_pending_vb_mutation(h, h1, OPERATION_REPLACE);
1803 static enum test_result test_vb_add_replica(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1804 return test_replica_vb_mutation(h, h1, OPERATION_ADD);
1807 static enum test_result test_vb_cas_replica(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1808 return test_replica_vb_mutation(h, h1, OPERATION_CAS);
1811 static enum test_result test_stats_seqno(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1812 check(set_vbucket_state(h, h1, 1, vbucket_state_active),
1813 "Failed to set vbucket state.");
1816 for (int ii = 0; ii < num_keys; ++ii) {
1817 std::stringstream ss;
1819 checkeq(ENGINE_SUCCESS,
1820 store(h, h1, NULL, OPERATION_SET, ss.str().c_str(),
1821 "value", NULL, 0, 0),
1822 "Failed to store an item.");
1824 wait_for_flusher_to_settle(h, h1);
1826 checkeq(100, get_int_stat(h, h1, "vb_0:high_seqno", "vbucket-seqno"),
1829 if (isPersistentBucket(h, h1)) {
1831 get_int_stat(h, h1, "vb_0:last_persisted_seqno", "vbucket-seqno"),
1832 "Unexpected last_persisted_seqno");
1834 checkeq(0, get_int_stat(h, h1, "vb_1:high_seqno", "vbucket-seqno"),
1836 checkeq(0, get_int_stat(h, h1, "vb_1:high_seqno", "vbucket-seqno 1"),
1838 if (isPersistentBucket(h, h1)) {
1840 get_int_stat(h, h1, "vb_1:last_persisted_seqno", "vbucket-seqno 1"),
1841 "Invalid last_persisted_seqno");
1844 uint64_t vb_uuid = get_ull_stat(h, h1, "vb_1:0:id", "failovers");
1846 auto seqno_stats = get_all_stats(h, h1, "vbucket-seqno 1");
1847 checkeq(vb_uuid, uint64_t(std::stoull(seqno_stats.at("vb_1:uuid"))),
1850 checkeq(size_t(7), seqno_stats.size(), "Expected seven stats");
1852 // Check invalid vbucket
1853 checkeq(ENGINE_NOT_MY_VBUCKET,
1854 h1->get_stats(h, NULL, "vbucket-seqno 2", 15, add_stats),
1855 "Expected not my vbucket");
1857 // Check bad vbucket parameter (not numeric)
1858 checkeq(ENGINE_EINVAL,
1859 h1->get_stats(h, NULL, "vbucket-seqno tt2", 17, add_stats),
1860 "Expected invalid");
1862 // Check extra spaces at the end
1863 checkeq(ENGINE_EINVAL,
1864 h1->get_stats(h, NULL, "vbucket-seqno ", 17, add_stats),
1865 "Expected invalid");
1870 static enum test_result test_stats_diskinfo(ENGINE_HANDLE *h,
1871 ENGINE_HANDLE_V1 *h1) {
1872 check(set_vbucket_state(h, h1, 1, vbucket_state_active),
1873 "Failed to set vbucket state.");
1876 for (int ii = 0; ii < num_keys; ++ii) {
1877 std::stringstream ss;
1879 checkeq(ENGINE_SUCCESS,
1880 store(h, h1, NULL, OPERATION_SET, ss.str().c_str(),
1881 "value", NULL, 0, 1),
1882 "Failed to store an item.");
1884 wait_for_flusher_to_settle(h, h1);
1886 size_t file_size = get_int_stat(h, h1, "ep_db_file_size", "diskinfo");
1887 size_t data_size = get_int_stat(h, h1, "ep_db_data_size", "diskinfo");
1888 check(file_size > 0, "DB file size should be greater than 0");
1889 check(data_size > 0, "DB data size should be greater than 0");
1890 check(file_size >= data_size, "DB file size should be >= DB data size");
1891 check(get_int_stat(h, h1, "vb_1:data_size", "diskinfo detail") > 0,
1892 "VB 1 data size should be greater than 0");
1894 checkeq(ENGINE_EINVAL,
1895 h1->get_stats(h, NULL, "diskinfo ", 9, add_stats),
1896 "Expected invalid");
1898 checkeq(ENGINE_EINVAL,
1899 h1->get_stats(h, NULL, "diskinfo detai", 14, add_stats),
1900 "Expected invalid");
1902 checkeq(ENGINE_EINVAL,
1903 h1->get_stats(h, NULL, "diskinfo detaillll", 18, add_stats),
1904 "Expected invalid");
1909 static enum test_result test_uuid_stats(ENGINE_HANDLE *h,
1910 ENGINE_HANDLE_V1 *h1)
1913 checkeq(ENGINE_SUCCESS,
1914 h1->get_stats(h, NULL, "uuid", 4, add_stats),
1915 "Failed to get stats.");
1916 check(vals["uuid"] == "foobar", "Incorrect uuid");
1920 static enum test_result test_item_stats(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1922 checkeq(ENGINE_SUCCESS,
1923 store(h, h1, NULL, OPERATION_SET, "key", "somevalue", &i, 0, 0),
1925 h1->release(h, NULL, i);
1926 wait_for_flusher_to_settle(h, h1);
1927 checkeq(ENGINE_SUCCESS,
1928 store(h, h1, NULL, OPERATION_SET, "key", "somevalueX", &i, 0, 0),
1930 h1->release(h, NULL, i);
1931 wait_for_flusher_to_settle(h, h1);
1932 checkeq(ENGINE_SUCCESS,
1933 store(h, h1, NULL, OPERATION_SET, "key1", "somevalueY", &i, 0, 0),
1935 h1->release(h, NULL, i);
1936 wait_for_flusher_to_settle(h, h1);
1938 check_key_value(h, h1, "key", "somevalueX", 10);
1939 check_key_value(h, h1, "key1", "somevalueY", 10);
1941 checkeq(ENGINE_SUCCESS, del(h, h1, "key1", 0, 0),
1942 "Failed remove with value.");
1943 wait_for_flusher_to_settle(h, h1);
1945 checkeq(ENGINE_SUCCESS,
1946 store(h, h1, NULL, OPERATION_SET, "key1", "someothervalue", &i, 0, 0),
1948 h1->release(h, NULL, i);
1949 wait_for_flusher_to_settle(h, h1);
1951 check_key_value(h, h1, "key1", "someothervalue", 14);
1954 get_int_stat(h, h1, "vb_active_ops_create"),
1955 "Expected 3 creations");
1957 get_int_stat(h, h1, "vb_active_ops_update"),
1958 "Expected 1 updation");
1960 get_int_stat(h, h1, "vb_active_ops_delete"),
1961 "Expected 1 deletion");
1966 static enum test_result test_stats(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1968 checkeq(ENGINE_SUCCESS,
1969 h1->get_stats(h, NULL, NULL, 0, add_stats),
1970 "Failed to get stats.");
1971 check(vals.size() > 10, "Kind of expected more stats than that.");
1972 check(vals.find("ep_version") != vals.end(), "Found no ep_version.");
1977 static enum test_result test_mem_stats(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1979 memset(value, 'b', sizeof(value));
1980 strcpy(value + sizeof(value) - 4, "\r\n");
1981 int itemsRemoved = get_int_stat(h, h1, "ep_items_rm_from_checkpoints");
1982 wait_for_persisted_value(h, h1, "key", value);
1983 testHarness.time_travel(65);
1984 if (isPersistentBucket(h, h1)) {
1985 wait_for_stat_change(h, h1, "ep_items_rm_from_checkpoints", itemsRemoved);
1987 int mem_used = get_int_stat(h, h1, "mem_used");
1988 int cache_size = get_int_stat(h, h1, "ep_total_cache_size");
1989 int overhead = get_int_stat(h, h1, "ep_overhead");
1990 int value_size = get_int_stat(h, h1, "ep_value_size");
1991 check((mem_used - overhead) > cache_size,
1992 "ep_kv_size should be greater than the hashtable cache size due to the checkpoint overhead");
1994 if (isPersistentBucket(h, h1)) {
1995 evict_key(h, h1, "key", 0, "Ejected.");
1997 check(get_int_stat(h, h1, "ep_total_cache_size") <= cache_size,
1998 "Evict a value shouldn't increase the total cache size");
1999 check(get_int_stat(h, h1, "mem_used") < mem_used,
2000 "Expected mem_used to decrease when an item is evicted");
2002 check_key_value(h, h1, "key", value, strlen(value), 0); // Load an item from disk again.
2004 check(get_int_stat(h, h1, "mem_used") >= mem_used,
2005 "Expected mem_used to remain the same after an item is loaded from disk");
2006 check(get_int_stat(h, h1, "ep_value_size") == value_size,
2007 "Expected ep_value_size to remain the same after item is loaded from disk");
2013 static enum test_result test_io_stats(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
2014 int exp_read_bytes = 4, exp_write_bytes;
2015 std::string backend = get_str_stat(h, h1, "ep_backend");
2016 if (backend == "forestdb") {
2017 exp_write_bytes = 35; /* TBD: Do not hard code the value */
2018 } else if (backend == "couchdb") {
2019 exp_write_bytes = 22; /* TBD: Do not hard code the value */
2024 h1->reset_stats(h, NULL);
2026 checkeq(0, get_int_stat(h, h1, "rw_0:io_num_read", "kvstore"),
2027 "Expected reset stats to set io_num_read to zero");
2028 checkeq(0, get_int_stat(h, h1, "rw_0:io_num_write", "kvstore"),
2029 "Expected reset stats to set io_num_write to zero");
2030 checkeq(0, get_int_stat(h, h1, "rw_0:io_read_bytes", "kvstore"),
2031 "Expected reset stats to set io_read_bytes to zero");
2032 checkeq(0, get_int_stat(h, h1, "rw_0:io_write_bytes", "kvstore"),
2033 "Expected reset stats to set io_write_bytes to zero");
2035 wait_for_persisted_value(h, h1, "a", "b\r\n");
2036 checkeq(0, get_int_stat(h, h1, "rw_0:io_num_read", "kvstore"),
2037 "Expected storing one value to not change the read counter");
2038 checkeq(0, get_int_stat(h, h1, "rw_0:io_read_bytes", "kvstore"),
2039 "Expected storing one value to not change the read bytes");
2040 checkeq(1, get_int_stat(h, h1, "rw_0:io_num_write", "kvstore"),
2041 "Expected storing the key to update the write counter");
2042 checkeq(exp_write_bytes,
2043 get_int_stat(h, h1, "rw_0:io_write_bytes", "kvstore"),
2044 "Expected storing the key to update the write bytes");
2046 evict_key(h, h1, "a", 0, "Ejected.");
2048 check_key_value(h, h1, "a", "b\r\n", 3, 0);
2050 std::stringstream numReadStatStr;
2051 std::stringstream readBytesStatStr;
2053 if (backend == "couchdb") {
2054 numReadStatStr << "ro_" << 0 << ":io_num_read";
2055 readBytesStatStr << "ro_" << 0 << ":io_read_bytes";
2056 } else if (backend == "forestdb") {
2057 numReadStatStr << "rw_" << 0 << ":io_num_read";
2058 readBytesStatStr << "rw_" << 0 << ":io_read_bytes";
2063 checkeq(1, get_int_stat(h, h1, numReadStatStr.str().c_str(), "kvstore"),
2064 "Expected reading the value back in to update the read counter");
2065 checkeq(exp_read_bytes,
2066 get_int_stat(h, h1, readBytesStatStr.str().c_str(), "kvstore"),
2067 "Expected reading the value back in to update the read bytes");
2068 checkeq(1, get_int_stat(h, h1, "rw_0:io_num_write", "kvstore"),
2069 "Expected reading the value back in to not update the write counter");
2070 checkeq(exp_write_bytes,
2071 get_int_stat(h, h1, "rw_0:io_write_bytes", "kvstore"),
2072 "Expected reading the value back in to not update the write bytes");
2077 static enum test_result test_vb_file_stats(ENGINE_HANDLE *h,
2078 ENGINE_HANDLE_V1 *h1) {
2079 wait_for_flusher_to_settle(h, h1);
2080 wait_for_stat_change(h, h1, "ep_db_data_size", 0);
2082 int old_data_size = get_int_stat(h, h1, "ep_db_data_size");
2083 int old_file_size = get_int_stat(h, h1, "ep_db_file_size");
2084 check(old_file_size != 0, "Expected a non-zero value for ep_db_file_size");
2086 // Write a value and test ...
2087 wait_for_persisted_value(h, h1, "a", "b\r\n");
2088 check(get_int_stat(h, h1, "ep_db_data_size") > old_data_size,
2089 "Expected the DB data size to increase");
2090 check(get_int_stat(h, h1, "ep_db_file_size") > old_file_size,
2091 "Expected the DB file size to increase");
2093 check(get_int_stat(h, h1, "vb_0:db_data_size", "vbucket-details 0") > 0,
2094 "Expected the vbucket DB data size to non-zero");
2095 check(get_int_stat(h, h1, "vb_0:db_file_size", "vbucket-details 0") > 0,
2096 "Expected the vbucket DB file size to non-zero");
2100 static enum test_result test_vb_file_stats_after_warmup(ENGINE_HANDLE *h,
2101 ENGINE_HANDLE_V1 *h1) {
2102 if (!isWarmupEnabled(h, h1)) {
2107 for (int i = 0; i < 100; ++i) {
2108 std::stringstream key;
2110 checkeq(ENGINE_SUCCESS,
2111 store(h, h1, NULL, OPERATION_SET, key.str().c_str(), "somevalue", &it),
2113 h1->release(h, NULL, it);
2115 wait_for_flusher_to_settle(h, h1);
2117 int fileSize = get_int_stat(h, h1, "vb_0:db_file_size", "vbucket-details 0");
2118 int spaceUsed = get_int_stat(h, h1, "vb_0:db_data_size", "vbucket-details 0");
2120 // Restart the engine.
2121 testHarness.reload_engine(&h, &h1,
2122 testHarness.engine_path,
2123 testHarness.get_current_testcase()->cfg,
2125 wait_for_warmup_complete(h, h1);
2127 int newFileSize = get_int_stat(h, h1, "vb_0:db_file_size", "vbucket-details 0");
2128 int newSpaceUsed = get_int_stat(h, h1, "vb_0:db_data_size", "vbucket-details 0");
2130 check((float)newFileSize >= 0.9 * fileSize, "Unexpected fileSize for vbucket");
2131 check((float)newSpaceUsed >= 0.9 * spaceUsed, "Unexpected spaceUsed for vbucket");
2136 static enum test_result test_bg_stats(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
2137 h1->reset_stats(h, NULL);
2138 wait_for_persisted_value(h, h1, "a", "b\r\n");
2139 evict_key(h, h1, "a", 0, "Ejected.");
2140 testHarness.time_travel(43);
2141 check_key_value(h, h1, "a", "b\r\n", 3, 0);
2143 auto stats = get_all_stats(h, h1);
2144 checkeq(1, std::stoi(stats.at("ep_bg_num_samples")),
2145 "Expected one sample");
2147 const char* bg_keys[] = { "ep_bg_min_wait",
2153 for (const auto* key : bg_keys) {
2154 check(stats.find(key) != stats.end(),
2155 (std::string("Found no ") + key).c_str());
2158 evict_key(h, h1, "a", 0, "Ejected.");
2159 check_key_value(h, h1, "a", "b\r\n", 3, 0);
2160 check(get_int_stat(h, h1, "ep_bg_num_samples") == 2,
2161 "Expected one sample");
2163 h1->reset_stats(h, NULL);
2164 checkeq(0, get_int_stat(h, h1, "ep_bg_fetched"),
2165 "ep_bg_fetched is not reset to 0");
2169 static enum test_result test_bg_meta_stats(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
2171 h1->reset_stats(h, NULL);
2173 wait_for_persisted_value(h, h1, "k1", "v1");
2174 wait_for_persisted_value(h, h1, "k2", "v2");
2176 evict_key(h, h1, "k1", 0, "Ejected.");
2177 checkeq(ENGINE_SUCCESS,
2178 del(h, h1, "k2", 0, 0), "Failed remove with value.");
2179 wait_for_stat_to_be(h, h1, "curr_items", 1);
2181 checkeq(0, get_int_stat(h, h1, "ep_bg_fetched"), "Expected bg_fetched to be 0");
2182 checkeq(0, get_int_stat(h, h1, "ep_bg_meta_fetched"), "Expected bg_meta_fetched to be 0");
2184 check(get_meta(h, h1, "k2"), "Get meta failed");
2185 checkeq(0, get_int_stat(h, h1, "ep_bg_fetched"), "Expected bg_fetched to be 0");
2186 checkeq(1, get_int_stat(h, h1, "ep_bg_meta_fetched"), "Expected bg_meta_fetched to be 1");
2188 checkeq(ENGINE_SUCCESS, get(h, h1, NULL, &itm, "k1", 0), "Missing key");
2189 checkeq(1, get_int_stat(h, h1, "ep_bg_fetched"), "Expected bg_fetched to be 1");
2190 checkeq(1, get_int_stat(h, h1, "ep_bg_meta_fetched"), "Expected bg_meta_fetched to be 1");
2191 h1->release(h, NULL, itm);
2193 // store new key with some random metadata
2194 const size_t keylen = strlen("k3");
2195 ItemMetaData itemMeta;
2196 itemMeta.revSeqno = 10;
2197 itemMeta.cas = 0xdeadbeef;
2198 itemMeta.exptime = 0;
2199 itemMeta.flags = 0xdeadbeef;
2201 add_with_meta(h, h1, "k3", keylen, NULL, 0, 0, &itemMeta);
2202 checkeq(PROTOCOL_BINARY_RESPONSE_SUCCESS, last_status.load(), "Set meta failed");
2204 check(get_meta(h, h1, "k2"), "Get meta failed");
2205 checkeq(1, get_int_stat(h, h1, "ep_bg_fetched"), "Expected bg_fetched to be 1");
2206 checkeq(1, get_int_stat(h, h1, "ep_bg_meta_fetched"),
2207 "Expected bg_meta_fetched to remain at 1");
2212 static enum test_result test_key_stats(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
2215 check(set_vbucket_state(h, h1, 1, vbucket_state_active), "Failed set vbucket 1 state.");
2217 // set (k1,v1) in vbucket 0
2218 checkeq(ENGINE_SUCCESS,
2219 store(h, h1, NULL, OPERATION_SET,"k1", "v1", &i, 0, 0),
2220 "Failed to store an item.");
2221 h1->release(h, NULL, i);
2222 // set (k2,v2) in vbucket 1
2223 checkeq(ENGINE_SUCCESS,
2224 store(h, h1, NULL, OPERATION_SET,"k2", "v2", &i, 0, 1),
2225 "Failed to store an item.");
2226 h1->release(h, NULL, i);
2228 const void *cookie = testHarness.create_cookie();
2230 // stat for key "k1" and vbucket "0"
2231 const char *statkey1 = "key k1 0";
2232 checkeq(ENGINE_SUCCESS,
2233 h1->get_stats(h, cookie, statkey1, strlen(statkey1), add_stats),
2234 "Failed to get stats.");
2235 check(vals.find("key_is_dirty") != vals.end(), "Found no key_is_dirty");
2236 check(vals.find("key_exptime") != vals.end(), "Found no key_exptime");
2237 check(vals.find("key_flags") != vals.end(), "Found no key_flags");
2238 check(vals.find("key_cas") != vals.end(), "Found no key_cas");
2239 check(vals.find("key_vb_state") != vals.end(), "Found no key_vb_state");
2241 // stat for key "k2" and vbucket "1"
2242 const char *statkey2 = "key k2 1";
2243 checkeq(ENGINE_SUCCESS,
2244 h1->get_stats(h, cookie, statkey2, strlen(statkey2), add_stats),
2245 "Failed to get stats.");
2246 check(vals.find("key_is_dirty") != vals.end(), "Found no key_is_dirty");
2247 check(vals.find("key_exptime") != vals.end(), "Found no key_exptime");
2248 check(vals.find("key_flags") != vals.end(), "Found no key_flags");
2249 check(vals.find("key_cas") != vals.end(), "Found no key_cas");
2250 check(vals.find("key_vb_state") != vals.end(), "Found no key_vb_state");
2252 testHarness.destroy_cookie(cookie);
2256 static enum test_result test_vkey_stats(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
2257 check(set_vbucket_state(h, h1, 1, vbucket_state_active), "Failed set vbucket 1 state.");
2258 check(set_vbucket_state(h, h1, 2, vbucket_state_active), "Failed set vbucket 2 state.");
2259 check(set_vbucket_state(h, h1, 3, vbucket_state_active), "Failed set vbucket 3 state.");
2260 check(set_vbucket_state(h, h1, 4, vbucket_state_active), "Failed set vbucket 4 state.");
2262 wait_for_persisted_value(h, h1, "k1", "v1");
2263 wait_for_persisted_value(h, h1, "k2", "v2", 1);
2264 wait_for_persisted_value(h, h1, "k3", "v3", 2);
2265 wait_for_persisted_value(h, h1, "k4", "v4", 3);
2266 wait_for_persisted_value(h, h1, "k5", "v5", 4);
2268 check(set_vbucket_state(h, h1, 2, vbucket_state_replica), "Failed to set VB2 state.");
2269 check(set_vbucket_state(h, h1, 3, vbucket_state_pending), "Failed to set VB3 state.");
2270 check(set_vbucket_state(h, h1, 4, vbucket_state_dead), "Failed to set VB4 state.");
2272 const void *cookie = testHarness.create_cookie();
2274 // stat for key "k1" and vbucket "0"
2275 const char *statkey1 = "vkey k1 0";
2276 checkeq(ENGINE_SUCCESS,
2277 h1->get_stats(h, cookie, statkey1, strlen(statkey1), add_stats),
2278 "Failed to get stats.");
2279 check(vals.find("key_is_dirty") != vals.end(), "Found no key_is_dirty");
2280 check(vals.find("key_exptime") != vals.end(), "Found no key_exptime");
2281 check(vals.find("key_flags") != vals.end(), "Found no key_flags");
2282 check(vals.find("key_cas") != vals.end(), "Found no key_cas");
2283 check(vals.find("key_vb_state") != vals.end(), "Found no key_vb_state");
2284 check(vals.find("key_valid") != vals.end(), "Found no key_valid");
2286 // stat for key "k2" and vbucket "1"
2287 const char *statkey2 = "vkey k2 1";
2288 checkeq(ENGINE_SUCCESS,
2289 h1->get_stats(h, cookie, statkey2, strlen(statkey2), add_stats),
2290 "Failed to get stats.");
2291 check(vals.find("key_is_dirty") != vals.end(), "Found no key_is_dirty");
2292 check(vals.find("key_exptime") != vals.end(), "Found no key_exptime");
2293 check(vals.find("key_flags") != vals.end(), "Found no key_flags");
2294 check(vals.find("key_cas") != vals.end(), "Found no key_cas");
2295 check(vals.find("key_vb_state") != vals.end(), "Found no key_vb_state");
2296 check(vals.find("key_valid") != vals.end(), "Found no key_valid");
2298 // stat for key "k3" and vbucket "2"
2299 const char *statkey3 = "vkey k3 2";
2300 checkeq(ENGINE_SUCCESS,
2301 h1->get_stats(h, cookie, statkey3, strlen(statkey3), add_stats),
2302 "Failed to get stats.");
2303 check(vals.find("key_is_dirty") != vals.end(), "Found no key_is_dirty");
2304 check(vals.find("key_exptime") != vals.end(), "Found no key_exptime");
2305 check(vals.find("key_flags") != vals.end(), "Found no key_flags");
2306 check(vals.find("key_cas") != vals.end(), "Found no key_cas");
2307 check(vals.find("key_vb_state") != vals.end(), "Found no key_vb_state");
2308 check(vals.find("key_valid") != vals.end(), "Found no key_valid");
2310 // stat for key "k4" and vbucket "3"
2311 const char *statkey4 = "vkey k4 3";
2312 checkeq(ENGINE_SUCCESS,
2313 h1->get_stats(h, cookie, statkey4, strlen(statkey4), add_stats),
2314 "Failed to get stats.");
2315 check(vals.find("key_is_dirty") != vals.end(), "Found no key_is_dirty");
2316 check(vals.find("key_exptime") != vals.end(), "Found no key_exptime");
2317 check(vals.find("key_flags") != vals.end(), "Found no key_flags");
2318 check(vals.find("key_cas") != vals.end(), "Found no key_cas");
2319 check(vals.find("key_vb_state") != vals.end(), "Found no key_vb_state");
2320 check(vals.find("key_valid") != vals.end(), "Found no key_valid");
2322 // stat for key "k5" and vbucket "4"
2323 const char *statkey5 = "vkey k5 4";
2324 checkeq(ENGINE_SUCCESS,
2325 h1->get_stats(h, cookie, statkey5, strlen(statkey5), add_stats),
2326 "Failed to get stats.");
2327 check(vals.find("key_is_dirty") != vals.end(), "Found no key_is_dirty");
2328 check(vals.find("key_exptime") != vals.end(), "Found no key_exptime");
2329 check(vals.find("key_flags") != vals.end(), "Found no key_flags");
2330 check(vals.find("key_cas") != vals.end(), "Found no key_cas");
2331 check(vals.find("key_vb_state") != vals.end(), "Found no key_vb_state");
2332 check(vals.find("key_valid") != vals.end(), "Found no key_valid");
2334 testHarness.destroy_cookie(cookie);
2338 static enum test_result test_warmup_conf(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
2339 if (!isWarmupEnabled(h, h1)) {
2343 checkeq(100, get_int_stat(h, h1, "ep_warmup_min_items_threshold"),
2344 "Incorrect initial warmup min items threshold.");
2345 checkeq(100, get_int_stat(h, h1, "ep_warmup_min_memory_threshold"),
2346 "Incorrect initial warmup min memory threshold.");
2348 check(!set_param(h, h1, protocol_binary_engine_param_flush,
2349 "warmup_min_items_threshold", "a"),
2350 "Set warmup_min_items_threshold should have failed");
2351 check(!set_param(h, h1, protocol_binary_engine_param_flush,
2352 "warmup_min_items_threshold", "a"),
2353 "Set warmup_min_memory_threshold should have failed");
2355 check(set_param(h, h1, protocol_binary_engine_param_flush,
2356 "warmup_min_items_threshold", "80"),
2357 "Set warmup_min_items_threshold should have worked");
2358 check(set_param(h, h1, protocol_binary_engine_param_flush,
2359 "warmup_min_memory_threshold", "80"),
2360 "Set warmup_min_memory_threshold should have worked");
2362 checkeq(80, get_int_stat(h, h1, "ep_warmup_min_items_threshold"),
2363 "Incorrect smaller warmup min items threshold.");
2364 checkeq(80, get_int_stat(h, h1, "ep_warmup_min_memory_threshold"),
2365 "Incorrect smaller warmup min memory threshold.");
2368 for (int i = 0; i < 100; ++i) {
2369 std::stringstream key;
2371 checkeq(ENGINE_SUCCESS,
2372 store(h, h1, NULL, OPERATION_SET, key.str().c_str(), "somevalue", &it),
2374 h1->release(h, NULL, it);
2377 // Restart the server.
2378 std::string config(testHarness.get_current_testcase()->cfg);
2379 config = config + "warmup_min_memory_threshold=0";
2380 testHarness.reload_engine(&h, &h1,
2381 testHarness.engine_path,
2384 wait_for_warmup_complete(h, h1);
2386 const std::string eviction_policy = get_str_stat(h, h1, "ep_item_eviction_policy");
2387 if (eviction_policy == "value_only") {
2388 checkeq(100, get_int_stat(h, h1, "ep_warmup_key_count", "warmup"),
2389 "Expected 100 keys loaded after warmup");
2390 } else { // Full eviction mode
2391 checkeq(0, get_int_stat(h, h1, "ep_warmup_key_count", "warmup"),
2392 "Expected 0 keys loaded after warmup");
2395 checkeq(0, get_int_stat(h, h1, "ep_warmup_value_count", "warmup"),
2396 "Expected 0 values loaded after warmup");
2401 static enum test_result test_bloomfilter_conf(ENGINE_HANDLE *h,
2402 ENGINE_HANDLE_V1 *h1) {
2404 if (get_bool_stat(h, h1, "ep_bfilter_enabled") == false) {
2405 check(set_param(h, h1, protocol_binary_engine_param_flush,
2406 "bfilter_enabled", "true"),
2407 "Set bloomfilter_enabled should have worked");
2409 check(get_bool_stat(h, h1, "ep_bfilter_enabled"),
2410 "Bloom filter wasn't enabled");
2412 check(get_float_stat(h, h1, "ep_bfilter_residency_threshold") == (float)0.1,
2413 "Incorrect initial bfilter_residency_threshold.");
2415 check(set_param(h, h1, protocol_binary_engine_param_flush,
2416 "bfilter_enabled", "false"),
2417 "Set bloomfilter_enabled should have worked.");
2418 check(set_param(h, h1, protocol_binary_engine_param_flush,
2419 "bfilter_residency_threshold", "0.15"),
2420 "Set bfilter_residency_threshold should have worked.");
2422 check(get_bool_stat(h, h1, "ep_bfilter_enabled") == false,
2423 "Bloom filter should have been disabled.");
2424 check(get_float_stat(h, h1, "ep_bfilter_residency_threshold") == (float)0.15,
2425 "Incorrect bfilter_residency_threshold.");
2430 static enum test_result test_bloomfilters(ENGINE_HANDLE *h,
2431 ENGINE_HANDLE_V1 *h1) {
2433 if (get_bool_stat(h, h1, "ep_bfilter_enabled") == false) {
2434 check(set_param(h, h1, protocol_binary_engine_param_flush,
2435 "bfilter_enabled", "true"),
2436 "Set bloomfilter_enabled should have worked");
2438 check(get_bool_stat(h, h1, "ep_bfilter_enabled"),
2439 "Bloom filter wasn't enabled");
2441 // Key is only present if bgOperations is non-zero.
2442 int num_read_attempts = get_int_stat_or_default(h, h1, 0,
2443 "ep_bg_num_samples");
2445 // Ensure vbucket's bloom filter is enabled
2446 checkeq(std::string("ENABLED"),
2447 get_str_stat(h, h1, "vb_0:bloom_filter", "vbucket-details 0"),
2448 "Vbucket 0's bloom filter wasn't enabled upon setup!");
2454 for (i = 0; i < 10; ++i) {
2455 std::stringstream key;
2457 checkeq(ENGINE_SUCCESS,
2458 store(h, h1, NULL, OPERATION_SET, key.str().c_str(),
2461 h1->release(h, NULL, it);
2463 wait_for_flusher_to_settle(h, h1);
2465 // Evict all 10 items.
2466 for (i = 0; i < 10; ++i) {
2467 std::stringstream key;
2469 evict_key(h, h1, key.str().c_str(), 0, "Ejected.");
2471 wait_for_flusher_to_settle(h, h1);
2473 // Ensure 10 items are non-resident.
2474 cb_assert(10 == get_int_stat(h, h1, "ep_num_non_resident"));
2476 // Issue delete on first 5 items.
2477 for (i = 0; i < 5; ++i) {
2478 std::stringstream key;
2480 checkeq(ENGINE_SUCCESS,
2481 del(h, h1, key.str().c_str(), 0, 0),
2482 "Failed remove with value.");
2484 wait_for_flusher_to_settle(h, h1);
2486 // Ensure that there are 5 non-resident items
2487 cb_assert(5 == get_int_stat(h, h1, "ep_num_non_resident"));
2488 cb_assert(5 == get_int_stat(h, h1, "curr_items"));
2490 checkeq(ENGINE_SUCCESS,
2491 h1->get_stats(h, NULL, NULL, 0, add_stats),
2492 "Failed to get stats.");
2493 std::string eviction_policy = vals.find("ep_item_eviction_policy")->second;
2495 useconds_t sleepTime = 128;
2497 if (eviction_policy == "value_only") { // VALUE-ONLY EVICTION MODE
2500 get_int_stat(h, h1, "vb_0:bloom_filter_key_count",
2501 "vbucket-details 0"),
2502 "Unexpected no. of keys in bloom filter");
2504 checkeq(num_read_attempts,
2505 get_int_stat_or_default(h, h1, 0, "ep_bg_num_samples"),
2506 "Expected bgFetch attempts to remain unchanged");
2508 for (i = 0; i < 5; ++i) {
2509 std::stringstream key;
2511 check(get_meta(h, h1, key.str().c_str()), "Get meta failed");
2514 // GetMeta would cause bgFetches as bloomfilter contains
2515 // the deleted items.
2516 checkeq(num_read_attempts + 5,
2517 get_int_stat(h, h1, "ep_bg_num_samples"),
2518 "Expected bgFetch attempts to increase by five");
2520 // Run compaction, with drop_deletes
2521 compact_db(h, h1, 0, 0, 15, 15, 1);
2522 while (get_int_stat(h, h1, "ep_pending_compactions") != 0) {
2523 decayingSleep(&sleepTime);
2526 for (i = 0; i < 5; ++i) {
2527 std::stringstream key;
2529 check(get_meta(h, h1, key.str().c_str()), "Get meta failed");
2531 checkeq(num_read_attempts + 5,
2532 get_int_stat(h, h1, "ep_bg_num_samples"),
2533 "Expected bgFetch attempts to stay as before");
2535 } else { // FULL EVICTION MODE
2538 get_int_stat(h, h1, "vb_0:bloom_filter_key_count",
2539 "vbucket-details 0"),
2540 "Unexpected no. of keys in bloom filter");
2543 // Because of issuing deletes on non-resident items
2544 checkeq(num_read_attempts + 5,
2545 get_int_stat(h, h1, "ep_bg_num_samples"),
2546 "Expected bgFetch attempts to increase by five, after deletes");
2548 // Run compaction, with drop_deletes, to exclude deleted items
2549 // from bloomfilter.
2550 compact_db(h, h1, 0, 0, 15, 15, 1);
2551 while (get_int_stat(h, h1, "ep_pending_compactions") != 0) {
2552 decayingSleep(&sleepTime);
2555 for (i = 0; i < 5; i++) {
2556 std::stringstream key;
2558 checkeq(ENGINE_KEY_ENOENT,
2559 get(h, h1, NULL, &it, key.str(), 0),
2560 "Unable to get stored item");
2562 // + 6 because last delete is not purged by the compactor
2563 checkeq(num_read_attempts + 6,
2564 get_int_stat(h, h1, "ep_bg_num_samples"),
2565 "Expected bgFetch attempts to stay as before");
2571 static enum test_result test_bloomfilters_with_store_apis(ENGINE_HANDLE *h,
2572 ENGINE_HANDLE_V1 *h1) {
2573 if (get_bool_stat(h, h1, "ep_bfilter_enabled") == false) {
2574 check(set_param(h, h1, protocol_binary_engine_param_flush,
2575 "bfilter_enabled", "true"),
2576 "Set bloomfilter_enabled should have worked");
2578 check(get_bool_stat(h, h1, "ep_bfilter_enabled"),
2579 "Bloom filter wasn't enabled");
2581 int num_read_attempts = get_int_stat_or_default(h, h1, 0,
2582 "ep_bg_num_samples");
2584 // Ensure vbucket's bloom filter is enabled
2585 checkeq(std::string("ENABLED"),
2586 get_str_stat(h, h1, "vb_0:bloom_filter", "vbucket-details 0"),
2587 "Vbucket 0's bloom filter wasn't enabled upon setup!");
2589 for (int i = 0; i < 1000; i++) {
2590 std::stringstream key;
2592 check(!get_meta(h, h1, key.str().c_str()),
2593 "Get meta should fail.");
2596 checkeq(num_read_attempts,
2597 get_int_stat_or_default(h, h1, 0, "ep_bg_num_samples"),
2598 "Expected no bgFetch attempts");
2600 checkeq(ENGINE_SUCCESS,
2601 h1->get_stats(h, NULL, NULL, 0, add_stats),
2602 "Failed to get stats.");
2603 std::string eviction_policy = vals.find("ep_item_eviction_policy")->second;
2605 if (eviction_policy == "full_eviction") { // FULL EVICTION MODE
2608 for (j = 0; j < 10; j++) {
2609 uint64_t cas_for_set = last_cas;
2610 // init some random metadata
2611 ItemMetaData itm_meta;
2612 itm_meta.revSeqno = 10;
2613 itm_meta.cas = 0xdeadbeef;
2614 itm_meta.exptime = time(NULL) + 300;
2615 itm_meta.flags = 0xdeadbeef;
2617 std::stringstream key;
2619 set_with_meta(h, h1, key.str().c_str(), key.str().length(),
2620 "somevalue", 9, 0, &itm_meta, cas_for_set);
2623 checkeq(num_read_attempts,
2624 get_int_stat_or_default(h, h1, 0, "ep_bg_num_samples"),
2625 "Expected no bgFetch attempts");
2629 for (j = 0; j < 10; j++) {
2630 std::stringstream key;
2633 checkeq(ENGINE_SUCCESS,
2634 store(h, h1, NULL, OPERATION_ADD, key.str().c_str(),
2636 "Failed to add value again.");
2637 h1->release(h, NULL, itm);
2640 checkeq(num_read_attempts,
2641 get_int_stat_or_default(h, h1, 0, "ep_bg_num_samples"),
2642 "Expected no bgFetch attempts");
2645 for (j = 0; j < 10; j++) {
2646 std::stringstream key;
2648 checkeq(ENGINE_KEY_ENOENT,
2649 del(h, h1, key.str().c_str(), 0, 0),
2650 "Failed remove with value.");
2653 checkeq(num_read_attempts,
2654 get_int_stat_or_default(h, h1, 0, "ep_bg_num_samples"),
2655 "Expected no bgFetch attempts");
2662 static enum test_result test_bloomfilter_delete_plus_set_scenario(
2663 ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
2664 if (get_bool_stat(h, h1, "ep_bfilter_enabled") == false) {
2665 check(set_param(h, h1, protocol_binary_engine_param_flush,
2666 "bfilter_enabled", "true"),
2667 "Set bloomfilter_enabled should have worked");
2669 check(get_bool_stat(h, h1, "ep_bfilter_enabled"),
2670 "Bloom filter wasn't enabled");
2672 // Ensure vbucket's bloom filter is enabled
2673 checkeq(std::string("ENABLED"),
2674 get_str_stat(h, h1, "vb_0:bloom_filter", "vbucket-details 0"),
2675 "Vbucket 0's bloom filter wasn't enabled upon setup!");
2678 checkeq(ENGINE_SUCCESS,
2679 store(h, h1, NULL, OPERATION_SET,"k1", "v1", &itm),
2680 "Failed to fail to store an item.");
2681 h1->release(h, NULL, itm);
2683 wait_for_flusher_to_settle(h, h1);
2684 int num_writes = get_int_stat(h, h1, "rw_0:io_num_write", "kvstore");
2685 int num_persisted = get_int_stat(h, h1, "ep_total_persisted");
2686 cb_assert(num_writes == 1 && num_persisted == 1);
2688 checkeq(ENGINE_SUCCESS,
2689 del(h, h1, "k1", 0, 0), "Failed remove with value.");
2690 stop_persistence(h, h1);
2691 checkeq(ENGINE_SUCCESS,
2692 store(h, h1, NULL, OPERATION_SET,"k1", "v2", &itm, 0, 0),
2693 "Failed to fail to store an item.");
2694 h1->release(h, NULL, itm);
2695 int key_count = get_int_stat(h, h1, "vb_0:bloom_filter_key_count",
2696 "vbucket-details 0");
2698 if (key_count == 0) {
2699 check(get_int_stat(h, h1, "rw_0:io_num_write", "kvstore") <= 2,
2700 "Unexpected number of writes");
2701 start_persistence(h, h1);
2702 wait_for_flusher_to_settle(h, h1);
2703 checkeq(0, get_int_stat(h, h1, "vb_0:bloom_filter_key_count",
2704 "vbucket-details 0"),
2705 "Unexpected number of keys in bloomfilter");
2707 cb_assert(key_count == 1);
2708 checkeq(2, get_int_stat(h, h1, "rw_0:io_num_write", "kvstore"),
2709 "Unexpected number of writes");
2710 start_persistence(h, h1);
2711 wait_for_flusher_to_settle(h, h1);
2712 checkeq(1, get_int_stat(h, h1, "vb_0:bloom_filter_key_count",
2713 "vbucket-details 0"),
2714 "Unexpected number of keys in bloomfilter");
2720 static enum test_result test_datatype(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
2721 const void *cookie = testHarness.create_cookie();
2722 testHarness.set_datatype_support(cookie, true);
2725 const std::string key("{\"foo\":\"bar\"}");
2726 const protocol_binary_datatype_t datatype = PROTOCOL_BINARY_DATATYPE_JSON;
2728 std::string value("x");
2729 checkeq(ENGINE_SUCCESS,
2730 storeCasOut(h, h1, NULL, 0, key, value, datatype, itm, cas),
2731 "Expected set to succeed");
2733 checkeq(ENGINE_SUCCESS,
2734 get(h, h1, cookie, &itm, key, 0),
2735 "Unable to get stored item");
2738 h1->get_item_info(h, cookie, itm, &info);
2739 h1->release(h, cookie, itm);
2740 checkeq(static_cast<uint8_t>(PROTOCOL_BINARY_DATATYPE_JSON),
2741 info.datatype, "Invalid datatype");
2743 const char* key1 = "foo";
2744 const char* val1 = "{\"foo1\":\"bar1\"}";
2745 ItemMetaData itm_meta;
2746 itm_meta.revSeqno = 10;
2747 itm_meta.cas = info.cas;
2748 itm_meta.exptime = info.exptime;
2749 itm_meta.flags = info.flags;
2750 set_with_meta(h, h1, key1, strlen(key1), val1, strlen(val1), 0, &itm_meta,
2751 last_cas, 0, info.datatype, cookie);
2753 checkeq(ENGINE_SUCCESS,
2754 get(h, h1, cookie, &itm, key1, 0),
2755 "Unable to get stored item");
2757 h1->get_item_info(h, cookie, itm, &info);
2758 h1->release(h, cookie, itm);
2759 checkeq(static_cast<uint8_t>(PROTOCOL_BINARY_DATATYPE_JSON),
2760 info.datatype, "Invalid datatype, when setWithMeta");
2762 testHarness.destroy_cookie(cookie);
2766 static enum test_result test_datatype_with_unknown_command(ENGINE_HANDLE *h,
2767 ENGINE_HANDLE_V1 *h1) {
2768 const void *cookie = testHarness.create_cookie();
2769 testHarness.set_datatype_support(cookie, true);
2771 const char* key = "foo";
2772 const char* val = "{\"foo\":\"bar\"}";
2773 uint8_t datatype = PROTOCOL_BINARY_DATATYPE_JSON;
2775 ItemMetaData itm_meta;
2776 itm_meta.revSeqno = 10;
2778 itm_meta.exptime = 0;
2782 set_with_meta(h, h1, key, strlen(key), val, strlen(val), 0, &itm_meta,
2783 0, 0, datatype, cookie);
2785 checkeq(ENGINE_SUCCESS,
2786 get(h, h1, cookie, &itm, key, 0),
2787 "Unable to get stored item");
2790 h1->get_item_info(h, cookie, itm, &info);
2791 h1->release(h, NULL, itm);
2792 checkeq(static_cast<uint8_t>(PROTOCOL_BINARY_DATATYPE_JSON),
2793 info.datatype, "Invalid datatype, when setWithMeta");
2796 set_ret_meta(h, h1, "foo1", 4, val, strlen(val), 0, 0, 0, 0, datatype,
2798 checkeq(PROTOCOL_BINARY_RESPONSE_SUCCESS, last_status.load(),
2799 "Expected set returing meta to succeed");
2800 checkeq(static_cast<uint8_t>(PROTOCOL_BINARY_DATATYPE_JSON),
2801 last_datatype.load(), "Invalid datatype, when set_return_meta");
2803 testHarness.destroy_cookie(cookie);
2807 static enum test_result test_session_cas_validation(ENGINE_HANDLE *h,
2808 ENGINE_HANDLE_V1 *h1) {
2809 //Testing PROTOCOL_BINARY_CMD_SET_VBUCKET..
2811 protocol_binary_request_header *pkt;
2812 vbucket_state_t state = vbucket_state_active;
2813 uint32_t val = static_cast<uint32_t>(state);
2815 memcpy(ext, (char*)&val, sizeof(val));
2817 uint64_t cas = 0x0101010101010101;
2818 pkt = createPacket(PROTOCOL_BINARY_CMD_SET_VBUCKET, 0, cas, ext, 4);
2819 checkeq(ENGINE_SUCCESS,
2820 h1->unknown_command(h, NULL, pkt, add_response, testHarness.doc_namespace),
2821 "SET_VBUCKET command failed");
2823 cb_assert(last_status == PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS);
2825 cas = 0x0102030405060708;
2826 pkt = createPacket(PROTOCOL_BINARY_CMD_SET_VBUCKET, 0, cas, ext, 4);
2827 checkeq(ENGINE_SUCCESS,
2828 h1->unknown_command(h, NULL, pkt, add_response, testHarness.doc_namespace),
2829 "SET_VBUCKET command failed");
2831 cb_assert(last_status == PROTOCOL_BINARY_RESPONSE_SUCCESS);
2836 static enum test_result test_access_scanner_settings(ENGINE_HANDLE *h,
2837 ENGINE_HANDLE_V1 *h1) {
2838 if (!isWarmupEnabled(h, h1)) {
2839 // Access scanner n/a without warmup.
2843 // Create a unique access log path by combining with the db path.
2844 checkeq(ENGINE_SUCCESS,
2845 h1->get_stats(h, NULL, NULL, 0, add_stats),
2846 "Failed to get stats.");
2847 std::string dbname = vals.find("ep_dbname")->second;
2849 const auto alog_path = std::string("alog_path=") + dbname +
2850 DIRECTORY_SEPARATOR_CHARACTER + "access.log";
2851 std::string newconfig = std::string(testHarness.get_current_testcase()->cfg)
2854 testHarness.reload_engine(&h, &h1,
2855 testHarness.engine_path,
2858 wait_for_warmup_complete(h, h1);
2860 std::string err_msg;
2861 // Check access scanner is enabled and alog_task_time is at default
2862 checkeq(true, get_bool_stat(h, h1, "ep_access_scanner_enabled"),
2863 "Expected access scanner to be enabled");
2864 cb_assert(get_int_stat(h, h1, "ep_alog_task_time") == 2);
2866 // Ensure access_scanner_task_time is what its expected to be.
2867 // Need to wait until the AccessScanner task has been setup.
2868 wait_for_stat_change(h, h1, "ep_access_scanner_task_time",
2869 std::string{"NOT_SCHEDULED"});
2871 std::string str = get_str_stat(h, h1, "ep_access_scanner_task_time");
2872 std::string expected_time = "02:00";
2873 err_msg.assign("Initial time incorrect, expect: " +
2874 expected_time + ", actual: " + str.substr(11, 5));
2875 checkeq(0, str.substr(11, 5).compare(expected_time), err_msg.c_str());
2877 // Update alog_task_time and ensure the update is successful
2878 set_param(h, h1, protocol_binary_engine_param_flush, "alog_task_time", "5");
2879 expected_time = "05:00";
2880 str = get_str_stat(h, h1, "ep_access_scanner_task_time");
2881 err_msg.assign("Updated time incorrect, expect: " +
2882 expected_time + ", actual: " + str.substr(11, 5));
2883 checkeq(0, str.substr(11, 5).compare(expected_time), err_msg.c_str());
2885 // Update alog_sleep_time by 10 mins and ensure the update is successful.
2886 const std::chrono::minutes update_by{10};
2887 std::string targetTaskTime1{make_time_string(std::chrono::system_clock::now() +
2890 set_param(h, h1, protocol_binary_engine_param_flush, "alog_sleep_time",
2891 std::to_string(update_by.count()).c_str());
2892 str = get_str_stat(h, h1, "ep_access_scanner_task_time");
2894 // Recalculate now() + 10mins as upper bound on when the task should be
2896 std::string targetTaskTime2{make_time_string(std::chrono::system_clock::now() +
2899 // ep_access_scanner_task_time should fall within the range of
2900 // targetTaskTime1 and targetTaskTime2
2901 err_msg.assign("Unexpected task time range, expect: " +
2902 targetTaskTime1 + " <= " + str + " <= " + targetTaskTime2);
2903 check(targetTaskTime1 <= str, err_msg.c_str());
2904 check(str <= targetTaskTime2, err_msg.c_str());
2909 static enum test_result test_access_scanner(ENGINE_HANDLE *h,
2910 ENGINE_HANDLE_V1 *h1) {
2911 if (!isWarmupEnabled(h, h1)) {
2912 // Access scanner not applicable without warmup.
2916 // Create a unique access log path by combining with the db path.
2917 checkeq(ENGINE_SUCCESS,
2918 h1->get_stats(h, NULL, NULL, 0, add_stats),
2919 "Failed to get stats.");
2920 const auto dbname = vals.find("ep_dbname")->second;
2922 const auto alog_path = std::string("alog_path=") + dbname +
2923 DIRECTORY_SEPARATOR_CHARACTER + "access.log";
2925 /* We do not want the access scanner task to be running while we initiate it
2926 explicitly below. Hence set the alog_task_time to about 1 ~ 2 hours
2928 const time_t now = time(nullptr);
2930 cb_gmtime_r(&now, &tm_now);
2931 const auto two_hours_hence = (tm_now.tm_hour + 2) % 24;
2933 const auto alog_task_time = std::string("alog_task_time=") +
2934 std::to_string(two_hours_hence);
2936 const auto newconfig = std::string(testHarness.get_current_testcase()->cfg)
2937 + alog_path + ";" + alog_task_time;
2939 testHarness.reload_engine(&h, &h1,
2940 testHarness.engine_path,
2943 wait_for_warmup_complete(h, h1);
2945 /* Check that alog_task_time was correctly updated. */
2946 checkeq(get_int_stat(h, h1, "ep_alog_task_time"),
2948 "Failed to set alog_task_time to 2 hours in the future");
2950 checkeq(ENGINE_SUCCESS,
2951 h1->get_stats(h, NULL, NULL, 0, add_stats),
2952 "Failed to get stats.");
2953 std::string name = vals.find("ep_alog_path")->second;
2955 /* Check access scanner is enabled */
2956 checkeq(true, get_bool_stat(h, h1, "ep_access_scanner_enabled"),
2957 "Access scanner task not enabled by default. Check test config");
2959 const int num_shards = get_int_stat(h, h1, "ep_workload:num_shards",
2962 std::string prev(name + ".old");
2964 /* Get the resident ratio down to below 95% - point at which access.log
2965 * generation occurs.
2968 // Size chosen to create ~2000 items (i.e. 2x more than we sanity-check below)
2969 // with the given max_size for this test.
2970 const std::string value(2000, 'x');
2972 // Gathering stats on every store is expensive, just check every 100 iterations
2973 if ((num_items % 100) == 0) {
2974 if (get_int_stat(h, h1, "vb_active_perc_mem_resident") < 94) {
2980 std::string key("key" + std::to_string(num_items));
2981 ENGINE_ERROR_CODE ret = store(h, h1, NULL, OPERATION_SET,
2982 key.c_str(), value.c_str(), &itm);
2984 case ENGINE_SUCCESS:
2986 h1->release(h, NULL, itm);
2990 case ENGINE_TMPFAIL:
2991 // Returned when at high watermark; simply retry the op.
2992 h1->release(h, NULL, itm);
2996 fprintf(stderr, "test_access_scanner: Unexpected result from store(): %d\n",
3003 // Sanity check - ensure we have enough vBucket quota (max_size)
3004 // such that we have 1000 items - enough to give us 0.1%
3005 // granuarity in any residency calculations. */
3006 if (num_items < 1000) {
3007 std::cerr << "Error: test_access_scanner: "
3008 "expected at least 1000 items after filling vbucket, "
3009 "but only have " << num_items << ". "
3010 "Check max_size setting for test." << std::endl;
3014 wait_for_flusher_to_settle(h, h1);
3015 verify_curr_items(h, h1, num_items, "Wrong number of items");
3016 int num_non_resident = get_int_stat(h, h1, "vb_active_num_non_resident");
3017 checkge(num_non_resident, num_items * 6 / 100,
3018 "Expected num_non_resident to be at least 6% of total items");
3020 /* Run access scanner task once and expect it to generate access log */
3021 check(set_param(h, h1, protocol_binary_engine_param_flush,
3022 "access_scanner_run", "true"),
3023 "Failed to trigger access scanner");
3025 // Wait for the number of runs to equal the number of shards.
3026 wait_for_stat_to_be(h, h1, "ep_num_access_scanner_runs", num_shards);
3028 /* This time since resident ratio is < 95% access log should be generated */
3029 checkeq(0, access(name.c_str(), F_OK),
3030 (std::string("access log file (") + name +
3031 ") should exist (got errno:" + std::to_string(errno)).c_str());
3033 /* Increase resident ratio by deleting items */
3034 vbucketDelete(h, h1, 0);
3035 check(set_vbucket_state(h, h1, 0, vbucket_state_active),
3036 "Failed to set VB0 state.");
3038 /* Run access scanner task once */
3039 const int access_scanner_skips =
3040 get_int_stat(h, h1, "ep_num_access_scanner_skips");
3041 check(set_param(h, h1, protocol_binary_engine_param_flush,
3042 "access_scanner_run", "true"),
3043 "Failed to trigger access scanner");
3044 wait_for_stat_to_be(h, h1, "ep_num_access_scanner_skips",
3045 access_scanner_skips + num_shards);
3047 /* Access log files should be removed because resident ratio > 95% */
3048 checkeq(-1, access(prev.c_str(), F_OK),
3049 ".old access log file should not exist");
3050 checkeq(-1, access(name.c_str(), F_OK), "access log file should not exist");
3055 static enum test_result test_set_param_message(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
3056 set_param(h, h1, protocol_binary_engine_param_flush, "alog_task_time", "50");
3058 checkeq(PROTOCOL_BINARY_RESPONSE_EINVAL, last_status.load(),
3059 "Expected an invalid value error for an out of bounds alog_task_time");
3060 check(std::string("Validation Error").compare(last_body), "Expected a "
3061 "validation error in the response body");
3065 static enum test_result test_warmup_stats(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
3066 if (!isWarmupEnabled(h, h1)) {
3071 check(set_vbucket_state(h, h1, 0, vbucket_state_active), "Failed to set VB0 state.");
3072 check(set_vbucket_state(h, h1, 1, vbucket_state_replica), "Failed to set VB1 state.");
3074 for (int i = 0; i < 5000; ++i) {
3075 std::stringstream key;
3077 checkeq(ENGINE_SUCCESS,
3078 store(h, h1, NULL, OPERATION_SET, key.str().c_str(),
3081 h1->release(h, NULL, it);
3084 // Restart the server.
3085 testHarness.reload_engine(&h, &h1,
3086 testHarness.engine_path,
3087 testHarness.get_current_testcase()->cfg,
3090 wait_for_warmup_complete(h, h1);
3092 const auto warmup_stats = get_all_stats(h, h1, "warmup");
3094 // Check all expected warmup stats exists.
3095 const char* warmup_keys[] = { "ep_warmup_thread",
3096 "ep_warmup_value_count",
3097 "ep_warmup_key_count",
3101 for (const auto* key : warmup_keys) {
3102 check(warmup_stats.find(key) != warmup_stats.end(),
3103 (std::string("Found no ") + key).c_str());
3106 std::string warmup_time = warmup_stats.at("ep_warmup_time");
3107 cb_assert(std::stoi(warmup_time) > 0);
3109 const auto prev_vb_stats = get_all_stats(h, h1, "prev-vbucket");
3111 check(prev_vb_stats.find("vb_0") != prev_vb_stats.end(),
3112 "Found no previous state for VB0");
3113 check(prev_vb_stats.find("vb_1") != prev_vb_stats.end(),
3114 "Found no previous state for VB1");
3116 checkeq(std::string("active"), prev_vb_stats.at("vb_0"),
3117 "Unexpected stats for vb 0");
3118 checkeq(std::string("replica"), prev_vb_stats.at("vb_1"),
3119 "Unexpected stats for vb 1");
3121 const auto vb_details_stats = get_all_stats(h, h1, "vbucket-details");
3122 checkeq(5000, std::stoi(vb_details_stats.at("vb_0:num_items")),
3123 "Unexpected item count for vb 0");
3124 checkeq(0, std::stoi(vb_details_stats.at("vb_1:num_items")),
3125 "Unexpected item count for vb 1");
3130 static enum test_result test_warmup_with_threshold(ENGINE_HANDLE *h,
3131 ENGINE_HANDLE_V1 *h1) {
3132 if (!isWarmupEnabled(h, h1)) {
3137 check(set_vbucket_state(h, h1, 0, vbucket_state_active), "Failed set vbucket 1 state.");
3138 check(set_vbucket_state(h, h1, 1, vbucket_state_active), "Failed set vbucket 2 state.");
3139 check(set_vbucket_state(h, h1, 2, vbucket_state_active), "Failed set vbucket 3 state.");
3140 check(set_vbucket_state(h, h1, 3, vbucket_state_active), "Failed set vbucket 4 state.");
3142 for (int i = 0; i < 10000; ++i) {
3143 std::stringstream key;
3145 checkeq(ENGINE_SUCCESS,
3146 store(h, h1, NULL, OPERATION_SET, key.str().c_str(), "somevalue", &it,
3149 h1->release(h, NULL, it);
3152 // Restart the server.
3153 testHarness.reload_engine(&h, &h1,
3154 testHarness.engine_path,
3155 testHarness.get_current_testcase()->cfg,
3158 wait_for_warmup_complete(h, h1);
3161 get_int_stat(h, h1, "ep_warmup_min_item_threshold", "warmup"),
3162 "Unable to set warmup_min_item_threshold to 1%");
3164 const std::string policy = get_str_stat(h, h1, "ep_item_eviction_policy");
3166 if (policy == "full_eviction") {
3167 checkeq(get_int_stat(h, h1, "ep_warmup_key_count", "warmup"),
3168 get_int_stat(h, h1, "ep_warmup_value_count", "warmup"),
3169 "Warmed up key count didn't match warmed up value count");
3171 checkeq(10000, get_int_stat(h, h1, "ep_warmup_key_count", "warmup"),
3172 "Warmup didn't warmup all keys");
3174 check(get_int_stat(h, h1, "ep_warmup_value_count", "warmup") <= 110,
3175 "Warmed up value count found to be greater than 1%");
3177 cb_assert(get_int_stat(h, h1, "ep_warmup_time", "warmup") > 0);
3183 // Comment out the entire test since the hack gave warnings on win32
3184 static enum test_result test_warmup_accesslog(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
3186 /* I'm getting a weird link error from clang.. disable the test until I
3193 int n_items_to_store1 = 10;
3194 for (int i = 0; i < n_items_to_store1; ++i) {
3195 std::stringstream key;
3197 const char* keystr = key.str().c_str();
3198 checkeq(ENGINE_SUCCESS,
3199 store(h, h1, NULL, OPERATION_SET, keystr, "somevalue", &it, 0, 0),
3201 h1->release(h, NULL, it);
3204 wait_for_flusher_to_settle(h, h1);
3206 int n_items_to_access = 10;
3207 for (int i = 0; i < n_items_to_access; ++i) {
3208 std::stringstream key;
3210 const char* keystr = key.str().c_str();
3211 checkeq(ENGINE_SUCCESS,
3212 get(h, h1, NULL, &it, keystr, 0),
3214 h1->release(h, NULL, it);
3217 // sleep so that scanner task can have timew to generate access log
3220 // store additional items
3221 int n_items_to_store2 = 10;
3222 for (int i = 0; i < n_items_to_store2; ++i) {
3223 std::stringstream key;
3224 key << "key2-" << i;
3225 const char* keystr = key.str().c_str();
3226 checkeq(ENGINE_SUCCESS,
3227 store(h, h1, NULL, OPERATION_SET, keystr, "somevalue", &it, 0, 0),
3229 h1->release(h, NULL, it);
3232 // Restart the server.
3233 testHarness.reload_engine(&h, &h1,
3234 testHarness.engine_path,
3235 testHarness.get_current_testcase()->cfg,
3238 wait_for_warmup_complete(h, h1);
3239 // n_items_to_access items should be loaded from access log first
3240 // but we continue to load until we hit 75% item watermark
3242 int warmedup = get_int_stat(h, h1, "ep_warmup_value_count", "warmup");
3243 // std::cout << "ep_warmup_value_count = " << warmedup << std::endl;
3244 int expected = (n_items_to_store1 + n_items_to_store2) * 0.75 + 1;
3246 check(warmedup == expected, "Expected 16 items to be resident");
3252 static enum test_result test_warmup_oom(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
3253 if (!isWarmupEnabled(h, h1)) {
3257 write_items(h, h1, 20000, 0, "superlongnameofkey1234567890123456789012345678902");
3259 wait_for_flusher_to_settle(h, h1);
3261 std::string config(testHarness.get_current_testcase()->cfg);
3262 config = config + "max_size=2097152;item_eviction_policy=value_only";
3264 testHarness.reload_engine(&h, &h1,
3265 testHarness.engine_path,
3269 wait_for_warmup_complete(h, h1);
3271 protocol_binary_request_header *pkt = createPacket(PROTOCOL_BINARY_CMD_ENABLE_TRAFFIC);
3272 checkeq(ENGINE_SUCCESS,
3273 h1->unknown_command(h, NULL, pkt, add_response, testHarness.doc_namespace),
3274 "Failed to send data traffic command to the services");
3275 checkeq(PROTOCOL_BINARY_RESPONSE_ENOMEM, last_status.load(),
3276 "Data traffic command should have failed with enomem");
3282 static enum test_result test_cbd_225(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
3285 // get engine startup token
3286 time_t token1 = get_int_stat(h, h1, "ep_startup_time");
3287 check(token1 != 0, "Expected non-zero startup token");
3289 // store some random data
3290 checkeq(ENGINE_SUCCESS,
3291 store(h, h1, NULL, OPERATION_SET,"k1", "v1", &i),
3292 "Failed to fail to store an item.");
3293 h1->release(h, NULL, i);
3294 checkeq(ENGINE_SUCCESS,
3295 store(h, h1, NULL, OPERATION_SET,"k2", "v2", &i),
3296 "Failed to fail to store an item.");
3297 h1->release(h, NULL, i);
3298 wait_for_flusher_to_settle(h, h1);
3300 // check token again, which should be the same as before
3301 time_t token2 = get_int_stat(h, h1, "ep_startup_time");
3302 check(token2 == token1, "Expected the same startup token");
3304 // reload the engine
3305 testHarness.time_travel(10);
3306 testHarness.reload_engine(&h, &h1,
3307 testHarness.engine_path,
3308 testHarness.get_current_testcase()->cfg,
3310 wait_for_warmup_complete(h, h1);
3312 // check token, this time we should get a different one
3313 time_t token3 = get_int_stat(h, h1, "ep_startup_time");
3314 check(token3 != token1, "Expected a different startup token");
3319 static enum test_result test_workload_stats(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
3320 const void* cookie = testHarness.create_cookie();
3321 checkeq(ENGINE_SUCCESS,
3322 h1->get_stats(h, cookie, "workload",
3323 strlen("workload"), add_stats),
3324 "Falied to get workload stats");
3325 testHarness.destroy_cookie(cookie);
3326 int num_read_threads = get_int_stat(h, h1, "ep_workload:num_readers",
3328 int num_write_threads = get_int_stat(h, h1, "ep_workload:num_writers",
3330 int num_auxio_threads = get_int_stat(h, h1, "ep_workload:num_auxio",
3332 int num_nonio_threads = get_int_stat(h, h1, "ep_workload:num_nonio",
3334 int max_read_threads = get_int_stat(h, h1, "ep_workload:max_readers",
3336 int max_write_threads = get_int_stat(h, h1, "ep_workload:max_writers",
3338 int max_auxio_threads = get_int_stat(h, h1, "ep_workload:max_auxio",
3340 int max_nonio_threads = get_int_stat(h, h1, "ep_workload:max_nonio",
3342 int num_shards = get_int_stat(h, h1, "ep_workload:num_shards", "workload");
3343 checkeq(4, num_read_threads, "Incorrect number of readers");
3344 // MB-12279: limiting max writers to 4 for DGM bgfetch performance
3345 checkeq(4, num_write_threads, "Incorrect number of writers");
3346 checkeq(1, num_auxio_threads, "Incorrect number of auxio threads");
3347 check(num_nonio_threads > 1 && num_nonio_threads <= 8,
3348 "Incorrect number of nonio threads");
3349 checkeq(4, max_read_threads, "Incorrect limit of readers");
3350 // MB-12279: limiting max writers to 4 for DGM bgfetch performance
3351 checkeq(4, max_write_threads, "Incorrect limit of writers");
3352 checkeq(1, max_auxio_threads, "Incorrect limit of auxio threads");
3353 check(max_nonio_threads > 1 && max_nonio_threads <=8,
3354 "Incorrect limit of nonio threads");
3355 checkeq(5, num_shards, "Incorrect number of shards");
3359 static enum test_result test_max_workload_stats(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
3360 const void* cookie = testHarness.create_cookie();
3361 checkeq(ENGINE_SUCCESS,
3362 h1->get_stats(h, cookie, "workload",
3363 strlen("workload"), add_stats),
3364 "Failed to get workload stats");
3365 testHarness.destroy_cookie(cookie);
3366 int num_read_threads = get_int_stat(h, h1, "ep_workload:num_readers",
3368 int num_write_threads = get_int_stat(h, h1, "ep_workload:num_writers",
3370 int num_auxio_threads = get_int_stat(h, h1, "ep_workload:num_auxio",
3372 int num_nonio_threads = get_int_stat(h, h1, "ep_workload:num_nonio",
3374 int max_read_threads = get_int_stat(h, h1, "ep_workload:max_readers",
3376 int max_write_threads = get_int_stat(h, h1, "ep_workload:max_writers",
3378 int max_auxio_threads = get_int_stat(h, h1, "ep_workload:max_auxio",
3380 int max_nonio_threads = get_int_stat(h, h1, "ep_workload:max_nonio",
3382 int num_shards = get_int_stat(h, h1, "ep_workload:num_shards", "workload");
3383 // if max limit on other groups missing use remaining for readers & writers
3384 checkeq(5, num_read_threads, "Incorrect number of readers");
3385 // MB-12279: limiting max writers to 4 for DGM bgfetch performance
3386 checkeq(4, num_write_threads, "Incorrect number of writers");
3388 checkeq(1, num_auxio_threads, "Incorrect number of auxio threads");// config
3389 checkeq(4, num_nonio_threads, "Incorrect number of nonio threads");// config
3390 checkeq(5, max_read_threads, "Incorrect limit of readers");// derived
3391 // MB-12279: limiting max writers to 4 for DGM bgfetch performance
3392 checkeq(4, max_write_threads, "Incorrect limit of writers");// max-capped
3393 checkeq(1, max_auxio_threads, "Incorrect limit of auxio threads");// config
3394 checkeq(4, max_nonio_threads, "Incorrect limit of nonio threads");// config
3395 checkeq(5, num_shards, "Incorrect number of shards");
3399 static enum test_result test_worker_stats(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
3400 checkeq(ENGINE_SUCCESS,
3401 h1->get_stats(h, NULL, "dispatcher",
3402 strlen("dispatcher"), add_stats),
3403 "Failed to get worker stats");
3405 std::set<std::string> tasklist;
3406 tasklist.insert("Running a flusher loop");
3407 tasklist.insert("Snapshotting vbucket states for the shard");
3408 tasklist.insert("Deleting VBucket");
3409 tasklist.insert("Updating stat snapshot on disk");
3410 tasklist.insert("Batching background fetch");
3411 tasklist.insert("Fetching item from disk for vkey stat");
3412 tasklist.insert("Fetching item from disk");
3413 tasklist.insert("Loading TAP backfill from disk");
3414 tasklist.insert("Tap connection notifier");
3415 tasklist.insert("Generating access log");
3416 tasklist.insert("Fetching item from disk for tap");
3417 tasklist.insert("Snapshotting vbucket states");
3418 tasklist.insert("Persisting a vbucket state for vbucket");
3419 tasklist.insert("Reaping tap or dcp connection");
3420 tasklist.insert("Warmup - initialize");
3421 tasklist.insert("Warmup - creating vbuckets");
3422 tasklist.insert("Warmup - estimate item count");
3423 tasklist.insert("Warmup - key dump");
3424 tasklist.insert("Warmup - check for access log");