3f61630c7ee5781266e3dea94a63d3d08bbb5191
[ep-engine.git] / tests / ep_testsuite.cc
1 /* -*- MODE: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2010 Couchbase, Inc
4  *
5  *   Licensed under the Apache License, Version 2.0 (the "License");
6  *   you may not use this file except in compliance with the License.
7  *   You may obtain a copy of the License at
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *   Unless required by applicable law or agreed to in writing, software
12  *   distributed under the License is distributed on an "AS IS" BASIS,
13  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *   See the License for the specific language governing permissions and
15  *   limitations under the License.
16  */
17
18 // Usage: (to run just a single test case)
19 // make engine_tests EP_TEST_NUM=3
20
21 #include "config.h"
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include <condition_variable>
28 #include <cstdlib>
29 #include <chrono>
30 #include <iostream>
31 #include <iomanip>
32 #include <map>
33 #include <mutex>
34 #include <set>
35 #include <sstream>
36 #include <string>
37 #include <thread>
38 #include <unordered_map>
39 #include <unordered_set>
40 #include <vector>
41
42 #include "atomic.h"
43 #include "ep_test_apis.h"
44
45 #include "ep_testsuite_common.h"
46 #include "locks.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>
57
58 #ifdef linux
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 ..
63  */
64 #undef ntohs
65 #undef ntohl
66 #undef htons
67 #undef htonl
68 #endif
69
70 // ptr_fun don't like the extern "C" thing for unlock cookie.. cast it
71 // away ;)
72 typedef void (*UNLOCK_COOKIE_T)(const void *cookie);
73
74 #define MULTI_DISPATCHER_CONFIG \
75     "ht_size=129;ht_locks=3;chk_remover_stime=1;chk_period=60"
76
77 class ThreadData {
78 public:
79     ThreadData(ENGINE_HANDLE *eh, ENGINE_HANDLE_V1 *ehv1,
80                int e=0) : h(eh), h1(ehv1), extra(e) {}
81     ENGINE_HANDLE    *h;
82     ENGINE_HANDLE_V1 *h1;
83     int               extra;
84 };
85
86 enum class BucketType { EP, Ephemeral };
87
88 static void check_observe_seqno(bool failover,
89                                 BucketType bucket_type,
90                                 uint8_t format_type,
91                                 uint16_t vb_id,
92                                 uint64_t vb_uuid,
93                                 uint64_t last_persisted_seqno,
94                                 uint64_t current_seqno,
95                                 uint64_t failover_vbuuid = 0,
96                                 uint64_t failover_seqno = 0) {
97     uint8_t recv_format_type;
98     uint16_t recv_vb_id;
99     uint64_t recv_vb_uuid;
100     uint64_t recv_last_persisted_seqno;
101     uint64_t recv_current_seqno;
102     uint64_t recv_failover_vbuuid;
103     uint64_t recv_failover_seqno;
104
105     memcpy(&recv_format_type, last_body.data(), sizeof(uint8_t));
106     checkeq(format_type, recv_format_type, "Wrong format type in result");
107     memcpy(&recv_vb_id, last_body.data() + 1, sizeof(uint16_t));
108     checkeq(vb_id, ntohs(recv_vb_id), "Wrong vbucket id in result");
109     memcpy(&recv_vb_uuid, last_body.data() + 3, sizeof(uint64_t));
110     checkeq(vb_uuid, ntohll(recv_vb_uuid), "Wrong vbucket uuid in result");
111     memcpy(&recv_last_persisted_seqno, last_body.data() + 11, sizeof(uint64_t));
112
113     switch (bucket_type) {
114     case BucketType::EP:
115         // Should get the "real" persisted seqno:
116         checkeq(last_persisted_seqno,
117                 ntohll(recv_last_persisted_seqno),
118                 "Wrong persisted seqno in result (EP)");
119         break;
120     case BucketType::Ephemeral:
121         // For ephemeral, this should always be zero, as there is no
122         // persistence.
123         checkeq(uint64_t(0),
124                 ntohll(recv_last_persisted_seqno),
125                 "Wrong persisted seqno in result (Ephemeral)");
126         break;
127     }
128
129     memcpy(&recv_current_seqno, last_body.data() + 19, sizeof(uint64_t));
130     checkeq(current_seqno, ntohll(recv_current_seqno), "Wrong current seqno in result");
131
132     if (failover) {
133         memcpy(&recv_failover_vbuuid, last_body.data() + 27, sizeof(uint64_t));
134         checkeq(failover_vbuuid, ntohll(recv_failover_vbuuid),
135                 "Wrong failover uuid in result");
136         memcpy(&recv_failover_seqno, last_body.data() + 35, sizeof(uint64_t));
137         checkeq(failover_seqno, ntohll(recv_failover_seqno),
138                 "Wrong failover seqno in result");
139     }
140 }
141
142 static enum test_result test_replace_with_eviction(ENGINE_HANDLE *h,
143                                                    ENGINE_HANDLE_V1 *h1) {
144     item *i = NULL;
145     checkeq(ENGINE_SUCCESS,
146             store(h, h1, NULL, OPERATION_SET,"key", "somevalue", &i),
147             "Failed to set value.");
148     h1->release(h, NULL, i);
149     wait_for_flusher_to_settle(h, h1);
150     evict_key(h, h1, "key");
151     int numBgFetched = get_int_stat(h, h1, "ep_bg_fetched");
152
153     checkeq(ENGINE_SUCCESS,
154             store(h, h1, NULL, OPERATION_REPLACE,"key", "somevalue1", &i),
155             "Failed to replace existing value.");
156
157     checkeq(ENGINE_SUCCESS,
158             h1->get_stats(h, NULL, NULL, 0, add_stats),
159             "Failed to get stats.");
160     std::string eviction_policy = vals.find("ep_item_eviction_policy")->second;
161     if (eviction_policy == "full_eviction") {
162         numBgFetched++;
163     }
164
165     checkeq(numBgFetched,
166             get_int_stat(h, h1, "ep_bg_fetched"),
167             "Bg fetched value didn't match");
168
169     h1->release(h, NULL, i);
170     check_key_value(h, h1, "key", "somevalue1", 10);
171     return SUCCESS;
172 }
173
174 static enum test_result test_wrong_vb_mutation(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1,
175                                                ENGINE_STORE_OPERATION op) {
176     item *i = NULL;
177     int numNotMyVBucket = get_int_stat(h, h1, "ep_num_not_my_vbuckets");
178     uint64_t cas = 11;
179     if (op == OPERATION_ADD) {
180         // Add operation with cas != 0 doesn't make sense
181         cas = 0;
182     }
183     checkeq(ENGINE_NOT_MY_VBUCKET,
184             store(h, h1, NULL, op, "key", "somevalue", &i, cas, 1),
185             "Expected not_my_vbucket");
186     h1->release(h, NULL, i);
187     wait_for_stat_change(h, h1, "ep_num_not_my_vbuckets", numNotMyVBucket);
188     return SUCCESS;
189 }
190
191 static enum test_result test_pending_vb_mutation(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1,
192                                                  ENGINE_STORE_OPERATION op) {
193     const void *cookie = testHarness.create_cookie();
194     testHarness.set_ewouldblock_handling(cookie, false);
195     item *i = NULL;
196     check(set_vbucket_state(h, h1, 1, vbucket_state_pending),
197           "Failed to set vbucket state.");
198     check(verify_vbucket_state(h, h1, 1, vbucket_state_pending),
199           "Bucket state was not set to pending.");
200     uint64_t cas = 11;
201     if (op == OPERATION_ADD) {
202         // Add operation with cas != 0 doesn't make sense..
203         cas = 0;
204     }
205     checkeq(ENGINE_EWOULDBLOCK,
206             store(h, h1, cookie, op, "key", "somevalue", &i, cas, 1),
207             "Expected ewouldblock");
208     h1->release(h, NULL, i);
209     testHarness.destroy_cookie(cookie);
210     return SUCCESS;
211 }
212
213 static enum test_result test_replica_vb_mutation(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1,
214                                                  ENGINE_STORE_OPERATION op) {
215     item *i = NULL;
216     check(set_vbucket_state(h, h1, 1, vbucket_state_replica),
217           "Failed to set vbucket state.");
218     check(verify_vbucket_state(h, h1, 1, vbucket_state_replica),
219           "Bucket state was not set to replica.");
220     int numNotMyVBucket = get_int_stat(h, h1, "ep_num_not_my_vbuckets");
221
222     uint64_t cas = 11;
223     if (op == OPERATION_ADD) {
224         // performing add with a CAS != 0 doesn't make sense...
225         cas = 0;
226     }
227     checkeq(ENGINE_NOT_MY_VBUCKET,
228             store(h, h1, NULL, op, "key", "somevalue", &i, cas, 1),
229             "Expected not my vbucket");
230     wait_for_stat_change(h, h1, "ep_num_not_my_vbuckets", numNotMyVBucket);
231     h1->release(h, NULL, i);
232     return SUCCESS;
233 }
234
235 //
236 // ----------------------------------------------------------------------
237 // The actual tests are below.
238 // ----------------------------------------------------------------------
239 //
240
241 static int checkCurrItemsAfterShutdown(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1,
242                                        int numItems2Load, bool shutdownForce) {
243     if (!isWarmupEnabled(h, h1)) {
244         return SKIPPED;
245     }
246
247     std::vector<std::string> keys;
248     for (int index = 0; index < numItems2Load; ++index) {
249         std::stringstream s;
250         s << "keys_2_load-" << index;
251         std::string key(s.str());
252         keys.push_back(key);
253     }
254
255     checkeq(0, get_int_stat(h, h1, "ep_total_persisted"),
256             "Expected ep_total_persisted equals 0");
257     checkeq(0, get_int_stat(h, h1, "curr_items"),
258             "Expected curr_items equals 0");
259
260     // stop flusher before loading new items
261     protocol_binary_request_header *pkt = createPacket(PROTOCOL_BINARY_CMD_STOP_PERSISTENCE);
262     checkeq(ENGINE_SUCCESS,
263             h1->unknown_command(h, NULL, pkt, add_response, testHarness.doc_namespace),
264             "CMD_STOP_PERSISTENCE failed!");
265     checkeq(PROTOCOL_BINARY_RESPONSE_SUCCESS,
266             last_status.load(),
267             "Failed to stop persistence!");
268     cb_free(pkt);
269
270     std::vector<std::string>::iterator itr;
271     for (itr = keys.begin(); itr != keys.end(); ++itr) {
272         item *i;
273         checkeq(ENGINE_SUCCESS,
274                 store(h, h1, NULL, OPERATION_SET, itr->c_str(), "oracle", &i, 0, 0),
275                 "Failed to store a value");
276         h1->release(h, NULL, i);
277     }
278
279     checkeq(0, get_int_stat(h, h1, "ep_total_persisted"),
280             "Incorrect ep_total_persisted, expected 0");
281     std::stringstream ss;
282     ss << "Incorrect curr_items, expected " << numItems2Load;
283     std::string errmsg(ss.str());
284     checkeq(numItems2Load, get_int_stat(h, h1, "curr_items"),
285             errmsg.c_str());
286
287     // resume flusher before shutdown + warmup
288     pkt = createPacket(PROTOCOL_BINARY_CMD_START_PERSISTENCE);
289     checkeq(ENGINE_SUCCESS, h1->unknown_command(h, NULL, pkt, add_response, testHarness.doc_namespace),
290             "CMD_START_PERSISTENCE failed!");
291     checkeq(PROTOCOL_BINARY_RESPONSE_SUCCESS, last_status.load(),
292           "Failed to start persistence!");
293     cb_free(pkt);
294
295     // shutdown engine force and restart
296     testHarness.reload_engine(&h, &h1,
297                               testHarness.engine_path,
298                               testHarness.get_current_testcase()->cfg,
299                               true, shutdownForce);
300     wait_for_warmup_complete(h, h1);
301     return get_int_stat(h, h1, "curr_items");
302 }
303
304 static enum test_result test_flush_shutdown_force(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
305     if (!isWarmupEnabled(h, h1)) {
306         return SKIPPED;
307     }
308
309     int numItems2load = 3000;
310     bool shutdownForce = true;
311     int currItems = checkCurrItemsAfterShutdown(h, h1, numItems2load, shutdownForce);
312     check (currItems <= numItems2load,
313            "Number of curr items should be <= 3000, unless previous "
314            "shutdown force had to wait for the flusher");
315     return SUCCESS;
316 }
317
318 static enum test_result test_flush_shutdown_noforce(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
319     if (!isWarmupEnabled(h, h1)) {
320         return SKIPPED;
321     }
322
323     int numItems2load = 3000;
324     bool shutdownForce = false;
325     int currItems = checkCurrItemsAfterShutdown(h, h1, numItems2load, shutdownForce);
326     check (currItems == numItems2load,
327            "Number of curr items should be equal to 3000, unless previous "
328            "shutdown did not wait for the flusher");
329     return SUCCESS;
330 }
331
332 static enum test_result test_flush_restart(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
333     if (!isWarmupEnabled(h, h1)) {
334         return SKIPPED;
335     }
336
337     item *i = NULL;
338     // First try to delete something we know to not be there.
339     checkeq(ENGINE_KEY_ENOENT, del(h, h1, "key", 0, 0),
340             "Failed to fail initial delete.");
341     checkeq(ENGINE_SUCCESS,
342             store(h, h1, NULL, OPERATION_SET, "key", "somevalue", &i),
343             "Failed set.");
344     h1->release(h, NULL, i);
345     check_key_value(h, h1, "key", "somevalue", 9);
346
347     // Restart once to ensure written to disk.
348     testHarness.reload_engine(&h, &h1,
349                               testHarness.engine_path,
350                               testHarness.get_current_testcase()->cfg,
351                               true, false);
352     wait_for_warmup_complete(h, h1);
353
354     // Read value from disk.
355     check_key_value(h, h1, "key", "somevalue", 9);
356
357     // Flush
358     set_degraded_mode(h, h1, NULL, true);
359     checkeq(ENGINE_SUCCESS, h1->flush(h, NULL),
360             "Failed to flush");
361     set_degraded_mode(h, h1, NULL, false);
362
363     checkeq(ENGINE_SUCCESS,
364             store(h, h1, NULL, OPERATION_SET, "key2", "somevalue", &i),
365             "Failed post-flush set.");
366     h1->release(h, NULL, i);
367     check_key_value(h, h1, "key2", "somevalue", 9);
368
369     // Restart again, ensure written to disk.
370     testHarness.reload_engine(&h, &h1,
371                               testHarness.engine_path,
372                               testHarness.get_current_testcase()->cfg,
373                               true, false);
374     wait_for_warmup_complete(h, h1);
375
376     checkeq(ENGINE_SUCCESS,
377             store(h, h1, NULL, OPERATION_SET, "key3", "somevalue", &i),
378             "Failed post-flush, post-restart set.");
379     h1->release(h, NULL, i);
380     check_key_value(h, h1, "key3", "somevalue", 9);
381
382     // Read value again, should not be there.
383     checkeq(ENGINE_KEY_ENOENT, verify_key(h, h1, "key"),
384             "Expected missing key");
385     return SUCCESS;
386 }
387
388 static enum test_result test_shutdown_snapshot_range(ENGINE_HANDLE *h,
389                                                      ENGINE_HANDLE_V1 *h1) {
390     if (!isWarmupEnabled(h, h1)) {
391         return SKIPPED;
392     }
393
394     const int num_items = 100;
395     for (int j = 0; j < num_items; ++j) {
396         item *i = NULL;
397         std::stringstream ss;
398         ss << "key" << j;
399         checkeq(ENGINE_SUCCESS,
400                 store(h, h1, NULL, OPERATION_SET, ss.str().c_str(), "data", &i),
401                 "Failed to store a value");
402         h1->release(h, NULL, i);
403     }
404
405     wait_for_flusher_to_settle(h, h1);
406     int end = get_int_stat(h, h1, "vb_0:high_seqno", "vbucket-seqno");
407
408     /* change vb state to replica before restarting (as it happens in graceful
409        failover)*/
410     check(set_vbucket_state(h, h1, 0, vbucket_state_replica),
411           "Failed set vbucket 0 to replica state.");
412
413     /* trigger persist vb state task */
414     check(set_param(h, h1, protocol_binary_engine_param_flush,
415                     "vb_state_persist_run", "0"),
416           "Failed to trigger vb state persist");
417
418     /* restart the engine */
419     testHarness.reload_engine(&h, &h1,
420                               testHarness.engine_path,
421                               testHarness.get_current_testcase()->cfg,
422                               true, false);
423     wait_for_warmup_complete(h, h1);
424
425     /* Check if snapshot range is persisted correctly */
426     checkeq(end, get_int_stat(h, h1, "vb_0:last_persisted_snap_start",
427                               "vbucket-seqno"),
428             "Wrong snapshot start persisted");
429     checkeq(end, get_int_stat(h, h1, "vb_0:last_persisted_snap_end",
430                                     "vbucket-seqno"),
431             "Wrong snapshot end persisted");
432
433     return SUCCESS;
434 }
435
436 static enum test_result test_flush_multiv_restart(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
437     if (!isWarmupEnabled(h, h1)) {
438         return SKIPPED;
439     }
440
441     item *i = NULL;
442     check(set_vbucket_state(h, h1, 2, vbucket_state_active),
443           "Failed to set vbucket state.");
444     checkeq(ENGINE_SUCCESS,
445             store(h, h1, NULL, OPERATION_SET, "key", "somevalue", &i),
446             "Failed set.");
447     h1->release(h, NULL, i);
448     checkeq(ENGINE_SUCCESS,
449             store(h, h1, NULL, OPERATION_SET, "key2", "somevalue", &i, 0, 2),
450             "Failed set in vb2.");
451     h1->release(h, NULL, i);
452
453     // Restart once to ensure written to disk.
454     testHarness.reload_engine(&h, &h1,
455                               testHarness.engine_path,
456                               testHarness.get_current_testcase()->cfg,
457                               true, false);
458     wait_for_warmup_complete(h, h1);
459
460     // Read value from disk.
461     check_key_value(h, h1, "key", "somevalue", 9);
462
463     // Flush
464     set_degraded_mode(h, h1, NULL, true);
465     checkeq(ENGINE_SUCCESS, h1->flush(h, NULL),
466             "Failed to flush");
467     set_degraded_mode(h, h1, NULL, false);
468
469     // Restart again, ensure written to disk.
470     testHarness.reload_engine(&h, &h1,
471                               testHarness.engine_path,
472                               testHarness.get_current_testcase()->cfg,
473                               true, false);
474     wait_for_warmup_complete(h, h1);
475
476     // Read value again, should not be there.
477     checkeq(ENGINE_KEY_ENOENT, verify_key(h, h1, "key"), "Expected missing key");
478     check(verify_vbucket_missing(h, h1, 2), "Bucket 2 came back.");
479     return SUCCESS;
480 }
481
482 static enum test_result test_restart(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
483     if (!isWarmupEnabled(h, h1)) {
484         return SKIPPED;
485     }
486
487     item *i = NULL;
488     static const char val[] = "somevalue";
489     ENGINE_ERROR_CODE ret = store(h, h1, NULL, OPERATION_SET, "key", val, &i);
490     checkeq(ENGINE_SUCCESS, ret, "Failed set.");
491     h1->release(h, NULL, i);
492
493     testHarness.reload_engine(&h, &h1,
494                               testHarness.engine_path,
495                               testHarness.get_current_testcase()->cfg,
496                               true, false);
497     wait_for_warmup_complete(h, h1);
498     check_key_value(h, h1, "key", val, strlen(val));
499     return SUCCESS;
500 }
501
502 static enum test_result test_restart_session_stats(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
503     const void* cookie = createTapConn(h, h1, "tap_client_thread");
504     testHarness.unlock_cookie(cookie);
505     testHarness.destroy_cookie(cookie);
506
507     testHarness.reload_engine(&h, &h1,
508                               testHarness.engine_path,
509                               testHarness.get_current_testcase()->cfg,
510                               true, false);
511     wait_for_warmup_complete(h, h1);
512     cookie = createTapConn(h, h1, "tap_client_thread");
513
514     checkeq(ENGINE_SUCCESS, h1->get_stats(h, NULL, "tap", 3, add_stats),
515             "Failed to get stats.");
516     std::string val = vals["eq_tapq:tap_client_thread:backfill_completed"];
517     checkeq(0, strcmp(val.c_str(), "true"), "Don't expect the backfill upon restart");
518     testHarness.unlock_cookie(cookie);
519     testHarness.destroy_cookie(cookie);
520     return SUCCESS;
521 }
522
523 static enum test_result test_specialKeys(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
524     item *i = NULL;
525     ENGINE_ERROR_CODE ret;
526
527     // Simplified Chinese "Couchbase"
528     static const char key0[] = "沙发数据库";
529     static const char val0[] = "some Chinese value";
530     check((ret = store(h, h1, NULL, OPERATION_SET, key0, val0, &i)) == ENGINE_SUCCESS,
531           "Failed set Chinese key");
532     check_key_value(h, h1, key0, val0, strlen(val0));
533     h1->release(h, NULL, i);
534     // Traditional Chinese "Couchbase"
535     static const char key1[] = "沙發數據庫";
536     static const char val1[] = "some Traditional Chinese value";
537     check((ret = store(h, h1, NULL, OPERATION_SET, key1, val1, &i)) == ENGINE_SUCCESS,
538           "Failed set Traditional Chinese key");
539     h1->release(h, NULL, i);
540     // Korean "couch potato"
541     static const char key2[] = "쇼파감자";
542     static const char val2[] = "some Korean value";
543     check((ret = store(h, h1, NULL, OPERATION_SET, key2, val2, &i)) == ENGINE_SUCCESS,
544           "Failed set Korean key");
545     h1->release(h, NULL, i);
546     // Russian "couch potato"
547     static const char key3[] = "лодырь, лентяй";
548     static const char val3[] = "some Russian value";
549     check((ret = store(h, h1, NULL, OPERATION_SET, key3, val3, &i)) == ENGINE_SUCCESS,
550           "Failed set Russian key");
551     h1->release(h, NULL, i);
552     // Japanese "couch potato"
553     static const char key4[] = "カウチポテト";
554     static const char val4[] = "some Japanese value";
555     check((ret = store(h, h1, NULL, OPERATION_SET, key4, val4, &i)) == ENGINE_SUCCESS,
556           "Failed set Japanese key");
557     h1->release(h, NULL, i);
558     // Indian char key, and no idea what it is
559     static const char key5[] = "हरियानवी";
560     static const char val5[] = "some Indian value";
561     check((ret = store(h, h1, NULL, OPERATION_SET, key5, val5, &i)) == ENGINE_SUCCESS,
562           "Failed set Indian key");
563     h1->release(h, NULL, i);
564     // Portuguese translation "couch potato"
565     static const char key6[] = "sedentário";
566     static const char val6[] = "some Portuguese value";
567     check((ret = store(h, h1, NULL, OPERATION_SET, key6, val6, &i)) == ENGINE_SUCCESS,
568           "Failed set Portuguese key");
569     h1->release(h, NULL, i);
570     // Arabic translation "couch potato"
571     static const char key7[] = "الحافلةالبطاطة";
572     static const char val7[] = "some Arabic value";
573     check((ret = store(h, h1, NULL, OPERATION_SET, key7, val7, &i)) == ENGINE_SUCCESS,
574           "Failed set Arabic key");
575     h1->release(h, NULL, i);
576
577     if (isWarmupEnabled(h, h1)) {
578         // Check that after warmup the keys are still present.
579         testHarness.reload_engine(&h, &h1,
580                                   testHarness.engine_path,
581                                   testHarness.get_current_testcase()->cfg,
582                                   true, false);
583         wait_for_warmup_complete(h, h1);
584         check_key_value(h, h1, key0, val0, strlen(val0));
585         check_key_value(h, h1, key1, val1, strlen(val1));
586         check_key_value(h, h1, key2, val2, strlen(val2));
587         check_key_value(h, h1, key3, val3, strlen(val3));
588         check_key_value(h, h1, key4, val4, strlen(val4));
589         check_key_value(h, h1, key5, val5, strlen(val5));
590         check_key_value(h, h1, key6, val6, strlen(val6));
591         check_key_value(h, h1, key7, val7, strlen(val7));
592     }
593     return SUCCESS;
594 }
595
596 static enum test_result test_binKeys(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
597     item *i = NULL;
598     ENGINE_ERROR_CODE ret;
599
600     // binary key with char values beyond 0x7F
601     static const char key0[] = "\xe0\xed\xf1\x6f\x7f\xf8\xfa";
602     static const char val0[] = "some value val8";
603     check((ret = store(h, h1, NULL, OPERATION_SET, key0, val0, &i)) == ENGINE_SUCCESS,
604           "Failed set binary key0");
605     check_key_value(h, h1, key0, val0, strlen(val0));
606     h1->release(h, NULL, i);
607     // binary keys with char values beyond 0x7F
608     static const char key1[] = "\xf1\xfd\xfe\xff\xf0\xf8\xef";
609     static const char val1[] = "some value val9";
610     check((ret = store(h, h1, NULL, OPERATION_SET, key1, val1, &i)) == ENGINE_SUCCESS,
611           "Failed set binary key1");
612     check_key_value(h, h1, key1, val1, strlen(val1));
613     h1->release(h, NULL, i);
614     // binary keys with special utf-8 BOM (Byte Order Mark) values 0xBB 0xBF 0xEF
615     static const char key2[] = "\xff\xfe\xbb\xbf\xef";
616     static const char val2[] = "some utf-8 bom value";
617     check((ret = store(h, h1, NULL, OPERATION_SET, key2, val2, &i)) == ENGINE_SUCCESS,
618           "Failed set binary utf-8 bom key");
619     check_key_value(h, h1, key2, val2, strlen(val2));
620     h1->release(h, NULL, i);
621     // binary keys with special utf-16BE BOM values "U+FEFF"
622     static const char key3[] = "U+\xfe\xff\xefU+\xff\xfe";
623     static const char val3[] = "some utf-16 bom value";
624     check((ret = store(h, h1, NULL, OPERATION_SET, key3, val3, &i)) == ENGINE_SUCCESS,
625           "Failed set binary utf-16 bom key");
626     check_key_value(h, h1, key3, val3, strlen(val3));
627     h1->release(h, NULL, i);
628
629     if (isWarmupEnabled(h, h1)) {
630         testHarness.reload_engine(&h, &h1,
631                                   testHarness.engine_path,
632                                   testHarness.get_current_testcase()->cfg,
633                                   true, false);
634         wait_for_warmup_complete(h, h1);
635         check_key_value(h, h1, key0, val0, strlen(val0));
636         check_key_value(h, h1, key1, val1, strlen(val1));
637         check_key_value(h, h1, key2, val2, strlen(val2));
638         check_key_value(h, h1, key3, val3, strlen(val3));
639     }
640     return SUCCESS;
641 }
642
643 static enum test_result test_restart_bin_val(ENGINE_HANDLE *h,
644                                              ENGINE_HANDLE_V1 *h1) {
645     if (!isWarmupEnabled(h, h1)) {
646         return SKIPPED;
647     }
648
649     char binaryData[] = "abcdefg\0gfedcba";
650     cb_assert(sizeof(binaryData) != strlen(binaryData));
651
652     item *i = NULL;
653     checkeq(ENGINE_SUCCESS,
654             storeCasVb11(h, h1, NULL, OPERATION_SET, "key",
655                          binaryData, sizeof(binaryData), 82758, &i, 0, 0),
656             "Failed set.");
657     h1->release(h, NULL, i);
658
659     testHarness.reload_engine(&h, &h1,
660                               testHarness.engine_path,
661                               testHarness.get_current_testcase()->cfg,
662                               true, false);
663     wait_for_warmup_complete(h, h1);
664
665     check_key_value(h, h1, "key", binaryData, sizeof(binaryData));
666     return SUCCESS;
667 }
668
669 static enum test_result test_wrong_vb_get(ENGINE_HANDLE *h,
670                                           ENGINE_HANDLE_V1 *h1) {
671     int numNotMyVBucket = get_int_stat(h, h1, "ep_num_not_my_vbuckets");
672     checkeq(ENGINE_NOT_MY_VBUCKET, verify_key(h, h1, "key", 1),
673             "Expected wrong bucket.");
674     wait_for_stat_change(h, h1, "ep_num_not_my_vbuckets", numNotMyVBucket);
675     return SUCCESS;
676 }
677
678 static enum test_result test_vb_get_pending(ENGINE_HANDLE *h,
679                                             ENGINE_HANDLE_V1 *h1) {
680     check(set_vbucket_state(h, h1, 1, vbucket_state_pending),
681           "Failed to set vbucket state.");
682     const void *cookie = testHarness.create_cookie();
683     testHarness.set_ewouldblock_handling(cookie, false);
684
685     item *i = NULL;
686     checkeq(ENGINE_EWOULDBLOCK,
687             get(h, h1, cookie, &i, "key", 1),
688             "Expected woodblock.");
689     h1->release(h, NULL, i);
690
691     testHarness.destroy_cookie(cookie);
692     return SUCCESS;
693 }
694
695 static enum test_result test_vb_get_replica(ENGINE_HANDLE *h,
696                                             ENGINE_HANDLE_V1 *h1) {
697     check(set_vbucket_state(h, h1, 1, vbucket_state_replica),
698           "Failed to set vbucket state.");
699     int numNotMyVBucket = get_int_stat(h, h1, "ep_num_not_my_vbuckets");
700     checkeq(ENGINE_NOT_MY_VBUCKET,
701             verify_key(h, h1, "key", 1),
702             "Expected not my bucket.");
703     wait_for_stat_change(h, h1, "ep_num_not_my_vbuckets", numNotMyVBucket);
704     return SUCCESS;
705 }
706
707 static enum test_result test_wrong_vb_set(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
708     return test_wrong_vb_mutation(h, h1, OPERATION_SET);
709 }
710
711 static enum test_result test_wrong_vb_cas(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
712     return test_wrong_vb_mutation(h, h1, OPERATION_CAS);
713 }
714
715 static enum test_result test_wrong_vb_add(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
716     return test_wrong_vb_mutation(h, h1, OPERATION_ADD);
717 }
718
719 static enum test_result test_wrong_vb_replace(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
720     return test_wrong_vb_mutation(h, h1, OPERATION_REPLACE);
721 }
722
723 static enum test_result test_wrong_vb_del(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
724     int numNotMyVBucket = get_int_stat(h, h1, "ep_num_not_my_vbuckets");
725     checkeq(ENGINE_NOT_MY_VBUCKET, del(h, h1, "key", 0, 1),
726             "Expected wrong bucket.");
727     wait_for_stat_change(h, h1, "ep_num_not_my_vbuckets", numNotMyVBucket);
728     return SUCCESS;
729 }
730
731 /* Returns a string in the format "%Y-%m-%d %H:%M:%S" of the specified
732  * time point.
733  */
734 std::string make_time_string(std::chrono::system_clock::time_point time_point) {
735     time_t tt = std::chrono::system_clock::to_time_t(time_point);
736 #ifdef _MSC_VER
737     // Windows' gmtime() is already thread-safe.
738     struct tm* split = gmtime(&tt);
739 #else
740     struct tm local_storage;
741     struct tm* split = gmtime_r(&tt, &local_storage);
742 #endif
743     char timeStr[20];
744     strftime(timeStr, 20, "%Y-%m-%d %H:%M:%S", split);
745     return timeStr;
746 }
747
748 static enum test_result test_expiry_pager_settings(ENGINE_HANDLE *h,
749                                                    ENGINE_HANDLE_V1 *h1) {
750
751     cb_assert(!get_bool_stat(h, h1, "ep_exp_pager_enabled"));
752     checkeq(3600, get_int_stat(h, h1, "ep_exp_pager_stime"),
753             "Expiry pager sleep time not expected");
754     set_param(h, h1, protocol_binary_engine_param_flush,
755               "exp_pager_stime", "1");
756     checkeq(1, get_int_stat(h, h1, "ep_exp_pager_stime"),
757             "Expiry pager sleep time not updated");
758     cb_assert(!get_bool_stat(h, h1, "ep_exp_pager_enabled"));
759     sleep(1);
760     checkeq(0, get_int_stat(h, h1, "ep_num_expiry_pager_runs"),
761             "Expiry pager run count is not zero");
762
763     set_param(h, h1, protocol_binary_engine_param_flush,
764               "exp_pager_enabled", "true");
765     checkeq(1, get_int_stat(h, h1, "ep_exp_pager_stime"),
766             "Expiry pager sleep time not updated");
767     wait_for_stat_to_be_gte(h, h1, "ep_num_expiry_pager_runs", 1);
768
769     // Reload engine
770     testHarness.reload_engine(&h, &h1,
771                               testHarness.engine_path,
772                               testHarness.get_current_testcase()->cfg,
773                               true, false);
774     wait_for_warmup_complete(h, h1);
775     cb_assert(!get_bool_stat(h, h1, "ep_exp_pager_enabled"));
776
777     // Enable expiry pager again
778     set_param(h, h1, protocol_binary_engine_param_flush,
779               "exp_pager_enabled", "true");
780
781     checkeq(get_int_stat(h, h1, "ep_exp_pager_initial_run_time"), -1,
782             "Task time should be disable upon warmup");
783
784     std::string err_msg;
785     // Update exp_pager_initial_run_time and ensure the update is successful
786     set_param(h, h1, protocol_binary_engine_param_flush,
787               "exp_pager_initial_run_time", "3");
788     std::string expected_time = "03:00";
789     std::string str = get_str_stat(h, h1, "ep_expiry_pager_task_time");
790     err_msg.assign("Updated time incorrect, expect: " +
791                    expected_time + ", actual: " + str.substr(11, 5));
792     checkeq(0, str.substr(11, 5).compare(expected_time), err_msg.c_str());
793
794     // Update exp_pager_stime by 30 minutes and ensure that the update is successful
795     const std::chrono::minutes update_by{30};
796     std::string targetTaskTime1{make_time_string(std::chrono::system_clock::now() +
797                                                  update_by)};
798
799     set_param(h, h1, protocol_binary_engine_param_flush, "exp_pager_stime",
800               std::to_string(update_by.count() * 60).c_str());
801     str = get_str_stat(h, h1, "ep_expiry_pager_task_time");
802
803     std::string targetTaskTime2{make_time_string(std::chrono::system_clock::now() +
804                                                  update_by)};
805
806     // ep_expiry_pager_task_time should fall within the range of
807     // targetTaskTime1 and targetTaskTime2
808     err_msg.assign("Unexpected task time range, expect: " +
809                    targetTaskTime1 + " <= " + str + " <= " + targetTaskTime2);
810     check(targetTaskTime1 <= str, err_msg.c_str());
811     check(str <= targetTaskTime2, err_msg.c_str());
812
813     return SUCCESS;
814 }
815
816 static enum test_result test_expiry_with_xattr(ENGINE_HANDLE* h,
817                                                ENGINE_HANDLE_V1* h1) {
818     const char* key = "test_expiry";
819     cb::xattr::Blob blob;
820
821     //Add a few XAttrs
822     blob.set(to_const_byte_buffer("user"),
823              to_const_byte_buffer("{\"author\":\"bubba\"}"));
824     blob.set(to_const_byte_buffer("_sync"),
825              to_const_byte_buffer("{\"cas\":\"0xdeadbeefcafefeed\"}"));
826     blob.set(to_const_byte_buffer("meta"),
827              to_const_byte_buffer("{\"content-type\":\"text\"}"));
828
829     auto xattr_value = blob.finalize();
830
831     //Now, append user data to the xattrs and store the data
832     std::string value_data("test_expiry_value");
833     std::vector<char> data;
834     std::copy(xattr_value.buf, xattr_value.buf + xattr_value.len,
835               std::back_inserter(data));
836     std::copy(value_data.c_str(), value_data.c_str() + value_data.length(),
837               std::back_inserter(data));
838
839     const void* cookie = testHarness.create_cookie();
840
841     item *itm = nullptr;
842     checkeq(ENGINE_SUCCESS,
843             storeCasVb11(h, h1, cookie, OPERATION_SET, key,
844                          reinterpret_cast<char*>(data.data()),
845                          data.size(), 9258, &itm, 0, 0, 10,
846                          PROTOCOL_BINARY_DATATYPE_XATTR),
847             "Failed to store xattr document");
848     h1->release(h, nullptr, itm);
849
850     if (isPersistentBucket(h, h1)) {
851         wait_for_flusher_to_settle(h, h1);
852     }
853
854     testHarness.time_travel(11);
855
856     checkeq(true,
857             get_meta(h, h1, "test_expiry", true, GetMetaVersion::V2, cookie),
858             "Get meta command failed");
859     auto prev_revseqno = last_meta.revSeqno;
860
861     checkeq(static_cast<uint8_t>(PROTOCOL_BINARY_DATATYPE_XATTR),
862             last_datatype.load(), "Datatype is not XATTR");
863
864     checkeq(ENGINE_SUCCESS,
865             get(h, h1, cookie, &itm, key, 0,
866                 DocStateFilter::AliveOrDeleted),
867                 "Unable to get a deleted item");
868
869     checkeq(true,
870             get_meta(h, h1, "test_expiry", false, GetMetaVersion::V1, cookie),
871             "Get meta command failed");
872
873     checkeq(last_meta.revSeqno, prev_revseqno + 1,
874             "rev seqno must have incremented by 1");
875
876     /* Retrieve the item info and create a new blob out of the data */
877     item_info info;
878     checkeq(true, h1->get_item_info(h, cookie, itm, &info),
879             "Unable to retrieve item info");
880
881     cb::byte_buffer value_buf{static_cast<uint8_t*>(info.value[0].iov_base),
882                               info.value[0].iov_len};
883
884     cb::xattr::Blob new_blob(value_buf);
885
886     /* Only system extended attributes need to be present at this point.
887      * Thus, check the blob length with the system size.
888      */
889     const auto systemsize = new_blob.finalize().len;
890
891     checkeq(systemsize, new_blob.get_system_size(),
892             "The size of the blob doesn't match the size of system attributes");
893
894     const std::string& cas_str{"{\"cas\":\"0xdeadbeefcafefeed\"}"};
895     const std::string& sync_str = to_string(blob.get(to_const_byte_buffer("_sync")));
896
897     checkeq(cas_str, sync_str , "system xattr is invalid");
898
899     h1->release(h, nullptr, itm);
900     testHarness.destroy_cookie(cookie);
901
902     return SUCCESS;
903 }
904
905 static enum test_result test_expiry(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
906     const char *key = "test_expiry";
907     const char *data = "some test data here.";
908
909     item *it = NULL;
910
911     ENGINE_ERROR_CODE rv;
912     rv = allocate(h, h1, NULL, &it, key, strlen(data), 0, 2,
913                   PROTOCOL_BINARY_RAW_BYTES, 0);
914     checkeq(ENGINE_SUCCESS, rv, "Allocation failed.");
915
916     item_info info;
917     if (!h1->get_item_info(h, NULL, it, &info)) {
918         abort();
919     }
920     memcpy(info.value[0].iov_base, data, strlen(data));
921
922     uint64_t cas = 0;
923     rv = h1->store(h, NULL, it, &cas, OPERATION_SET, DocumentState::Alive);
924     checkeq(ENGINE_SUCCESS, rv, "Set failed.");
925     check_key_value(h, h1, key, data, strlen(data));
926     h1->release(h, NULL, it);
927
928     testHarness.time_travel(5);
929     checkeq(ENGINE_KEY_ENOENT,
930             get(h, h1, NULL, &it, key, 0),
931             "Item didn't expire");
932
933     int expired_access = get_int_stat(h, h1, "ep_expired_access");
934     int expired_pager = get_int_stat(h, h1, "ep_expired_pager");
935     int active_expired = get_int_stat(h, h1, "vb_active_expired");
936     checkeq(0, expired_pager, "Expected zero expired item by pager");
937     checkeq(1, expired_access, "Expected an expired item on access");
938     checkeq(1, active_expired, "Expected an expired active item");
939     checkeq(ENGINE_SUCCESS, store(h, h1, NULL, OPERATION_SET, key, data, &it),
940             "Failed set.");
941     h1->release(h, NULL, it);
942
943     // When run under full eviction, the total item stats are set from the
944     // flusher. So we need to wait for it to finish before checking the
945     // total number of items.
946     wait_for_flusher_to_settle(h, h1);
947
948     std::stringstream ss;
949     ss << "curr_items stat should be still 1 after ";
950     ss << "overwriting the key that was expired, but not purged yet";
951     checkeq(1, get_int_stat(h, h1, "curr_items"), ss.str().c_str());
952
953     return SUCCESS;
954 }
955
956 static enum test_result test_expiry_loader(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
957     if (!isWarmupEnabled(h, h1)) {
958         return SKIPPED;
959     }
960     const char *key = "test_expiry_loader";
961     const char *data = "some test data here.";
962
963     item *it = NULL;
964
965     ENGINE_ERROR_CODE rv;
966     rv = allocate(h, h1, NULL, &it, key, strlen(data), 0, 2,
967                   PROTOCOL_BINARY_RAW_BYTES, 0);
968     checkeq(ENGINE_SUCCESS, rv, "Allocation failed.");
969
970     item_info info;
971     if (!h1->get_item_info(h, NULL, it, &info)) {
972         abort();
973     }
974     memcpy(info.value[0].iov_base, data, strlen(data));
975
976     uint64_t cas = 0;
977     rv = h1->store(h, NULL, it, &cas, OPERATION_SET, DocumentState::Alive);
978     checkeq(ENGINE_SUCCESS, rv, "Set failed.");
979     check_key_value(h, h1, key, data, strlen(data));
980     h1->release(h, NULL, it);
981
982     testHarness.time_travel(3);
983
984     checkeq(ENGINE_KEY_ENOENT,
985             get(h, h1, NULL, &it, key, 0),
986             "Item didn't expire");
987
988     // Restart the engine to ensure the above expired item is not loaded
989     testHarness.reload_engine(&h, &h1,
990                               testHarness.engine_path,
991                               testHarness.get_current_testcase()->cfg,
992                               true, false);
993     wait_for_warmup_complete(h, h1);
994     cb_assert(0 == get_int_stat(h, h1, "ep_warmup_value_count", "warmup"));
995
996     return SUCCESS;
997 }
998
999 static enum test_result test_expiration_on_compaction(ENGINE_HANDLE *h,
1000                                                       ENGINE_HANDLE_V1 *h1) {
1001     if (get_bool_stat(h, h1, "ep_exp_pager_enabled")) {
1002         set_param(h, h1, protocol_binary_engine_param_flush,
1003                   "exp_pager_enabled", "false");
1004     }
1005
1006     checkeq(1, get_int_stat(h, h1, "vb_0:persistence:num_visits",
1007                             "checkpoint"), "Cursor moved before item load");
1008
1009     for (int i = 0; i < 50; i++) {
1010         item *itm = NULL;
1011         std::stringstream ss;
1012         ss << "key" << i;
1013         checkeq(ENGINE_SUCCESS,
1014                 store(h, h1, NULL, OPERATION_SET, ss.str().c_str(),
1015                       "somevalue", &itm, 0, 0, 10,
1016                       PROTOCOL_BINARY_RAW_BYTES),
1017                 "Set failed.");
1018         h1->release(h, NULL, itm);
1019     }
1020
1021     wait_for_flusher_to_settle(h, h1);
1022     checkeq(50, get_int_stat(h, h1, "curr_items"),
1023             "Unexpected number of items on database");
1024     check(1 < get_int_stat(h, h1, "vb_0:persistence:num_visits", "checkpoint"),
1025           "Cursor not moved even after flusher runs");
1026
1027     testHarness.time_travel(15);
1028
1029     // Compaction on VBucket
1030     compact_db(h, h1, 0, 0, 0, 0, 0);
1031     wait_for_stat_to_be(h, h1, "ep_pending_compactions", 0);
1032
1033     checkeq(50, get_int_stat(h, h1, "ep_expired_compactor"),
1034             "Unexpected expirations by compactor");
1035
1036     return SUCCESS;
1037 }
1038
1039 static enum test_result test_expiration_on_warmup(ENGINE_HANDLE *h,
1040                                                   ENGINE_HANDLE_V1 *h1) {
1041     if (!isWarmupEnabled(h, h1)) {
1042         return SKIPPED;
1043     }
1044
1045     set_param(h, h1, protocol_binary_engine_param_flush,
1046               "exp_pager_enabled", "false");
1047     int pager_runs = get_int_stat(h, h1, "ep_num_expiry_pager_runs");
1048
1049     const char *key = "KEY";
1050     const char *data = "VALUE";
1051
1052     item *it = NULL;
1053
1054     ENGINE_ERROR_CODE rv;
1055     rv = allocate(h, h1, NULL, &it, key, strlen(data), 0, 10,
1056                       PROTOCOL_BINARY_RAW_BYTES, 0);
1057     checkeq(ENGINE_SUCCESS, rv, "Allocation failed.");
1058
1059     item_info info;
1060     if (!h1->get_item_info(h, NULL, it, &info)) {
1061         abort();
1062     }
1063     memcpy(info.value[0].iov_base, data, strlen(data));
1064
1065     uint64_t cas = 0;
1066     rv = h1->store(h, NULL, it, &cas, OPERATION_SET, DocumentState::Alive);
1067     checkeq(ENGINE_SUCCESS, rv, "Set failed.");
1068     check_key_value(h, h1, key, data, strlen(data));
1069     h1->release(h, NULL, it);
1070     wait_for_flusher_to_settle(h, h1);
1071
1072     checkeq(1, get_int_stat(h, h1, "curr_items"), "Failed store item");
1073     testHarness.time_travel(15);
1074
1075     checkeq(pager_runs, get_int_stat(h, h1, "ep_num_expiry_pager_runs"),
1076             "Expiry pager shouldn't have run during this time");
1077
1078     // Restart the engine to ensure the above item is expired
1079     testHarness.reload_engine(&h, &h1,
1080                               testHarness.engine_path,
1081                               testHarness.get_current_testcase()->cfg,
1082                               true, false);
1083     wait_for_warmup_complete(h, h1);
1084     check(get_bool_stat(h, h1, "ep_exp_pager_enabled"),
1085           "Expiry pager should be enabled on warmup");
1086
1087     // Wait for the expiry pager to run and expire our item.
1088     wait_for_stat_to_be_gte(h, h1, "ep_expired_pager", 1, nullptr, /*secs*/10);
1089
1090     // Note: previously we checked that curr_items was zero here (immediately
1091     // after waiting for ep_expired_pager == 1), however we cannot assume that
1092     // - items are actually expired asynchronously.
1093     // See EPStore::deleteExpiredItem - for non-temporary, expired items we
1094     // call processSoftDelete (soft-marking the item as deleted in the
1095     // hashtable), and then call queueDirty to queue a deletion, and then
1096     // increment the expired stat. Only when that delete is actually persisted
1097     // and the deleted callback is invoked -
1098     // PeristenceCallback::callback(int&) - is curr_items finally decremented.
1099     // Therefore we need to wait for the flusher to settle (i.e. delete
1100     // callback to be called) for the curr_items stat to be accurate.
1101     wait_for_flusher_to_settle(h, h1);
1102
1103     checkeq(0, get_int_stat(h, h1, "curr_items"),
1104             "The item should have been expired.");
1105
1106     return SUCCESS;
1107
1108 }
1109
1110 static enum test_result test_bug3454(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1111     if (!isWarmupEnabled(h, h1)) {
1112         return SKIPPED;
1113     }
1114
1115     const char *key = "test_expiry_duplicate_warmup";
1116     const char *data = "some test data here.";
1117
1118     item *it = NULL;
1119
1120     ENGINE_ERROR_CODE rv;
1121     rv = allocate(h, h1, NULL, &it, key, strlen(data), 0, 5,
1122                       PROTOCOL_BINARY_RAW_BYTES, 0);
1123     checkeq(ENGINE_SUCCESS, rv, "Allocation failed.");
1124
1125     item_info info;
1126     if (!h1->get_item_info(h, NULL, it, &info)) {
1127         abort();
1128     }
1129     memcpy(info.value[0].iov_base, data, strlen(data));
1130
1131     uint64_t cas = 0;
1132     rv = h1->store(h, NULL, it, &cas, OPERATION_SET, DocumentState::Alive);
1133     checkeq(ENGINE_SUCCESS, rv, "Set failed.");
1134     check_key_value(h, h1, key, data, strlen(data));
1135     h1->release(h, NULL, it);
1136     wait_for_flusher_to_settle(h, h1);
1137
1138     // Advance the ep_engine time by 10 sec for the above item to be expired.
1139     testHarness.time_travel(10);
1140     checkeq(ENGINE_KEY_ENOENT,
1141             get(h, h1, NULL, &it, key, 0),
1142             "Item didn't expire");
1143
1144     rv = allocate(h, h1, NULL, &it, key, strlen(data), 0, 0,
1145                       PROTOCOL_BINARY_RAW_BYTES, 0);
1146     checkeq(ENGINE_SUCCESS, rv, "Allocation failed.");
1147
1148     if (!h1->get_item_info(h, NULL, it, &info)) {
1149         abort();
1150     }
1151     memcpy(info.value[0].iov_base, data, strlen(data));
1152
1153     cas = 0;
1154     // Add a new item with the same key.
1155     rv = h1->store(h, NULL, it, &cas, OPERATION_ADD, DocumentState::Alive);
1156     checkeq(ENGINE_SUCCESS, rv, "Add failed.");
1157     check_key_value(h, h1, key, data, strlen(data));
1158     h1->release(h, NULL, it);
1159     wait_for_flusher_to_settle(h, h1);
1160
1161     checkeq(ENGINE_SUCCESS,
1162             get(h, h1, NULL, &it, key, 0),
1163             "Item shouldn't expire");
1164     h1->release(h, NULL, it);
1165
1166     // Restart the engine to ensure the above unexpired new item is loaded
1167     testHarness.reload_engine(&h, &h1,
1168                               testHarness.engine_path,
1169                               testHarness.get_current_testcase()->cfg,
1170                               true, false);
1171     wait_for_warmup_complete(h, h1);
1172     cb_assert(1 == get_int_stat(h, h1, "ep_warmup_value_count", "warmup"));
1173     cb_assert(0 == get_int_stat(h, h1, "ep_warmup_dups", "warmup"));
1174
1175     return SUCCESS;
1176 }
1177
1178 static enum test_result test_bug3522(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1179     if (!isWarmupEnabled(h, h1)) {
1180         return SKIPPED;
1181     }
1182
1183     const char *key = "test_expiry_no_items_warmup";
1184     const char *data = "some test data here.";
1185
1186     item *it = NULL;
1187
1188     ENGINE_ERROR_CODE rv;
1189     rv = allocate(h, h1, NULL, &it, key, strlen(data), 0, 0,
1190                   PROTOCOL_BINARY_RAW_BYTES, 0);
1191     checkeq(ENGINE_SUCCESS, rv, "Allocation failed.");
1192
1193     item_info info;
1194     if (!h1->get_item_info(h, NULL, it, &info)) {
1195         abort();
1196     }
1197     memcpy(info.value[0].iov_base, data, strlen(data));
1198
1199     uint64_t cas = 0;
1200     rv = h1->store(h, NULL, it, &cas, OPERATION_SET, DocumentState::Alive);
1201     checkeq(ENGINE_SUCCESS, rv, "Set failed.");
1202     check_key_value(h, h1, key, data, strlen(data));
1203     h1->release(h, NULL, it);
1204     wait_for_flusher_to_settle(h, h1);
1205
1206     // Add a new item with the same key and 2 sec of expiration.
1207     const char *new_data = "new data here.";
1208     rv = allocate(h, h1, NULL, &it, key, strlen(new_data), 0, 2,
1209                   PROTOCOL_BINARY_RAW_BYTES, 0);
1210     checkeq(ENGINE_SUCCESS, rv, "Allocation failed.");
1211
1212     if (!h1->get_item_info(h, NULL, it, &info)) {
1213         abort();
1214     }
1215     memcpy(info.value[0].iov_base, new_data, strlen(new_data));
1216
1217     int pager_runs = get_int_stat(h, h1, "ep_num_expiry_pager_runs");
1218     cas = 0;
1219     rv = h1->store(h, NULL, it, &cas, OPERATION_SET, DocumentState::Alive);
1220     checkeq(ENGINE_SUCCESS, rv, "Set failed.");
1221     check_key_value(h, h1, key, new_data, strlen(new_data));
1222     h1->release(h, NULL, it);
1223     testHarness.time_travel(3);
1224     wait_for_stat_change(h, h1, "ep_num_expiry_pager_runs", pager_runs);
1225     wait_for_flusher_to_settle(h, h1);
1226
1227     // Restart the engine.
1228     testHarness.reload_engine(&h, &h1,
1229                               testHarness.engine_path,
1230                               testHarness.get_current_testcase()->cfg,
1231                               true, false);
1232     wait_for_warmup_complete(h, h1);
1233     // TODO: modify this for a better test case
1234     cb_assert(0 == get_int_stat(h, h1, "ep_warmup_dups", "warmup"));
1235
1236     return SUCCESS;
1237 }
1238
1239 static enum test_result test_get_replica_active_state(ENGINE_HANDLE *h,
1240                                                       ENGINE_HANDLE_V1 *h1) {
1241     protocol_binary_request_header *pkt;
1242     pkt = prepare_get_replica(h, h1, vbucket_state_active);
1243     checkeq(ENGINE_SUCCESS,
1244             h1->unknown_command(h, NULL, pkt, add_response, testHarness.doc_namespace),
1245             "Get Replica Failed");
1246     checkeq(PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET, last_status.load(),
1247             "Expected PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET response.");
1248
1249     cb_free(pkt);
1250     return SUCCESS;
1251 }
1252
1253 static enum test_result test_get_replica_pending_state(ENGINE_HANDLE *h,
1254                                                        ENGINE_HANDLE_V1 *h1) {
1255     protocol_binary_request_header *pkt;
1256
1257     const void *cookie = testHarness.create_cookie();
1258     testHarness.set_ewouldblock_handling(cookie, false);
1259     pkt = prepare_get_replica(h, h1, vbucket_state_pending);
1260     checkeq(ENGINE_EWOULDBLOCK,
1261             h1->unknown_command(h, cookie, pkt, add_response, testHarness.doc_namespace),
1262             "Should have returned error for pending state");
1263     testHarness.destroy_cookie(cookie);
1264     cb_free(pkt);
1265     return SUCCESS;
1266 }
1267
1268 static enum test_result test_get_replica_dead_state(ENGINE_HANDLE *h,
1269                                                     ENGINE_HANDLE_V1 *h1) {
1270     protocol_binary_request_header *pkt;
1271     pkt = prepare_get_replica(h, h1, vbucket_state_dead);
1272     checkeq(ENGINE_SUCCESS,
1273             h1->unknown_command(h, NULL, pkt, add_response, testHarness.doc_namespace),
1274             "Get Replica Failed");
1275     checkeq(PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET, last_status.load(),
1276             "Expected PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET response.");
1277
1278     cb_free(pkt);
1279     return SUCCESS;
1280 }
1281
1282 static enum test_result test_get_replica(ENGINE_HANDLE *h,
1283                                          ENGINE_HANDLE_V1 *h1) {
1284     protocol_binary_request_header *pkt;
1285     pkt = prepare_get_replica(h, h1, vbucket_state_replica);
1286     checkeq(ENGINE_SUCCESS,
1287             h1->unknown_command(h, NULL, pkt, add_response, testHarness.doc_namespace),
1288             "Get Replica Failed");
1289     checkeq(PROTOCOL_BINARY_RESPONSE_SUCCESS, last_status.load(),
1290             "Expected PROTOCOL_BINARY_RESPONSE_SUCCESS response.");
1291     checkeq(std::string("replicadata"), last_body,
1292             "Should have returned identical value");
1293
1294     cb_free(pkt);
1295     return SUCCESS;
1296 }
1297
1298 static enum test_result test_get_replica_non_resident(ENGINE_HANDLE *h,
1299                                                       ENGINE_HANDLE_V1 *h1) {
1300
1301     item *i = NULL;
1302     checkeq(ENGINE_SUCCESS,
1303             store(h, h1, NULL, OPERATION_SET, "key", "value", &i, 0, 0),
1304             "Store Failed");
1305     h1->release(h, NULL, i);
1306     wait_for_flusher_to_settle(h, h1);
1307     wait_for_stat_to_be(h, h1, "ep_total_persisted", 1);
1308
1309     evict_key(h, h1, "key", 0, "Ejected.");
1310     check(set_vbucket_state(h, h1, 0, vbucket_state_replica),
1311           "Failed to set vbucket to replica");
1312
1313     get_replica(h, h1, "key", 0);
1314     checkeq(PROTOCOL_BINARY_RESPONSE_SUCCESS, last_status.load(),
1315             "Expected success");
1316
1317     return SUCCESS;
1318 }
1319
1320 static enum test_result test_get_replica_invalid_key(ENGINE_HANDLE *h,
1321                                                      ENGINE_HANDLE_V1 *h1) {
1322     protocol_binary_request_header *pkt;
1323     bool makeinvalidkey = true;
1324     pkt = prepare_get_replica(h, h1, vbucket_state_replica, makeinvalidkey);
1325     checkeq(ENGINE_SUCCESS,
1326             h1->unknown_command(h, NULL, pkt, add_response, testHarness.doc_namespace),
1327             "Get Replica Failed");
1328     checkeq(PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET, last_status.load(),
1329             "Expected PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET response.");
1330     cb_free(pkt);
1331     return SUCCESS;
1332 }
1333
1334 static enum test_result test_vb_del_pending(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1335     const void *cookie = testHarness.create_cookie();
1336     testHarness.set_ewouldblock_handling(cookie, false);
1337     check(set_vbucket_state(h, h1, 1, vbucket_state_pending),
1338           "Failed to set vbucket state.");
1339     checkeq(ENGINE_EWOULDBLOCK, del(h, h1, "key", 0, 1, cookie),
1340             "Expected woodblock.");
1341     testHarness.destroy_cookie(cookie);
1342     return SUCCESS;
1343 }
1344
1345 static enum test_result test_vb_del_replica(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1346     check(set_vbucket_state(h, h1, 1, vbucket_state_replica),
1347           "Failed to set vbucket state.");
1348     int numNotMyVBucket = get_int_stat(h, h1, "ep_num_not_my_vbuckets");
1349     checkeq(ENGINE_NOT_MY_VBUCKET, del(h, h1, "key", 0, 1),
1350             "Expected not my vbucket.");
1351     wait_for_stat_change(h, h1, "ep_num_not_my_vbuckets", numNotMyVBucket);
1352     return SUCCESS;
1353 }
1354
1355 static enum test_result test_vbucket_get_miss(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1356     return verify_vbucket_missing(h, h1, 1) ? SUCCESS : FAIL;
1357 }
1358
1359 static enum test_result test_vbucket_get(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1360     return verify_vbucket_state(h, h1, 0, vbucket_state_active) ? SUCCESS : FAIL;
1361 }
1362
1363 static enum test_result test_vbucket_create(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1364     if (!verify_vbucket_missing(h, h1, 1)) {
1365         fprintf(stderr, "vbucket wasn't missing.\n");
1366         return FAIL;
1367     }
1368
1369     if (!set_vbucket_state(h, h1, 1, vbucket_state_active)) {
1370         fprintf(stderr, "set state failed.\n");
1371         return FAIL;
1372     }
1373
1374     return verify_vbucket_state(h, h1, 1, vbucket_state_active) ? SUCCESS : FAIL;
1375 }
1376
1377 static enum test_result test_takeover_stats_race_with_vb_create_TAP(
1378                                     ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1379     check(set_vbucket_state(h, h1, 1, vbucket_state_active),
1380           "Failed to set vbucket state information");
1381
1382     checkeq(0,
1383             get_int_stat(h, h1, "on_disk_deletes", "tap-vbtakeover 1"),
1384             "Invalid number of on-disk deletes");
1385
1386     return SUCCESS;
1387 }
1388
1389 static enum test_result test_takeover_stats_race_with_vb_create_DCP(
1390                                     ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1391     check(set_vbucket_state(h, h1, 1, vbucket_state_active),
1392           "Failed to set vbucket state information");
1393
1394     checkeq(0,
1395             get_int_stat(h, h1, "on_disk_deletes", "dcp-vbtakeover 1"),
1396             "Invalid number of on-disk deletes");
1397
1398     return SUCCESS;
1399 }
1400
1401 static enum test_result test_takeover_stats_num_persisted_deletes(
1402                                     ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1403     /* set an item */
1404     std::string key("key");
1405     checkeq(ENGINE_SUCCESS,
1406             store(h, h1, NULL, OPERATION_SET, key.c_str(), "data", nullptr),
1407             "Failed to store an item");
1408
1409     /* delete the item */
1410     checkeq(ENGINE_SUCCESS, del(h, h1, key.c_str(), 0, 0),
1411             "Failed to delete the item");
1412
1413     /* wait for persistence */
1414     wait_for_flusher_to_settle(h, h1);
1415
1416     /* check if persisted deletes stats is got correctly */
1417     checkeq(1,
1418             get_int_stat(h, h1, "on_disk_deletes", "dcp-vbtakeover 0"),
1419             "Invalid number of on-disk deletes");
1420
1421     return SUCCESS;
1422 }
1423
1424 static enum test_result test_vbucket_compact(ENGINE_HANDLE *h,
1425                                              ENGINE_HANDLE_V1 *h1) {
1426     const char *key = "Carss";
1427     const char *value = "pollute";
1428     if (!verify_vbucket_missing(h, h1, 0)) {
1429         fprintf(stderr, "vbucket wasn't missing.\n");
1430         return FAIL;
1431     }
1432
1433     if (!set_vbucket_state(h, h1, 0, vbucket_state_active)) {
1434         fprintf(stderr, "set state failed.\n");
1435         return FAIL;
1436     }
1437
1438     check(verify_vbucket_state(h, h1, 0, vbucket_state_active),
1439             "VBucket state not active");
1440
1441     // Set two keys - one to be expired and other to remain...
1442     item *itm = NULL;
1443     checkeq(ENGINE_SUCCESS,
1444             store(h, h1, NULL, OPERATION_SET, key, value, &itm),
1445             "Failed set.");
1446     h1->release(h, NULL, itm);
1447
1448     check_key_value(h, h1, key, value, strlen(value));
1449
1450     // Set a non-expiring key...
1451     checkeq(ENGINE_SUCCESS,
1452             store(h, h1, NULL, OPERATION_SET, "trees", "cleanse", &itm),
1453             "Failed set.");
1454     h1->release(h, NULL, itm);
1455
1456     check_key_value(h, h1, "trees", "cleanse", strlen("cleanse"));
1457
1458     checkeq(ENGINE_SUCCESS, touch(h, h1, key, 0, 11), "touch Carss");
1459
1460     testHarness.time_travel(12);
1461     wait_for_flusher_to_settle(h, h1);
1462
1463     // Store a dummy item since we do not purge the item with highest seqno
1464     checkeq(ENGINE_SUCCESS,
1465             store(h, h1, NULL, OPERATION_SET, "dummykey", "dummyvalue", &itm,
1466                   0, 0, 0),
1467             "Error setting.");
1468     h1->release(h, NULL, itm);
1469
1470     wait_for_flusher_to_settle(h, h1);
1471
1472     checkeq(0, get_int_stat(h, h1, "vb_0:purge_seqno", "vbucket-seqno"),
1473             "purge_seqno not found to be zero before compaction");
1474
1475     // Compaction on VBucket
1476     compact_db(h, h1, 0, 0, 2, 3, 1);
1477
1478     wait_for_stat_to_be(h, h1, "ep_pending_compactions", 0);
1479
1480     // the key tree and its value should be intact...
1481     checkeq(ENGINE_SUCCESS, verify_key(h, h1, "trees"),
1482             "key trees should be found.");
1483     // the key Carrs should have disappeared...
1484     ENGINE_ERROR_CODE val = verify_key(h, h1, "Carss");
1485     checkeq(ENGINE_KEY_ENOENT, val, "Key Carss has not expired.");
1486
1487     checkeq(4, get_int_stat(h, h1, "vb_0:purge_seqno", "vbucket-seqno"),
1488             "purge_seqno didn't match expected value");
1489
1490     return SUCCESS;
1491 }
1492
1493 static enum test_result test_compaction_config(ENGINE_HANDLE *h,
1494                                                ENGINE_HANDLE_V1 *h1) {
1495
1496     checkeq(10000,
1497             get_int_stat(h, h1, "ep_compaction_write_queue_cap"),
1498             "Expected compaction queue cap to be 10000");
1499     set_param(h, h1, protocol_binary_engine_param_flush,
1500               "compaction_write_queue_cap", "100000");
1501     checkeq(100000, get_int_stat(h, h1, "ep_compaction_write_queue_cap"),
1502             "Expected compaction queue cap to be 100000");
1503     return SUCCESS;
1504 }
1505
1506 struct comp_thread_ctx {
1507     ENGINE_HANDLE *h;
1508     ENGINE_HANDLE_V1 *h1;
1509     uint16_t vbid;
1510     uint16_t db_file_id;
1511 };
1512
1513 extern "C" {
1514     static void compaction_thread(void *arg) {
1515         struct comp_thread_ctx *ctx = static_cast<comp_thread_ctx *>(arg);
1516         compact_db(ctx->h, ctx->h1, ctx->vbid, ctx->db_file_id, 0, 0, 0);
1517     }
1518 }
1519
1520 static enum test_result test_multiple_vb_compactions(ENGINE_HANDLE *h,
1521                                                      ENGINE_HANDLE_V1 *h1) {
1522     for (uint16_t i = 0; i < 4; ++i) {
1523         if (!set_vbucket_state(h, h1, i, vbucket_state_active)) {
1524             fprintf(stderr, "set state failed for vbucket %d.\n", i);
1525             return FAIL;
1526         }
1527         check(verify_vbucket_state(h, h1, i, vbucket_state_active),
1528               "VBucket state not active");
1529     }
1530
1531     std::vector<std::string> keys;
1532     for (int j = 0; j < 20000; ++j) {
1533         std::stringstream ss;
1534         ss << "key" << j;
1535         std::string key(ss.str());
1536         keys.push_back(key);
1537     }
1538
1539     int count = 0;
1540     std::vector<std::string>::iterator it;
1541     for (it = keys.begin(); it != keys.end(); ++it) {
1542         uint16_t vbid = count % 4;
1543         item *i;
1544         checkeq(ENGINE_SUCCESS,
1545                 store(h, h1, NULL, OPERATION_SET, it->c_str(), it->c_str(), &i, 0, vbid),
1546                 "Failed to store a value");
1547         h1->release(h, NULL, i);
1548         ++count;
1549     }
1550
1551     // Compact multiple vbuckets.
1552     const int n_threads = 4;
1553     cb_thread_t threads[n_threads];
1554     struct comp_thread_ctx ctx[n_threads];
1555
1556     const int num_shards = get_int_stat(h, h1, "ep_workload:num_shards",
1557                                         "workload");
1558
1559     for (int i = 0; i < n_threads; i++) {
1560         ctx[i].h = h;
1561         ctx[i].h1 = h1;
1562         ctx[i].vbid = static_cast<uint16_t>(i);
1563         ctx[i].db_file_id = ctx[i].vbid % num_shards;
1564         int r = cb_create_thread(&threads[i], compaction_thread, &ctx[i], 0);
1565         cb_assert(r == 0);
1566     }
1567
1568     for (int i = 0; i < n_threads; i++) {
1569         int r = cb_join_thread(threads[i]);
1570         cb_assert(r == 0);
1571     }
1572
1573     wait_for_stat_to_be(h, h1, "ep_pending_compactions", 0);
1574
1575     return SUCCESS;
1576 }
1577
1578 static enum test_result
1579 test_multi_vb_compactions_with_workload(ENGINE_HANDLE *h,
1580                                         ENGINE_HANDLE_V1 *h1) {
1581     for (uint16_t i = 0; i < 4; ++i) {
1582         if (!set_vbucket_state(h, h1, i, vbucket_state_active)) {
1583             fprintf(stderr, "set state failed for vbucket %d.\n", i);
1584             return FAIL;
1585         }
1586         check(verify_vbucket_state(h, h1, i, vbucket_state_active),
1587               "VBucket state not active");
1588     }
1589
1590     std::vector<std::string> keys;
1591     for (int j = 0; j < 10000; ++j) {
1592         std::stringstream ss;
1593         ss << "key" << j;
1594         std::string key(ss.str());
1595         keys.push_back(key);
1596     }
1597
1598     int count = 0;
1599     std::vector<std::string>::iterator it;
1600     for (it = keys.begin(); it != keys.end(); ++it) {
1601         uint16_t vbid = count % 4;
1602         item *i;
1603         checkeq(ENGINE_SUCCESS,
1604                 store(h, h1, NULL, OPERATION_SET, it->c_str(), it->c_str(),
1605                       &i, 0, vbid),
1606                 "Failed to store a value");
1607         h1->release(h, NULL, i);
1608         ++count;
1609     }
1610     wait_for_flusher_to_settle(h, h1);
1611
1612     for (int i = 0; i < 2; ++i) {
1613         count = 0;
1614         for (it = keys.begin(); it != keys.end(); ++it) {
1615             uint16_t vbid = count % 4;
1616             item *i = NULL;
1617             checkeq(ENGINE_SUCCESS,
1618                     get(h, h1, NULL, &i, it->c_str(), vbid),
1619                     "Unable to get stored item");
1620             h1->release(h, NULL, i);
1621             ++count;
1622         }
1623     }
1624     wait_for_stat_to_be(h, h1, "ep_workload_pattern", std::string{"read_heavy"});
1625
1626     // Compact multiple vbuckets.
1627     const int n_threads = 4;
1628     cb_thread_t threads[n_threads];
1629     struct comp_thread_ctx ctx[n_threads];
1630
1631     for (int i = 0; i < n_threads; i++) {
1632         ctx[i].h = h;
1633         ctx[i].h1 = h1;
1634         ctx[i].vbid = static_cast<uint16_t>(i);
1635         int r = cb_create_thread(&threads[i], compaction_thread, &ctx[i], 0);
1636         cb_assert(r == 0);
1637     }
1638
1639     for (int i = 0; i < n_threads; i++) {
1640         int r = cb_join_thread(threads[i]);
1641         cb_assert(r == 0);
1642     }
1643
1644     wait_for_stat_to_be(h, h1, "ep_pending_compactions", 0);
1645
1646     return SUCCESS;
1647 }
1648
1649 static enum test_result vbucket_destroy(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1,
1650                                              const char* value = NULL) {
1651     check(set_vbucket_state(h, h1, 1, vbucket_state_active),
1652           "Failed to set vbucket state.");
1653
1654     vbucketDelete(h, h1, 2, value);
1655     checkeq(PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET,
1656             last_status.load(),
1657             "Expected failure deleting non-existent bucket.");
1658
1659     check(set_vbucket_state(h, h1, 1, vbucket_state_dead),
1660           "Failed set set vbucket 1 state.");
1661
1662     vbucketDelete(h, h1, 1, value);
1663     checkeq(PROTOCOL_BINARY_RESPONSE_SUCCESS, last_status.load(),
1664             "Expected failure deleting non-existent bucket.");
1665
1666     check(verify_vbucket_missing(h, h1, 1),
1667           "vbucket 0 was not missing after deleting it.");
1668
1669     return SUCCESS;
1670 }
1671
1672 static enum test_result test_vbucket_destroy_stats(ENGINE_HANDLE *h,
1673                                                    ENGINE_HANDLE_V1 *h1) {
1674
1675     int cacheSize = get_int_stat(h, h1, "ep_total_cache_size");
1676     int overhead = get_int_stat(h, h1, "ep_overhead");
1677     int nonResident = get_int_stat(h, h1, "ep_num_non_resident");
1678
1679     check(set_vbucket_state(h, h1, 1, vbucket_state_active),
1680           "Failed to set vbucket state.");
1681
1682     std::vector<std::string> keys;
1683     for (int j = 0; j < 2000; ++j) {
1684         std::stringstream ss;
1685         ss << "key" << j;
1686         std::string key(ss.str());
1687         keys.push_back(key);
1688     }
1689
1690     int itemsRemoved = get_int_stat(h, h1, "ep_items_rm_from_checkpoints");
1691     std::vector<std::string>::iterator it;
1692     for (it = keys.begin(); it != keys.end(); ++it) {
1693         item *i;
1694         checkeq(ENGINE_SUCCESS,
1695                 store(h, h1, NULL, OPERATION_SET, it->c_str(), it->c_str(),
1696                       &i, 0, 1),
1697                 "Failed to store a value");
1698         h1->release(h, NULL, i);
1699     }
1700     wait_for_flusher_to_settle(h, h1);
1701     testHarness.time_travel(65);
1702     wait_for_stat_change(h, h1, "ep_items_rm_from_checkpoints", itemsRemoved);
1703
1704     check(set_vbucket_state(h, h1, 1, vbucket_state_dead),
1705           "Failed set set vbucket 1 state.");
1706
1707     int vbucketDel = get_int_stat(h, h1, "ep_vbucket_del");
1708     vbucketDelete(h, h1, 1);
1709     checkeq(PROTOCOL_BINARY_RESPONSE_SUCCESS,
1710             last_status.load(),
1711             "Expected failure deleting non-existent bucket.");
1712
1713     check(verify_vbucket_missing(h, h1, 1),
1714           "vbucket 1 was not missing after deleting it.");
1715
1716     wait_for_stat_change(h, h1, "ep_vbucket_del", vbucketDel);
1717
1718     wait_for_stat_to_be(h, h1, "ep_total_cache_size", cacheSize);
1719     wait_for_stat_to_be(h, h1, "ep_overhead", overhead);
1720     wait_for_stat_to_be(h, h1, "ep_num_non_resident", nonResident);
1721
1722     return SUCCESS;
1723 }
1724
1725 static enum test_result vbucket_destroy_restart(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1,
1726                                                 const char* value = NULL) {
1727     if (!isWarmupEnabled(h, h1)) {
1728         return SKIPPED;
1729     }
1730
1731     check(set_vbucket_state(h, h1, 1, vbucket_state_active),
1732           "Failed to set vbucket state.");
1733
1734     // Store a value so the restart will try to resurrect it.
1735     item *i = NULL;
1736     checkeq(ENGINE_SUCCESS,
1737             store(h, h1, NULL, OPERATION_SET, "key", "somevalue", &i, 0, 1),
1738             "Failed to set a value");
1739     check_key_value(h, h1, "key", "somevalue", 9, 1);
1740     h1->release(h, NULL, i);
1741
1742     // Reload to get a flush forced.
1743     testHarness.reload_engine(&h, &h1,
1744                               testHarness.engine_path,
1745                               testHarness.get_current_testcase()->cfg,
1746                               true, false);
1747     wait_for_warmup_complete(h, h1);
1748
1749     check(verify_vbucket_state(h, h1, 1, vbucket_state_active),
1750           "Bucket state was what it was initially, after restart.");
1751     check(set_vbucket_state(h, h1, 1, vbucket_state_active),
1752           "Failed to set vbucket state.");
1753     check_key_value(h, h1, "key", "somevalue", 9, 1);
1754
1755     check(set_vbucket_state(h, h1, 1, vbucket_state_dead),
1756           "Failed set set vbucket 1 state.");
1757
1758     vbucketDelete(h, h1, 1, value);
1759     checkeq(PROTOCOL_BINARY_RESPONSE_SUCCESS, last_status.load(),
1760             "Expected failure deleting non-existent bucket.");
1761
1762     check(verify_vbucket_missing(h, h1, 1),
1763           "vbucket 1 was not missing after deleting it.");
1764
1765     testHarness.reload_engine(&h, &h1,
1766                               testHarness.engine_path,
1767                               testHarness.get_current_testcase()->cfg,
1768                               true, false);
1769     wait_for_warmup_complete(h, h1);
1770
1771     if (verify_vbucket_state(h, h1, 1, vbucket_state_pending, true)) {
1772         std::cerr << "Bucket came up in pending state after delete." << std::endl;
1773         abort();
1774     }
1775
1776     check(verify_vbucket_missing(h, h1, 1),
1777           "vbucket 1 was not missing after restart.");
1778
1779     return SUCCESS;
1780 }
1781
1782 static enum test_result test_async_vbucket_destroy(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1783     return vbucket_destroy(h, h1);
1784 }
1785
1786 static enum test_result test_sync_vbucket_destroy(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1787     return vbucket_destroy(h, h1, "async=0");
1788 }
1789
1790 static enum test_result test_async_vbucket_destroy_restart(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1791     return vbucket_destroy_restart(h, h1);
1792 }
1793
1794 static enum test_result test_sync_vbucket_destroy_restart(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1795     return vbucket_destroy_restart(h, h1, "async=0");
1796 }
1797
1798 static enum test_result test_vb_set_pending(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1799     return test_pending_vb_mutation(h, h1, OPERATION_SET);
1800 }
1801
1802 static enum test_result test_vb_add_pending(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1803     return test_pending_vb_mutation(h, h1, OPERATION_ADD);
1804 }
1805
1806 static enum test_result test_vb_cas_pending(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1807     return test_pending_vb_mutation(h, h1, OPERATION_CAS);
1808 }
1809
1810 static enum test_result test_vb_set_replica(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1811     return test_replica_vb_mutation(h, h1, OPERATION_SET);
1812 }
1813
1814 static enum test_result test_vb_replace_replica(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1815     return test_replica_vb_mutation(h, h1, OPERATION_REPLACE);
1816 }
1817
1818 static enum test_result test_vb_replace_pending(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1819     return test_pending_vb_mutation(h, h1, OPERATION_REPLACE);
1820 }
1821
1822 static enum test_result test_vb_add_replica(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1823     return test_replica_vb_mutation(h, h1, OPERATION_ADD);
1824 }
1825
1826 static enum test_result test_vb_cas_replica(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1827     return test_replica_vb_mutation(h, h1, OPERATION_CAS);
1828 }
1829
1830 static enum test_result test_stats_seqno(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1831     check(set_vbucket_state(h, h1, 1, vbucket_state_active),
1832           "Failed to set vbucket state.");
1833
1834     int num_keys = 100;
1835     for (int ii = 0; ii < num_keys; ++ii) {
1836         std::stringstream ss;
1837         ss << "key" << ii;
1838         checkeq(ENGINE_SUCCESS,
1839                 store(h, h1, NULL, OPERATION_SET, ss.str().c_str(),
1840                       "value", NULL, 0, 0),
1841                 "Failed to store an item.");
1842     }
1843     wait_for_flusher_to_settle(h, h1);
1844
1845     checkeq(100, get_int_stat(h, h1, "vb_0:high_seqno", "vbucket-seqno"),
1846             "Invalid seqno");
1847
1848     if (isPersistentBucket(h, h1)) {
1849         checkeq(100,
1850                 get_int_stat(h, h1, "vb_0:last_persisted_seqno", "vbucket-seqno"),
1851                 "Unexpected last_persisted_seqno");
1852     }
1853     checkeq(0, get_int_stat(h, h1, "vb_1:high_seqno", "vbucket-seqno"),
1854             "Invalid seqno");
1855     checkeq(0, get_int_stat(h, h1, "vb_1:high_seqno", "vbucket-seqno 1"),
1856             "Invalid seqno");
1857     if (isPersistentBucket(h, h1)) {
1858         checkeq(0,
1859                 get_int_stat(h, h1, "vb_1:last_persisted_seqno", "vbucket-seqno 1"),
1860                 "Invalid last_persisted_seqno");
1861     }
1862
1863     uint64_t vb_uuid = get_ull_stat(h, h1, "vb_1:0:id", "failovers");
1864
1865     auto seqno_stats = get_all_stats(h, h1, "vbucket-seqno 1");
1866     checkeq(vb_uuid, uint64_t(std::stoull(seqno_stats.at("vb_1:uuid"))),
1867             "Invalid uuid");
1868
1869     checkeq(size_t(7), seqno_stats.size(), "Expected seven stats");
1870
1871     // Check invalid vbucket
1872     checkeq(ENGINE_NOT_MY_VBUCKET,
1873             h1->get_stats(h, NULL, "vbucket-seqno 2", 15, add_stats),
1874             "Expected not my vbucket");
1875
1876     // Check bad vbucket parameter (not numeric)
1877     checkeq(ENGINE_EINVAL,
1878             h1->get_stats(h, NULL, "vbucket-seqno tt2", 17, add_stats),
1879             "Expected invalid");
1880
1881     // Check extra spaces at the end
1882     checkeq(ENGINE_EINVAL,
1883             h1->get_stats(h, NULL, "vbucket-seqno    ", 17, add_stats),
1884             "Expected invalid");
1885
1886     return SUCCESS;
1887 }
1888
1889 static enum test_result test_stats_diskinfo(ENGINE_HANDLE *h,
1890                                             ENGINE_HANDLE_V1 *h1) {
1891     check(set_vbucket_state(h, h1, 1, vbucket_state_active),
1892           "Failed to set vbucket state.");
1893
1894     int num_keys = 100;
1895     for (int ii = 0; ii < num_keys; ++ii) {
1896         std::stringstream ss;
1897         ss << "key" << ii;
1898         checkeq(ENGINE_SUCCESS,
1899                 store(h, h1, NULL, OPERATION_SET, ss.str().c_str(),
1900                       "value", NULL, 0, 1),
1901                 "Failed to store an item.");
1902     }
1903     wait_for_flusher_to_settle(h, h1);
1904
1905     size_t file_size = get_int_stat(h, h1, "ep_db_file_size", "diskinfo");
1906     size_t data_size = get_int_stat(h, h1, "ep_db_data_size", "diskinfo");
1907     check(file_size > 0, "DB file size should be greater than 0");
1908     check(data_size > 0, "DB data size should be greater than 0");
1909     check(file_size >= data_size, "DB file size should be >= DB data size");
1910     check(get_int_stat(h, h1, "vb_1:data_size", "diskinfo detail") > 0,
1911           "VB 1 data size should be greater than 0");
1912
1913     checkeq(ENGINE_EINVAL,
1914             h1->get_stats(h, NULL, "diskinfo ", 9, add_stats),
1915             "Expected invalid");
1916
1917     checkeq(ENGINE_EINVAL,
1918             h1->get_stats(h, NULL, "diskinfo detai", 14, add_stats),
1919             "Expected invalid");
1920
1921     checkeq(ENGINE_EINVAL,
1922             h1->get_stats(h, NULL, "diskinfo detaillll", 18, add_stats),
1923             "Expected invalid");
1924
1925     return SUCCESS;
1926 }
1927
1928 static enum test_result test_uuid_stats(ENGINE_HANDLE *h,
1929                                         ENGINE_HANDLE_V1 *h1)
1930 {
1931     vals.clear();
1932     checkeq(ENGINE_SUCCESS,
1933             h1->get_stats(h, NULL, "uuid", 4, add_stats),
1934             "Failed to get stats.");
1935     check(vals["uuid"] == "foobar", "Incorrect uuid");
1936     return SUCCESS;
1937 }
1938
1939 static enum test_result test_item_stats(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1940     item *i = NULL;
1941     checkeq(ENGINE_SUCCESS,
1942             store(h, h1, NULL, OPERATION_SET, "key", "somevalue", &i, 0, 0),
1943             "Failed set.");
1944     h1->release(h, NULL, i);
1945     wait_for_flusher_to_settle(h, h1);
1946     checkeq(ENGINE_SUCCESS,
1947             store(h, h1, NULL, OPERATION_SET, "key", "somevalueX", &i, 0, 0),
1948             "Failed set.");
1949     h1->release(h, NULL, i);
1950     wait_for_flusher_to_settle(h, h1);
1951     checkeq(ENGINE_SUCCESS,
1952             store(h, h1, NULL, OPERATION_SET, "key1", "somevalueY", &i, 0, 0),
1953             "Failed set.");
1954     h1->release(h, NULL, i);
1955     wait_for_flusher_to_settle(h, h1);
1956
1957     check_key_value(h, h1, "key", "somevalueX", 10);
1958     check_key_value(h, h1, "key1", "somevalueY", 10);
1959
1960     checkeq(ENGINE_SUCCESS, del(h, h1, "key1", 0, 0),
1961             "Failed remove with value.");
1962     wait_for_flusher_to_settle(h, h1);
1963
1964     checkeq(ENGINE_SUCCESS,
1965             store(h, h1, NULL, OPERATION_SET, "key1", "someothervalue", &i, 0, 0),
1966             "Failed set.");
1967     h1->release(h, NULL, i);
1968     wait_for_flusher_to_settle(h, h1);
1969
1970     check_key_value(h, h1, "key1", "someothervalue", 14);
1971
1972     checkeq(3,
1973             get_int_stat(h, h1, "vb_active_ops_create"),
1974             "Expected 3 creations");
1975     checkeq(1,
1976             get_int_stat(h, h1, "vb_active_ops_update"),
1977             "Expected 1 updation");
1978     checkeq(1,
1979             get_int_stat(h, h1, "vb_active_ops_delete"),
1980             "Expected 1 deletion");
1981
1982     return SUCCESS;
1983 }
1984
1985 static enum test_result test_stats(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1986     vals.clear();
1987     checkeq(ENGINE_SUCCESS,
1988             h1->get_stats(h, NULL, NULL, 0, add_stats),
1989             "Failed to get stats.");
1990     check(vals.size() > 10, "Kind of expected more stats than that.");
1991     check(vals.find("ep_version") != vals.end(), "Found no ep_version.");
1992
1993     return SUCCESS;
1994 }
1995
1996 static enum test_result test_mem_stats(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1997     char value[2048];
1998     memset(value, 'b', sizeof(value));
1999     strcpy(value + sizeof(value) - 4, "\r\n");
2000     int itemsRemoved = get_int_stat(h, h1, "ep_items_rm_from_checkpoints");
2001     wait_for_persisted_value(h, h1, "key", value);
2002     testHarness.time_travel(65);
2003     if (isPersistentBucket(h, h1)) {
2004         wait_for_stat_change(h, h1, "ep_items_rm_from_checkpoints", itemsRemoved);
2005     }
2006     int mem_used = get_int_stat(h, h1, "mem_used");
2007     int cache_size = get_int_stat(h, h1, "ep_total_cache_size");
2008     int overhead = get_int_stat(h, h1, "ep_overhead");
2009     int value_size = get_int_stat(h, h1, "ep_value_size");
2010     check((mem_used - overhead) > cache_size,
2011           "ep_kv_size should be greater than the hashtable cache size due to the checkpoint overhead");
2012
2013     if (isPersistentBucket(h, h1)) {
2014         evict_key(h, h1, "key", 0, "Ejected.");
2015
2016         check(get_int_stat(h, h1, "ep_total_cache_size") <= cache_size,
2017               "Evict a value shouldn't increase the total cache size");
2018         check(get_int_stat(h, h1, "mem_used") < mem_used,
2019               "Expected mem_used to decrease when an item is evicted");
2020
2021         check_key_value(h, h1, "key", value, strlen(value), 0); // Load an item from disk again.
2022
2023         check(get_int_stat(h, h1, "mem_used") >= mem_used,
2024               "Expected mem_used to remain the same after an item is loaded from disk");
2025         check(get_int_stat(h, h1, "ep_value_size") == value_size,
2026               "Expected ep_value_size to remain the same after item is loaded from disk");
2027     }
2028
2029     return SUCCESS;
2030 }
2031
2032 static enum test_result test_io_stats(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
2033     int exp_read_bytes = 4, exp_write_bytes;
2034     std::string backend = get_str_stat(h, h1, "ep_backend");
2035     if (backend == "forestdb") {
2036         exp_write_bytes = 35; /* TBD: Do not hard code the value */
2037     } else if (backend == "couchdb") {
2038         exp_write_bytes = 22; /* TBD: Do not hard code the value */
2039     } else {
2040         return SKIPPED;
2041     }
2042
2043     h1->reset_stats(h, NULL);
2044
2045     checkeq(0, get_int_stat(h, h1, "rw_0:io_num_read", "kvstore"),
2046             "Expected reset stats to set io_num_read to zero");
2047     checkeq(0, get_int_stat(h, h1, "rw_0:io_num_write", "kvstore"),
2048             "Expected reset stats to set io_num_write to zero");
2049     checkeq(0, get_int_stat(h, h1, "rw_0:io_read_bytes", "kvstore"),
2050             "Expected reset stats to set io_read_bytes to zero");
2051     checkeq(0, get_int_stat(h, h1, "rw_0:io_write_bytes", "kvstore"),
2052             "Expected reset stats to set io_write_bytes to zero");
2053
2054     wait_for_persisted_value(h, h1, "a", "b\r\n");
2055     checkeq(0, get_int_stat(h, h1, "rw_0:io_num_read", "kvstore"),
2056             "Expected storing one value to not change the read counter");
2057     checkeq(0, get_int_stat(h, h1, "rw_0:io_read_bytes", "kvstore"),
2058             "Expected storing one value to not change the read bytes");
2059     checkeq(1, get_int_stat(h, h1, "rw_0:io_num_write", "kvstore"),
2060             "Expected storing the key to update the write counter");
2061     checkeq(exp_write_bytes,
2062             get_int_stat(h, h1, "rw_0:io_write_bytes", "kvstore"),
2063             "Expected storing the key to update the write bytes");
2064
2065     evict_key(h, h1, "a", 0, "Ejected.");
2066
2067     check_key_value(h, h1, "a", "b\r\n", 3, 0);
2068
2069     std::stringstream numReadStatStr;
2070     std::stringstream readBytesStatStr;
2071
2072     if (backend == "couchdb") {
2073         numReadStatStr << "ro_" << 0 << ":io_num_read";
2074         readBytesStatStr << "ro_" << 0 << ":io_read_bytes";
2075     } else if (backend == "forestdb") {
2076         numReadStatStr << "rw_" << 0 << ":io_num_read";
2077         readBytesStatStr << "rw_" << 0 << ":io_read_bytes";
2078     } else {
2079         cb_assert(false);
2080     }
2081
2082     checkeq(1, get_int_stat(h, h1, numReadStatStr.str().c_str(), "kvstore"),
2083             "Expected reading the value back in to update the read counter");
2084     checkeq(exp_read_bytes,
2085             get_int_stat(h, h1, readBytesStatStr.str().c_str(), "kvstore"),
2086             "Expected reading the value back in to update the read bytes");
2087     checkeq(1, get_int_stat(h, h1, "rw_0:io_num_write", "kvstore"),
2088             "Expected reading the value back in to not update the write counter");
2089     checkeq(exp_write_bytes,
2090             get_int_stat(h, h1, "rw_0:io_write_bytes", "kvstore"),
2091             "Expected reading the value back in to not update the write bytes");
2092
2093     return SUCCESS;
2094 }
2095
2096 static enum test_result test_vb_file_stats(ENGINE_HANDLE *h,
2097                                         ENGINE_HANDLE_V1 *h1) {
2098     wait_for_flusher_to_settle(h, h1);
2099     wait_for_stat_change(h, h1, "ep_db_data_size", 0);
2100
2101     int old_data_size = get_int_stat(h, h1, "ep_db_data_size");
2102     int old_file_size = get_int_stat(h, h1, "ep_db_file_size");
2103     check(old_file_size != 0, "Expected a non-zero value for ep_db_file_size");
2104
2105     // Write a value and test ...
2106     wait_for_persisted_value(h, h1, "a", "b\r\n");
2107     check(get_int_stat(h, h1, "ep_db_data_size") > old_data_size,
2108           "Expected the DB data size to increase");
2109     check(get_int_stat(h, h1, "ep_db_file_size") > old_file_size,
2110           "Expected the DB file size to increase");
2111
2112     check(get_int_stat(h, h1, "vb_0:db_data_size", "vbucket-details 0") > 0,
2113           "Expected the vbucket DB data size to non-zero");
2114     check(get_int_stat(h, h1, "vb_0:db_file_size", "vbucket-details 0") > 0,
2115           "Expected the vbucket DB file size to non-zero");
2116     return SUCCESS;
2117 }
2118
2119 static enum test_result test_vb_file_stats_after_warmup(ENGINE_HANDLE *h,
2120                                                         ENGINE_HANDLE_V1 *h1) {
2121     if (!isWarmupEnabled(h, h1)) {
2122         return SKIPPED;
2123     }
2124
2125     item *it = NULL;
2126     for (int i = 0; i < 100; ++i) {
2127         std::stringstream key;
2128         key << "key-" << i;
2129         checkeq(ENGINE_SUCCESS,
2130                 store(h, h1, NULL, OPERATION_SET, key.str().c_str(), "somevalue", &it),
2131                 "Error setting.");
2132         h1->release(h, NULL, it);
2133     }
2134     wait_for_flusher_to_settle(h, h1);
2135
2136     int fileSize = get_int_stat(h, h1, "vb_0:db_file_size", "vbucket-details 0");
2137     int spaceUsed = get_int_stat(h, h1, "vb_0:db_data_size", "vbucket-details 0");
2138
2139     // Restart the engine.
2140     testHarness.reload_engine(&h, &h1,
2141                               testHarness.engine_path,
2142                               testHarness.get_current_testcase()->cfg,
2143                               true, false);
2144     wait_for_warmup_complete(h, h1);
2145
2146     int newFileSize = get_int_stat(h, h1, "vb_0:db_file_size", "vbucket-details 0");
2147     int newSpaceUsed = get_int_stat(h, h1, "vb_0:db_data_size", "vbucket-details 0");
2148
2149     check((float)newFileSize >= 0.9 * fileSize, "Unexpected fileSize for vbucket");
2150     check((float)newSpaceUsed >= 0.9 * spaceUsed, "Unexpected spaceUsed for vbucket");
2151
2152     return SUCCESS;
2153 }
2154
2155 static enum test_result test_bg_stats(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
2156     h1->reset_stats(h, NULL);
2157     wait_for_persisted_value(h, h1, "a", "b\r\n");
2158     evict_key(h, h1, "a", 0, "Ejected.");
2159     testHarness.time_travel(43);
2160     check_key_value(h, h1, "a", "b\r\n", 3, 0);
2161
2162     auto stats = get_all_stats(h, h1);
2163     checkeq(1, std::stoi(stats.at("ep_bg_num_samples")),
2164                "Expected one sample");
2165
2166     const char* bg_keys[] = { "ep_bg_min_wait",
2167                               "ep_bg_max_wait",
2168                               "ep_bg_wait_avg",
2169                               "ep_bg_min_load",
2170                               "ep_bg_max_load",
2171                               "ep_bg_load_avg"};
2172     for (const auto* key : bg_keys) {
2173         check(stats.find(key) != stats.end(),
2174               (std::string("Found no ") + key).c_str());
2175     }
2176
2177     evict_key(h, h1, "a", 0, "Ejected.");
2178     check_key_value(h, h1, "a", "b\r\n", 3, 0);
2179     check(get_int_stat(h, h1, "ep_bg_num_samples") == 2,
2180           "Expected one sample");
2181
2182     h1->reset_stats(h, NULL);
2183     checkeq(0, get_int_stat(h, h1, "ep_bg_fetched"),
2184             "ep_bg_fetched is not reset to 0");
2185     return SUCCESS;
2186 }
2187
2188 static enum test_result test_bg_meta_stats(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
2189     item *itm = NULL;
2190     h1->reset_stats(h, NULL);
2191
2192     wait_for_persisted_value(h, h1, "k1", "v1");
2193     wait_for_persisted_value(h, h1, "k2", "v2");
2194
2195     evict_key(h, h1, "k1", 0, "Ejected.");
2196     checkeq(ENGINE_SUCCESS,
2197             del(h, h1, "k2", 0, 0), "Failed remove with value.");
2198     wait_for_stat_to_be(h, h1, "curr_items", 1);
2199
2200     checkeq(0, get_int_stat(h, h1, "ep_bg_fetched"), "Expected bg_fetched to be 0");
2201     checkeq(0, get_int_stat(h, h1, "ep_bg_meta_fetched"), "Expected bg_meta_fetched to be 0");
2202
2203     check(get_meta(h, h1, "k2"), "Get meta failed");
2204     checkeq(0, get_int_stat(h, h1, "ep_bg_fetched"), "Expected bg_fetched to be 0");
2205     checkeq(1, get_int_stat(h, h1, "ep_bg_meta_fetched"), "Expected bg_meta_fetched to be 1");
2206
2207     checkeq(ENGINE_SUCCESS, get(h, h1, NULL, &itm, "k1", 0), "Missing key");
2208     checkeq(1, get_int_stat(h, h1, "ep_bg_fetched"), "Expected bg_fetched to be 1");
2209     checkeq(1, get_int_stat(h, h1, "ep_bg_meta_fetched"), "Expected bg_meta_fetched to be 1");
2210     h1->release(h, NULL, itm);
2211
2212     // store new key with some random metadata
2213     const size_t keylen = strlen("k3");
2214     ItemMetaData itemMeta;
2215     itemMeta.revSeqno = 10;
2216     itemMeta.cas = 0xdeadbeef;
2217     itemMeta.exptime = 0;
2218     itemMeta.flags = 0xdeadbeef;
2219
2220     add_with_meta(h, h1, "k3", keylen, NULL, 0, 0, &itemMeta);
2221     checkeq(PROTOCOL_BINARY_RESPONSE_SUCCESS, last_status.load(), "Set meta failed");
2222
2223     check(get_meta(h, h1, "k2"), "Get meta failed");
2224     checkeq(1, get_int_stat(h, h1, "ep_bg_fetched"), "Expected bg_fetched to be 1");
2225     checkeq(1, get_int_stat(h, h1, "ep_bg_meta_fetched"),
2226             "Expected bg_meta_fetched to remain at 1");
2227
2228     return SUCCESS;
2229 }
2230
2231 static enum test_result test_key_stats(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
2232     item *i = NULL;
2233
2234     check(set_vbucket_state(h, h1, 1, vbucket_state_active), "Failed set vbucket 1 state.");
2235
2236     // set (k1,v1) in vbucket 0
2237     checkeq(ENGINE_SUCCESS,
2238             store(h, h1, NULL, OPERATION_SET,"k1", "v1", &i, 0, 0),
2239             "Failed to store an item.");
2240     h1->release(h, NULL, i);
2241     // set (k2,v2) in vbucket 1
2242     checkeq(ENGINE_SUCCESS,
2243             store(h, h1, NULL, OPERATION_SET,"k2", "v2", &i, 0, 1),
2244             "Failed to store an item.");
2245     h1->release(h, NULL, i);
2246
2247     const void *cookie = testHarness.create_cookie();
2248
2249     // stat for key "k1" and vbucket "0"
2250     const char *statkey1 = "key k1 0";
2251     checkeq(ENGINE_SUCCESS,
2252             h1->get_stats(h, cookie, statkey1, strlen(statkey1), add_stats),
2253             "Failed to get stats.");
2254     check(vals.find("key_is_dirty") != vals.end(), "Found no key_is_dirty");
2255     check(vals.find("key_exptime") != vals.end(), "Found no key_exptime");
2256     check(vals.find("key_flags") != vals.end(), "Found no key_flags");
2257     check(vals.find("key_cas") != vals.end(), "Found no key_cas");
2258     check(vals.find("key_vb_state") != vals.end(), "Found no key_vb_state");
2259
2260     // stat for key "k2" and vbucket "1"
2261     const char *statkey2 = "key k2 1";
2262     checkeq(ENGINE_SUCCESS,
2263             h1->get_stats(h, cookie, statkey2, strlen(statkey2), add_stats),
2264             "Failed to get stats.");
2265     check(vals.find("key_is_dirty") != vals.end(), "Found no key_is_dirty");
2266     check(vals.find("key_exptime") != vals.end(), "Found no key_exptime");
2267     check(vals.find("key_flags") != vals.end(), "Found no key_flags");
2268     check(vals.find("key_cas") != vals.end(), "Found no key_cas");
2269     check(vals.find("key_vb_state") != vals.end(), "Found no key_vb_state");
2270
2271     testHarness.destroy_cookie(cookie);
2272     return SUCCESS;
2273 }
2274
2275 static enum test_result test_vkey_stats(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
2276     check(set_vbucket_state(h, h1, 1, vbucket_state_active), "Failed set vbucket 1 state.");
2277     check(set_vbucket_state(h, h1, 2, vbucket_state_active), "Failed set vbucket 2 state.");
2278     check(set_vbucket_state(h, h1, 3, vbucket_state_active), "Failed set vbucket 3 state.");
2279     check(set_vbucket_state(h, h1, 4, vbucket_state_active), "Failed set vbucket 4 state.");
2280
2281     wait_for_persisted_value(h, h1, "k1", "v1");
2282     wait_for_persisted_value(h, h1, "k2", "v2", 1);
2283     wait_for_persisted_value(h, h1, "k3", "v3", 2);
2284     wait_for_persisted_value(h, h1, "k4", "v4", 3);
2285     wait_for_persisted_value(h, h1, "k5", "v5", 4);
2286
2287     check(set_vbucket_state(h, h1, 2, vbucket_state_replica), "Failed to set VB2 state.");
2288     check(set_vbucket_state(h, h1, 3, vbucket_state_pending), "Failed to set VB3 state.");
2289     check(set_vbucket_state(h, h1, 4, vbucket_state_dead), "Failed to set VB4 state.");
2290
2291     const void *cookie = testHarness.create_cookie();
2292
2293     // stat for key "k1" and vbucket "0"
2294     const char *statkey1 = "vkey k1 0";
2295     checkeq(ENGINE_SUCCESS,
2296             h1->get_stats(h, cookie, statkey1, strlen(statkey1), add_stats),
2297             "Failed to get stats.");
2298     check(vals.find("key_is_dirty") != vals.end(), "Found no key_is_dirty");
2299     check(vals.find("key_exptime") != vals.end(), "Found no key_exptime");
2300     check(vals.find("key_flags") != vals.end(), "Found no key_flags");
2301     check(vals.find("key_cas") != vals.end(), "Found no key_cas");
2302     check(vals.find("key_vb_state") != vals.end(), "Found no key_vb_state");
2303     check(vals.find("key_valid") != vals.end(), "Found no key_valid");
2304
2305     // stat for key "k2" and vbucket "1"
2306     const char *statkey2 = "vkey k2 1";
2307     checkeq(ENGINE_SUCCESS,
2308             h1->get_stats(h, cookie, statkey2, strlen(statkey2), add_stats),
2309             "Failed to get stats.");
2310     check(vals.find("key_is_dirty") != vals.end(), "Found no key_is_dirty");
2311     check(vals.find("key_exptime") != vals.end(), "Found no key_exptime");
2312     check(vals.find("key_flags") != vals.end(), "Found no key_flags");
2313     check(vals.find("key_cas") != vals.end(), "Found no key_cas");
2314     check(vals.find("key_vb_state") != vals.end(), "Found no key_vb_state");
2315     check(vals.find("key_valid") != vals.end(), "Found no key_valid");
2316
2317     // stat for key "k3" and vbucket "2"
2318     const char *statkey3 = "vkey k3 2";
2319     checkeq(ENGINE_SUCCESS,
2320             h1->get_stats(h, cookie, statkey3, strlen(statkey3), add_stats),
2321             "Failed to get stats.");
2322     check(vals.find("key_is_dirty") != vals.end(), "Found no key_is_dirty");
2323     check(vals.find("key_exptime") != vals.end(), "Found no key_exptime");
2324     check(vals.find("key_flags") != vals.end(), "Found no key_flags");
2325     check(vals.find("key_cas") != vals.end(), "Found no key_cas");
2326     check(vals.find("key_vb_state") != vals.end(), "Found no key_vb_state");
2327     check(vals.find("key_valid") != vals.end(), "Found no key_valid");
2328
2329     // stat for key "k4" and vbucket "3"
2330     const char *statkey4 = "vkey k4 3";
2331     checkeq(ENGINE_SUCCESS,
2332             h1->get_stats(h, cookie, statkey4, strlen(statkey4), add_stats),
2333             "Failed to get stats.");
2334     check(vals.find("key_is_dirty") != vals.end(), "Found no key_is_dirty");
2335     check(vals.find("key_exptime") != vals.end(), "Found no key_exptime");
2336     check(vals.find("key_flags") != vals.end(), "Found no key_flags");
2337     check(vals.find("key_cas") != vals.end(), "Found no key_cas");
2338     check(vals.find("key_vb_state") != vals.end(), "Found no key_vb_state");
2339     check(vals.find("key_valid") != vals.end(), "Found no key_valid");
2340
2341     // stat for key "k5" and vbucket "4"
2342     const char *statkey5 = "vkey k5 4";
2343     checkeq(ENGINE_SUCCESS,
2344             h1->get_stats(h, cookie, statkey5, strlen(statkey5), add_stats),
2345             "Failed to get stats.");
2346     check(vals.find("key_is_dirty") != vals.end(), "Found no key_is_dirty");
2347     check(vals.find("key_exptime") != vals.end(), "Found no key_exptime");
2348     check(vals.find("key_flags") != vals.end(), "Found no key_flags");
2349     check(vals.find("key_cas") != vals.end(), "Found no key_cas");
2350     check(vals.find("key_vb_state") != vals.end(), "Found no key_vb_state");
2351     check(vals.find("key_valid") != vals.end(), "Found no key_valid");
2352
2353     testHarness.destroy_cookie(cookie);
2354     return SUCCESS;
2355 }
2356
2357 static enum test_result test_warmup_conf(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
2358     if (!isWarmupEnabled(h, h1)) {
2359         return SKIPPED;
2360     }
2361
2362     checkeq(100, get_int_stat(h, h1, "ep_warmup_min_items_threshold"),
2363             "Incorrect initial warmup min items threshold.");
2364     checkeq(100, get_int_stat(h, h1, "ep_warmup_min_memory_threshold"),
2365             "Incorrect initial warmup min memory threshold.");
2366
2367     check(!set_param(h, h1, protocol_binary_engine_param_flush,
2368                      "warmup_min_items_threshold", "a"),
2369           "Set warmup_min_items_threshold should have failed");
2370     check(!set_param(h, h1, protocol_binary_engine_param_flush,
2371                      "warmup_min_items_threshold", "a"),
2372           "Set warmup_min_memory_threshold should have failed");
2373
2374     check(set_param(h, h1, protocol_binary_engine_param_flush,
2375                     "warmup_min_items_threshold", "80"),
2376           "Set warmup_min_items_threshold should have worked");
2377     check(set_param(h, h1, protocol_binary_engine_param_flush,
2378                     "warmup_min_memory_threshold", "80"),
2379           "Set warmup_min_memory_threshold should have worked");
2380
2381     checkeq(80, get_int_stat(h, h1, "ep_warmup_min_items_threshold"),
2382             "Incorrect smaller warmup min items threshold.");
2383     checkeq(80, get_int_stat(h, h1, "ep_warmup_min_memory_threshold"),
2384             "Incorrect smaller warmup min memory threshold.");
2385
2386     item *it = NULL;
2387     for (int i = 0; i < 100; ++i) {
2388         std::stringstream key;
2389         key << "key-" << i;
2390         checkeq(ENGINE_SUCCESS,
2391                 store(h, h1, NULL, OPERATION_SET, key.str().c_str(), "somevalue", &it),
2392                 "Error setting.");
2393         h1->release(h, NULL, it);
2394     }
2395
2396     // Restart the server.
2397     std::string config(testHarness.get_current_testcase()->cfg);
2398     config = config + "warmup_min_memory_threshold=0";
2399     testHarness.reload_engine(&h, &h1,
2400                               testHarness.engine_path,
2401                               config.c_str(),
2402                               true, false);
2403     wait_for_warmup_complete(h, h1);
2404
2405     const std::string eviction_policy = get_str_stat(h, h1, "ep_item_eviction_policy");
2406     if (eviction_policy == "value_only") {
2407         checkeq(100, get_int_stat(h, h1, "ep_warmup_key_count", "warmup"),
2408                 "Expected 100 keys loaded after warmup");
2409     } else { // Full eviction mode
2410         checkeq(0, get_int_stat(h, h1, "ep_warmup_key_count", "warmup"),
2411                 "Expected 0 keys loaded after warmup");
2412     }
2413
2414     checkeq(0, get_int_stat(h, h1, "ep_warmup_value_count", "warmup"),
2415             "Expected 0 values loaded after warmup");
2416
2417     return SUCCESS;
2418 }
2419
2420 static enum test_result test_bloomfilter_conf(ENGINE_HANDLE *h,
2421                                               ENGINE_HANDLE_V1 *h1) {
2422
2423     if (get_bool_stat(h, h1, "ep_bfilter_enabled") == false) {
2424         check(set_param(h, h1, protocol_binary_engine_param_flush,
2425                         "bfilter_enabled", "true"),
2426               "Set bloomfilter_enabled should have worked");
2427     }
2428     check(get_bool_stat(h, h1, "ep_bfilter_enabled"),
2429           "Bloom filter wasn't enabled");
2430
2431     check(get_float_stat(h, h1, "ep_bfilter_residency_threshold") == (float)0.1,
2432           "Incorrect initial bfilter_residency_threshold.");
2433
2434     check(set_param(h, h1, protocol_binary_engine_param_flush,
2435           "bfilter_enabled", "false"),
2436           "Set bloomfilter_enabled should have worked.");
2437     check(set_param(h, h1, protocol_binary_engine_param_flush,
2438           "bfilter_residency_threshold", "0.15"),
2439           "Set bfilter_residency_threshold should have worked.");
2440
2441     check(get_bool_stat(h, h1, "ep_bfilter_enabled") == false,
2442           "Bloom filter should have been disabled.");
2443     check(get_float_stat(h, h1, "ep_bfilter_residency_threshold") == (float)0.15,
2444           "Incorrect bfilter_residency_threshold.");
2445
2446     return SUCCESS;
2447 }
2448
2449 static enum test_result test_bloomfilters(ENGINE_HANDLE *h,
2450                                           ENGINE_HANDLE_V1 *h1) {
2451
2452     if (get_bool_stat(h, h1, "ep_bfilter_enabled") == false) {
2453         check(set_param(h, h1, protocol_binary_engine_param_flush,
2454                     "bfilter_enabled", "true"),
2455                 "Set bloomfilter_enabled should have worked");
2456     }
2457     check(get_bool_stat(h, h1, "ep_bfilter_enabled"),
2458             "Bloom filter wasn't enabled");
2459
2460     // Key is only present if bgOperations is non-zero.
2461     int num_read_attempts = get_int_stat_or_default(h, h1, 0,
2462                                                     "ep_bg_num_samples");
2463
2464     // Ensure vbucket's bloom filter is enabled
2465     checkeq(std::string("ENABLED"),
2466             get_str_stat(h, h1, "vb_0:bloom_filter", "vbucket-details 0"),
2467             "Vbucket 0's bloom filter wasn't enabled upon setup!");
2468
2469     int i;
2470     item *it = NULL;
2471
2472     // Insert 10 items.
2473     for (i = 0; i < 10; ++i) {
2474         std::stringstream key;
2475         key << "key-" << i;
2476         checkeq(ENGINE_SUCCESS,
2477                 store(h, h1, NULL, OPERATION_SET, key.str().c_str(),
2478                       "somevalue", &it),
2479                 "Error setting.");
2480         h1->release(h, NULL, it);
2481     }
2482     wait_for_flusher_to_settle(h, h1);
2483
2484     // Evict all 10 items.
2485     for (i = 0; i < 10; ++i) {
2486         std::stringstream key;
2487         key << "key-" << i;
2488         evict_key(h, h1, key.str().c_str(), 0, "Ejected.");
2489     }
2490     wait_for_flusher_to_settle(h, h1);
2491
2492     // Ensure 10 items are non-resident.
2493     cb_assert(10 == get_int_stat(h, h1, "ep_num_non_resident"));
2494
2495     // Issue delete on first 5 items.
2496     for (i = 0; i < 5; ++i) {
2497         std::stringstream key;
2498         key << "key-" << i;
2499         checkeq(ENGINE_SUCCESS,
2500                 del(h, h1, key.str().c_str(), 0, 0),
2501                 "Failed remove with value.");
2502     }
2503     wait_for_flusher_to_settle(h, h1);
2504
2505     // Ensure that there are 5 non-resident items
2506     cb_assert(5 == get_int_stat(h, h1, "ep_num_non_resident"));
2507     cb_assert(5 == get_int_stat(h, h1, "curr_items"));
2508
2509     checkeq(ENGINE_SUCCESS,
2510             h1->get_stats(h, NULL, NULL, 0, add_stats),
2511             "Failed to get stats.");
2512     std::string eviction_policy = vals.find("ep_item_eviction_policy")->second;
2513
2514     useconds_t sleepTime = 128;
2515
2516     if (eviction_policy == "value_only") {  // VALUE-ONLY EVICTION MODE
2517
2518         checkeq(5,
2519                 get_int_stat(h, h1, "vb_0:bloom_filter_key_count",
2520                              "vbucket-details 0"),
2521                 "Unexpected no. of keys in bloom filter");
2522
2523         checkeq(num_read_attempts,
2524                 get_int_stat_or_default(h, h1, 0, "ep_bg_num_samples"),
2525                 "Expected bgFetch attempts to remain unchanged");
2526
2527         for (i = 0; i < 5; ++i) {
2528             std::stringstream key;
2529             key << "key-" << i;
2530             check(get_meta(h, h1, key.str().c_str()), "Get meta failed");
2531         }
2532
2533         // GetMeta would cause bgFetches as bloomfilter contains
2534         // the deleted items.
2535         checkeq(num_read_attempts + 5,
2536                 get_int_stat(h, h1, "ep_bg_num_samples"),
2537                 "Expected bgFetch attempts to increase by five");
2538
2539         // Run compaction, with drop_deletes
2540         compact_db(h, h1, 0, 0, 15, 15, 1);
2541         while (get_int_stat(h, h1, "ep_pending_compactions") != 0) {
2542             decayingSleep(&sleepTime);
2543         }
2544
2545         for (i = 0; i < 5; ++i) {
2546             std::stringstream key;
2547             key << "key-" << i;
2548             check(get_meta(h, h1, key.str().c_str()), "Get meta failed");
2549         }
2550         checkeq(num_read_attempts + 5,
2551                 get_int_stat(h, h1, "ep_bg_num_samples"),
2552                 "Expected bgFetch attempts to stay as before");
2553
2554     } else {                                // FULL EVICTION MODE
2555
2556         checkeq(10,
2557                 get_int_stat(h, h1, "vb_0:bloom_filter_key_count",
2558                              "vbucket-details 0"),
2559                 "Unexpected no. of keys in bloom filter");
2560
2561
2562         // Because of issuing deletes on non-resident items
2563         checkeq(num_read_attempts + 5,
2564                 get_int_stat(h, h1, "ep_bg_num_samples"),
2565                 "Expected bgFetch attempts to increase by five, after deletes");
2566
2567         // Run compaction, with drop_deletes, to exclude deleted items
2568         // from bloomfilter.
2569         compact_db(h, h1, 0, 0, 15, 15, 1);
2570         while (get_int_stat(h, h1, "ep_pending_compactions") != 0) {
2571             decayingSleep(&sleepTime);
2572         }
2573
2574         for (i = 0; i < 5; i++) {
2575             std::stringstream key;
2576             key << "key-" << i;
2577             checkeq(ENGINE_KEY_ENOENT,
2578                     get(h, h1, NULL, &it, key.str(), 0),
2579                     "Unable to get stored item");
2580         }
2581         // + 6 because last delete is not purged by the compactor
2582         checkeq(num_read_attempts + 6,
2583                 get_int_stat(h, h1, "ep_bg_num_samples"),
2584                 "Expected bgFetch attempts to stay as before");
2585     }
2586
2587     return SUCCESS;
2588 }
2589
2590 static enum test_result test_bloomfilters_with_store_apis(ENGINE_HANDLE *h,
2591                                                           ENGINE_HANDLE_V1 *h1) {
2592     if (get_bool_stat(h, h1, "ep_bfilter_enabled") == false) {
2593         check(set_param(h, h1, protocol_binary_engine_param_flush,
2594                     "bfilter_enabled", "true"),
2595                 "Set bloomfilter_enabled should have worked");
2596     }
2597     check(get_bool_stat(h, h1, "ep_bfilter_enabled"),
2598             "Bloom filter wasn't enabled");
2599
2600     int num_read_attempts = get_int_stat_or_default(h, h1, 0,
2601                                                     "ep_bg_num_samples");
2602
2603     // Ensure vbucket's bloom filter is enabled
2604     checkeq(std::string("ENABLED"),
2605             get_str_stat(h, h1, "vb_0:bloom_filter", "vbucket-details 0"),
2606             "Vbucket 0's bloom filter wasn't enabled upon setup!");
2607
2608     for (int i = 0; i < 1000; i++) {
2609         std::stringstream key;
2610         key << "key-" << i;
2611         check(!get_meta(h, h1, key.str().c_str()),
2612                 "Get meta should fail.");
2613     }
2614
2615     checkeq(num_read_attempts,
2616             get_int_stat_or_default(h, h1, 0, "ep_bg_num_samples"),
2617             "Expected no bgFetch attempts");
2618
2619     checkeq(ENGINE_SUCCESS,
2620             h1->get_stats(h, NULL, NULL, 0, add_stats),
2621             "Failed to get stats.");
2622     std::string eviction_policy = vals.find("ep_item_eviction_policy")->second;
2623
2624     if (eviction_policy == "full_eviction") {  // FULL EVICTION MODE
2625         // Set with Meta
2626         int j;
2627         for (j = 0; j < 10; j++) {
2628             uint64_t cas_for_set = last_cas;
2629             // init some random metadata
2630             ItemMetaData itm_meta;
2631             itm_meta.revSeqno = 10;
2632             itm_meta.cas = 0xdeadbeef;
2633             itm_meta.exptime = time(NULL) + 300;
2634             itm_meta.flags = 0xdeadbeef;
2635
2636             std::stringstream key;
2637             key << "swm-" << j;
2638             set_with_meta(h, h1, key.str().c_str(), key.str().length(),
2639                           "somevalue", 9, 0, &itm_meta, cas_for_set);
2640         }
2641
2642         checkeq(num_read_attempts,
2643                 get_int_stat_or_default(h, h1, 0, "ep_bg_num_samples"),
2644                 "Expected no bgFetch attempts");
2645
2646         item *itm = NULL;
2647         // Add
2648         for (j = 0; j < 10; j++) {
2649             std::stringstream key;
2650             key << "add-" << j;
2651
2652             checkeq(ENGINE_SUCCESS,
2653                     store(h, h1, NULL, OPERATION_ADD, key.str().c_str(),
2654                           "newvalue", &itm),
2655                     "Failed to add value again.");
2656             h1->release(h, NULL, itm);
2657         }
2658
2659         checkeq(num_read_attempts,
2660                 get_int_stat_or_default(h, h1, 0, "ep_bg_num_samples"),
2661                 "Expected no bgFetch attempts");
2662
2663         // Delete
2664         for (j = 0; j < 10; j++) {
2665             std::stringstream key;
2666             key << "del-" << j;
2667             checkeq(ENGINE_KEY_ENOENT,
2668                     del(h, h1, key.str().c_str(), 0, 0),
2669                     "Failed remove with value.");
2670         }
2671
2672         checkeq(num_read_attempts,
2673                 get_int_stat_or_default(h, h1, 0, "ep_bg_num_samples"),
2674                 "Expected no bgFetch attempts");
2675
2676     }
2677
2678     return SUCCESS;
2679 }
2680
2681 static enum test_result test_bloomfilter_delete_plus_set_scenario(
2682                                        ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
2683     if (get_bool_stat(h, h1, "ep_bfilter_enabled") == false) {
2684         check(set_param(h, h1, protocol_binary_engine_param_flush,
2685                     "bfilter_enabled", "true"),
2686                 "Set bloomfilter_enabled should have worked");
2687     }
2688     check(get_bool_stat(h, h1, "ep_bfilter_enabled"),
2689             "Bloom filter wasn't enabled");
2690
2691     // Ensure vbucket's bloom filter is enabled
2692     checkeq(std::string("ENABLED"),
2693             get_str_stat(h, h1, "vb_0:bloom_filter", "vbucket-details 0"),
2694             "Vbucket 0's bloom filter wasn't enabled upon setup!");
2695
2696     item *itm = NULL;
2697     checkeq(ENGINE_SUCCESS,
2698             store(h, h1, NULL, OPERATION_SET,"k1", "v1", &itm),
2699             "Failed to fail to store an item.");
2700     h1->release(h, NULL, itm);
2701
2702     wait_for_flusher_to_settle(h, h1);
2703     int num_writes = get_int_stat(h, h1, "rw_0:io_num_write", "kvstore");
2704     int num_persisted = get_int_stat(h, h1, "ep_total_persisted");
2705     cb_assert(num_writes == 1 && num_persisted == 1);
2706
2707     checkeq(ENGINE_SUCCESS,
2708             del(h, h1, "k1", 0, 0), "Failed remove with value.");
2709     stop_persistence(h, h1);
2710     checkeq(ENGINE_SUCCESS,
2711             store(h, h1, NULL, OPERATION_SET,"k1", "v2", &itm, 0, 0),
2712             "Failed to fail to store an item.");
2713     h1->release(h, NULL, itm);
2714     int key_count = get_int_stat(h, h1, "vb_0:bloom_filter_key_count",
2715                                  "vbucket-details 0");
2716
2717     if (key_count == 0) {
2718         check(get_int_stat(h, h1, "rw_0:io_num_write", "kvstore") <= 2,
2719                 "Unexpected number of writes");
2720         start_persistence(h, h1);
2721         wait_for_flusher_to_settle(h, h1);
2722         checkeq(0, get_int_stat(h, h1, "vb_0:bloom_filter_key_count",
2723                                 "vbucket-details 0"),
2724                 "Unexpected number of keys in bloomfilter");
2725     } else {
2726         cb_assert(key_count == 1);
2727         checkeq(2, get_int_stat(h, h1, "rw_0:io_num_write", "kvstore"),
2728                 "Unexpected number of writes");
2729         start_persistence(h, h1);
2730         wait_for_flusher_to_settle(h, h1);
2731         checkeq(1, get_int_stat(h, h1, "vb_0:bloom_filter_key_count",
2732                                 "vbucket-details 0"),
2733                 "Unexpected number of keys in bloomfilter");
2734     }
2735
2736     return SUCCESS;
2737 }
2738
2739 static enum test_result test_datatype(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
2740     const void *cookie = testHarness.create_cookie();
2741     testHarness.set_datatype_support(cookie, true);
2742
2743     item *itm = NULL;
2744     const std::string key("{\"foo\":\"bar\"}");
2745     const protocol_binary_datatype_t datatype = PROTOCOL_BINARY_DATATYPE_JSON;
2746     uint64_t cas = 0;
2747     std::string value("x");
2748     checkeq(ENGINE_SUCCESS,
2749             storeCasOut(h, h1, NULL, 0, key, value, datatype, itm, cas),
2750             "Expected set to succeed");
2751
2752     checkeq(ENGINE_SUCCESS,
2753             get(h, h1, cookie, &itm, key, 0),
2754             "Unable to get stored item");
2755
2756     item_info info;
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");
2761
2762     const char* key1 = "foo";
2763     const char* val1 = "{\"foo1\":\"bar1\"}";
2764     ItemMetaData itm_meta;
2765     itm_meta.revSeqno = 10;
2766     itm_meta.cas = info.cas;
2767     itm_meta.exptime = info.exptime;
2768     itm_meta.flags = info.flags;
2769     set_with_meta(h, h1, key1, strlen(key1), val1, strlen(val1), 0, &itm_meta,
2770                   last_cas, 0, info.datatype, cookie);
2771
2772     checkeq(ENGINE_SUCCESS,
2773             get(h, h1, cookie, &itm, key1, 0),
2774             "Unable to get stored item");
2775
2776     h1->get_item_info(h, cookie, itm, &info);
2777     h1->release(h, cookie, itm);
2778     checkeq(static_cast<uint8_t>(PROTOCOL_BINARY_DATATYPE_JSON),
2779             info.datatype, "Invalid datatype, when setWithMeta");
2780
2781     testHarness.destroy_cookie(cookie);
2782     return SUCCESS;
2783 }
2784
2785 static enum test_result test_datatype_with_unknown_command(ENGINE_HANDLE *h,
2786                                                            ENGINE_HANDLE_V1 *h1) {
2787     const void *cookie = testHarness.create_cookie();
2788     testHarness.set_datatype_support(cookie, true);
2789     item *itm = NULL;
2790     const char* key = "foo";
2791     const char* val = "{\"foo\":\"bar\"}";
2792     uint8_t datatype = PROTOCOL_BINARY_DATATYPE_JSON;
2793
2794     ItemMetaData itm_meta;
2795     itm_meta.revSeqno = 10;
2796     itm_meta.cas = 0x1;
2797     itm_meta.exptime = 0;
2798     itm_meta.flags = 0;
2799
2800     //SET_WITH_META
2801     set_with_meta(h, h1, key, strlen(key), val, strlen(val), 0, &itm_meta,
2802                   0, 0, datatype, cookie);
2803
2804     checkeq(ENGINE_SUCCESS,
2805             get(h, h1, cookie, &itm, key, 0),
2806             "Unable to get stored item");
2807
2808     item_info info;
2809     h1->get_item_info(h, cookie, itm, &info);
2810     h1->release(h, NULL, itm);
2811     checkeq(static_cast<uint8_t>(PROTOCOL_BINARY_DATATYPE_JSON),
2812             info.datatype, "Invalid datatype, when setWithMeta");
2813
2814     //SET_RETURN_META
2815     set_ret_meta(h, h1, "foo1", 4, val, strlen(val), 0, 0, 0, 0, datatype,
2816                  cookie);
2817     checkeq(PROTOCOL_BINARY_RESPONSE_SUCCESS, last_status.load(),
2818             "Expected set returing meta to succeed");
2819     checkeq(static_cast<uint8_t>(PROTOCOL_BINARY_DATATYPE_JSON),
2820             last_datatype.load(), "Invalid datatype, when set_return_meta");
2821
2822     testHarness.destroy_cookie(cookie);
2823     return SUCCESS;
2824 }
2825
2826 static enum test_result test_session_cas_validation(ENGINE_HANDLE *h,
2827                                                     ENGINE_HANDLE_V1 *h1) {
2828     //Testing PROTOCOL_BINARY_CMD_SET_VBUCKET..
2829     char ext[4];
2830     protocol_binary_request_header *pkt;
2831     vbucket_state_t state = vbucket_state_active;
2832     uint32_t val = static_cast<uint32_t>(state);
2833     val = htonl(val);
2834     memcpy(ext, (char*)&val, sizeof(val));
2835
2836     uint64_t cas = 0x0101010101010101;
2837     pkt = createPacket(PROTOCOL_BINARY_CMD_SET_VBUCKET, 0, cas, ext, 4);
2838     checkeq(ENGINE_SUCCESS,
2839             h1->unknown_command(h, NULL, pkt, add_response, testHarness.doc_namespace),
2840             "SET_VBUCKET command failed");
2841     cb_free(pkt);
2842     cb_assert(last_status == PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS);
2843
2844     cas = 0x0102030405060708;
2845     pkt = createPacket(PROTOCOL_BINARY_CMD_SET_VBUCKET, 0, cas, ext, 4);
2846     checkeq(ENGINE_SUCCESS,
2847             h1->unknown_command(h, NULL, pkt, add_response, testHarness.doc_namespace),
2848             "SET_VBUCKET command failed");
2849     cb_free(pkt);
2850     cb_assert(last_status == PROTOCOL_BINARY_RESPONSE_SUCCESS);
2851
2852     return SUCCESS;
2853 }
2854
2855 static enum test_result test_access_scanner_settings(ENGINE_HANDLE *h,
2856                                                      ENGINE_HANDLE_V1 *h1) {
2857     if (!isWarmupEnabled(h, h1)) {
2858         // Access scanner n/a without warmup.
2859         return SKIPPED;
2860     }
2861
2862     // Create a unique access log path by combining with the db path.
2863     checkeq(ENGINE_SUCCESS,
2864             h1->get_stats(h, NULL, NULL, 0, add_stats),
2865             "Failed to get stats.");
2866     std::string dbname = vals.find("ep_dbname")->second;
2867
2868     const auto alog_path = std::string("alog_path=") + dbname +
2869             DIRECTORY_SEPARATOR_CHARACTER + "access.log";
2870     std::string newconfig = std::string(testHarness.get_current_testcase()->cfg)
2871                             + alog_path;
2872
2873     testHarness.reload_engine(&h, &h1,
2874                               testHarness.engine_path,
2875                               newconfig.c_str(),
2876                               true, false);
2877     wait_for_warmup_complete(h, h1);
2878
2879     std::string err_msg;
2880     // Check access scanner is enabled and alog_task_time is at default
2881     checkeq(true, get_bool_stat(h, h1, "ep_access_scanner_enabled"),
2882             "Expected access scanner to be enabled");
2883     cb_assert(get_int_stat(h, h1, "ep_alog_task_time") == 2);
2884
2885     // Ensure access_scanner_task_time is what its expected to be.
2886     // Need to wait until the AccessScanner task has been setup.
2887     wait_for_stat_change(h, h1, "ep_access_scanner_task_time",
2888                          std::string{"NOT_SCHEDULED"});
2889
2890     std::string str = get_str_stat(h, h1, "ep_access_scanner_task_time");
2891     std::string expected_time = "02:00";
2892     err_msg.assign("Initial time incorrect, expect: " +
2893                    expected_time + ", actual: " + str.substr(11, 5));
2894     checkeq(0, str.substr(11, 5).compare(expected_time), err_msg.c_str());
2895
2896     // Update alog_task_time and ensure the update is successful
2897     set_param(h, h1, protocol_binary_engine_param_flush, "alog_task_time", "5");
2898     expected_time = "05:00";
2899     str = get_str_stat(h, h1, "ep_access_scanner_task_time");
2900     err_msg.assign("Updated time incorrect, expect: " +
2901                    expected_time + ", actual: " + str.substr(11, 5));
2902     checkeq(0, str.substr(11, 5).compare(expected_time), err_msg.c_str());
2903
2904     // Update alog_sleep_time by 10 mins and ensure the update is successful.
2905     const std::chrono::minutes update_by{10};
2906     std::string targetTaskTime1{make_time_string(std::chrono::system_clock::now() +
2907                                                  update_by)};
2908
2909     set_param(h, h1, protocol_binary_engine_param_flush, "alog_sleep_time",
2910               std::to_string(update_by.count()).c_str());
2911     str = get_str_stat(h, h1, "ep_access_scanner_task_time");
2912
2913     // Recalculate now() + 10mins as upper bound on when the task should be
2914     // scheduled.
2915     std::string targetTaskTime2{make_time_string(std::chrono::system_clock::now() +
2916                                                  update_by)};
2917
2918     // ep_access_scanner_task_time should fall within the range of
2919     // targetTaskTime1 and targetTaskTime2
2920     err_msg.assign("Unexpected task time range, expect: " +
2921                    targetTaskTime1 + " <= " + str + " <= " + targetTaskTime2);
2922     check(targetTaskTime1 <= str, err_msg.c_str());
2923     check(str <= targetTaskTime2, err_msg.c_str());
2924
2925     return SUCCESS;
2926 }
2927
2928 static enum test_result test_access_scanner(ENGINE_HANDLE *h,
2929                                             ENGINE_HANDLE_V1 *h1) {
2930     if (!isWarmupEnabled(h, h1)) {
2931         // Access scanner not applicable without warmup.
2932         return SKIPPED;
2933     }
2934
2935     // Create a unique access log path by combining with the db path.
2936     checkeq(ENGINE_SUCCESS,
2937             h1->get_stats(h, NULL, NULL, 0, add_stats),
2938             "Failed to get stats.");
2939     const auto dbname = vals.find("ep_dbname")->second;
2940
2941     const auto alog_path = std::string("alog_path=") + dbname +
2942             DIRECTORY_SEPARATOR_CHARACTER + "access.log";
2943
2944     /* We do not want the access scanner task to be running while we initiate it
2945        explicitly below. Hence set the alog_task_time to about 1 ~ 2 hours
2946        from now */
2947     const time_t now = time(nullptr);
2948     struct tm tm_now;
2949     cb_gmtime_r(&now, &tm_now);
2950     const auto two_hours_hence = (tm_now.tm_hour + 2) % 24;
2951
2952     const auto alog_task_time = std::string("alog_task_time=") +
2953             std::to_string(two_hours_hence);
2954
2955     const auto newconfig = std::string(testHarness.get_current_testcase()->cfg)
2956                            + alog_path + ";" + alog_task_time;
2957
2958     testHarness.reload_engine(&h, &h1,
2959                               testHarness.engine_path,
2960                               newconfig.c_str(),
2961                               true, false);
2962     wait_for_warmup_complete(h, h1);
2963
2964     /* Check that alog_task_time was correctly updated. */
2965     checkeq(get_int_stat(h, h1, "ep_alog_task_time"),
2966             two_hours_hence,
2967             "Failed to set alog_task_time to 2 hours in the future");
2968
2969     checkeq(ENGINE_SUCCESS,
2970             h1->get_stats(h, NULL, NULL, 0, add_stats),
2971             "Failed to get stats.");
2972     std::string name = vals.find("ep_alog_path")->second;
2973
2974     /* Check access scanner is enabled */
2975     checkeq(true, get_bool_stat(h, h1, "ep_access_scanner_enabled"),
2976             "Access scanner task not enabled by default. Check test config");
2977
2978     const int num_shards = get_int_stat(h, h1, "ep_workload:num_shards",
2979                                         "workload");
2980     name = name + ".0";
2981     std::string prev(name + ".old");
2982
2983     /* Get the resident ratio down to below 95% - point at which access.log
2984      * generation occurs.
2985      */
2986     int num_items = 0;
2987     // Size chosen to create ~2000 items (i.e. 2x more than we sanity-check below)
2988     // with the given max_size for this test.
2989     const std::string value(2000, 'x');
2990     while (true) {
2991         // Gathering stats on every store is expensive, just check every 100 iterations
2992         if ((num_items % 100) == 0) {
2993             if (get_int_stat(h, h1, "vb_active_perc_mem_resident") < 94) {
2994                 break;
2995             }
2996         }
2997
2998         item *itm = NULL;
2999         std::string key("key" + std::to_string(num_items));
3000         ENGINE_ERROR_CODE ret = store(h, h1, NULL, OPERATION_SET,
3001                                       key.c_str(), value.c_str(), &itm);
3002         switch (ret) {
3003             case ENGINE_SUCCESS:
3004                 num_items++;
3005                 h1->release(h, NULL, itm);
3006                 break;
3007
3008             case ENGINE_ENOMEM:
3009             case ENGINE_TMPFAIL:
3010                 // Returned when at high watermark; simply retry the op.
3011                 h1->release(h, NULL, itm);
3012                 break;
3013
3014             default:
3015                 fprintf(stderr, "test_access_scanner: Unexpected result from store(): %d\n",
3016                         ret);
3017                 abort();
3018         }
3019
3020     }
3021
3022     // Sanity check - ensure we have enough vBucket quota (max_size)
3023     // such that we have 1000 items - enough to give us 0.1%
3024     // granuarity in any residency calculations. */
3025     if (num_items < 1000) {
3026         std::cerr << "Error: test_access_scanner: "
3027             "expected at least 1000 items after filling vbucket, "
3028             "but only have " << num_items << ". "
3029             "Check max_size setting for test." << std::endl;
3030         return FAIL;
3031     }
3032
3033     wait_for_flusher_to_settle(h, h1);
3034     verify_curr_items(h, h1, num_items, "Wrong number of items");
3035     int num_non_resident = get_int_stat(h, h1, "vb_active_num_non_resident");
3036     checkge(num_non_resident, num_items * 6 / 100,
3037             "Expected num_non_resident to be at least 6% of total items");
3038
3039     /* Run access scanner task once and expect it to generate access log */
3040     check(set_param(h, h1, protocol_binary_engine_param_flush,
3041                     "access_scanner_run", "true"),
3042           "Failed to trigger access scanner");
3043
3044     // Wait for the number of runs to equal the number of shards.
3045     wait_for_stat_to_be(h, h1, "ep_num_access_scanner_runs", num_shards);
3046
3047     /* This time since resident ratio is < 95% access log should be generated */
3048     checkeq(0, access(name.c_str(), F_OK),
3049             (std::string("access log file (") + name +
3050              ") should exist (got errno:" + std::to_string(errno)).c_str());
3051
3052     /* Increase resident ratio by deleting items */
3053     vbucketDelete(h, h1, 0);
3054     check(set_vbucket_state(h, h1, 0, vbucket_state_active),
3055           "Failed to set VB0 state.");
3056
3057     /* Run access scanner task once */
3058     const int access_scanner_skips =
3059             get_int_stat(h, h1, "ep_num_access_scanner_skips");
3060     check(set_param(h, h1, protocol_binary_engine_param_flush,
3061                     "access_scanner_run", "true"),
3062           "Failed to trigger access scanner");
3063     wait_for_stat_to_be(h, h1, "ep_num_access_scanner_skips",
3064                         access_scanner_skips + num_shards);
3065
3066     /* Access log files should be removed because resident ratio > 95% */
3067     checkeq(-1, access(prev.c_str(), F_OK),
3068             ".old access log file should not exist");
3069     checkeq(-1, access(name.c_str(), F_OK), "access log file should not exist");
3070
3071     return SUCCESS;
3072 }
3073
3074 static enum test_result test_set_param_message(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
3075     set_param(h, h1, protocol_binary_engine_param_flush, "alog_task_time", "50");
3076
3077     checkeq(PROTOCOL_BINARY_RESPONSE_EINVAL, last_status.load(),
3078         "Expected an invalid value error for an out of bounds alog_task_time");
3079     check(std::string("Validation Error").compare(last_body), "Expected a "
3080             "validation error in the response body");
3081     return SUCCESS;
3082 }
3083
3084 static enum test_result test_warmup_stats(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
3085     if (!isWarmupEnabled(h, h1)) {
3086         return SKIPPED;
3087     }
3088
3089     item *it = NULL;
3090     check(set_vbucket_state(h, h1, 0, vbucket_state_active), "Failed to set VB0 state.");
3091     check(set_vbucket_state(h, h1, 1, vbucket_state_replica), "Failed to set VB1 state.");
3092
3093     for (int i = 0; i < 5000; ++i) {
3094         std::stringstream key;
3095         key << "key-" << i;
3096         checkeq(ENGINE_SUCCESS,
3097                 store(h, h1, NULL, OPERATION_SET, key.str().c_str(),
3098                       "somevalue", &it),
3099                 "Error setting.");
3100         h1->release(h, NULL, it);
3101     }
3102
3103     // Restart the server.
3104     testHarness.reload_engine(&h, &h1,
3105                               testHarness.engine_path,
3106                               testHarness.get_current_testcase()->cfg,
3107                               true, false);
3108
3109     wait_for_warmup_complete(h, h1);
3110
3111     const auto warmup_stats = get_all_stats(h, h1, "warmup");
3112
3113     // Check all expected warmup stats exists.
3114     const char* warmup_keys[] = { "ep_warmup_thread",
3115                                   "ep_warmup_value_count",
3116                                   "ep_warmup_key_count",
3117                                   "ep_warmup_dups",
3118                                   "ep_warmup_oom",
3119                                   "ep_warmup_time"};
3120     for (const auto* key : warmup_keys) {
3121         check(warmup_stats.find(key) != warmup_stats.end(),
3122               (std::string("Found no ") + key).c_str());
3123     }
3124
3125     std::string warmup_time = warmup_stats.at("ep_warmup_time");
3126     cb_assert(std::stoi(warmup_time) > 0);
3127
3128     const auto prev_vb_stats = get_all_stats(h, h1, "prev-vbucket");
3129
3130     check(prev_vb_stats.find("vb_0") != prev_vb_stats.end(),
3131           "Found no previous state for VB0");
3132     check(prev_vb_stats.find("vb_1") != prev_vb_stats.end(),
3133           "Found no previous state for VB1");
3134
3135     checkeq(std::string("active"), prev_vb_stats.at("vb_0"),
3136             "Unexpected stats for vb 0");
3137     checkeq(std::string("replica"), prev_vb_stats.at("vb_1"),
3138             "Unexpected stats for vb 1");
3139
3140     const auto vb_details_stats = get_all_stats(h, h1, "vbucket-details");
3141     checkeq(5000, std::stoi(vb_details_stats.at("vb_0:num_items")),
3142             "Unexpected item count for vb 0");
3143     checkeq(0, std::stoi(vb_details_stats.at("vb_1:num_items")),
3144             "Unexpected item count for vb 1");
3145
3146     return SUCCESS;
3147 }
3148
3149 static enum test_result test_warmup_with_threshold(ENGINE_HANDLE *h,
3150                                                    ENGINE_HANDLE_V1 *h1) {
3151     if (!isWarmupEnabled(h, h1)) {
3152         return SKIPPED;
3153     }
3154
3155     item *it = NULL;
3156     check(set_vbucket_state(h, h1, 0, vbucket_state_active), "Failed set vbucket 1 state.");
3157     check(set_vbucket_state(h, h1, 1, vbucket_state_active), "Failed set vbucket 2 state.");
3158     check(set_vbucket_state(h, h1, 2, vbucket_state_active), "Failed set vbucket 3 state.");
3159     check(set_vbucket_state(h, h1, 3, vbucket_state_active), "Failed set vbucket 4 state.");
3160
3161     for (int i = 0; i < 10000; ++i) {
3162         std::stringstream key;
3163         key << "key+" << i;
3164         checkeq(ENGINE_SUCCESS,
3165                 store(h, h1, NULL, OPERATION_SET, key.str().c_str(), "somevalue", &it,
3166                      0, (i % 4)),
3167                 "Error setting.");
3168         h1->release(h, NULL, it);
3169     }
3170
3171     // Restart the server.
3172     testHarness.reload_engine(&h, &h1,
3173                               testHarness.engine_path,
3174                               testHarness.get_current_testcase()->cfg,
3175                               true, false);
3176
3177     wait_for_warmup_complete(h, h1);
3178
3179     checkeq(1,
3180             get_int_stat(h, h1, "ep_warmup_min_item_threshold", "warmup"),
3181             "Unable to set warmup_min_item_threshold to 1%");
3182
3183     const std::string policy = get_str_stat(h, h1, "ep_item_eviction_policy");
3184
3185     if (policy == "full_eviction") {
3186         checkeq(get_int_stat(h, h1, "ep_warmup_key_count", "warmup"),
3187                 get_int_stat(h, h1, "ep_warmup_value_count", "warmup"),
3188                 "Warmed up key count didn't match warmed up value count");
3189     } else {
3190         checkeq(10000, get_int_stat(h, h1, "ep_warmup_key_count", "warmup"),
3191                 "Warmup didn't warmup all keys");
3192     }
3193     check(get_int_stat(h, h1, "ep_warmup_value_count", "warmup") <= 110,
3194             "Warmed up value count found to be greater than 1%");
3195
3196     cb_assert(get_int_stat(h, h1, "ep_warmup_time", "warmup") > 0);
3197
3198     return SUCCESS;
3199 }
3200
3201 #if 0
3202 // Comment out the entire test since the hack gave warnings on win32
3203 static enum test_result test_warmup_accesslog(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
3204 #ifdef __APPLE__
3205     /* I'm getting a weird link error from clang.. disable the test until I
3206     ** understand why
3207     */
3208     return SKIPPED;
3209 #else
3210     item *it = NULL;
3211
3212     int n_items_to_store1 = 10;
3213     for (int i = 0; i < n_items_to_store1; ++i) {
3214         std::stringstream key;
3215         key << "key-" << i;
3216         const char* keystr = key.str().c_str();
3217         checkeq(ENGINE_SUCCESS,
3218                 store(h, h1, NULL, OPERATION_SET, keystr, "somevalue", &it, 0, 0),
3219                 "Error setting.");
3220         h1->release(h, NULL, it);
3221     }
3222
3223     wait_for_flusher_to_settle(h, h1);
3224
3225     int n_items_to_access = 10;
3226     for (int i = 0; i < n_items_to_access; ++i) {
3227         std::stringstream key;
3228         key << "key-" << i;
3229         const char* keystr = key.str().c_str();
3230         checkeq(ENGINE_SUCCESS,
3231                 get(h, h1, NULL, &it, keystr, 0),
3232                 "Error getting.");
3233         h1->release(h, NULL, it);
3234     }
3235
3236     // sleep so that scanner task can have timew to generate access log
3237     sleep(61);
3238
3239     // store additional items
3240     int n_items_to_store2 = 10;
3241     for (int i = 0; i < n_items_to_store2; ++i) {
3242         std::stringstream key;
3243         key << "key2-" << i;
3244         const char* keystr = key.str().c_str();
3245         checkeq(ENGINE_SUCCESS,
3246                 store(h, h1, NULL, OPERATION_SET, keystr, "somevalue", &it, 0, 0),
3247                 "Error setting.");
3248         h1->release(h, NULL, it);
3249     }
3250
3251     // Restart the server.
3252     testHarness.reload_engine(&h, &h1,
3253                               testHarness.engine_path,
3254                               testHarness.get_current_testcase()->cfg,
3255                               true, false);
3256
3257     wait_for_warmup_complete(h, h1);
3258     // n_items_to_access items should be loaded from access log first
3259     // but we continue to load until we hit 75% item watermark
3260
3261     int warmedup = get_int_stat(h, h1, "ep_warmup_value_count", "warmup");
3262     //    std::cout << "ep_warmup_value_count = " << warmedup << std::endl;
3263     int expected = (n_items_to_store1 + n_items_to_store2) * 0.75 + 1;
3264
3265     check(warmedup == expected, "Expected 16 items to be resident");
3266     return SUCCESS;
3267 #endif
3268 }
3269 #endif
3270
3271 static enum test_result test_warmup_oom(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
3272     if (!isWarmupEnabled(h, h1)) {
3273         return SKIPPED;
3274     }
3275
3276     write_items(h, h1, 20000, 0, "superlongnameofkey1234567890123456789012345678902");
3277
3278     wait_for_flusher_to_settle(h, h1);
3279
3280     std::string config(testHarness.get_current_testcase()->cfg);
3281     config = config + "max_size=2097152;item_eviction_policy=value_only";
3282
3283     testHarness.reload_engine(&h, &h1,
3284                               testHarness.engine_path,
3285                               config.c_str(),
3286                               true, false);
3287
3288     wait_for_warmup_complete(h, h1);
3289
3290     protocol_binary_request_header *pkt = createPacket(PROTOCOL_BINARY_CMD_ENABLE_TRAFFIC);
3291     checkeq(ENGINE_SUCCESS,
3292             h1->unknown_command(h, NULL, pkt, add_response, testHarness.doc_namespace),
3293             "Failed to send data traffic command to the services");
3294     checkeq(PROTOCOL_BINARY_RESPONSE_ENOMEM, last_status.load(),
3295             "Data traffic command should have failed with enomem");
3296     cb_free(pkt);
3297
3298     return SUCCESS;
3299 }
3300
3301 static enum test_result test_cbd_225(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
3302     item *i = NULL;
3303
3304     // get engine startup token
3305     time_t token1 = get_int_stat(h, h1, "ep_startup_time");
3306     check(token1 != 0, "Expected non-zero startup token");
3307
3308     // store some random data
3309     checkeq(ENGINE_SUCCESS,
3310             store(h, h1, NULL, OPERATION_SET,"k1", "v1", &i),
3311             "Failed to fail to store an item.");
3312     h1->release(h, NULL, i);
3313     checkeq(ENGINE_SUCCESS,
3314             store(h, h1, NULL, OPERATION_SET,"k2", "v2", &i),
3315             "Failed to fail to store an item.");
3316     h1->release(h, NULL, i);
3317     wait_for_flusher_to_settle(h, h1);
3318
3319     // check token again, which should be the same as before
3320     time_t token2 = get_int_stat(h, h1, "ep_startup_time");
3321     check(token2 == token1, "Expected the same startup token");
3322
3323     // reload the engine
3324     testHarness.time_travel(10);
3325     testHarness.reload_engine(&h, &h1,
3326                               testHarness.engine_path,
3327                               testHarness.get_current_testcase()->cfg,
3328                               true, false);
3329     wait_for_warmup_complete(h, h1);
3330
3331     // check token, this time we should get a different one
3332     time_t token3 = get_int_stat(h, h1, "ep_startup_time");
3333     check(token3 != token1, "Expected a different startup token");
3334
3335     return SUCCESS;
3336 }
3337
3338 static enum test_result test_workload_stats(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
3339     const void* cookie = testHarness.create_cookie();
3340     checkeq(ENGINE_SUCCESS,
3341             h1->get_stats(h, cookie, "workload",
3342                           strlen("workload"), add_stats),
3343             "Falied to get workload stats");
3344     testHarness.destroy_cookie(cookie);
3345     int num_read_threads = get_int_stat(h, h1, "ep_workload:num_readers",
3346                                                "workload");
3347     int num_write_threads = get_int_stat(h, h1, "ep_workload:num_writers",
3348                                                 "workload");
3349     int num_auxio_threads = get_int_stat(h, h1, "ep_workload:num_auxio",
3350                                                 "workload");
3351     int num_nonio_threads = get_int_stat(h, h1, "ep_workload:num_nonio",
3352                                                 "workload");
3353     int max_read_threads = get_int_stat(h, h1, "ep_workload:max_readers",
3354                                                "workload");
3355     int max_write_threads = get_int_stat(h, h1, "ep_workload:max_writers",
3356                                                 "workload");
3357     int max_auxio_threads = get_int_stat(h, h1, "ep_workload:max_auxio",
3358                                                 "workload");
3359     int max_nonio_threads = get_int_stat(h, h1, "ep_workload:max_nonio",
3360                                                 "workload");
3361     int num_shards = get_int_stat(h, h1, "ep_workload:num_shards", "workload");
3362     checkeq(4, num_read_threads, "Incorrect number of readers");
3363     // MB-12279: limiting max writers to 4 for DGM bgfetch performance
3364     checkeq(4, num_write_threads, "Incorrect number of writers");
3365     checkeq(1, num_auxio_threads, "Incorrect number of auxio threads");
3366     check(num_nonio_threads > 1 && num_nonio_threads <= 8,
3367           "Incorrect number of nonio threads");
3368     checkeq(4, max_read_threads, "Incorrect limit of readers");
3369     // MB-12279: limiting max writers to 4 for DGM bgfetch performance
3370     checkeq(4, max_write_threads, "Incorrect limit of writers");
3371     checkeq(1, max_auxio_threads, "Incorrect limit of auxio threads");
3372     check(max_nonio_threads > 1 && max_nonio_threads <=8,
3373           "Incorrect limit of nonio threads");
3374     checkeq(5, num_shards, "Incorrect number of shards");
3375     return SUCCESS;
3376 }
3377
3378 static enum test_result test_max_workload_stats(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
3379     const void* cookie = testHarness.create_cookie();
3380     checkeq(ENGINE_SUCCESS,
3381             h1->get_stats(h, cookie, "workload",
3382                           strlen("workload"), add_stats),
3383             "Failed to get workload stats");
3384     testHarness.destroy_cookie(cookie);
3385     int num_read_threads = get_int_stat(h, h1, "ep_workload:num_readers",
3386                                                "workload");
3387     int num_write_threads = get_int_stat(h, h1, "ep_workload:num_writers",
3388                                                 "workload");
3389     int num_auxio_threads = get_int_stat(h, h1, "ep_workload:num_auxio",
3390                                                 "workload");
3391     int num_nonio_threads = get_int_stat(h, h1, "ep_workload:num_nonio",
3392                                                 "workload");
3393     int max_read_threads = get_int_stat(h, h1, "ep_workload:max_readers",
3394                                                "workload");
3395     int max_write_threads = get_int_stat(h, h1, "ep_workload:max_writers",
3396                                                 "workload");
3397     int max_auxio_threads = get_int_stat(h, h1, "ep_workload:max_auxio",
3398                                                 "workload");
3399     int max_nonio_threads = get_int_stat(h, h1, "ep_workload:max_nonio",
3400                                                 "workload");
3401     int num_shards = get_int_stat(h, h1, "ep_workload:num_shards", "workload");
3402     // if max limit on other groups missing use remaining for readers & writers
3403     checkeq(5, num_read_threads, "Incorrect number of readers");
3404     // MB-12279: limiting max writers to 4 for DGM bgfetch performance
3405     checkeq(4, num_write_threads, "Incorrect number of writers");
3406
3407     checkeq(1, num_auxio_threads, "Incorrect number of auxio threads");// config
3408     checkeq(4, num_nonio_threads, "Incorrect number of nonio threads");// config
3409     checkeq(5, max_read_threads, "Incorrect limit of readers");// derived
3410     // MB-12279: limiting max writers to 4 for DGM bgfetch performance
3411     checkeq(4, max_write_threads, "Incorrect limit of writers");// max-capped
3412     checkeq(1, max_auxio_threads, "Incorrect limit of auxio threads");// config
3413     checkeq(4, max_nonio_threads, "Incorrect limit of nonio threads");// config
3414     checkeq(5, num_shards, "Incorrect number of shards");
3415     return SUCCESS;
3416 }
3417
3418 static enum test_result test_worker_stats(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
3419     checkeq(ENGINE_SUCCESS,
3420             h1->get_stats(h, NULL, "dispatcher",
3421                           strlen("dispatcher"), add_stats),
3422             "Failed to get worker stats");
3423
3424     std::set<std::string> tasklist;
3425     tasklist.insert("Running a flusher loop");
3426     tasklist.insert("Snapshotting vbucket states for the shard");
3427     tasklist.insert("Deleting VBucket");
3428     tasklist.insert("Updating stat snapshot on disk");
3429     tasklist.insert("Batching background fetch");
3430     tasklist.insert("Fetching item from disk for vkey stat");
3431     tasklist.insert("Fetching item from disk");
3432     tasklist.insert("Loading TAP backfill from disk");
3433     tasklist.insert("Tap connection notifier");
3434     tasklist.insert("Generating access log");
3435     tasklist.insert("Fetching item from disk for tap");
3436     tasklist.insert("Snapshotting vbucket states");
3437     tasklist.insert("Persisting a vbucket state for vbucket");
3438     tasklist.insert("Reaping tap or dcp connection");
3439     tasklist.insert("Warmup - initialize");
3440     tasklist.insert("Warmup - creating vbuckets");
3441     tasklist.insert("Warmup - estimate item count");
3442     tasklist.insert("Warmup - key dump");
3443     tasklist.insert("Warmup - check for access log");
3444     tasklist.insert("Warmup - loading access log");
3445     tasklist.insert("Warmup - loading KV Pairs");
3446     tasklist.insert("Warmup - loading data");
3447     tasklist.insert("Warmup - completion");
3448     tasklist.insert("Not currently running any task");
3449
3450     std::set<std::string> statelist;
3451     statelist.insert("creating");
3452     statelist.insert("running");
3453     statelist.insert("waiting");
3454     statelist.insert("sleeping");
3455     statelist.insert("shutdown");
3456     statelist.insert("dead");
3457
3458     std::string worker_0_task = vals["reader_worker_0:task"];
3459     unsigned pos = worker_0_task.find(":");
3460     worker_0_task = worker_0_task.substr(0, pos ? pos : worker_0_task.size());
3461     std::string worker_0_state = vals["reader_worker_0:state"];
3462     check(tasklist.find(worker_0_task)!=tasklist.end(),
3463           "worker_0's Current task incorrect");
3464     check(statelist.find(worker_0_state)!=statelist.end(),
3465           "worker_0's state incorrect");
3466     std::string worker_1_task = vals["reader_worker_1:task"];
3467     pos = worker_1_task.find(":");
3468     worker_1_task = worker_1_task.substr(0, pos ? pos : worker_1_task.size());
3469     std::string worker_1_state = vals["reader_worker_1:state"];
3470     check(tasklist.find(worker_1_task)!=tasklist.end(),
3471           "worker_1's Current task incorrect");
3472     check(statelist.find(worker_1_state)!=statelist.end(),
3473           "worker_1's state incorrect");
3474
3475     checkeq(11, get_int_stat(h, h1, "ep_num_workers"), // cannot spawn less
3476             "Incorrect number of threads spawned");
3477     return SUCCESS;
3478 }
3479
3480 static enum test_result test_cluster_config(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
3481     check(set_vbucket_state(h, h1, 1, vbucket_state_active), "Failed set vbucket 1 state.");
3482     check(verify_vbucket_state(h, h1, 1, vbucket_state_active),
3483                     "VBucket state not active");
3484     uint64_t var = 1234;
3485     protocol_binary_request_header *pkt1 =
3486         createPacket(PROTOCOL_BINARY_CMD_SET_CLUSTER_CONFIG, 1, 0, NULL, 0, NULL, 0, (char*)&var, 8);
3487     checkeq(ENGINE_SUCCESS,
3488             h1->unknown_command(h, NULL, pkt1, add_response, testHarness.doc_namespace),
3489             "Failed to set cluster configuration");
3490     cb_free(pkt1);
3491
3492     protocol_binary_request_header *pkt2 =
3493         createPacket(PROTOCOL_BINARY_CMD_GET_CLUSTER_CONFIG, 1, 0, NULL, 0, NULL, 0, NULL, 0);
3494     checkeq(ENGINE_SUCCESS, h1->unknown_command(h, NULL, pkt2, add_response, testHarness.doc_namespace),
3495             "Failed to get cluster configuration");
3496     cb_free(pkt2);
3497     if (last_body.compare(0, sizeof(var), reinterpret_cast<char*>(&var),
3498                           sizeof(var)) != 0) {
3499         return FAIL;
3500     } else {
3501         return SUCCESS;
3502     }
3503 }
3504
3505 static enum test_result test_not_my_vbucket_with_cluster_config(ENGINE_HANDLE *h,
3506                                                                 ENGINE_HANDLE_V1 *h1) {
3507     uint64_t var = 4321;
3508     protocol_binary_request_header *pkt1 =
3509         createPacket(PROTOCOL_BINARY_CMD_SET_CLUSTER_CONFIG, 1, 0, NULL, 0, NULL, 0, (char*)&var, 8);
3510     checkeq(ENGINE_SUCCESS, h1->unknown_command(h, NULL, pkt1, add_response, testHarness.doc_namespace),
3511             "Failed to set cluster configuration");
3512     cb_free(pkt1);
3513
3514     protocol_binary_request_header *pkt2 =
3515         createPacket(PROTOCOL_BINARY_CMD_GET_VBUCKET, 1, 0, NULL, 0, NULL, 0, NULL, 0);
3516     ENGINE_ERROR_CODE ret = h1->unknown_command(h, NULL, pkt2,
3517                                                 add_response,
3518                                                 testHarness.doc_namespace);
3519     checkeq(ENGINE_SUCCESS, ret, "Should've received not_my_vbucket/cluster config");
3520     cb_free(pkt2);
3521     if (last_body.compare(0, sizeof(var), reinterpret_cast<char*>(&var),
3522                           sizeof(var)) != 0) {
3523         return FAIL;
3524     } else {
3525         return SUCCESS;
3526     }
3527     check(verify_key(h, h1, "key", 2) == ENGINE_NOT_MY_VBUCKET, "Expected miss");
3528     checkeq(ENGINE_SUCCESS,
3529             h1->get_engine_vb_map(h, NULL, vb_map_response),
3530             "Failed to recover cluster configuration");
3531     if (last_body.compare(0, sizeof(var), reinterpret_cast<char*>(&var),
3532                           sizeof(var)) != 0) {
3533         return FAIL;
3534     } else {
3535         return SUCCESS;
3536     }
3537 }
3538
3539 static enum test_result test_all_keys_api(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
3540     std::vector<std::string> keys;
3541     const int start_key_idx = 10, del_key_idx = 12, num_keys = 5,
3542               total_keys = 100;
3543
3544     for (uint32_t i = 0; i < total_keys; ++i) {
3545         std::string key("key_" + std::to_string(i));
3546         keys.push_back(key);
3547     }
3548     std::vector<std::string>::iterator it;
3549     for (it = keys.begin(); it != keys.end(); ++it) {
3550         item *itm;
3551         checkeq(ENGINE_SUCCESS, store(h, h1, NULL, OPERATION_SET, it->c_str(),
3552                                       it->c_str(), &itm, 0, 0),
3553                 "Failed to store a value");
3554         h1->release(h, NULL, itm);
3555     }
3556     std::string del_key("key_" + std::to_string(del_key_idx));
3557     checkeq(ENGINE_SUCCESS, del(h, h1, del_key.c_str(), 0, 0),
3558             "Failed to delete key");
3559     wait_for_flusher_to_settle(h, h1);
3560     checkeq(total_keys - 1, get_int_stat(h, h1, "curr_items"),
3561             "Item count mismatch");
3562
3563     std::string start_key("key_" + std::to_string(start_key_idx));
3564     const uint16_t keylen = start_key.length();
3565     uint32_t count = htonl(num_keys);
3566
3567     protocol_binary_request_header *pkt1 =
3568         createPacket(PROTOCOL_BINARY_CMD_GET_KEYS, 0, 0,
3569                      reinterpret_cast<char*>(&count),
3570                      sizeof(count), start_key.c_str(), keylen, NULL, 0, 0x00);
3571
3572     checkeq(ENGINE_SUCCESS, h1->unknown_command(h, NULL, pkt1, add_response, testHarness.doc_namespace),
3573             "Failed to get all_keys, sort: ascending");
3574     cb_free(pkt1);
3575
3576     /* Check the keys. */
3577     size_t offset = 0;
3578     /* Since we have one deleted key, we must go till num_keys + 1 */
3579     for (size_t i = 0; i < num_keys + 1; ++i) {
3580         if (del_key_idx == start_key_idx + i) {
3581             continue;
3582         }
3583         uint16_t len;
3584         memcpy(&len, last_body.data() + offset, sizeof(uint16_t));
3585         len = ntohs(len);
3586         checkeq(keylen, len, "Key length mismatch in all_docs response");
3587         std::string key("key_" + std::to_string(start_key_idx + i));
3588         offset += sizeof(uint16_t);
3589         checkeq(0, last_body.compare(offset, keylen, key.c_str()),
3590                 "Key mismatch in all_keys response");
3591         offset += keylen;
3592     }
3593
3594     return SUCCESS;
3595 }
3596
3597 static enum test_result test_all_keys_api_during_bucket_creation(
3598                                 ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
3599
3600     uint32_t count = htonl(5);
3601     const char key[] = "key_10";
3602
3603     protocol_binary_request_header *pkt1 =
3604         createPacket(PROTOCOL_BINARY_CMD_GET_KEYS, 1, 0,
3605                      reinterpret_cast<char*>(&count),
3606                      sizeof(count), key, strlen(key), NULL, 0, 0x00);
3607
3608     stop_persistence(h, h1);
3609     check(set_vbucket_state(h, h1, 1, vbucket_state_active),
3610           "Failed set vbucket 1 state.");
3611
3612     ENGINE_ERROR_CODE err = h1->unknown_command(h, NULL, pkt1,
3613                                                 add_response,
3614                                                 testHarness.doc_namespace);
3615     cb_free(pkt1);
3616     start_persistence(h, h1);
3617
3618     checkeq(ENGINE_SUCCESS, err,
3619             "Unexpected return code from all_keys_api");
3620     checkeq(PROTOCOL_BINARY_RESPONSE_SUCCESS, last_status.load(),
3621             "Unexpected response status");
3622
3623     return SUCCESS;
3624 }
3625
3626 static enum test_result test_curr_items_add_set(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
3627     item *i = NULL;
3628
3629     // Verify initial case.
3630     verify_curr_items(h, h1, 0, "init");
3631
3632     const auto initial_enqueued = get_int_stat(h, h1, "ep_total_enqueued");
3633
3634     // Verify set and add case
3635     checkeq(ENGINE_SUCCESS,
3636             store(h, h1, NULL, OPERATION_ADD,"k1", "v1", &i),
3637             "Failed to fail to store an item.");
3638     h1->release(h, NULL, i);
3639     checkeq(ENGINE_SUCCESS,
3640             store(h, h1, NULL, OPERATION_SET,"k2", "v2", &i),
3641             "Failed to fail to store an item.");
3642     h1->release(h, NULL, i);
3643     checkeq(ENGINE_SUCCESS,
3644             store(h, h1, NULL, OPERATION_SET,"k3", "v3", &i),
3645             "Failed to fail to store an item.");
3646     h1->release(h, NULL, i);
3647     if (isPersistentBucket(h, h1) && is_full_eviction(h, h1)) {
3648         // MB-21957: FE mode - curr_items is only valid once we flush documents
3649         wait_for_flusher_to_settle(h, h1);
3650     }
3651     verify_curr_items(h, h1, 3, "three items stored");
3652     checkeq(initial_enqueued + 3, get_int_stat(h, h1, "ep_total_enqueued"),
3653             "Expected total_enqueued to increase by 3 after 3 new items");
3654
3655     return SUCCESS;
3656 }
3657
3658 static enum test_result test_curr_items_delete(ENGINE_HANDLE *h,
3659                                                ENGINE_HANDLE_V1 *h1) {
3660     // Verify initial case.
3661     verify_curr_items(h, h1, 0, "init");
3662
3663     // Store some items
3664     write_items(h, h1, 3);
3665     wait_for_flusher_to_settle(h, h1);
3666
3667     // Verify delete case.
3668     checkeq(ENGINE_SUCCESS, del(h, h1, "key1", 0, 0),
3669             "Failed remove with value.");
3670
3671     wait_for_stat_change(h, h1, "curr_items", 3);
3672     verify_curr_items(h, h1, 2, "one item deleted - persisted");
3673
3674     return SUCCESS;
3675 }
3676
3677 static enum test_result test_curr_items_flush(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
3678
3679     // Verify initial case.
3680     verify_curr_items(h, h1, 0, "init");
3681
3682     // Store some items
3683     write_items(h, h1, 3);
3684     wait_for_flusher_to_settle(h, h1);
3685
3686     // Verify flush case.
3687     set_degraded_mode(h, h1, nullptr, true);
3688     checkeq(ENGINE_SUCCESS, h1->flush(h, nullptr),
3689             "Failed to flush");
3690     set_degraded_mode(h, h1, nullptr, false);
3691     verify_curr_items(h, h1, 0, "flush");
3692
3693     return SUCCESS;
3694 }
3695
3696
3697 static enum test_result test_curr_items_dead(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
3698
3699     // Verify initial case.
3700     verify_curr_items(h, h1, 0, "init");
3701
3702     // Store some items
3703     write_items(h, h1, 3);
3704     wait_for_flusher_to_settle(h, h1);
3705
3706     // Verify dead vbucket case.
3707     check(set_vbucket_state(h, h1, 0, vbucket_state_dead),
3708           "Failed set vbucket 0 state to dead");
3709
3710     verify_curr_items(h, h1, 0, "dead vbucket");
3711     checkeq(0, get_int_stat(h, h1, "curr_items_tot"),
3712             "Expected curr_items_tot to be 0 with a dead vbucket");
3713
3714     // Then resurrect.
3715     check(set_vbucket_state(h, h1, 0, vbucket_state_active),
3716           "Failed set vbucket 0 state to active");
3717
3718     verify_curr_items(h, h1, 3, "resurrected vbucket");
3719
3720     // Now completely delete it.
3721     check(set_vbucket_state(h, h1, 0, vbucket_state_dead),
3722           "Failed set vbucket 0 state to dead (2)");
3723     vbucketDelete(h, h1, 0);
3724     checkeq(PROTOCOL_BINARY_RESPONSE_SUCCESS, last_status.load(),
3725             "Expected success deleting vbucket.");
3726     verify_curr_items(h, h1, 0, "del vbucket");
3727     checkeq(0, get_int_stat(h, h1, "curr_items_tot"),
3728             "Expected curr_items_tot to be 0 after deleting a vbucket");
3729
3730     return SUCCESS;
3731 }
3732
3733 static enum test_result test_value_eviction(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
3734     check(set_vbucket_state(h, h1, 1, vbucket_state_active), "Failed to set vbucket state.");
3735
3736     item *i = NULL;
3737     h1->reset_stats(h, NULL);
3738     checkeq(0, get_int_stat(h, h1, "ep_num_value_ejects"),
3739             "Expected reset stats to set ep_num_value_ejects to zero");
3740     checkeq(0, get_int_stat(h, h1, "ep_num_non_resident"),
3741             "Expected all items to be resident");
3742     checkeq(0, get_int_stat(h, h1, "vb_active_num_non_resident"),
3743             "Expected all active vbucket items to be resident");
3744
3745
3746     stop_persistence(h, h1);
3747     checkeq(ENGINE_SUCCESS,
3748             store(h, h1, NULL, OPERATION_SET,"k1", "v1", &i, 0, 0),
3749             "Failed to fail to store an item.");
3750     h1->release(h, NULL, i);
3751     evict_key(h, h1, "k1", 0, "Can't eject: Dirty object.", true);
3752     start_persistence(h, h1);
3753     wait_for_flusher_to_settle(h, h1);
3754     stop_persistence(h, h1);
3755     checkeq(ENGINE_SUCCESS,
3756             store(h, h1, NULL, OPERATION_SET,"k2", "v2", &i, 0, 1),
3757             "Failed to fail to store an item.");
3758     h1->release(h, NULL, i);
3759     evict_key(h, h1, "k2", 1, "Can't eject: Dirty object.", true);
3760     start_persistence(h, h1);
3761     wait_for_flusher_to_settle(h, h1);
3762
3763     evict_key(h, h1, "k1", 0, "Ejected.");
3764     evict_key(h, h1, "k2", 1, "Ejected.");
3765
3766     checkeq(2, get_int_stat(h, h1, "vb_active_num_non_resident"),
3767             "Expected two non-resident items for active vbuckets");
3768
3769     evict_key(h, h1, "k1", 0, "Already ejected.");
3770     evict_key(h, h1, "k2", 1, "Already ejected.");
3771
3772     protocol_binary_request_header *pkt = createPacket(PROTOCOL_BINARY_CMD_EVICT_KEY, 0, 0,
3773                                                        NULL, 0, "missing-key", 11);
3774     pkt->request.vbucket = htons(0);
3775
3776     checkeq(ENGINE_SUCCESS,
3777             h1->unknown_command(h, NULL, pkt, add_response, testHarness.doc_namespace),
3778             "Failed to evict key.");
3779
3780     checkeq(ENGINE_SUCCESS, h1->get_stats(h, NULL, NULL, 0, add_stats),
3781             "Failed to get stats.");
3782     std::string eviction_policy = vals.find("ep_item_eviction_policy")->second;
3783     if (eviction_policy == "value_only") {
3784         checkeq(PROTOCOL_BINARY_RESPONSE_KEY_ENOENT, last_status.load(),
3785                 "expected the key to be missing...");
3786     } else {
3787         // Note that we simply return SUCCESS when EVICT_KEY is issued to
3788         // a non-resident or non-existent key with full eviction to avoid a disk lookup.
3789         checkeq(PROTOCOL_BINARY_RESPONSE_SUCCESS, last_status.load(),
3790             "expected the success for evicting a non-existent key with full eviction");
3791     }
3792     cb_free(pkt);
3793
3794     h1->reset_stats(h, NULL);
3795     checkeq(0, get_int_stat(h, h1, "ep_num_value_ejects"),
3796             "Expected reset stats to set ep_num_value_ejects to zero");
3797
3798     check_key_value(h, h1, "k1", "v1", 2);
3799     checkeq(1, get_int_stat(h, h1, "vb_active_num_non_resident"),
3800             "Expected only one active vbucket item to be non-resident");
3801
3802     check(set_vbucket_state(h, h1, 0, vbucket_state_replica), "Failed to set vbucket state.");
3803     check(set_vbucket_state(h, h1, 1, vbucket_state_replica), "Failed to set vbucket state.");
3804     checkeq(0, get_int_stat(h, h1, "vb_active_num_non_resident"),
3805             "Expected no non-resident items");
3806
3807     return SUCCESS;
3808 }
3809
3810 static enum test_result test_duplicate_items_disk(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
3811     if (!isWarmupEnabled(h, h1)) {
3812         return SKIPPED;
3813     }
3814
3815     check(set_vbucket_state(h, h1, 1, vbucket_state_active), "Failed to set vbucket state.");
3816
3817     std::vector<std::string> keys;
3818     for (int j = 0; j < 100; ++j) {
3819         std::stringstream ss;
3820         ss << "key" << j;
3821         std::string key(ss.str());
3822         keys.push_back(key);
3823     }
3824     std::vector<std::string>::iterator it;
3825     for (it = keys.begin(); it != keys.end(); ++it) {
3826         item *i;
3827         checkeq(ENGINE_SUCCESS,
3828                 store(h, h1, NULL, OPERATION_SET, it->c_str(), "value", &i, 0, 1),
3829                 "Failed to store a value");
3830         h1->release(h, NULL, i);
3831     }
3832     wait_for_flusher_to_settle(h, h1);
3833
3834     // don't need to explicitly set the vbucket state to dead as this is
3835     // done as part of the vbucketDelete. See KVBucket::deleteVBucket
3836     int vb_del_num = get_int_stat(h, h1, "ep_vbucket_del");
3837     vbucketDelete(h, h1, 1);
3838     checkeq(PROTOCOL_BINARY_RESPONSE_SUCCESS, last_status.load(),
3839             "Failure deleting dead bucket.");
3840     check(verify_vbucket_missing(h, h1, 1),
3841           "vbucket 1 was not missing after deleting it.");
3842     // wait for the deletion to successfully complete before setting the
3843     // vbucket state active (which creates the vbucket)
3844     wait_for_stat_change(h, h1, "ep_vbucket_del", vb_del_num);
3845
3846     check(set_vbucket_state(h, h1, 1, vbucket_state_active), "Failed to set vbucket state.");
3847
3848     for (it = keys.begin(); it != keys.end(); ++it) {
3849         item *i;
3850         checkeq(ENGINE_SUCCESS,
3851                 store(h, h1, NULL, OPERATION_SET, it->c_str(), it->c_str(), &i, 0, 1),
3852                 "Failed to store a value");
3853         h1->release(h, NULL, i);
3854     }
3855     wait_for_flusher_to_settle(h, h1);
3856
3857     testHarness.reload_engine(&h, &h1,
3858                               testHarness.engine_path,
3859                               testHarness.get_current_testcase()->cfg,
3860                               true, false);
3861     wait_for_warmup_complete(h, h1);
3862     check(set_vbucket_state(h, h1, 1, vbucket_state_active), "Failed to set vbucket state.");
3863     // Make sure that a key/value item is persisted correctly
3864     for (it = keys.begin(); it != keys.end(); ++it) {
3865         evict_key(h, h1, it->c_str(), 1, "Ejected.");
3866     }
3867     for (it = keys.begin(); it != keys.end(); ++it) {
3868         check_key_value(h, h1, it->c_str(), it->data(), it->size(), 1);
3869     }
3870     checkeq(0, get_int_stat(h, h1, "ep_warmup_dups"),
3871             "Expected no duplicate items from disk");
3872
3873     return SUCCESS;
3874 }
3875
3876 static enum test_result test_disk_gt_ram_golden(ENGINE_HANDLE *h,
3877                                                 ENGINE_HANDLE_V1 *h1) {
3878     // Check/grab initial state.
3879     const auto initial_enqueued = get_int_stat(h, h1, "ep_total_enqueued");
3880     int itemsRemoved = get_int_stat(h, h1, "ep_items_rm_from_checkpoints");
3881
3882     // Store some data and check post-set state.
3883     wait_for_persisted_value(h, h1, "k1", "some value");
3884     testHarness.time_travel(65);
3885     wait_for_stat_change(h, h1, "ep_items_rm_from_checkpoints", itemsRemoved);
3886
3887     checkeq(0, get_int_stat(h, h1, "ep_bg_fetched"),
3888             "Should start with zero bg fetches");
3889     checkeq((initial_enqueued + 1),
3890             get_int_stat(h, h1, "ep_total_enqueued"),
3891             "Should have additional item enqueued after store");
3892     int kv_size = get_int_stat(h, h1, "ep_kv_size");
3893     int mem_used = get_int_stat(h, h1, "mem_used");
3894
3895     // Evict the data.
3896     evict_key(h, h1, "k1");
3897
3898     int kv_size2 = get_int_stat(h, h1, "ep_kv_size");
3899     int mem_used2 = get_int_stat(h, h1, "mem_used");
3900
3901     checkgt(kv_size, kv_size2, "kv_size should have decreased after eviction");
3902     checkgt(mem_used, mem_used2, "mem_used should have decreased after eviction");
3903
3904     // Reload the data.
3905     check_key_value(h, h1, "k1", "some value", 10);
3906
3907     int kv_size3 = get_int_stat(h, h1, "ep_kv_size");
3908     int mem_used3 = get_int_stat(h, h1, "mem_used");
3909
3910     checkeq(1, get_int_stat(h, h1, "ep_bg_fetched"),
3911             "BG fetches should be one after reading an evicted key");
3912     checkeq((initial_enqueued + 1), get_int_stat(h, h1, "ep_total_enqueued"),
3913             "Item should not be marked dirty after reading an evicted key");
3914
3915     checkeq(kv_size, kv_size3,
3916             "kv_size should have returned to initial value after restoring evicted item");
3917     checkle(mem_used, mem_used3,
3918             "mem_used should have returned to initial value (or less) after restoring evicted item");
3919
3920     itemsRemoved = get_int_stat(h, h1, "ep_items_rm_from_checkpoints");
3921     // Delete the value and make sure things return correctly.
3922     int numStored = get_int_stat(h, h1, "ep_total_persisted");
3923     checkeq(ENGINE_SUCCESS,
3924             del(h, h1, "k1", 0, 0), "Failed remove with value.");
3925     wait_for_stat_change(h, h1, "ep_total_persisted", numStored);
3926     testHarness.time_travel(65);
3927     wait_for_stat_change(h, h1, "ep_items_rm_from_checkpoints", itemsRemoved);
3928
3929     return SUCCESS;
3930 }
3931
3932 static enum test_result test_disk_gt_ram_paged_rm(ENGINE_HANDLE *h,
3933                                                   ENGINE_HANDLE_V1 *h1) {
3934     // Check/grab initial state.
3935     int overhead = get_int_stat(h, h1, "ep_overhead");
3936     const auto initial_enqueued = get_int_stat(h, h1, "ep_total_enqueued");
3937
3938     // Store some data and check post-set state.
3939     wait_for_persisted_value(h, h1, "k1", "some value");
3940     checkeq(0, get_int_stat(h, h1, "ep_bg_fetched"),
3941             "bg_fetched should initially be zero");
3942     checkeq(initial_enqueued + 1, get_int_stat(h, h1, "ep_total_enqueued"),
3943             "Expected total_enqueued to increase by 1 after storing 1 value");
3944     checkge(get_int_stat(h, h1, "ep_overhead"), overhead,
3945             "Fell below initial overhead.");
3946
3947     // Evict the data.
3948     evict_key(h, h1, "k1");
3949
3950     // Delete the value and make sure things return correctly.
3951     int itemsRemoved = get_int_stat(h, h1, "ep_items_rm_from_checkpoints");
3952     int numStored = get_int_stat(h, h1, "ep_total_persisted");
3953     checkeq(ENGINE_SUCCESS,
3954             del(h, h1, "k1", 0, 0), "Failed remove with value.");
3955     wait_for_stat_change(h, h1, "ep_total_persisted", numStored);
3956     testHarness.time_travel(65);
3957     wait_for_stat_change(h, h1, "ep_items_rm_from_checkpoints", itemsRemoved);
3958
3959     return SUCCESS;
3960 }
3961
3962 static enum test_result test_disk_gt_ram_update_paged_out(ENGINE_HANDLE *h,
3963                                                           ENGINE_HANDLE_V1 *h1) {
3964     wait_for_persisted_value(h, h1, "k1", "some value");
3965
3966     evict_key(h, h1, "k1");
3967
3968     item *i = NULL;
3969     checkeq(ENGINE_SUCCESS,
3970             store(h, h1, NULL, OPERATION_SET, "k1", "new value", &i),
3971             "Failed to update an item.");
3972     h1->release(h, NULL, i);
3973
3974     check_key_value(h, h1, "k1", "new value", 9);
3975
3976     checkeq(0, get_int_stat(h, h1, "ep_bg_fetched"), "bg fetched something");
3977
3978     return SUCCESS;
3979 }
3980
3981 static enum test_result test_disk_gt_ram_delete_paged_out(ENGINE_HANDLE *h,
3982                                                           ENGINE_HANDLE_V1 *h1) {
3983     wait_for_persisted_value(h, h1, "k1", "some value");
3984
3985     evict_key(h, h1, "k1");
3986
3987     checkeq(ENGINE_SUCCESS,
3988             del(h, h1, "k1", 0, 0), "Failed to delete.");
3989
3990     check(verify_key(h, h1, "k1") == ENGINE_KEY_ENOENT, "Expected miss.");
3991
3992     cb_assert(0 == get_int_stat(h, h1, "ep_bg_fetched"));
3993
3994     return SUCCESS;
3995 }
3996
3997 extern "C" {
3998     static void bg_set_thread(void *arg) {
3999         ThreadData *td(static_cast<ThreadData*>(arg));
4000
4001         usleep(2600); // Exacerbate race condition.
4002
4003         item *i = NULL;
4004         checkeq(ENGINE_SUCCESS,
4005                 store(td->h, td->h1, NULL, OPERATION_SET,
4006                       "k1", "new value", &i),
4007                 "Failed to update an item.");