1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2012 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 #include "ep_test_apis.h"
19 #include "ep_testsuite_common.h"
20 
21 #include <folly/portability/SysStat.h>
22 #include <mcbp/protocol/framebuilder.h>
23 #include <memcached/util.h>
24 #include <platform/cb_malloc.h>
25 #include <platform/cbassert.h>
26 #include <platform/dirutils.h>
27 #include <platform/strerror.h>
28 
29 #include <stdlib.h>
30 #include <string.h>
31 
32 #include <algorithm>
33 #include <chrono>
34 #include <iostream>
35 #include <list>
36 #include <mutex>
37 #include <sstream>
38 #include <thread>
39 
40 #include "mock/mock_dcp.h"
41 
42 using namespace std::string_literals;
43 
CouchstoreFileAccessGuard(std::string dbName,CouchstoreFileAccessGuard::Mode mode)44 CouchstoreFileAccessGuard::CouchstoreFileAccessGuard(
45         std::string dbName, CouchstoreFileAccessGuard::Mode mode) {
46     /* Make the couchstore files in the db directory unwritable.
47      *
48      * Note we can't just make the directory itself unwritable as other
49      * files (e.g. stats.json) need to be written and we are just testing
50      * document write failures here.
51      */
52     const auto dbFiles = cb::io::findFilesContaining(dbName, ".couch.");
53     checkeq(size_t{1},
54             dbFiles.size(),
55             ("Expected to find exactly 1 data file in db directory '"s +
56              dbName + "', found:" + std::to_string(dbFiles.size()))
57                     .c_str());
58 
59     filename = dbFiles.front();
60     // Save existing permissions
61     checkeq(0,
62             lstat(filename.c_str(), &originalStat),
63             ("Failed to read existing permissions for file '"s + filename +
64              "': " + cb_strerror())
65                     .c_str());
66 
67     const auto perms =
68             (mode == Mode::ReadOnly) ? (S_IRUSR | S_IRGRP | S_IROTH) : 0;
69 
70     checkeq(0,
71             chmod(filename.c_str(), perms),
72             ("Failed to make file '"s + dbFiles.at(0) +
73              "' read-only: " + cb_strerror())
74                     .c_str());
75 }
76 
~CouchstoreFileAccessGuard()77 CouchstoreFileAccessGuard::~CouchstoreFileAccessGuard() {
78     // Restore permissions to before we changed them.
79     checkeq(0,
80             chmod(filename.c_str(), originalStat.st_mode),
81             ("Failed to make restore permissions to file '"s + filename +
82              "': " + cb_strerror())
83                     .c_str());
84 }
85 
86 template<typename T> class HistogramStats;
87 
88 // Due to the limitations of the add_stats callback (essentially we cannot pass
89 // a context into it) we instead have a single, global `vals` map. The
90 // vals_mutex is to ensure serialised modifications to this data structure.
91 std::mutex vals_mutex;
92 statistic_map vals;
93 
94 // get_stat and get_histo_stat can only be called one at a time as they use
95 // the three global variables (requested_stat_name, actual_stat_value and
96 // histogram_stat_int_value).  Therefore the two functions need to acquire a
97 // lock and keep it for the whole function duration.
98 
99 // The requested_stat_name and actual_stat_value are used in an optimized
100 // add_stats callback (add_individual_stat) which checks for one stat
101 // (and hence doesn't have to keep a map of all of them).
102 struct {
103     std::mutex mutex;
104     std::string requested_stat_name;
105     std::string actual_stat_value;
106     /* HistogramStats<T>* is supported C++14 onwards.
107      * Until then use a separate ptr for each type.
108      */
109     HistogramStats<uint64_t>* histogram_stat_int_value;
110 } get_stat_context;
111 
112 bool dump_stats = false;
113 std::atomic<cb::mcbp::Status> last_status(cb::mcbp::Status::Success);
114 std::string last_key;
115 std::string last_body;
116 std::string last_ext;
117 std::atomic<uint64_t> last_cas(0);
118 std::atomic<uint8_t> last_datatype(0x00);
119 ItemMetaData last_meta;
120 std::atomic<uint64_t> last_uuid(0);
121 std::atomic<uint64_t> last_seqno(0);
122 
123 /* HistogramBinStats is used to hold a histogram bin object a histogram stat.
124    This is a class used to hold already computed stats. Hence we do not expect
125    any change once a bin object is created */
126 template<typename T>
127 class HistogramBinStats {
128 public:
HistogramBinStats(const T & s,const T & e,uint64_t count)129     HistogramBinStats(const T& s, const T& e, uint64_t count)
130         : start_(s), end_(e), count_(count) { }
131 
start() const132     T start() const {
133         return start_;
134     }
135 
end() const136     T end() const {
137         return end_;
138     }
139 
count() const140     uint64_t count() const {
141         return count_;
142     }
143 
144 private:
145     T start_;
146     T end_;
147     uint64_t count_;
148 };
149 
150 
151 /* HistogramStats is used to hold necessary info from a histogram stat.
152    Since this class used to hold already computed stats, only write apis to add
153    new bins is implemented */
154 template<typename T>
155 class HistogramStats {
156 public:
HistogramStats()157     HistogramStats() : total_count(0) {}
158 
159     /* Add a new bin */
add_bin(const T & start,const T & end,uint64_t count)160     void add_bin(const T& start, const T& end, uint64_t count) {
161         bins.push_back(HistogramBinStats<T>(start, end, count));
162         total_count += count;
163     }
164 
165     /* Num of bins in the histogram */
num_bins() const166     size_t num_bins() const {
167         return bins.size();
168     }
169 
total() const170     uint64_t total() const {
171         return total_count;
172     }
173 
174     /* Add a bin iterator when needed */
175 private:
176     /* List of all the bins in the histogram stats */
177     std::list<HistogramBinStats<T>> bins;
178     /* Total number of samples across all histogram bins */
179     uint64_t total_count;
180 };
181 
182 static void get_histo_stat(EngineIface* h,
183                            const char* statname,
184                            const char* statkey);
185 
186 void encodeExt(char* buffer, uint32_t val, size_t offset = 0);
187 void encodeWithMetaExt(char *buffer, ItemMetaData *meta);
188 
decayingSleep(useconds_t * sleepTime)189 void decayingSleep(useconds_t *sleepTime) {
190     static const useconds_t maxSleepTime = 500000;
191     std::this_thread::sleep_for(std::chrono::microseconds(*sleepTime));
192     *sleepTime = std::min(*sleepTime << 1, maxSleepTime);
193 }
194 
add_response(const void * key,uint16_t keylen,const void * ext,uint8_t extlen,const void * body,uint32_t bodylen,uint8_t datatype,cb::mcbp::Status status,uint64_t cas,const void * cookie)195 bool add_response(const void* key,
196                   uint16_t keylen,
197                   const void* ext,
198                   uint8_t extlen,
199                   const void* body,
200                   uint32_t bodylen,
201                   uint8_t datatype,
202                   cb::mcbp::Status status,
203                   uint64_t cas,
204                   const void* cookie) {
205     (void)cookie;
206     static std::mutex m;
207     std::lock_guard<std::mutex> lg(m);
208     last_status.store(status);
209     last_body.assign(static_cast<const char*>(body), bodylen);
210     last_ext.assign(static_cast<const char*>(ext), extlen);
211     last_key.assign(static_cast<const char*>(key), keylen);
212     last_cas.store(cas);
213     last_datatype.store(datatype);
214     return true;
215 }
216 
add_response_set_del_meta(const void * key,uint16_t keylen,const void * ext,uint8_t extlen,const void * body,uint32_t bodylen,uint8_t datatype,cb::mcbp::Status status,uint64_t cas,const void * cookie)217 bool add_response_set_del_meta(const void* key,
218                                uint16_t keylen,
219                                const void* ext,
220                                uint8_t extlen,
221                                const void* body,
222                                uint32_t bodylen,
223                                uint8_t datatype,
224                                cb::mcbp::Status status,
225                                uint64_t cas,
226                                const void* cookie) {
227     (void)cookie;
228     const auto* ext_bytes = reinterpret_cast<const uint8_t*>(ext);
229     if (ext && extlen > 0) {
230         uint64_t vb_uuid;
231         uint64_t seqno;
232         memcpy(&vb_uuid, ext_bytes, 8);
233         memcpy(&seqno, ext_bytes + 8, 8);
234         last_uuid.store(ntohll(vb_uuid));
235         last_seqno.store(ntohll(seqno));
236     }
237 
238     return add_response(key, keylen, ext, extlen, body, bodylen, datatype,
239                         status, cas, cookie);
240 }
241 
add_response_ret_meta(const void * key,uint16_t keylen,const void * ext,uint8_t extlen,const void * body,uint32_t bodylen,uint8_t datatype,cb::mcbp::Status status,uint64_t cas,const void * cookie)242 bool add_response_ret_meta(const void* key,
243                            uint16_t keylen,
244                            const void* ext,
245                            uint8_t extlen,
246                            const void* body,
247                            uint32_t bodylen,
248                            uint8_t datatype,
249                            cb::mcbp::Status status,
250                            uint64_t cas,
251                            const void* cookie) {
252     (void)cookie;
253     const auto* ext_bytes = reinterpret_cast<const uint8_t*>(ext);
254     if (ext && extlen == 16) {
255         memcpy(&last_meta.flags, ext_bytes, 4);
256         memcpy(&last_meta.exptime, ext_bytes + 4, 4);
257         last_meta.exptime = ntohl(last_meta.exptime);
258         uint64_t revId = 0;
259         memcpy(&revId, ext_bytes + 8, 8);
260         last_meta.revSeqno = ntohll(revId);
261         last_meta.cas = cas;
262     }
263     return add_response(key, keylen, ext, extlen, body, bodylen, datatype,
264                         status, cas, cookie);
265 }
266 
add_stats(const char * key,const uint16_t klen,const char * val,const uint32_t vlen,gsl::not_null<const void * >)267 void add_stats(const char* key,
268                const uint16_t klen,
269                const char* val,
270                const uint32_t vlen,
271                gsl::not_null<const void*>) {
272     std::string k(key, klen);
273     std::string v(val, vlen);
274 
275     if (dump_stats) {
276         std::cout << "stat[" << k << "] = " << v << std::endl;
277     }
278 
279     std::lock_guard<std::mutex> lh(vals_mutex);
280     vals[k] = v;
281 }
282 
283 /* Callback passed to engine interface `get_stats`, used by get_int_stat and
284  * friends to lookup a specific stat. If `key` matches the requested key name,
285  * then record its value in actual_stat_value.
286  */
add_individual_stat(const char * key,const uint16_t klen,const char * val,const uint32_t vlen,gsl::not_null<const void * >)287 void add_individual_stat(const char* key,
288                          const uint16_t klen,
289                          const char* val,
290                          const uint32_t vlen,
291                          gsl::not_null<const void*>) {
292     if (get_stat_context.actual_stat_value.empty() &&
293             get_stat_context.requested_stat_name.compare(
294                     0, get_stat_context.requested_stat_name.size(),
295                     key, klen) == 0) {
296         get_stat_context.actual_stat_value = std::string(val, vlen);
297     }
298 }
299 
add_individual_histo_stat(const char * key,const uint16_t klen,const char * val,const uint32_t vlen,gsl::not_null<const void * > cookie)300 void add_individual_histo_stat(const char* key,
301                                const uint16_t klen,
302                                const char* val,
303                                const uint32_t vlen,
304                                gsl::not_null<const void*> cookie) {
305     /* Convert key to string */
306     std::string key_str(key, klen);
307     /* Exclude mean value keys e.g. backfill_tasks_mean */
308     if (key_str.find("_mean") != std::string::npos) {
309         return;
310     }
311 
312     size_t pos1 = key_str.find(get_stat_context.requested_stat_name);
313     if (pos1 != std::string::npos) {
314         get_stat_context.actual_stat_value.append(val, vlen);
315         /* Parse start and end from the key.
316            Key is in the format task_name_START,END (backfill_tasks_20,100)
317          */
318         pos1 += get_stat_context.requested_stat_name.length();
319         /* Find ',' to move to end of bin_start */
320         size_t pos2 = key_str.find(',', pos1);
321         if ((std::string::npos == pos2) || (pos1 >= pos2)) {
322             throw std::invalid_argument("Malformed histogram stat: " + key_str);
323         }
324         auto start = std::stoull(std::string(key_str, pos1, pos2));
325 
326         /* Move next to ',' for starting character of bin_end */
327         pos1 = pos2 + 1;
328         /* key_str ends with bin_end */
329         pos2 = key_str.length();
330         if (pos1 >= pos2) {
331             throw std::invalid_argument("Malformed histogram stat: " + key_str);
332         }
333         auto end = std::stoull(std::string(key_str, pos1, pos2));
334         get_stat_context.histogram_stat_int_value->add_bin(
335                 start, end, std::stoull(val));
336     }
337 }
338 
encodeExt(char * buffer,uint32_t val,size_t offset)339 void encodeExt(char* buffer, uint32_t val, size_t offset) {
340     val = htonl(val);
341     memcpy(buffer + offset, (char*)&val, sizeof(val));
342 }
343 
encodeWithMetaExt(char * buffer,uint64_t cas,uint64_t revSeqno,uint32_t flags,uint32_t exp)344 void encodeWithMetaExt(char* buffer,
345                        uint64_t cas,
346                        uint64_t revSeqno,
347                        uint32_t flags,
348                        uint32_t exp) {
349     memcpy(buffer, (char*)&flags, sizeof(flags));
350     memcpy(buffer + 4, (char*)&exp, sizeof(exp));
351     memcpy(buffer + 8, (char*)&revSeqno, sizeof(revSeqno));
352     memcpy(buffer + 16, (char*)&cas, sizeof(cas));
353 }
354 
encodeWithMetaExt(char * buffer,RawItemMetaData * meta)355 void encodeWithMetaExt(char* buffer, RawItemMetaData* meta) {
356     uint32_t flags = meta->flags;
357     uint32_t exp = htonl(meta->exptime);
358     uint64_t seqno = htonll(meta->revSeqno);
359     uint64_t cas = htonll(meta->cas);
360     encodeWithMetaExt(buffer, cas, seqno, flags, exp);
361 }
362 
encodeWithMetaExt(char * buffer,ItemMetaData * meta)363 void encodeWithMetaExt(char* buffer, ItemMetaData* meta) {
364     uint32_t flags = meta->flags;
365     uint32_t exp = htonl(meta->exptime);
366     uint64_t seqno = htonll(meta->revSeqno);
367     uint64_t cas = htonll(meta->cas);
368     encodeWithMetaExt(buffer, cas, seqno, flags, exp);
369 }
370 
createCheckpoint(EngineIface * h)371 void createCheckpoint(EngineIface* h) {
372     auto request = createPacket(cb::mcbp::ClientOpcode::CreateCheckpoint);
373     checkeq(ENGINE_SUCCESS,
374             h->unknown_command(nullptr, *request, add_response),
375             "Failed to create a new checkpoint.");
376 }
377 
del(EngineIface * h,const char * key,uint64_t cas,Vbid vbucket,const void * cookie)378 ENGINE_ERROR_CODE del(EngineIface* h,
379                       const char* key,
380                       uint64_t cas,
381                       Vbid vbucket,
382                       const void* cookie) {
383     mutation_descr_t mut_info{};
384     return del(h, key, &cas, vbucket, cookie, &mut_info);
385 }
386 
del(EngineIface * h,const char * key,uint64_t * cas,Vbid vbucket,const void * cookie,mutation_descr_t * mut_info)387 ENGINE_ERROR_CODE del(EngineIface* h,
388                       const char* key,
389                       uint64_t* cas,
390                       Vbid vbucket,
391                       const void* cookie,
392                       mutation_descr_t* mut_info) {
393     bool create_cookie = false;
394     if (cookie == nullptr) {
395         cookie = testHarness->create_cookie();
396         create_cookie = true;
397     }
398 
399     auto ret = h->remove(cookie,
400                          DocKey(key, DocKeyEncodesCollectionId::No),
401                          *cas,
402                          vbucket,
403                          {},
404                          *mut_info);
405     if (create_cookie) {
406         testHarness->destroy_cookie(cookie);
407     }
408 
409     return ret;
410 }
411 
412 /** Simplified version of store for handling the common case of performing
413  * a delete with a value.
414  */
delete_with_value(EngineIface * h,const void * cookie,uint64_t cas,const char * key,cb::const_char_buffer value,cb::mcbp::Datatype datatype)415 ENGINE_ERROR_CODE delete_with_value(EngineIface* h,
416                                     const void* cookie,
417                                     uint64_t cas,
418                                     const char* key,
419                                     cb::const_char_buffer value,
420                                     cb::mcbp::Datatype datatype) {
421     auto ret = storeCasVb11(h,
422                             cookie,
423                             OPERATION_SET,
424                             key,
425                             value.data(),
426                             value.size(),
427                             9258,
428                             cas,
429                             Vbid(0),
430                             /*exp*/ 0,
431                             uint8_t(datatype),
432                             DocumentState::Deleted);
433     wait_for_flusher_to_settle(h);
434 
435     return ENGINE_ERROR_CODE(ret.first);
436 }
437 
del_with_meta(EngineIface * h,const char * key,const size_t keylen,const Vbid vb,ItemMetaData * itemMeta,uint64_t cas_for_delete,uint32_t options,const void * cookie,const std::vector<char> & nmeta,protocol_binary_datatype_t datatype,const std::vector<char> & value)438 ENGINE_ERROR_CODE del_with_meta(EngineIface* h,
439                                 const char* key,
440                                 const size_t keylen,
441                                 const Vbid vb,
442                                 ItemMetaData* itemMeta,
443                                 uint64_t cas_for_delete,
444                                 uint32_t options,
445                                 const void* cookie,
446                                 const std::vector<char>& nmeta,
447                                 protocol_binary_datatype_t datatype,
448                                 const std::vector<char>& value) {
449     RawItemMetaData meta{itemMeta->cas,
450                          itemMeta->revSeqno,
451                          itemMeta->flags,
452                          itemMeta->exptime};
453     return del_with_meta(h,
454                          key,
455                          keylen,
456                          vb,
457                          &meta,
458                          cas_for_delete,
459                          options,
460                          cookie,
461                          nmeta,
462                          datatype,
463                          value);
464 }
465 
del_with_meta(EngineIface * h,const char * key,const size_t keylen,const Vbid vb,RawItemMetaData * itemMeta,uint64_t cas_for_delete,uint32_t options,const void * cookie,const std::vector<char> & nmeta,protocol_binary_datatype_t datatype,const std::vector<char> & value)466 ENGINE_ERROR_CODE del_with_meta(EngineIface* h,
467                                 const char* key,
468                                 const size_t keylen,
469                                 const Vbid vb,
470                                 RawItemMetaData* itemMeta,
471                                 uint64_t cas_for_delete,
472                                 uint32_t options,
473                                 const void* cookie,
474                                 const std::vector<char>& nmeta,
475                                 protocol_binary_datatype_t datatype,
476                                 const std::vector<char>& value) {
477     size_t blen = 24;
478     std::unique_ptr<char[]> ext(new char[30]);
479     std::unique_ptr<ExtendedMetaData> emd;
480 
481     encodeWithMetaExt(ext.get(), itemMeta);
482 
483     if (options) {
484         uint32_t optionsSwapped = htonl(options);
485         memcpy(ext.get() + blen, (char*)&optionsSwapped, sizeof(optionsSwapped));
486         blen += sizeof(uint32_t);
487     }
488 
489     if (nmeta.size() > 0) {
490         uint16_t nmetaSize = htons(nmeta.size());
491         memcpy(ext.get() + blen, (char*)&nmetaSize, sizeof(nmetaSize));
492         blen += sizeof(uint16_t);
493     }
494 
495     auto pkt = createPacket(cb::mcbp::ClientOpcode::DelWithMeta,
496                             vb,
497                             cas_for_delete,
498                             {ext.get(), blen},
499                             {key, keylen},
500                             {value.data(), value.size()},
501                             datatype,
502                             {nmeta.data(), nmeta.size()});
503 
504     return h->unknown_command(cookie, *pkt, add_response_set_del_meta);
505 }
506 
evict_key(EngineIface * h,const char * key,Vbid vbucketId,const char * msg,bool expectError)507 void evict_key(EngineIface* h,
508                const char* key,
509                Vbid vbucketId,
510                const char* msg,
511                bool expectError) {
512     int nonResidentItems = get_int_stat(h, "ep_num_non_resident");
513     int numEjectedItems = get_int_stat(h, "ep_num_value_ejects");
514     auto pkt = createPacket(cb::mcbp::ClientOpcode::EvictKey,
515                             vbucketId,
516                             0,
517                             {},
518                             {key, strlen(key)});
519     checkeq(ENGINE_SUCCESS,
520             h->unknown_command(NULL, *pkt, add_response),
521             "Failed to perform CMD_EVICT_KEY.");
522 
523     if (expectError) {
524         checkeq(cb::mcbp::Status::KeyEexists, last_status.load(),
525                 "evict_key: expected KEY_EEXISTS when evicting key");
526     } else {
527         if (last_body != "Already ejected.") {
528             nonResidentItems++;
529             numEjectedItems++;
530         }
531         checkeq(cb::mcbp::Status::Success, last_status.load(),
532                 "evict_key: expected SUCCESS when evicting key.");
533     }
534 
535     checkeq(nonResidentItems,
536             get_int_stat(h, "ep_num_non_resident"),
537             "Incorrect number of non-resident items");
538     checkeq(numEjectedItems,
539             get_int_stat(h, "ep_num_value_ejects"),
540             "Incorrect number of ejected items");
541 
542     if (msg != NULL && last_body != msg) {
543         fprintf(stderr, "Expected evict to return ``%s'', but it returned ``%s''\n",
544                 msg, last_body.c_str());
545         abort();
546     }
547 }
548 
checkpointPersistence(EngineIface * h,uint64_t checkpoint_id,Vbid vb)549 ENGINE_ERROR_CODE checkpointPersistence(EngineIface* h,
550                                         uint64_t checkpoint_id,
551                                         Vbid vb) {
552     checkpoint_id = htonll(checkpoint_id);
553     auto request =
554             createPacket(cb::mcbp::ClientOpcode::CheckpointPersistence,
555                          vb,
556                          0,
557                          {},
558                          {},
559                          {(const char*)&checkpoint_id, sizeof(uint64_t)});
560     ENGINE_ERROR_CODE rv = h->unknown_command(nullptr, *request, add_response);
561     return rv;
562 }
563 
seqnoPersistence(EngineIface * h,const void * cookie,Vbid vbucket,uint64_t seqno)564 ENGINE_ERROR_CODE seqnoPersistence(EngineIface* h,
565                                    const void* cookie,
566                                    Vbid vbucket,
567                                    uint64_t seqno) {
568     seqno = htonll(seqno);
569     char buffer[8];
570     memcpy(buffer, &seqno, sizeof(uint64_t));
571     auto request = createPacket(
572             cb::mcbp::ClientOpcode::SeqnoPersistence, vbucket, 0, {buffer, 8});
573     return h->unknown_command(cookie, *request, add_response);
574 }
575 
gat(EngineIface * h,const char * key,Vbid vb,uint32_t exp)576 cb::EngineErrorItemPair gat(EngineIface* h,
577                             const char* key,
578                             Vbid vb,
579                             uint32_t exp) {
580     const auto* cookie = testHarness->create_cookie();
581     auto ret = h->get_and_touch(
582             cookie, DocKey(key, DocKeyEncodesCollectionId::No), vb, exp, {});
583     testHarness->destroy_cookie(cookie);
584 
585     if (ret.first == cb::engine_errc::success) {
586         item_info info;
587         check(h->get_item_info(ret.second.get(), &info),
588               "gat Failed to get item info");
589 
590         last_body.assign((const char*)info.value[0].iov_base,
591                          info.value[0].iov_len);
592     }
593     return ret;
594 }
595 
get_item_info(EngineIface * h,item_info * info,const char * key,Vbid vb)596 bool get_item_info(EngineIface* h, item_info* info, const char* key, Vbid vb) {
597     auto ret = get(h, NULL, key, vb);
598     if (ret.first != cb::engine_errc::success) {
599         return false;
600     }
601     if (!h->get_item_info(ret.second.get(), info)) {
602         fprintf(stderr, "get_item_info failed\n");
603         return false;
604     }
605 
606     return true;
607 }
608 
getl(EngineIface * h,const void * cookie,const char * key,Vbid vb,uint32_t lock_timeout)609 cb::EngineErrorItemPair getl(EngineIface* h,
610                              const void* cookie,
611                              const char* key,
612                              Vbid vb,
613                              uint32_t lock_timeout) {
614     bool create_cookie = false;
615     if (cookie == nullptr) {
616         cookie = testHarness->create_cookie();
617         create_cookie = true;
618     }
619     auto ret = h->get_locked(cookie,
620                              DocKey(key, DocKeyEncodesCollectionId::No),
621                              vb,
622                              lock_timeout);
623     if (create_cookie) {
624         testHarness->destroy_cookie(cookie);
625     }
626 
627     return ret;
628 }
629 
get_meta(EngineIface * h,const char * key,const void * cookie)630 bool get_meta(EngineIface* h,
631               const char* key,
632               const void* cookie) {
633     cb::EngineErrorMetadataPair out;
634 
635     return get_meta(h, key, out, cookie);
636 }
637 
get_meta(EngineIface * h,const char * key,cb::EngineErrorMetadataPair & out,const void * cookie)638 bool get_meta(EngineIface* h,
639               const char* key,
640               cb::EngineErrorMetadataPair& out,
641               const void* cookie) {
642     DocKey docKey(key, DocKeyEncodesCollectionId::No);
643     bool cookie_create = false;
644     if (cookie == nullptr) {
645         cookie = testHarness->create_cookie();
646         cookie_create = true;
647     }
648 
649     out = h->get_meta(cookie, docKey, Vbid(0));
650 
651     if (cookie_create) {
652         testHarness->destroy_cookie(cookie);
653     }
654 
655     return out.first == cb::engine_errc::success;
656 }
657 
observe(EngineIface * h,std::map<std::string,Vbid> obskeys)658 ENGINE_ERROR_CODE observe(EngineIface* h, std::map<std::string, Vbid> obskeys) {
659     std::stringstream value;
660     std::map<std::string, Vbid>::iterator it;
661     for (it = obskeys.begin(); it != obskeys.end(); ++it) {
662         Vbid vb = it->second.hton();
663         uint16_t keylen = htons(it->first.length());
664         value.write((char*)&vb, sizeof(Vbid));
665         value.write((char*) &keylen, sizeof(uint16_t));
666         value.write(it->first.c_str(), it->first.length());
667     }
668 
669     auto request = createPacket(
670             cb::mcbp::ClientOpcode::Observe, Vbid(0), 0, {}, {}, value.str());
671 
672     return h->unknown_command(nullptr, *request, add_response);
673 }
674 
observe_seqno(EngineIface * h,Vbid vb_id,uint64_t uuid)675 ENGINE_ERROR_CODE observe_seqno(EngineIface* h, Vbid vb_id, uint64_t uuid) {
676     uint64_t vb_uuid = htonll(uuid);
677     std::stringstream data;
678     data.write((char *) &vb_uuid, sizeof(uint64_t));
679 
680     auto request = createPacket(
681             cb::mcbp::ClientOpcode::ObserveSeqno, vb_id, 0, {}, {}, data.str());
682     return h->unknown_command(nullptr, *request, add_response);
683 }
684 
get_replica(EngineIface * h,const char * key,Vbid vbid)685 void get_replica(EngineIface* h, const char* key, Vbid vbid) {
686     auto request = createPacket(cb::mcbp::ClientOpcode::GetReplica,
687                                 vbid,
688                                 0,
689                                 {},
690                                 {key, strlen(key)});
691     checkeq(ENGINE_SUCCESS,
692             h->unknown_command(nullptr, *request, add_response),
693             "Get Replica Failed");
694 }
695 
prepare_get_replica(EngineIface * h,vbucket_state_t state,bool makeinvalidkey)696 unique_request_ptr prepare_get_replica(EngineIface* h,
697                                        vbucket_state_t state,
698                                        bool makeinvalidkey) {
699     Vbid id(0);
700     const char *key = "k0";
701     auto request = createPacket(
702             cb::mcbp::ClientOpcode::GetReplica, id, 0, {}, {key, strlen(key)});
703 
704     if (!makeinvalidkey) {
705         checkeq(ENGINE_SUCCESS,
706                 store(h,
707                       nullptr,
708                       OPERATION_SET,
709                       key,
710                       "replicadata",
711                       nullptr,
712                       0,
713                       id),
714                 "Get Replica Failed");
715 
716         check(set_vbucket_state(h, id, state),
717               "Failed to set vbucket active state, Get Replica Failed");
718     }
719 
720     return request;
721 }
722 
set_param(EngineIface * h,cb::mcbp::request::SetParamPayload::Type paramtype,const char * param,const char * val,Vbid vb)723 bool set_param(EngineIface* h,
724                cb::mcbp::request::SetParamPayload::Type paramtype,
725                const char* param,
726                const char* val,
727                Vbid vb) {
728     cb::mcbp::request::SetParamPayload payload;
729     payload.setParamType(paramtype);
730     auto buffer = payload.getBuffer();
731     auto request = createPacket(
732             cb::mcbp::ClientOpcode::SetParam,
733             vb,
734             0,
735             {reinterpret_cast<const char*>(buffer.data()), buffer.size()},
736             {param, strlen(param)},
737             {val, strlen(val)});
738 
739     if (h->unknown_command(nullptr, *request, add_response) != ENGINE_SUCCESS) {
740         return false;
741     }
742 
743     return last_status == cb::mcbp::Status::Success;
744 }
745 
set_vbucket_state(EngineIface * h,Vbid vb,vbucket_state_t state,cb::const_char_buffer meta)746 bool set_vbucket_state(EngineIface* h,
747                        Vbid vb,
748                        vbucket_state_t state,
749                        cb::const_char_buffer meta) {
750     uint8_t datatype = 0x0;
751     std::unique_ptr<char[]> extras;
752     uint8_t extSize = 0;
753     if (!meta.empty()) {
754         // mad-hatter encoding
755         datatype = 0x1;
756         extSize = 1;
757         extras = std::make_unique<char[]>(extSize);
758         *extras.get() = static_cast<char>(state);
759     } else {
760         // pre mad-hatter encoding
761         extSize = 4;
762         extras = std::make_unique<char[]>(extSize);
763         encodeExt(extras.get(), static_cast<int32_t>(state));
764     }
765 
766     auto request = createPacket(cb::mcbp::ClientOpcode::SetVbucket,
767                                 vb,
768                                 0 /*cas*/,
769                                 {extras.get(), extSize},
770                                 {} /*key*/,
771                                 meta /*value*/,
772                                 datatype);
773 
774     if (h->unknown_command(nullptr, *request, add_response) != ENGINE_SUCCESS) {
775         return false;
776     }
777 
778     return last_status == cb::mcbp::Status::Success;
779 }
780 
get_all_vb_seqnos(EngineIface * h,boost::optional<RequestedVBState> state,const void * cookie,boost::optional<CollectionIDType> collection)781 bool get_all_vb_seqnos(EngineIface* h,
782                        boost::optional<RequestedVBState> state,
783                        const void* cookie,
784                        boost::optional<CollectionIDType> collection) {
785     unique_request_ptr pkt;
786 
787     if (collection) {
788         if (!state) {
789             // Do the same check so we can print for the user...
790             checkeq(state.is_initialized(),
791                     true,
792                     "State must be set when "
793                     "collection is specified");
794             return false;
795         }
796 
797         char ext[sizeof(vbucket_state_t) + sizeof(CollectionIDType)];
798         encodeExt(ext, static_cast<uint32_t>(*state));
799         encodeExt(ext, *collection, sizeof(vbucket_state_t));
800         pkt = createPacket(cb::mcbp::ClientOpcode::GetAllVbSeqnos,
801                            Vbid(0),
802                            0,
803                            {ext,sizeof(vbucket_state_t) + sizeof(CollectionIDType)});
804     } else if (state) {
805         char ext[sizeof(vbucket_state_t)];
806         encodeExt(ext, static_cast<uint32_t>(*state));
807         pkt = createPacket(cb::mcbp::ClientOpcode::GetAllVbSeqnos,
808                            Vbid(0),
809                            0,
810                            {ext, sizeof(vbucket_state_t)});
811     } else {
812         pkt = createPacket(cb::mcbp::ClientOpcode::GetAllVbSeqnos);
813     }
814 
815     checkeq(ENGINE_SUCCESS,
816             h->unknown_command(cookie, *pkt, add_response),
817             "Error in getting all vb info");
818 
819     return last_status == cb::mcbp::Status::Success;
820 }
821 
verify_all_vb_seqnos(EngineIface * h,int vb_start,int vb_end,boost::optional<CollectionID> cid)822 void verify_all_vb_seqnos(EngineIface* h,
823                           int vb_start,
824                           int vb_end,
825                           boost::optional<CollectionID> cid) {
826     const int per_vb_resp_size = sizeof(uint16_t) + sizeof(uint64_t);
827     const int high_seqno_offset = sizeof(uint16_t);
828 
829     /* Check if the total response length is as expected. We expect 10 bytes
830      (2 for vb_id + 8 for seqno) */
831     checkeq((vb_end - vb_start + 1) * per_vb_resp_size,
832             static_cast<int>(last_body.size()),
833             "Failed to get all vb info.");
834     /* Check if the contents are correct */
835     for (int i = 0; i < (vb_end - vb_start + 1); i++) {
836         /* Check for correct vb_id */
837         checkeq(static_cast<const uint16_t>(vb_start + i),
838                 ntohs(*(reinterpret_cast<const uint16_t*>(last_body.data() +
839                                                           per_vb_resp_size*i))),
840               "vb_id mismatch");
841 
842         uint64_t high_seqno_vb;
843         if (cid) {
844             // Get high seqno for the collection in the vBucket
845             std::string vb_stat_seqno("vb_" + std::to_string(vb_start + i) +
846                                       ":collection:" + cid->to_string() +
847                                       ":entry:high_seqno");
848             high_seqno_vb = get_ull_stat(
849                     h, vb_stat_seqno.c_str(), "collections-details");
850         } else {
851             // Get high seqno for the vBucket
852             std::string vb_stat_seqno("vb_" + std::to_string(vb_start + i) +
853                                       ":high_seqno");
854             high_seqno_vb =
855                     get_ull_stat(h, vb_stat_seqno.c_str(), "vbucket-seqno");
856         }
857 
858         checkeq(high_seqno_vb,
859                 ntohll(*(reinterpret_cast<const uint64_t*>(last_body.data() +
860                                                            per_vb_resp_size*i +
861                                                            high_seqno_offset))),
862                 "high_seqno mismatch");
863     }
864 }
865 
store_with_meta(EngineIface * h,cb::mcbp::ClientOpcode cmd,const char * key,const size_t keylen,const char * val,const size_t vallen,const Vbid vb,ItemMetaData * itemMeta,uint64_t cas_for_store,uint32_t options,uint8_t datatype,const void * cookie,const std::vector<char> & nmeta)866 static ENGINE_ERROR_CODE store_with_meta(EngineIface* h,
867                                          cb::mcbp::ClientOpcode cmd,
868                                          const char* key,
869                                          const size_t keylen,
870                                          const char* val,
871                                          const size_t vallen,
872                                          const Vbid vb,
873                                          ItemMetaData* itemMeta,
874                                          uint64_t cas_for_store,
875                                          uint32_t options,
876                                          uint8_t datatype,
877                                          const void* cookie,
878                                          const std::vector<char>& nmeta) {
879     size_t blen = 24;
880     std::unique_ptr<char[]> ext(new char[30]);
881     std::unique_ptr<ExtendedMetaData> emd;
882 
883     encodeWithMetaExt(ext.get(), itemMeta);
884 
885     if (options) {
886         uint32_t optionsSwapped = htonl(options);
887         memcpy(ext.get() + blen, (char*)&optionsSwapped, sizeof(optionsSwapped));
888         blen += sizeof(uint32_t);
889     }
890 
891     if (nmeta.size() > 0) {
892         uint16_t nmetaSize = htons(nmeta.size());
893         memcpy(ext.get() + blen, (char*)&nmetaSize, sizeof(nmetaSize));
894         blen += sizeof(uint16_t);
895     }
896 
897     auto request = createPacket(cmd,
898                                 vb,
899                                 cas_for_store,
900                                 {ext.get(), blen},
901                                 {key, keylen},
902                                 {val, vallen},
903                                 datatype,
904                                 {nmeta.data(), nmeta.size()});
905 
906     return h->unknown_command(cookie, *request, add_response_set_del_meta);
907 }
908 
set_with_meta(EngineIface * h,const char * key,const size_t keylen,const char * val,const size_t vallen,const Vbid vb,ItemMetaData * itemMeta,uint64_t cas_for_set,uint32_t options,uint8_t datatype,const void * cookie,const std::vector<char> & nmeta)909 ENGINE_ERROR_CODE set_with_meta(EngineIface* h,
910                                 const char* key,
911                                 const size_t keylen,
912                                 const char* val,
913                                 const size_t vallen,
914                                 const Vbid vb,
915                                 ItemMetaData* itemMeta,
916                                 uint64_t cas_for_set,
917                                 uint32_t options,
918                                 uint8_t datatype,
919                                 const void* cookie,
920                                 const std::vector<char>& nmeta) {
921     return store_with_meta(h,
922                            cb::mcbp::ClientOpcode::SetWithMeta,
923                            key,
924                            keylen,
925                            val,
926                            vallen,
927                            vb,
928                            itemMeta,
929                            cas_for_set,
930                            options,
931                            datatype,
932                            cookie,
933                            nmeta);
934 }
935 
add_with_meta(EngineIface * h,const char * key,const size_t keylen,const char * val,const size_t vallen,const Vbid vb,ItemMetaData * itemMeta,uint64_t cas_for_add,uint32_t options,uint8_t datatype,const void * cookie,const std::vector<char> & nmeta)936 ENGINE_ERROR_CODE add_with_meta(EngineIface* h,
937                                 const char* key,
938                                 const size_t keylen,
939                                 const char* val,
940                                 const size_t vallen,
941                                 const Vbid vb,
942                                 ItemMetaData* itemMeta,
943                                 uint64_t cas_for_add,
944                                 uint32_t options,
945                                 uint8_t datatype,
946                                 const void* cookie,
947                                 const std::vector<char>& nmeta) {
948     return store_with_meta(h,
949                            cb::mcbp::ClientOpcode::AddWithMeta,
950                            key,
951                            keylen,
952                            val,
953                            vallen,
954                            vb,
955                            itemMeta,
956                            cas_for_add,
957                            options,
958                            datatype,
959                            cookie,
960                            nmeta);
961 }
962 
return_meta(EngineIface * h,const char * key,const size_t keylen,const char * val,const size_t vallen,const Vbid vb,const uint64_t cas,const uint32_t flags,const uint32_t exp,cb::mcbp::request::ReturnMetaType type,uint8_t datatype,const void * cookie)963 static ENGINE_ERROR_CODE return_meta(EngineIface* h,
964                                      const char* key,
965                                      const size_t keylen,
966                                      const char* val,
967                                      const size_t vallen,
968                                      const Vbid vb,
969                                      const uint64_t cas,
970                                      const uint32_t flags,
971                                      const uint32_t exp,
972                                      cb::mcbp::request::ReturnMetaType type,
973                                      uint8_t datatype,
974                                      const void* cookie) {
975     cb::mcbp::request::ReturnMetaPayload meta;
976     meta.setMutationType(type);
977     meta.setFlags(flags);
978     meta.setExpiration(exp);
979 
980     auto pkt =
981             createPacket(cb::mcbp::ClientOpcode::ReturnMeta,
982                          vb,
983                          cas,
984                          {reinterpret_cast<const char*>(&meta), sizeof(meta)},
985                          {key, keylen},
986                          {val, vallen},
987                          datatype);
988     return h->unknown_command(cookie, *pkt, add_response_ret_meta);
989 }
990 
set_ret_meta(EngineIface * h,const char * key,const size_t keylen,const char * val,const size_t vallen,const Vbid vb,const uint64_t cas,const uint32_t flags,const uint32_t exp,uint8_t datatype,const void * cookie)991 ENGINE_ERROR_CODE set_ret_meta(EngineIface* h,
992                                const char* key,
993                                const size_t keylen,
994                                const char* val,
995                                const size_t vallen,
996                                const Vbid vb,
997                                const uint64_t cas,
998                                const uint32_t flags,
999                                const uint32_t exp,
1000                                uint8_t datatype,
1001                                const void* cookie) {
1002     return return_meta(h,
1003                        key,
1004                        keylen,
1005                        val,
1006                        vallen,
1007                        vb,
1008                        cas,
1009                        flags,
1010                        exp,
1011                        cb::mcbp::request::ReturnMetaType::Set,
1012                        datatype,
1013                        cookie);
1014 }
1015 
add_ret_meta(EngineIface * h,const char * key,const size_t keylen,const char * val,const size_t vallen,const Vbid vb,const uint64_t cas,const uint32_t flags,const uint32_t exp,uint8_t datatype,const void * cookie)1016 ENGINE_ERROR_CODE add_ret_meta(EngineIface* h,
1017                                const char* key,
1018                                const size_t keylen,
1019                                const char* val,
1020                                const size_t vallen,
1021                                const Vbid vb,
1022                                const uint64_t cas,
1023                                const uint32_t flags,
1024                                const uint32_t exp,
1025                                uint8_t datatype,
1026                                const void* cookie) {
1027     return return_meta(h,
1028                        key,
1029                        keylen,
1030                        val,
1031                        vallen,
1032                        vb,
1033                        cas,
1034                        flags,
1035                        exp,
1036                        cb::mcbp::request::ReturnMetaType::Add,
1037                        datatype,
1038                        cookie);
1039 }
1040 
del_ret_meta(EngineIface * h,const char * key,const size_t keylen,const Vbid vb,const uint64_t cas,const void * cookie)1041 ENGINE_ERROR_CODE del_ret_meta(EngineIface* h,
1042                                const char* key,
1043                                const size_t keylen,
1044                                const Vbid vb,
1045                                const uint64_t cas,
1046                                const void* cookie) {
1047     return return_meta(h,
1048                        key,
1049                        keylen,
1050                        NULL,
1051                        0,
1052                        vb,
1053                        cas,
1054                        0,
1055                        0,
1056                        cb::mcbp::request::ReturnMetaType::Del,
1057                        0x00,
1058                        cookie);
1059 }
1060 
disable_traffic(EngineIface * h)1061 void disable_traffic(EngineIface* h) {
1062     auto pkt = createPacket(cb::mcbp::ClientOpcode::DisableTraffic);
1063     checkeq(ENGINE_SUCCESS,
1064             h->unknown_command(nullptr, *pkt, add_response),
1065             "Failed to send data traffic command to the server");
1066     checkeq(cb::mcbp::Status::Success,
1067             last_status.load(),
1068             "Failed to disable data traffic");
1069 }
1070 
enable_traffic(EngineIface * h)1071 void enable_traffic(EngineIface* h) {
1072     auto pkt = createPacket(cb::mcbp::ClientOpcode::EnableTraffic);
1073     checkeq(ENGINE_SUCCESS,
1074             h->unknown_command(nullptr, *pkt, add_response),
1075             "Failed to send data traffic command to the server");
1076     checkeq(cb::mcbp::Status::Success,
1077             last_status.load(),
1078             "Failed to enable data traffic");
1079 }
1080 
start_persistence(EngineIface * h)1081 void start_persistence(EngineIface* h) {
1082     if (!isPersistentBucket(h)) {
1083         // Nothing to do for non-persistent buckets
1084         return;
1085     }
1086 
1087     auto pkt = createPacket(cb::mcbp::ClientOpcode::StartPersistence);
1088     checkeq(ENGINE_SUCCESS,
1089             h->unknown_command(nullptr, *pkt, add_response),
1090             "Failed to stop persistence.");
1091     checkeq(cb::mcbp::Status::Success,
1092             last_status.load(),
1093             "Error starting persistence.");
1094 }
1095 
stop_persistence(EngineIface * h)1096 void stop_persistence(EngineIface* h) {
1097     if (!isPersistentBucket(h)) {
1098         // Nothing to do for non-persistent buckets
1099         return;
1100     }
1101 
1102     useconds_t sleepTime = 128;
1103     while (true) {
1104         if (get_str_stat(h, "ep_flusher_state", 0) == "running") {
1105             break;
1106         }
1107         decayingSleep(&sleepTime);
1108     }
1109 
1110     auto pkt = createPacket(cb::mcbp::ClientOpcode::StopPersistence);
1111     checkeq(ENGINE_SUCCESS,
1112             h->unknown_command(nullptr, *pkt, add_response),
1113             "Failed to stop persistence.");
1114     checkeq(cb::mcbp::Status::Success,
1115             last_status.load(),
1116             "Error stopping persistence.");
1117 }
1118 
store(EngineIface * h,const void * cookie,ENGINE_STORE_OPERATION op,const char * key,const char * value,item ** outitem,uint64_t casIn,Vbid vb,uint32_t exp,uint8_t datatype,DocumentState docState,const boost::optional<cb::durability::Requirements> & durReqs)1119 ENGINE_ERROR_CODE store(
1120         EngineIface* h,
1121         const void* cookie,
1122         ENGINE_STORE_OPERATION op,
1123         const char* key,
1124         const char* value,
1125         item** outitem,
1126         uint64_t casIn,
1127         Vbid vb,
1128         uint32_t exp,
1129         uint8_t datatype,
1130         DocumentState docState,
1131         const boost::optional<cb::durability::Requirements>& durReqs) {
1132     auto ret = storeCasVb11(h,
1133                             cookie,
1134                             op,
1135                             key,
1136                             value,
1137                             strlen(value),
1138                             9258,
1139                             casIn,
1140                             vb,
1141                             exp,
1142                             datatype,
1143                             docState,
1144                             durReqs);
1145     if (outitem) {
1146         *outitem = ret.second.release();
1147     }
1148     return ENGINE_ERROR_CODE(ret.first);
1149 }
1150 
storeCasOut(EngineIface * h,const void * cookie,Vbid vb,const std::string & key,const std::string & value,protocol_binary_datatype_t datatype,item * & out_item,uint64_t & out_cas,DocumentState docState)1151 ENGINE_ERROR_CODE storeCasOut(EngineIface* h,
1152                               const void* cookie,
1153                               Vbid vb,
1154                               const std::string& key,
1155                               const std::string& value,
1156                               protocol_binary_datatype_t datatype,
1157                               item*& out_item,
1158                               uint64_t& out_cas,
1159                               DocumentState docState) {
1160     bool create_cookie = false;
1161     if (cookie == nullptr) {
1162         cookie = testHarness->create_cookie();
1163         create_cookie = true;
1164     }
1165 
1166     auto ret = allocate(h, cookie, key, value.size(), 0, 0, datatype, vb);
1167     checkeq(cb::engine_errc::success, ret.first, "Allocation failed.");
1168     item_info info;
1169     check(h->get_item_info(ret.second.get(), &info), "Unable to get item_info");
1170     memcpy(info.value[0].iov_base, value.data(), value.size());
1171     ENGINE_ERROR_CODE res = h->store(
1172             cookie, ret.second.get(), out_cas, OPERATION_SET, {}, docState);
1173 
1174     if (create_cookie) {
1175         testHarness->destroy_cookie(cookie);
1176     }
1177 
1178     return res;
1179 }
1180 
storeCasVb11(EngineIface * h,const void * cookie,ENGINE_STORE_OPERATION op,const char * key,const char * value,size_t vlen,uint32_t flags,uint64_t casIn,Vbid vb,uint32_t exp,uint8_t datatype,DocumentState docState,const boost::optional<cb::durability::Requirements> & durReqs)1181 cb::EngineErrorItemPair storeCasVb11(
1182         EngineIface* h,
1183         const void* cookie,
1184         ENGINE_STORE_OPERATION op,
1185         const char* key,
1186         const char* value,
1187         size_t vlen,
1188         uint32_t flags,
1189         uint64_t casIn,
1190         Vbid vb,
1191         uint32_t exp,
1192         uint8_t datatype,
1193         DocumentState docState,
1194         const boost::optional<cb::durability::Requirements>& durReqs) {
1195     uint64_t cas = 0;
1196 
1197     auto rv = allocate(h, cookie, key, vlen, flags, exp, datatype, vb);
1198     if (rv.first != cb::engine_errc::success) {
1199         return rv;
1200     }
1201     item_info info;
1202     if (!h->get_item_info(rv.second.get(), &info)) {
1203         abort();
1204     }
1205 
1206     cb_assert(info.value[0].iov_len == vlen);
1207     std::copy(value, value + vlen, reinterpret_cast<char*>(info.value[0].iov_base));
1208     h->item_set_cas(rv.second.get(), casIn);
1209 
1210     bool create_cookie = false;
1211     if (cookie == nullptr) {
1212         cookie = testHarness->create_cookie();
1213         create_cookie = true;
1214     }
1215 
1216     auto storeRet =
1217             h->store(cookie, rv.second.get(), cas, op, durReqs, docState);
1218 
1219     if (create_cookie) {
1220         testHarness->destroy_cookie(cookie);
1221     }
1222 
1223     return {cb::engine_errc(storeRet), std::move(rv.second)};
1224 }
1225 
replace(EngineIface * h,const void * cookie,const char * key,const char * value,uint32_t flags,Vbid vb)1226 ENGINE_ERROR_CODE replace(EngineIface* h,
1227                           const void* cookie,
1228                           const char* key,
1229                           const char* value,
1230                           uint32_t flags,
1231                           Vbid vb) {
1232     Expects(cookie);
1233 
1234     const auto allocRes = allocate(h,
1235                                    cookie,
1236                                    key,
1237                                    strlen(value),
1238                                    flags,
1239                                    0 /*expiry*/,
1240                                    0 /*datatype*/,
1241                                    vb);
1242     if (allocRes.first != cb::engine_errc::success) {
1243         return ENGINE_ERROR_CODE(allocRes.first);
1244     }
1245 
1246     const auto& item = allocRes.second;
1247     item_info info;
1248     if (!h->get_item_info(item.get(), &info)) {
1249         abort();
1250     }
1251     h->item_set_cas(allocRes.second.get(), 0);
1252 
1253     // A predicate that allows the replace.
1254     // This simulates the behaviour of replace when the doc being updated does
1255     // not contain any xattr, and the vbucket that owns the doc has surely never
1256     // seen a doc with xattr. Which means that we do not need any pre-fetch for
1257     // preserving xattrs, the replace can just proceed.
1258     const cb::StoreIfPredicate predicate = [](const boost::optional<item_info>&,
1259                                               cb::vbucket_info) {
1260         return cb::StoreIfStatus::Continue;
1261     };
1262 
1263     auto res = h->store_if(cookie,
1264                            item.get(),
1265                            0 /*cas*/,
1266                            ENGINE_STORE_OPERATION::OPERATION_REPLACE,
1267                            predicate,
1268                            {} /*durReqs*/,
1269                            DocumentState::Alive);
1270 
1271     return ENGINE_ERROR_CODE(res.status);
1272 }
1273 
touch(EngineIface * h,const char * key,Vbid vb,uint32_t exp)1274 ENGINE_ERROR_CODE touch(EngineIface* h,
1275                         const char* key,
1276                         Vbid vb,
1277                         uint32_t exp) {
1278     const auto* cookie = testHarness->create_cookie();
1279     auto result = h->get_and_touch(
1280             cookie, DocKey(key, DocKeyEncodesCollectionId::No), vb, exp, {});
1281     testHarness->destroy_cookie(cookie);
1282 
1283     // Update the global cas value (used by some tests)
1284     if (result.first == cb::engine_errc::success) {
1285         item_info info{};
1286         check(h->get_item_info(result.second.get(), &info),
1287               "Failed to get item info");
1288         last_cas.store(info.cas);
1289     }
1290 
1291     return ENGINE_ERROR_CODE(result.first);
1292 }
1293 
unl(EngineIface * h,const void * cookie,const char * key,Vbid vb,uint64_t cas)1294 ENGINE_ERROR_CODE unl(EngineIface* h,
1295                       const void* cookie,
1296                       const char* key,
1297                       Vbid vb,
1298                       uint64_t cas) {
1299     bool create_cookie = false;
1300     if (cookie == nullptr) {
1301         cookie = testHarness->create_cookie();
1302         create_cookie = true;
1303     }
1304     auto ret = h->unlock(
1305             cookie, DocKey(key, DocKeyEncodesCollectionId::No), vb, cas);
1306 
1307     if (create_cookie) {
1308         testHarness->destroy_cookie(cookie);
1309     }
1310     return ret;
1311 }
1312 
compact_db(EngineIface * h,const Vbid vbucket_id,const Vbid db_file_id,const uint64_t purge_before_ts,const uint64_t purge_before_seq,const uint8_t drop_deletes)1313 void compact_db(EngineIface* h,
1314                 const Vbid vbucket_id,
1315                 const Vbid db_file_id,
1316                 const uint64_t purge_before_ts,
1317                 const uint64_t purge_before_seq,
1318                 const uint8_t drop_deletes) {
1319     cb::mcbp::request::CompactDbPayload payload;
1320     payload.setPurgeBeforeTs(purge_before_ts);
1321     payload.setPurgeBeforeSeq(purge_before_seq);
1322     payload.setDropDeletes(drop_deletes);
1323     payload.setDbFileId(db_file_id);
1324 
1325     auto pkt = createPacket(
1326             cb::mcbp::ClientOpcode::CompactDb,
1327             vbucket_id,
1328             0,
1329             {reinterpret_cast<const char*>(&payload), sizeof(payload)});
1330     auto ret = h->unknown_command(nullptr, *pkt, add_response);
1331 
1332     const auto backend = get_str_stat(h, "ep_backend");
1333 
1334     if (backend == "couchdb" || backend == "magma") {
1335         if (ret == ENGINE_ENOTSUP) {
1336             // Ephemeral, couchdb and magma (but not rocksdb) buckets can
1337             // return ENGINE_ENOTSUP.  This method is called from a lot
1338             // of test cases we run. Lets remap the error code to success.
1339             // Note: Ephemeral buckets use couchdb as backend.
1340             ret = ENGINE_SUCCESS;
1341         }
1342         checkeq(ENGINE_SUCCESS, ret, "Failed to request compact vbucket");
1343     } else {
1344         checkeq(ENGINE_FAILED,
1345                 ret,
1346                 "checkForDBExistence returns ENGINE_FAILED for !couchdb");
1347     }
1348 }
1349 
vbucketDelete(EngineIface * h,Vbid vb,const char * args)1350 ENGINE_ERROR_CODE vbucketDelete(EngineIface* h, Vbid vb, const char* args) {
1351     uint32_t argslen = args ? strlen(args) : 0;
1352     auto pkt = createPacket(
1353             cb::mcbp::ClientOpcode::DelVbucket, vb, 0, {}, {}, {args, argslen});
1354 
1355     return h->unknown_command(nullptr, *pkt, add_response);
1356 }
1357 
verify_key(EngineIface * h,const char * key,Vbid vbucket)1358 ENGINE_ERROR_CODE verify_key(EngineIface* h, const char* key, Vbid vbucket) {
1359     auto rv = get(h, NULL, key, vbucket);
1360     return ENGINE_ERROR_CODE(rv.first);
1361 }
1362 
get_value(EngineIface * h,const void * cookie,const char * key,Vbid vbucket,DocStateFilter state)1363 std::pair<ENGINE_ERROR_CODE, std::string> get_value(EngineIface* h,
1364                                                     const void* cookie,
1365                                                     const char* key,
1366                                                     Vbid vbucket,
1367                                                     DocStateFilter state) {
1368     auto rv = get(h, cookie, key, vbucket, state);
1369     if (rv.first != cb::engine_errc::success) {
1370         return {ENGINE_ERROR_CODE(rv.first), ""};
1371     }
1372     item_info info;
1373     if (!h->get_item_info(rv.second.get(), &info)) {
1374         return {ENGINE_FAILED, ""};
1375     }
1376     auto value = std::string(reinterpret_cast<char*>(info.value[0].iov_base),
1377                              info.value[0].iov_len);
1378     return make_pair(ENGINE_ERROR_CODE(rv.first), value);
1379 }
1380 
verify_vbucket_missing(EngineIface * h,Vbid vb)1381 bool verify_vbucket_missing(EngineIface* h, Vbid vb) {
1382     const auto vbstr = "vb_" + std::to_string(vb.get());
1383 
1384     // Try up to three times to verify the bucket is missing.  Bucket
1385     // state changes are async.
1386     {
1387         std::lock_guard<std::mutex> lh(vals_mutex);
1388         vals.clear();
1389     }
1390 
1391     const auto* cookie = testHarness->create_cookie();
1392     checkeq(ENGINE_SUCCESS,
1393             h->get_stats(cookie, {}, {}, add_stats),
1394             "Failed to get stats.");
1395     testHarness->destroy_cookie(cookie);
1396 
1397     {
1398         std::lock_guard<std::mutex> lh(vals_mutex);
1399         if (vals.find(vbstr) == vals.end()) {
1400             return true;
1401         }
1402 
1403         std::cerr << "Expected bucket missing, got " <<
1404                 vals[vbstr] << std::endl;
1405     }
1406     return false;
1407 }
1408 
verify_vbucket_state(EngineIface * h,Vbid vb,vbucket_state_t expected,bool mute)1409 bool verify_vbucket_state(EngineIface* h,
1410                           Vbid vb,
1411                           vbucket_state_t expected,
1412                           bool mute) {
1413     auto pkt = createPacket(cb::mcbp::ClientOpcode::GetVbucket, vb, 0);
1414 
1415     ENGINE_ERROR_CODE errcode = h->unknown_command(NULL, *pkt, add_response);
1416     if (errcode != ENGINE_SUCCESS) {
1417         if (!mute) {
1418             fprintf(stderr, "Error code when getting vbucket %d\n", errcode);
1419         }
1420         return false;
1421     }
1422 
1423     if (last_status != cb::mcbp::Status::Success) {
1424         if (!mute) {
1425             fprintf(stderr, "Last protocol status was %s (%s)\n",
1426                     to_string(last_status.load()).c_str(),
1427                     last_body.size() > 0 ? last_body.c_str() : "unknown");
1428         }
1429         return false;
1430     }
1431 
1432     vbucket_state_t state;
1433     memcpy(&state, last_body.data(), sizeof(state));
1434     state = static_cast<vbucket_state_t>(ntohl(state));
1435     return state == expected;
1436 }
1437 
sendDcpAck(EngineIface * h,const void * cookie,cb::mcbp::ClientOpcode opcode,cb::mcbp::Status status,uint32_t opaque)1438 void sendDcpAck(EngineIface* h,
1439                 const void* cookie,
1440                 cb::mcbp::ClientOpcode opcode,
1441                 cb::mcbp::Status status,
1442                 uint32_t opaque) {
1443     protocol_binary_response_header pkt;
1444     pkt.response.setMagic(cb::mcbp::Magic::ClientResponse);
1445     pkt.response.setOpcode(opcode);
1446     pkt.response.setStatus(status);
1447     pkt.response.setOpaque(opaque);
1448 
1449     auto& dcp = dynamic_cast<DcpIface&>(*h);
1450     checkeq(ENGINE_SUCCESS, dcp.response_handler(cookie, &pkt),
1451           "Expected success");
1452 }
1453 
1454 class engine_error : public std::exception {
1455 public:
engine_error(ENGINE_ERROR_CODE code_)1456     engine_error(ENGINE_ERROR_CODE code_)
1457         : code(code_) {}
1458 
what() const1459     virtual const char* what() const NOEXCEPT {
1460         return "engine_error";
1461     }
1462 
1463     ENGINE_ERROR_CODE code;
1464 };
1465 
1466 /* The following set of functions get a given stat as the specified type
1467  * (int, float, unsigned long, string, bool, etc).
1468  * If the engine->get_stats() call fails, throws a engine_error exception.
1469  * If the given statname doesn't exist under the given statname, throws a
1470  * std::out_of_range exception.
1471  */
1472 template <>
get_stat(EngineIface * h,const char * statname,const char * statkey)1473 int get_stat(EngineIface* h,
1474              const char* statname,
1475              const char* statkey) {
1476     return std::stoi(get_str_stat(h, statname, statkey));
1477 }
1478 template <>
get_stat(EngineIface * h,const char * statname,const char * statkey)1479 uint64_t get_stat(EngineIface* h,
1480                   const char* statname,
1481                   const char* statkey) {
1482     return std::stoull(get_str_stat(h, statname, statkey));
1483 }
1484 
1485 template <>
get_stat(EngineIface * h,const char * statname,const char * statkey)1486 bool get_stat(EngineIface* h,
1487               const char* statname,
1488               const char* statkey) {
1489     return get_str_stat(h, statname, statkey) == "true";
1490 }
1491 
1492 template <>
get_stat(EngineIface * h,const char * statname,const char * statkey)1493 float get_stat(EngineIface* h, const char* statname, const char* statkey) {
1494     return std::stof(get_str_stat(h, statname, statkey));
1495 }
1496 
1497 template <>
get_stat(EngineIface * h,const char * statname,const char * statkey)1498 std::string get_stat(EngineIface* h,
1499                      const char* statname,
1500                      const char* statkey) {
1501     std::lock_guard<std::mutex> lh(get_stat_context.mutex);
1502 
1503     get_stat_context.requested_stat_name = statname;
1504     get_stat_context.actual_stat_value.clear();
1505 
1506     const auto* cookie = testHarness->create_cookie();
1507     ENGINE_ERROR_CODE err =
1508             h->get_stats(cookie,
1509                          {statkey, statkey == NULL ? 0 : strlen(statkey)},
1510                          {},
1511                          add_individual_stat);
1512     testHarness->destroy_cookie(cookie);
1513 
1514     if (err != ENGINE_SUCCESS) {
1515         throw engine_error(err);
1516     }
1517 
1518     if (get_stat_context.actual_stat_value.empty()) {
1519         throw std::out_of_range(std::string("Failed to find requested statname '") +
1520                                 statname + "'");
1521     }
1522 
1523     // Here we are explictly forcing a copy of the object to work
1524     // around std::string copy-on-write data-race issues seen on some
1525     // versions of libstdc++ - see MB-18510 / MB-19688.
1526     return std::string(get_stat_context.actual_stat_value.begin(),
1527                        get_stat_context.actual_stat_value.end());
1528 }
1529 
1530 /// Backward-compatible functions (encode type name in function name).
get_int_stat(EngineIface * h,const char * statname,const char * statkey)1531 int get_int_stat(EngineIface* h,
1532                  const char* statname,
1533                  const char* statkey) {
1534     return get_stat<int>(h, statname, statkey);
1535 }
1536 
get_float_stat(EngineIface * h,const char * statname,const char * statkey)1537 float get_float_stat(EngineIface* h,
1538                      const char* statname,
1539                      const char* statkey) {
1540     return std::stof(get_str_stat(h, statname, statkey));
1541 }
1542 
get_ul_stat(EngineIface * h,const char * statname,const char * statkey)1543 uint32_t get_ul_stat(EngineIface* h,
1544                      const char* statname,
1545                      const char* statkey) {
1546     return std::stoul(get_str_stat(h, statname, statkey));
1547 }
1548 
get_ull_stat(EngineIface * h,const char * statname,const char * statkey)1549 uint64_t get_ull_stat(EngineIface* h,
1550                       const char* statname,
1551                       const char* statkey) {
1552     return get_stat<uint64_t>(h, statname, statkey);
1553 }
1554 
get_str_stat(EngineIface * h,const char * statname,const char * statkey)1555 std::string get_str_stat(EngineIface* h,
1556                          const char* statname,
1557                          const char* statkey) {
1558     return get_stat<std::string>(h, statname, statkey);
1559 }
1560 
get_bool_stat(EngineIface * h,const char * statname,const char * statkey)1561 bool get_bool_stat(EngineIface* h,
1562                    const char* statname,
1563                    const char* statkey) {
1564     const auto s = get_str_stat(h, statname, statkey);
1565 
1566     if (s == "true") {
1567         return true;
1568     } else if (s == "false") {
1569         return false;
1570     } else {
1571         throw std::invalid_argument("Unable to convert string '" + s + "' to type bool");
1572     }
1573 }
1574 
1575 /* Fetches the value for a given statname in the given statkey set.
1576  * @return te value of statname, or default_value if that statname was not
1577  * found.
1578  */
get_int_stat_or_default(EngineIface * h,int default_value,const char * statname,const char * statkey)1579 int get_int_stat_or_default(EngineIface* h,
1580                             int default_value,
1581                             const char* statname,
1582                             const char* statkey) {
1583     try {
1584         return get_int_stat(h, statname, statkey);
1585     } catch (std::out_of_range&) {
1586         return default_value;
1587     }
1588 }
1589 
get_histo_stat(EngineIface * h,const char * statname,const char * statkey,const Histo_stat_info histo_info)1590 uint64_t get_histo_stat(EngineIface* h,
1591                         const char* statname,
1592                         const char* statkey,
1593                         const Histo_stat_info histo_info) {
1594     std::lock_guard<std::mutex> lh(get_stat_context.mutex);
1595 
1596     get_stat_context.histogram_stat_int_value = new HistogramStats<uint64_t>();
1597     get_histo_stat(h, statname, statkey);
1598 
1599     /* Get the necessary info from the histogram */
1600     uint64_t ret_val = 0;
1601     switch (histo_info) {
1602         case Histo_stat_info::TOTAL_COUNT:
1603             ret_val = get_stat_context.histogram_stat_int_value->total();
1604             break;
1605         case Histo_stat_info::NUM_BINS:
1606             ret_val =
1607                     static_cast<uint64_t>(get_stat_context.
1608                                           histogram_stat_int_value->num_bins());
1609             break;
1610     }
1611 
1612     delete get_stat_context.histogram_stat_int_value;
1613     return ret_val;
1614 }
1615 
get_histo_stat(EngineIface * h,const char * statname,const char * statkey)1616 static void get_histo_stat(EngineIface* h,
1617                            const char* statname,
1618                            const char* statkey) {
1619     get_stat_context.requested_stat_name = statname;
1620     /* Histo stats for tasks are append as task_name_START,END.
1621        Hence append _ */
1622     get_stat_context.requested_stat_name.append("_");
1623 
1624     const auto* cookie = testHarness->create_cookie();
1625     auto err = h->get_stats(cookie,
1626                             {statkey, statkey == NULL ? 0 : strlen(statkey)},
1627                             {},
1628                             add_individual_histo_stat);
1629     testHarness->destroy_cookie(cookie);
1630 
1631     if (err != ENGINE_SUCCESS) {
1632         throw engine_error(err);
1633     }
1634 }
1635 
get_all_stats(EngineIface * h,const char * statset)1636 statistic_map get_all_stats(EngineIface* h, const char* statset) {
1637     {
1638         std::lock_guard<std::mutex> lh(vals_mutex);
1639         vals.clear();
1640     }
1641     const auto* cookie = testHarness->create_cookie();
1642     auto err = h->get_stats(cookie,
1643                             {statset, statset == NULL ? 0 : strlen(statset)},
1644                             {},
1645                             add_stats);
1646     testHarness->destroy_cookie(cookie);
1647 
1648     if (err != ENGINE_SUCCESS) {
1649         throw engine_error(err);
1650     }
1651 
1652     std::lock_guard<std::mutex> lh(vals_mutex);
1653     return vals;
1654 }
1655 
verify_curr_items(EngineIface * h,int exp,const char * msg)1656 void verify_curr_items(EngineIface* h,
1657                        int exp,
1658                        const char* msg) {
1659     int curr_items = get_int_stat(h, "curr_items");
1660     if (curr_items != exp) {
1661         std::cerr << "Expected "<< exp << " curr_items after " << msg
1662                   << ", got " << curr_items << std::endl;
1663         abort();
1664     }
1665 }
1666 
wait_for_stat_to_be_gte(EngineIface * h,const char * stat,int final,const char * stat_key,const time_t max_wait_time_in_secs)1667 void wait_for_stat_to_be_gte(EngineIface* h,
1668                              const char* stat,
1669                              int final,
1670                              const char* stat_key,
1671                              const time_t max_wait_time_in_secs) {
1672     useconds_t sleepTime = 128;
1673     WaitTimeAccumulator<int> accumulator("to be greater or equal than", stat,
1674                                          stat_key, final,
1675                                          max_wait_time_in_secs);
1676     for (;;) {
1677         auto current = get_int_stat(h, stat, stat_key);
1678         if (current >= final) {
1679             break;
1680         }
1681         accumulator.incrementAndAbortIfLimitReached(current, sleepTime);
1682         decayingSleep(&sleepTime);
1683     }
1684 }
1685 
wait_for_expired_items_to_be(EngineIface * h,int final,const time_t max_wait_time_in_secs)1686 void wait_for_expired_items_to_be(EngineIface* h,
1687                                   int final,
1688                                   const time_t max_wait_time_in_secs) {
1689     useconds_t sleepTime = 128;
1690     WaitTimeAccumulator<int> accumulator("to be", "expired items",
1691                                          NULL, final,
1692                                          max_wait_time_in_secs);
1693     for (;;) {
1694         auto current = get_int_stat(h, "ep_expired_access") +
1695                        get_int_stat(h, "ep_expired_compactor") +
1696                        get_int_stat(h, "ep_expired_pager");
1697         if (current == final) {
1698             break;
1699         }
1700         accumulator.incrementAndAbortIfLimitReached(current, sleepTime);
1701         decayingSleep(&sleepTime);
1702     }
1703 }
1704 
wait_for_memory_usage_below(EngineIface * h,int mem_threshold,const time_t max_wait_time_in_secs)1705 void wait_for_memory_usage_below(EngineIface* h,
1706                                  int mem_threshold,
1707                                  const time_t max_wait_time_in_secs) {
1708     useconds_t sleepTime = 128;
1709     WaitTimeAccumulator<int> accumulator("to be below", "mem_used", NULL,
1710                                          mem_threshold,
1711                                          max_wait_time_in_secs);
1712     for (;;) {
1713         auto current = get_int_stat(h, "mem_used");
1714         if (current <= mem_threshold) {
1715             break;
1716         }
1717         accumulator.incrementAndAbortIfLimitReached(current, sleepTime);
1718         decayingSleep(&sleepTime);
1719     }
1720 }
1721 
wait_for_warmup_complete(EngineIface * h)1722 bool wait_for_warmup_complete(EngineIface* h) {
1723     if (!isWarmupEnabled(h)) {
1724         return true;
1725     }
1726 
1727     useconds_t sleepTime = 128;
1728     do {
1729         try {
1730             if (get_str_stat(h, "ep_warmup_thread", "warmup") == "complete") {
1731                 return true;
1732             }
1733         } catch (engine_error&) {
1734             // If the stat call failed then the warmup stats group no longer
1735             // exists and hence warmup is complete.
1736             return true;
1737         }
1738         decayingSleep(&sleepTime);
1739     } while(true);
1740 }
1741 
wait_for_flusher_to_settle(EngineIface * h)1742 void wait_for_flusher_to_settle(EngineIface* h) {
1743     wait_for_stat_to_be(h, "ep_queue_size", 0);
1744 
1745     if (!isPersistentBucket(h)) {
1746         // We don't run flusher in non-persistent buckets
1747         return;
1748     }
1749     // We also need to to wait for any outstanding flushes to disk to
1750     // complete - specifically so when in full eviction mode we have
1751     // waited for the item counts in each vBucket to be synced with
1752     // the number of items on disk. See
1753     // EPBucket::commit().
1754     wait_for_stat_to_be(h, "ep_flusher_todo", 0);
1755 }
1756 
wait_for_item_compressor_to_settle(EngineIface * h)1757 void wait_for_item_compressor_to_settle(EngineIface* h) {
1758     int visited_count = get_int_stat(h, "ep_item_compressor_num_visited");
1759 
1760     // We need to wait for at least one more run of the item compressor
1761     wait_for_stat_to_be(h, "ep_item_compressor_num_visited", visited_count + 1);
1762 }
1763 
wait_for_rollback_to_finish(EngineIface * h)1764 void wait_for_rollback_to_finish(EngineIface* h) {
1765     useconds_t sleepTime = 128;
1766     while (get_int_stat(h, "ep_rollback_count") == 0) {
1767         decayingSleep(&sleepTime);
1768     }
1769 }
1770 
wait_for_persisted_value(EngineIface * h,const char * key,const char * val,Vbid vbucketId)1771 void wait_for_persisted_value(EngineIface* h,
1772                               const char* key,
1773                               const char* val,
1774                               Vbid vbucketId) {
1775     int commitNum = 0;
1776     if (isPersistentBucket(h)) {
1777         commitNum = get_int_stat(h, "ep_commit_num");
1778     }
1779     checkeq(ENGINE_SUCCESS,
1780             store(h, nullptr, OPERATION_SET, key, val, nullptr, 0, vbucketId),
1781             "Failed to store an item.");
1782 
1783     if (isPersistentBucket(h)) {
1784         // Wait for persistence...
1785         wait_for_flusher_to_settle(h);
1786         wait_for_stat_change(h, "ep_commit_num", commitNum);
1787     }
1788 }
1789 
abort_msg(const char * expr,const char * msg,const char * file,int line)1790 void abort_msg(const char* expr, const char* msg, const char* file, int line) {
1791     fprintf(stderr, "%s:%d Test failed: `%s' (%s)\n",
1792             file, line, msg, expr);
1793     throw TestExpectationFailed();
1794 }
1795 
1796 /* Helper function to validate the return from store() */
validate_store_resp(ENGINE_ERROR_CODE ret,int & num_items)1797 void validate_store_resp(ENGINE_ERROR_CODE ret, int& num_items)
1798 {
1799     switch (ret) {
1800         case ENGINE_SUCCESS:
1801             num_items++;
1802             break;
1803         case ENGINE_TMPFAIL:
1804             /* TMPFAIL means we are hitting high memory usage; retry */
1805             break;
1806         default:
1807             check(false,
1808                   ("write_items_upto_mem_perc: Unexpected response from "
1809                    "store(): " + std::to_string(ret)).c_str());
1810             break;
1811     }
1812 }
1813 
write_items(EngineIface * h,int num_items,int start_seqno,const char * key_prefix,const char * value,uint32_t expiry,Vbid vb,DocumentState docState)1814 void write_items(EngineIface* h,
1815                  int num_items,
1816                  int start_seqno,
1817                  const char* key_prefix,
1818                  const char* value,
1819                  uint32_t expiry,
1820                  Vbid vb,
1821                  DocumentState docState) {
1822     int j = 0;
1823     while (1) {
1824         if (j == num_items) {
1825             break;
1826         }
1827         std::string key(key_prefix + std::to_string(j + start_seqno));
1828         ENGINE_ERROR_CODE ret = store(h,
1829                                       nullptr,
1830                                       OPERATION_SET,
1831                                       key.c_str(),
1832                                       value,
1833                                       nullptr,
1834                                       /*cas*/ 0,
1835                                       vb,
1836                                       expiry,
1837                                       0,
1838                                       docState);
1839         validate_store_resp(ret, j);
1840     }
1841 }
1842 
1843 /* Helper function to write unique items starting from keyXX until memory usage
1844    hits "mem_thresh_perc" (XX is start_seqno) */
write_items_upto_mem_perc(EngineIface * h,int mem_thresh_perc,int start_seqno,const char * key_prefix,const char * value)1845 int write_items_upto_mem_perc(EngineIface* h,
1846                               int mem_thresh_perc,
1847                               int start_seqno,
1848                               const char* key_prefix,
1849                               const char* value) {
1850     float maxSize =
1851             static_cast<float>(get_int_stat(h, "ep_max_size", "memory"));
1852     float mem_thresh = static_cast<float>(mem_thresh_perc) / (100.0);
1853     int num_items = 0;
1854     while (1) {
1855         /* Load items into server until mem_thresh_perc of the mem quota
1856          is used. Getting stats is expensive, only check every 100
1857          iterations. */
1858         if ((num_items % 100) == 0) {
1859             float memUsed = float(get_int_stat(h, "mem_used", "memory"));
1860             if (memUsed > (maxSize * mem_thresh)) {
1861                 /* Persist all items written so far. */
1862                 break;
1863             }
1864         }
1865         std::string key("key" + std::to_string(num_items + start_seqno));
1866         ENGINE_ERROR_CODE ret =
1867                 store(h, nullptr, OPERATION_SET, key.c_str(), "somevalue");
1868         validate_store_resp(ret, num_items);
1869     }
1870     return num_items;
1871 }
1872 
get_CAS(EngineIface * h,const std::string & key)1873 uint64_t get_CAS(EngineIface* h, const std::string& key) {
1874     auto ret = get(h, nullptr, key, Vbid(0));
1875     checkeq(cb::engine_errc::success, ret.first, "get_CAS: Failed to get key");
1876 
1877     item_info info;
1878     check(h->get_item_info(ret.second.get(), &info),
1879           "get_CAS: Failed to get item info for key");
1880 
1881     return info.cas;
1882 }
1883 
allocate(EngineIface * h,const void * cookie,const std::string & key,size_t nbytes,int flags,rel_time_t exptime,uint8_t datatype,Vbid vb)1884 cb::EngineErrorItemPair allocate(EngineIface* h,
1885                                  const void* cookie,
1886                                  const std::string& key,
1887                                  size_t nbytes,
1888                                  int flags,
1889                                  rel_time_t exptime,
1890                                  uint8_t datatype,
1891                                  Vbid vb) {
1892     bool cookie_created = false;
1893     if (cookie == nullptr) {
1894         cookie = testHarness->create_cookie();
1895         cookie_created = true;
1896     }
1897     auto ret = h->allocate(cookie,
1898                            DocKey(key, DocKeyEncodesCollectionId::No),
1899                            nbytes,
1900                            flags,
1901                            exptime,
1902                            datatype,
1903                            vb);
1904     if (cookie_created) {
1905         testHarness->destroy_cookie(cookie);
1906     }
1907 
1908     return ret;
1909 }
1910 
get(EngineIface * h,const void * cookie,const std::string & key,Vbid vb,DocStateFilter documentStateFilter)1911 cb::EngineErrorItemPair get(EngineIface* h,
1912                             const void* cookie,
1913                             const std::string& key,
1914                             Vbid vb,
1915                             DocStateFilter documentStateFilter) {
1916     bool create_cookie = false;
1917     if (cookie == nullptr) {
1918         cookie = testHarness->create_cookie();
1919         create_cookie = true;
1920     }
1921 
1922     auto ret = h->get(cookie,
1923                       DocKey(key, DocKeyEncodesCollectionId::No),
1924                       vb,
1925                       documentStateFilter);
1926 
1927     if (create_cookie) {
1928         testHarness->destroy_cookie(cookie);
1929     }
1930     return ret;
1931 }
1932 
repeat_till_true(std::function<bool ()> functor,uint16_t max_repeat,std::chrono::microseconds sleepTime)1933 bool repeat_till_true(std::function<bool()> functor,
1934                       uint16_t max_repeat,
1935                       std::chrono::microseconds sleepTime) {
1936     bool fSuccess = false;
1937     do {
1938         fSuccess = functor();
1939         if (!fSuccess) {
1940             std::this_thread::sleep_for(sleepTime);
1941             max_repeat--;
1942         }
1943     } while (!fSuccess && max_repeat > 0);
1944     return fSuccess;
1945 }
1946 
reset_stats(gsl::not_null<EngineIface * > h)1947 void reset_stats(gsl::not_null<EngineIface*> h) {
1948     const auto* cookie = testHarness->create_cookie();
1949     h->reset_stats(cookie);
1950     testHarness->destroy_cookie(cookie);
1951 }
1952 
get_stats(gsl::not_null<EngineIface * > h,cb::const_char_buffer key,cb::const_char_buffer value,const AddStatFn & callback)1953 ENGINE_ERROR_CODE get_stats(gsl::not_null<EngineIface*> h,
1954                             cb::const_char_buffer key,
1955                             cb::const_char_buffer value,
1956                             const AddStatFn& callback) {
1957     const auto* cookie = testHarness->create_cookie();
1958     auto ret = h->get_stats(cookie, key, value, callback);
1959     testHarness->destroy_cookie(cookie);
1960     return ret;
1961 }
1962