179a9d87dSDavid Liao/* -*- MODE: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2ba5b814bSMatt Ingenthron/*
366eb94d0SMike Wiederhold *     Copyright 2010 Couchbase, Inc
4ba5b814bSMatt Ingenthron *
5ba5b814bSMatt Ingenthron *   Licensed under the Apache License, Version 2.0 (the "License");
6ba5b814bSMatt Ingenthron *   you may not use this file except in compliance with the License.
7ba5b814bSMatt Ingenthron *   You may obtain a copy of the License at
8ba5b814bSMatt Ingenthron *
9ba5b814bSMatt Ingenthron *       http://www.apache.org/licenses/LICENSE-2.0
10ba5b814bSMatt Ingenthron *
11ba5b814bSMatt Ingenthron *   Unless required by applicable law or agreed to in writing, software
1290a18674STrond Norbye *   distributed under the License is distributed on an "AS IS" BASIS,
1390a18674STrond Norbye *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14ba5b814bSMatt Ingenthron *   See the License for the specific language governing permissions and
15ba5b814bSMatt Ingenthron *   limitations under the License.
16ba5b814bSMatt Ingenthron */
17244c0146SMike Wiederhold
187955d86dSChiyoung Seo// Usage: (to repeatedly run just a single test case)
197955d86dSChiyoung Seo// make engine_tests IS_LOOP=-L EP_TEST_NUM=3
20225850baSSundar Sridharan
215f546033STrond Norbye#include "config.h"
228e20ce5eSDustin Sallings
236a56cf03SSean Lynch#include <stdio.h>
2453e068aeSDustin Sallings#include <stdlib.h>
2553e068aeSDustin Sallings#include <string.h>
26244c0146SMike Wiederhold#include <sys/stat.h>
27d0d91da7STrond Norbye#ifdef _MSC_VER
28d0d91da7STrond Norbye#include <direct.h>
29d0d91da7STrond Norbye#define mkdir(a, b) _mkdir(a)
30d0d91da7STrond Norbye#else
317d42f348SDustin Sallings#include <sys/wait.h>
32d0d91da7STrond Norbye#endif
33aa83d3a0SDustin Sallings
34244c0146SMike Wiederhold#include <cstdlib>
35244c0146SMike Wiederhold#include <iostream>
36244c0146SMike Wiederhold#include <map>
37ed729846SMike Wiederhold#include <set>
38244c0146SMike Wiederhold#include <sstream>
39244c0146SMike Wiederhold#include <string>
40244c0146SMike Wiederhold#include <vector>
416a56cf03SSean Lynch
42a9bc0ba9STrond Norbye#include <platform/dirutils.h>
43a9bc0ba9STrond Norbye
44fc9615cdSMike Wiederhold#include "atomic.h"
45bf3b555bSMike Wiederhold#include "ep-engine/command_ids.h"
46ccdfed7dSMike Wiederhold#include "ep_test_apis.h"
47244c0146SMike Wiederhold#include "ep_testsuite.h"
48244c0146SMike Wiederhold#include "locks.h"
495ad7924aSMike Wiederhold#include "mock/mock_dcp.h"
50244c0146SMike Wiederhold#include "mutex.h"
51cbf9a300SDustin Sallings
5282a6f303Sabhinavdangeti#include <snappy-c.h>
53c8f54d3eSabhinavdangeti#include <JSON_checker.h>
55aa83d3a0SDustin Sallings#ifdef linux
56aa83d3a0SDustin Sallings/* /usr/include/netinet/in.h defines macros from ntohs() to _bswap_nn to
57aa83d3a0SDustin Sallings * optimize the conversion functions, but the prototypes generate warnings
58aa83d3a0SDustin Sallings * from gcc. The conversion methods isn't the bottleneck for my app, so
59aa83d3a0SDustin Sallings * just remove the warnings by undef'ing the optimization ..
60aa83d3a0SDustin Sallings */
61aa83d3a0SDustin Sallings#undef ntohs
62aa83d3a0SDustin Sallings#undef ntohl
63aa83d3a0SDustin Sallings#undef htons
64aa83d3a0SDustin Sallings#undef htonl
65aa83d3a0SDustin Sallings#endif
66aa83d3a0SDustin Sallings
67da3303e4STrond Norbye// ptr_fun don't like the extern "C" thing for unlock cookie.. cast it
68da3303e4STrond Norbye// away ;)
69da3303e4STrond Norbyetypedef void (*UNLOCK_COOKIE_T)(const void *cookie);
70418b8d81SChiyoung Seo
711ef67ba0SDustin Sallingsextern "C" bool abort_msg(const char *expr, const char *msg, int line);
721ef67ba0SDustin Sallings
731ef67ba0SDustin Sallingstemplate <typename T>
741ef67ba0SDustin Sallingsstatic void checkeqfn(T exp, T got, const char *msg, const char *file, const int linenum) {
751ef67ba0SDustin Sallings    if (exp != got) {
761ef67ba0SDustin Sallings        std::stringstream ss;
771ef67ba0SDustin Sallings        ss << "Expected `" << exp << "', got `" << got << "' - " << msg;
781ef67ba0SDustin Sallings        abort_msg(ss.str().c_str(), file, linenum);
791ef67ba0SDustin Sallings    }
801ef67ba0SDustin Sallings}
810c44517dSTrond Norbye
821ef67ba0SDustin Sallings#define checkeq(a, b, c) checkeqfn(a, b, c, __FILE__, __LINE__)
831ef67ba0SDustin Sallings
841ef67ba0SDustin Sallingsextern "C" {
857d8580e4SDustin Sallings
867d8580e4SDustin Sallings#define check(expr, msg) \
8757aa7259SDustin Sallings    static_cast<void>((expr) ? 0 : abort_msg(#expr, msg, __LINE__))
887d8580e4SDustin Sallings
898e16218aSDustin Sallings#define WHITESPACE_DB "whitespace sucks.db"
906b38323eSChiyoung Seo#define MULTI_DISPATCHER_CONFIG \
91d34de537SMike Wiederhold    "ht_size=129;ht_locks=3;chk_remover_stime=1;chk_period=60"
92873f71cdSDustin Sallings
93873f71cdSDustin Sallingsstruct test_harness testHarness;
94873f71cdSDustin Sallings
95f5613908SDustin Sallingsclass ThreadData {
96f5613908SDustin Sallingspublic:
977d42f348SDustin Sallings    ThreadData(ENGINE_HANDLE *eh, ENGINE_HANDLE_V1 *ehv1,
987d42f348SDustin Sallings               int e=0) : h(eh), h1(ehv1), extra(e) {}
99f5613908SDustin Sallings    ENGINE_HANDLE    *h;
100f5613908SDustin Sallings    ENGINE_HANDLE_V1 *h1;
1017d42f348SDustin Sallings    int               extra;
102f5613908SDustin Sallings};
103f5613908SDustin Sallings
1047d8580e4SDustin Sallingsbool abort_msg(const char *expr, const char *msg, int line) {
1057d8580e4SDustin Sallings    fprintf(stderr, "%s:%d Test failed: `%s' (%s)\n",
1067d8580e4SDustin Sallings            __FILE__, line, msg, expr);
1077d8580e4SDustin Sallings    abort();
1087d8580e4SDustin Sallings    // UNREACHABLE
1097d8580e4SDustin Sallings    return false;
1107d8580e4SDustin Sallings}
1117d8580e4SDustin Sallings
112cd326610SSundar Sridharanstatic const char *dbname_env;
113905c61d8STrond Norbyestatic enum test_result rmdb(void)
114905c61d8STrond Norbye{
115905c61d8STrond Norbye    const char *files[] = { WHITESPACE_DB,
116793a3c8fSMike Wiederhold                            "/tmp/test",
1179d0355e4SChiyoung Seo                            "/tmp/mutation.log",
118cd326610SSundar Sridharan                            dbname_env,
119905c61d8STrond Norbye                            NULL };
120905c61d8STrond Norbye    int ii = 0;
121905c61d8STrond Norbye    while (files[ii] != NULL) {
122ff6aa7afSTrond Norbye        CouchbaseDirectoryUtilities::rmrf(files[ii]);
123905c61d8STrond Norbye        if (access(files[ii], F_OK) != -1) {
12469c82c9eSTrond Norbye            std::cerr << "Failed to remove: " << files[ii] << " " << std::endl;
125905c61d8STrond Norbye            return FAIL;
126905c61d8STrond Norbye        }
127905c61d8STrond Norbye        ++ii;
128b31e9044STrond Norbye    }
129b31e9044STrond Norbye
130b31e9044STrond Norbye    return SUCCESS;
131b31e9044STrond Norbye}
132b31e9044STrond Norbye
133dd37f93bSMike Wiederholdstatic enum test_result skipped_test_function(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
134dd37f93bSMike Wiederhold    (void) h;
135dd37f93bSMike Wiederhold    (void) h1;
136dd37f93bSMike Wiederhold    return SKIPPED;
137dd37f93bSMike Wiederhold}
138dd37f93bSMike Wiederhold
139e0ed5f06SDustin Sallingsstatic bool teardown(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
140e0ed5f06SDustin Sallings    (void)h; (void)h1;
1412e17c130SDustin Sallings    vals.clear();
14253e068aeSDustin Sallings    return true;
14353e068aeSDustin Sallings}
14453e068aeSDustin Sallings
14550b7e585SChiyoung Seostatic const void* createTapConn(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1,
14650b7e585SChiyoung Seo                                 const char *name) {
14750b7e585SChiyoung Seo    const void *cookie = testHarness.create_cookie();
14850b7e585SChiyoung Seo    testHarness.lock_cookie(cookie);
14950b7e585SChiyoung Seo    TAP_ITERATOR iter = h1->get_tap_iterator(h, cookie, name,
15050b7e585SChiyoung Seo                                             strlen(name),
15150b7e585SChiyoung Seo                                             TAP_CONNECT_FLAG_DUMP, NULL,
15250b7e585SChiyoung Seo                                             0);
15350b7e585SChiyoung Seo    check(iter != NULL, "Failed to create a tap iterator");
15450b7e585SChiyoung Seo    return cookie;
15550b7e585SChiyoung Seo}
15650b7e585SChiyoung Seo
15700e90515SMike Wiederholdstatic void check_key_value(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1,
15800e90515SMike Wiederhold                            const char* key, const char* val, size_t vlen,
15900e90515SMike Wiederhold                            uint16_t vbucket = 0) {
1608e20ce5eSDustin Sallings    item_info info;
16100e90515SMike Wiederhold    check(get_item_info(h, h1, &info, key, vbucket), "checking key and value");
16200e90515SMike Wiederhold    check(info.nvalue == 1, "info.nvalue != 1");
16300e90515SMike Wiederhold    check(vlen == info.value[0].iov_len, "Value length mismatch");
16400e90515SMike Wiederhold    check(memcmp(info.value[0].iov_base, val, vlen) == 0, "Data mismatch");
16553e068aeSDustin Sallings}
16653e068aeSDustin Sallings
167a88a4ea6SMike Wiederholdstatic bool test_setup(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
168a88a4ea6SMike Wiederhold    wait_for_warmup_complete(h, h1);
16946df358fSSundar Sridharan
17046df358fSSundar Sridharan    check(h1->get_stats(h, NULL, "prev-vbucket", 12, add_stats) == ENGINE_SUCCESS,
17146df358fSSundar Sridharan          "Failed to get the previous state of vbuckets");
17246df358fSSundar Sridharan    if (vals.find("vb_0") == vals.end()) {
17346df358fSSundar Sridharan        check(set_vbucket_state(h, h1, 0, vbucket_state_active),
17446df358fSSundar Sridharan              "Failed to set VB0 state.");
17546df358fSSundar Sridharan    }
17646df358fSSundar Sridharan
177ffeea187SChiyoung Seo    wait_for_stat_change(h, h1, "ep_vb_snapshot_total", 0);
178f4739115SChiyoung Seo
179a88a4ea6SMike Wiederhold    // warmup is complete, notify ep engine that it must now enable
180a88a4ea6SMike Wiederhold    // data traffic
1810389c521STrond Norbye    protocol_binary_request_header *pkt = createPacket(PROTOCOL_BINARY_CMD_ENABLE_TRAFFIC);
182a88a4ea6SMike Wiederhold    check(h1->unknown_command(h, NULL, pkt, add_response) == ENGINE_SUCCESS,
183a88a4ea6SMike Wiederhold          "Failed to enable data traffic");
184d8a8869fSJin Lim    free(pkt);
185f4739115SChiyoung Seo
186a88a4ea6SMike Wiederhold    return true;
187f4739115SChiyoung Seo}
188f4739115SChiyoung Seo
189d86d4d9cSManik Tanejastatic enum test_result test_getl(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
190d86d4d9cSManik Taneja    const char *key = "k1";
191d86d4d9cSManik Taneja    uint16_t vbucketId = 0;
1927ef00e44SMike Wiederhold    uint32_t expiration = 25;
193d86d4d9cSManik Taneja
194b7bb3215SMike Wiederhold    getl(h, h1, key, vbucketId, expiration);
195d86d4d9cSManik Taneja    check(last_status == PROTOCOL_BINARY_RESPONSE_KEY_ENOENT,
196d86d4d9cSManik Taneja          "expected the key to be missing...");
197d86d4d9cSManik Taneja    if (last_body != NULL && (strcmp(last_body, "NOT_FOUND") != 0)) {
198d86d4d9cSManik Taneja        fprintf(stderr, "Should have returned NOT_FOUND. Getl Failed");
199d86d4d9cSManik Taneja        abort();
200d86d4d9cSManik Taneja    }
201d86d4d9cSManik Taneja
202d86d4d9cSManik Taneja    item *i = NULL;
20309036c32Sabhinavdangeti    check(store(h, h1, NULL, OPERATION_SET, key, "{\"lock\":\"data\"}",
20409036c32Sabhinavdangeti                &i, 0, vbucketId, 3600, PROTOCOL_BINARY_DATATYPE_JSON)
205d86d4d9cSManik Taneja          == ENGINE_SUCCESS, "Failed to store an item.");
20657c4a064SJin Lim    h1->release(h, NULL, i);
207d86d4d9cSManik Taneja
208d86d4d9cSManik Taneja    /* retry getl, should succeed */
209b7bb3215SMike Wiederhold    getl(h, h1, key, vbucketId, expiration);
210d86d4d9cSManik Taneja    check(last_status == PROTOCOL_BINARY_RESPONSE_SUCCESS,
211d86d4d9cSManik Taneja          "Expected to be able to getl on first try");
21209036c32Sabhinavdangeti    check(strcmp("{\"lock\":\"data\"}", last_body) == 0, "Body was malformed.");
21309036c32Sabhinavdangeti    check(last_datatype == PROTOCOL_BINARY_DATATYPE_JSON,
21409036c32Sabhinavdangeti            "Expected datatype to be JSON");
215d86d4d9cSManik Taneja
2167ef00e44SMike Wiederhold    /* wait 16 seconds */
2177ef00e44SMike Wiederhold    testHarness.time_travel(16);
2187ef00e44SMike Wiederhold
219d86d4d9cSManik Taneja    /* lock's taken so this should fail */
220b7bb3215SMike Wiederhold    getl(h, h1, key, vbucketId, expiration);
221d86d4d9cSManik Taneja    check(last_status == PROTOCOL_BINARY_RESPONSE_ETMPFAIL,
222d86d4d9cSManik Taneja          "Expected to fail getl on second try");
223d86d4d9cSManik Taneja
224d86d4d9cSManik Taneja    if (last_body != NULL && (strcmp(last_body, "LOCK_ERROR") != 0)) {
225d86d4d9cSManik Taneja        fprintf(stderr, "Should have returned LOCK_ERROR. Getl Failed");
226d86d4d9cSManik Taneja        abort();
227d86d4d9cSManik Taneja    }
228d86d4d9cSManik Taneja
229d86d4d9cSManik Taneja    check(store(h, h1, NULL, OPERATION_SET, key, "lockdata2", &i, 0, vbucketId)
230d86d4d9cSManik Taneja          != ENGINE_SUCCESS, "Should have failed to store an item.");
23157c4a064SJin Lim    h1->release(h, NULL, i);
232d86d4d9cSManik Taneja
2337ef00e44SMike Wiederhold    /* wait another 10 seconds */
2347ef00e44SMike Wiederhold    testHarness.time_travel(10);
235d86d4d9cSManik Taneja
236d86d4d9cSManik Taneja    /* retry set, should succeed */
2370359c2efSManik Taneja    check(store(h, h1, NULL, OPERATION_SET, key, "lockdata", &i, 0, vbucketId)
238d86d4d9cSManik Taneja          == ENGINE_SUCCESS, "Failed to store an item.");
23957c4a064SJin Lim    h1->release(h, NULL, i);
2400359c2efSManik Taneja
2412ae8baaaSabhinavdangeti    /* point to wrong vbucket, to test NOT_MY_VB response */
2422ae8baaaSabhinavdangeti    getl(h, h1, key, 10, expiration);
2432ae8baaaSabhinavdangeti    check(last_status == PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET,
2442ae8baaaSabhinavdangeti          "Should have received not my vbucket response");
2460359c2efSManik Taneja    /* acquire lock, should succeed */
247b7bb3215SMike Wiederhold    getl(h, h1, key, vbucketId, expiration);
248b7bb3215SMike Wiederhold    check(last_status == PROTOCOL_BINARY_RESPONSE_SUCCESS,
249b7bb3215SMike Wiederhold          "Aquire lock should have succeeded");
25009036c32Sabhinavdangeti    check(last_datatype == PROTOCOL_BINARY_RAW_BYTES,
25109036c32Sabhinavdangeti            "Expected datatype to be RAW BYTES");
2520359c2efSManik Taneja
2530359c2efSManik Taneja    /* try an incr operation followed by a delete, both of which should fail */
2540359c2efSManik Taneja    uint64_t cas = 0;
2550359c2efSManik Taneja    uint64_t result = 0;
2560359c2efSManik Taneja
2570359c2efSManik Taneja    check(h1->arithmetic(h, NULL, key, 2, true, false, 1, 1, 0,
258ca67f157Sabhinavdangeti                         &cas, PROTOCOL_BINARY_RAW_BYTES, &result,
2590359c2efSManik Taneja                         0)  == ENGINE_TMPFAIL, "Incr failed");
2600359c2efSManik Taneja
2610359c2efSManik Taneja
2620b8fd7deSMike Wiederhold    check(del(h, h1, key, 0, 0) == ENGINE_TMPFAIL, "Delete failed");
263d86d4d9cSManik Taneja
2648cf6dc1dSManik Taneja
2658cf6dc1dSManik Taneja    /* bug MB 2699 append after getl should fail with ENGINE_TMPFAIL */
2668cf6dc1dSManik Taneja
2677ef00e44SMike Wiederhold    testHarness.time_travel(26);
2688cf6dc1dSManik Taneja
269594db19cSTrond Norbye    char binaryData1[] = "abcdefg\0gfedcba";
270594db19cSTrond Norbye    char binaryData2[] = "abzdefg\0gfedcba";
2718cf6dc1dSManik Taneja
2728cf6dc1dSManik Taneja    check(storeCasVb11(h, h1, NULL, OPERATION_SET, key,
2738cf6dc1dSManik Taneja                       binaryData1, sizeof(binaryData1) - 1, 82758, &i, 0, 0)
2748cf6dc1dSManik Taneja          == ENGINE_SUCCESS,
2758cf6dc1dSManik Taneja          "Failed set.");
27657c4a064SJin Lim    h1->release(h, NULL, i);
2778cf6dc1dSManik Taneja
2788cf6dc1dSManik Taneja    /* acquire lock, should succeed */
279b7bb3215SMike Wiederhold    getl(h, h1, key, vbucketId, expiration);
280b7bb3215SMike Wiederhold    check(last_status == PROTOCOL_BINARY_RESPONSE_SUCCESS,
281b7bb3215SMike Wiederhold          "Aquire lock should have succeeded");
2828cf6dc1dSManik Taneja
2838cf6dc1dSManik Taneja    /* append should fail */
2848cf6dc1dSManik Taneja    check(storeCasVb11(h, h1, NULL, OPERATION_APPEND, key,
2858cf6dc1dSManik Taneja                       binaryData2, sizeof(binaryData2) - 1, 82758, &i, 0, 0)
2868cf6dc1dSManik Taneja          == ENGINE_TMPFAIL,
2878cf6dc1dSManik Taneja          "Append should fail.");
28857c4a064SJin Lim    h1->release(h, NULL, i);
2898cf6dc1dSManik Taneja
290effd5f7aSManik Taneja    /* bug MB 3252 & MB 3354.
291effd5f7aSManik Taneja     * 1. Set a key with an expiry value.
292effd5f7aSManik Taneja     * 2. Take a lock on the item before it expires
293effd5f7aSManik Taneja     * 3. Wait for the item to expire
294effd5f7aSManik Taneja     * 4. Perform a CAS operation, should fail
295effd5f7aSManik Taneja     * 5. Perform a set operation, should succeed
296effd5f7aSManik Taneja     */
297effd5f7aSManik Taneja    const char *ekey = "test_expiry";
298effd5f7aSManik Taneja    const char *edata = "some test data here.";
299effd5f7aSManik Taneja
300effd5f7aSManik Taneja    item *it = NULL;
301effd5f7aSManik Taneja
302ca67f157Sabhinavdangeti    check(h1->allocate(h, NULL, &it, ekey, strlen(ekey), strlen(edata), 0, 2,
303ca67f157Sabhinavdangeti          PROTOCOL_BINARY_RAW_BYTES) == ENGINE_SUCCESS, "Allocation Failed");
304effd5f7aSManik Taneja
305effd5f7aSManik Taneja    item_info info;
306effd5f7aSManik Taneja    info.nvalue = 1;
307effd5f7aSManik Taneja    if (!h1->get_item_info(h, NULL, it, &info)) {
308effd5f7aSManik Taneja        abort();
309effd5f7aSManik Taneja    }
310effd5f7aSManik Taneja    memcpy(info.value[0].iov_base, edata, strlen(edata));
311effd5f7aSManik Taneja
312effd5f7aSManik Taneja    check(h1->store(h, NULL, it, &cas, OPERATION_SET, 0) ==
313effd5f7aSManik Taneja        ENGINE_SUCCESS, "Failed to Store item");
314effd5f7aSManik Taneja    check_key_value(h, h1, ekey, edata, strlen(edata));
315effd5f7aSManik Taneja    h1->release(h, NULL, it);
316effd5f7aSManik Taneja
317effd5f7aSManik Taneja    testHarness.time_travel(3);
318effd5f7aSManik Taneja    cas = last_cas;
319effd5f7aSManik Taneja
320effd5f7aSManik Taneja    /* cas should fail */
321effd5f7aSManik Taneja    check(storeCasVb11(h, h1, NULL, OPERATION_CAS, ekey,
322effd5f7aSManik Taneja                       binaryData1, sizeof(binaryData1) - 1, 82758, &i, cas, 0)
323effd5f7aSManik Taneja          != ENGINE_SUCCESS,
324effd5f7aSManik Taneja          "CAS succeeded.");
32557c4a064SJin Lim    h1->release(h, NULL, i);
326effd5f7aSManik Taneja
327effd5f7aSManik Taneja    /* but a simple store should succeed */
328effd5f7aSManik Taneja    check(store(h, h1, NULL, OPERATION_SET, ekey, edata, &i, 0, vbucketId)
329effd5f7aSManik Taneja          == ENGINE_SUCCESS, "Failed to store an item.");
33057c4a064SJin Lim    h1->release(h, NULL, i);
331d86d4d9cSManik Taneja    return SUCCESS;
332d86d4d9cSManik Taneja}
333d86d4d9cSManik Taneja
334d201a5ccSManik Tanejastatic enum test_result test_unl(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
335d201a5ccSManik Taneja
336d201a5ccSManik Taneja    const char *key = "k2";
337d201a5ccSManik Taneja    uint16_t vbucketId = 0;
338d201a5ccSManik Taneja
339b7bb3215SMike Wiederhold    unl(h, h1, key, vbucketId);
3409a8679e2SChiyoung Seo    check(last_status != PROTOCOL_BINARY_RESPONSE_SUCCESS,
341d201a5ccSManik Taneja          "expected the key to be missing...");
342d201a5ccSManik Taneja
343d201a5ccSManik Taneja    item *i = NULL;
344d201a5ccSManik Taneja    check(store(h, h1, NULL, OPERATION_SET, key, "lockdata", &i, 0, vbucketId)
345d201a5ccSManik Taneja          == ENGINE_SUCCESS, "Failed to store an item.");
34657c4a064SJin Lim    h1->release(h, NULL, i);
347d201a5ccSManik Taneja
348d201a5ccSManik Taneja    /* getl, should succeed */
349b7bb3215SMike Wiederhold    getl(h, h1, key, vbucketId, 0);
350d201a5ccSManik Taneja    check(last_status == PROTOCOL_BINARY_RESPONSE_SUCCESS,
351d201a5ccSManik Taneja          "Expected to be able to getl on first try");
352d201a5ccSManik Taneja
353d201a5ccSManik Taneja    /* save the returned cas value for later */
354d201a5ccSManik Taneja    uint64_t cas = last_cas;
355d201a5ccSManik Taneja
356d201a5ccSManik Taneja    /* lock's taken unlocking with a random cas value should fail */
357b7bb3215SMike Wiederhold    unl(h, h1, key, vbucketId);
358d201a5ccSManik Taneja    check(last_status == PROTOCOL_BINARY_RESPONSE_ETMPFAIL,
359d201a5ccSManik Taneja          "Expected to fail getl on second try");
360d201a5ccSManik Taneja
361d201a5ccSManik Taneja    if (last_body != NULL && (strcmp(last_body, "UNLOCK_ERROR") != 0)) {
362d201a5ccSManik Taneja        fprintf(stderr, "Should have returned UNLOCK_ERROR. Unl Failed");
363d201a5ccSManik Taneja        abort();
364d201a5ccSManik Taneja    }
365d201a5ccSManik Taneja
366b7bb3215SMike Wiederhold    unl(h, h1, key, vbucketId, cas);
367d201a5ccSManik Taneja    check(last_status == PROTOCOL_BINARY_RESPONSE_SUCCESS,
368d201a5ccSManik Taneja          "Expected to succed unl with correct cas");
369d201a5ccSManik Taneja
370d201a5ccSManik Taneja    /* acquire lock, should succeed */
371b7bb3215SMike Wiederhold    getl(h, h1, key, vbucketId, 0);
372d201a5ccSManik Taneja
373d201a5ccSManik Taneja    /* wait 16 seconds */
374d201a5ccSManik Taneja    testHarness.time_travel(16);
375d201a5ccSManik Taneja
376d201a5ccSManik Taneja    /* lock has expired, unl should fail */
377b7bb3215SMike Wiederhold    unl(h, h1, key, vbucketId, last_cas);
378d201a5ccSManik Taneja    check(last_status == PROTOCOL_BINARY_RESPONSE_ETMPFAIL,
379d201a5ccSManik Taneja          "Expected to fail unl on lock timeout");
380d201a5ccSManik Taneja
381d201a5ccSManik Taneja    return SUCCESS;
382d201a5ccSManik Taneja}
383d201a5ccSManik Taneja
384fd8548b7SChiyoung Seostatic enum test_result test_wrong_vb_mutation(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1,
385fd8548b7SChiyoung Seo                                               ENGINE_STORE_OPERATION op) {
386fd8548b7SChiyoung Seo    item *i = NULL;
387fd8548b7SChiyoung Seo    int numNotMyVBucket = get_int_stat(h, h1, "ep_num_not_my_vbuckets");
38861a661a9STrond Norbye    uint64_t cas = 11;
38961a661a9STrond Norbye    if (op == OPERATION_ADD) {
39061a661a9STrond Norbye        // Add operation with cas != 0 doesn't make sense
39161a661a9STrond Norbye        cas = 0;
39261a661a9STrond Norbye    }
393fd8548b7SChiyoung Seo    check(store(h, h1, NULL, op,
39461a661a9STrond Norbye                "key", "somevalue", &i, cas, 1) == ENGINE_NOT_MY_VBUCKET,
395fd8548b7SChiyoung Seo        "Expected not_my_vbucket");
39657c4a064SJin Lim    h1->release(h, NULL, i);
397fd8548b7SChiyoung Seo    wait_for_stat_change(h, h1, "ep_num_not_my_vbuckets", numNotMyVBucket);
398fd8548b7SChiyoung Seo    return SUCCESS;
399fd8548b7SChiyoung Seo}
400fd8548b7SChiyoung Seo
401fd8548b7SChiyoung Seostatic enum test_result test_pending_vb_mutation(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1,
402fd8548b7SChiyoung Seo                                                 ENGINE_STORE_OPERATION op) {
403fd8548b7SChiyoung Seo    const void *cookie = testHarness.create_cookie();
404fd8548b7SChiyoung Seo    testHarness.set_ewouldblock_handling(cookie, false);
405fd8548b7SChiyoung Seo    item *i = NULL;
4067a27df1eSTrond Norbye    check(set_vbucket_state(h, h1, 1, vbucket_state_pending), "Failed to set vbucket state.");
4077a27df1eSTrond Norbye    check(verify_vbucket_state(h, h1, 1, vbucket_state_pending), "Bucket state was not set to pending.");
40861a661a9STrond Norbye    uint64_t cas = 11;
40961a661a9STrond Norbye    if (op == OPERATION_ADD) {
41061a661a9STrond Norbye        // Add operation with cas != 0 doesn't make sense..
41161a661a9STrond Norbye        cas = 0;
41261a661a9STrond Norbye    }
413fd8548b7SChiyoung Seo    check(store(h, h1, cookie, op,
41461a661a9STrond Norbye                "key", "somevalue", &i, cas, 1) == ENGINE_EWOULDBLOCK,
415fd8548b7SChiyoung Seo        "Expected woodblock");
41657c4a064SJin Lim    h1->release(h, NULL, i);
417fd8548b7SChiyoung Seo    testHarness.destroy_cookie(cookie);
418fd8548b7SChiyoung Seo    return SUCCESS;
419fd8548b7SChiyoung Seo}
420fd8548b7SChiyoung Seo
421fd8548b7SChiyoung Seostatic enum test_result test_replica_vb_mutation(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1,
422fd8548b7SChiyoung Seo                                                 ENGINE_STORE_OPERATION op) {
423fd8548b7SChiyoung Seo    item *i = NULL;
4247a27df1eSTrond Norbye    check(set_vbucket_state(h, h1, 1, vbucket_state_replica), "Failed to set vbucket state.");
4257a27df1eSTrond Norbye    check(verify_vbucket_state(h, h1, 1, vbucket_state_replica), "Bucket state was not set to replica.");
426fd8548b7SChiyoung Seo    int numNotMyVBucket = get_int_stat(h, h1, "ep_num_not_my_vbuckets");
42761a661a9STrond Norbye
42861a661a9STrond Norbye    uint64_t cas = 11;
42961a661a9STrond Norbye    if (op == OPERATION_ADD) {
43061a661a9STrond Norbye        // performing add with a CAS != 0 doesn't make sense...
43161a661a9STrond Norbye        cas = 0;
43261a661a9STrond Norbye    }
433fd8548b7SChiyoung Seo    check(store(h, h1, NULL, op,
43461a661a9STrond Norbye                "key", "somevalue", &i, cas, 1) == ENGINE_NOT_MY_VBUCKET,
435fd8548b7SChiyoung Seo        "Expected not my vbucket");
436fd8548b7SChiyoung Seo    wait_for_stat_change(h, h1, "ep_num_not_my_vbuckets", numNotMyVBucket);
43757c4a064SJin Lim    h1->release(h, NULL, i);
438fd8548b7SChiyoung Seo    return SUCCESS;
439fd8548b7SChiyoung Seo}
440fd8548b7SChiyoung Seo
44153e068aeSDustin Sallings//
44253e068aeSDustin Sallings// ----------------------------------------------------------------------
44353e068aeSDustin Sallings// The actual tests are below.
44453e068aeSDustin Sallings// ----------------------------------------------------------------------
44553e068aeSDustin Sallings//
44653e068aeSDustin Sallings
44753e068aeSDustin Sallingsstatic enum test_result test_get_miss(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
4487d8580e4SDustin Sallings    check(verify_key(h, h1, "k") == ENGINE_KEY_ENOENT, "Expected miss.");
4497d8580e4SDustin Sallings    return SUCCESS;
45053e068aeSDustin Sallings}
45153e068aeSDustin Sallings
45253e068aeSDustin Sallingsstatic enum test_result test_set(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
45353e068aeSDustin Sallings    item *i = NULL;
4547d8580e4SDustin Sallings    check(ENGINE_SUCCESS ==
45590a18674STrond Norbye          store(h, h1, NULL, OPERATION_SET, "key", "somevalue", &i),
4567d8580e4SDustin Sallings          "Error setting.");
45757c4a064SJin Lim    h1->release(h, NULL, i);
4587d8580e4SDustin Sallings    return SUCCESS;
45953e068aeSDustin Sallings}
46053e068aeSDustin Sallings
461f9730d2bSDustin Sallingsstruct handle_pair {
462f9730d2bSDustin Sallings    ENGINE_HANDLE *h;
463f9730d2bSDustin Sallings    ENGINE_HANDLE_V1 *h1;
464f9730d2bSDustin Sallings};
465f9730d2bSDustin Sallings
466f9730d2bSDustin Sallingsextern "C" {
4674c5302deSTrond Norbye    static void conc_del_set_thread(void *arg) {
468f9730d2bSDustin Sallings        struct handle_pair *hp = static_cast<handle_pair *>(arg);
469f9730d2bSDustin Sallings        item *it = NULL;
470f9730d2bSDustin Sallings
4713b947f69SDustin Sallings        for (int i = 0; i < 5000; ++i) {
4723b947f69SDustin Sallings            store(hp->h, hp->h1, NULL, OPERATION_ADD,
4733b947f69SDustin Sallings                  "key", "somevalue", &it);
47457c4a064SJin Lim            hp->h1->release(hp->h, NULL, it);
4753b947f69SDustin Sallings            usleep(10);
4761ef67ba0SDustin Sallings            checkeq(ENGINE_SUCCESS,
4771ef67ba0SDustin Sallings                    store(hp->h, hp->h1, NULL, OPERATION_SET,
4781ef67ba0SDustin Sallings                          "key", "somevalue", &it),
4791ef67ba0SDustin Sallings                    "Error setting.");
48057c4a064SJin Lim            hp->h1->release(hp->h, NULL, it);
481f9730d2bSDustin Sallings            usleep(10);
482f9730d2bSDustin Sallings            // Ignoring the result here -- we're racing.
4830b8fd7deSMike Wiederhold            del(hp->h, hp->h1, "key", 0, 0);
484f9730d2bSDustin Sallings            usleep(10);
485f9730d2bSDustin Sallings        }
486f9730d2bSDustin Sallings    }
487dfe4c89cSSriram Ganesan
488dfe4c89cSSriram Ganesan    static void conc_incr_thread(void *arg) {
489dfe4c89cSSriram Ganesan        struct handle_pair *hp = static_cast<handle_pair *>(arg);
490dfe4c89cSSriram Ganesan        uint64_t cas = 0, result = 0;
491dfe4c89cSSriram Ganesan
492dfe4c89cSSriram Ganesan        for (int i = 0; i < 10; i++) {
493dfe4c89cSSriram Ganesan            check(hp->h1->arithmetic(hp->h, NULL, "key", 3, true, true, 1, 1, 0,
494dfe4c89cSSriram Ganesan                                     &cas, PROTOCOL_BINARY_RAW_BYTES, &result,
495dfe4c89cSSriram Ganesan                                     0) == ENGINE_SUCCESS,
496dfe4c89cSSriram Ganesan                                     "Failed arithmetic operation");
497dfe4c89cSSriram Ganesan        }
498dfe4c89cSSriram Ganesan    }
499f9730d2bSDustin Sallings}
500f9730d2bSDustin Sallings
501f9730d2bSDustin Sallingsstatic enum test_result test_conc_set(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
502f9730d2bSDustin Sallings
503f9730d2bSDustin Sallings    const int n_threads = 8;
5044c5302deSTrond Norbye    cb_thread_t threads[n_threads];
505f9730d2bSDustin Sallings    struct handle_pair hp = {h, h1};
506f9730d2bSDustin Sallings
507f9730d2bSDustin Sallings    wait_for_persisted_value(h, h1, "key", "value1");
508f9730d2bSDustin Sallings
509f9730d2bSDustin Sallings    for (int i = 0; i < n_threads; i++) {
5104c5302deSTrond Norbye        int r = cb_create_thread(&threads[i], conc_del_set_thread, &hp, 0);
511c649b2d9STrond Norbye        cb_assert(r == 0);
512f9730d2bSDustin Sallings    }
513f9730d2bSDustin Sallings
514f9730d2bSDustin Sallings    for (int i = 0; i < n_threads; i++) {
5154c5302deSTrond Norbye        int r = cb_join_thread(threads[i]);
516c649b2d9STrond Norbye        cb_assert(r == 0);
517f9730d2bSDustin Sallings    }
518f9730d2bSDustin Sallings
5193b947f69SDustin Sallings    wait_for_flusher_to_settle(h, h1);
520f9730d2bSDustin Sallings
521f9730d2bSDustin Sallings    testHarness.reload_engine(&h, &h1,
522f9730d2bSDustin Sallings                              testHarness.engine_path,
52329097179STrond Norbye                              testHarness.get_current_testcase()->cfg,
52414cff449SChiyoung Seo                              true, false);