MB-23875: Implement the engine API for GAT 31/77031/6
authorTrond Norbye <trond.norbye@gmail.com>
Thu, 13 Apr 2017 13:48:00 +0000 (15:48 +0200)
committerTrond Norbye <trond.norbye@gmail.com>
Fri, 21 Apr 2017 12:46:57 +0000 (12:46 +0000)
Change-Id: If8b40447f72089413e7a89b5acdebde3734c179e
Reviewed-on: http://review.couchbase.org/77031
Tested-by: Build Bot <build@couchbase.com>
Reviewed-by: Dave Rigby <daver@couchbase.com>
src/ep_engine.cc
src/ep_engine.h
tests/ep_test_apis.cc
tests/ep_test_apis.h
tests/ep_testsuite.cc
tests/ep_testsuite_basic.cc

index 44a40dc..29693c9 100644 (file)
@@ -277,6 +277,15 @@ static cb::EngineErrorItemPair EvpGetIf(ENGINE_HANDLE* handle,
     return acquireEngine(handle)->get_if(cookie, key, vbucket, filter);
 }
 
+static cb::EngineErrorItemPair EvpGetAndTouch(ENGINE_HANDLE* handle,
+                                              const void* cookie,
+                                              const DocKey& key,
+                                              uint16_t vbucket,
+                                              uint32_t expiry_time) {
+    return acquireEngine(handle)->get_and_touch(cookie, key, vbucket,
+                                                expiry_time);
+}
+
 static ENGINE_ERROR_CODE EvpGetLocked(ENGINE_HANDLE* handle,
                                       const void* cookie,
                                       item** itm,
@@ -1775,6 +1784,7 @@ EventuallyPersistentEngine::EventuallyPersistentEngine(
     ENGINE_HANDLE_V1::release = EvpItemRelease;
     ENGINE_HANDLE_V1::get = EvpGet;
     ENGINE_HANDLE_V1::get_if = EvpGetIf;
+    ENGINE_HANDLE_V1::get_and_touch = EvpGetAndTouch;
     ENGINE_HANDLE_V1::get_locked = EvpGetLocked;
     ENGINE_HANDLE_V1::unlock = EvpUnlock;
     ENGINE_HANDLE_V1::get_stats = EvpGetStats;
@@ -2096,6 +2106,50 @@ ENGINE_ERROR_CODE EventuallyPersistentEngine::flush(const void *cookie){
     }
 }
 
+cb::EngineErrorItemPair EventuallyPersistentEngine::get_and_touch(const void* cookie,
+                                                           const DocKey& key,
+                                                           uint16_t vbucket,
+                                                           uint32_t exptime) {
+    auto* handle = reinterpret_cast<ENGINE_HANDLE*>(this);
+
+    time_t expiry_time = exptime;
+    if (exptime != 0) {
+        auto* core = serverApi->core;
+        expiry_time = core->abstime(core->realtime(exptime));
+    }
+    GetValue gv(kvBucket->getAndUpdateTtl(key, vbucket, cookie, expiry_time));
+
+    auto rv = gv.getStatus();
+    if (rv == ENGINE_SUCCESS) {
+        ++stats.numOpsGet;
+        ++stats.numOpsStore;
+        return std::make_pair(cb::engine_errc::success,
+                              cb::unique_item_ptr{gv.getValue(),
+                                                  cb::ItemDeleter{handle}});
+    }
+
+    if (isDegradedMode()) {
+        // Remap all some of the error codes
+        switch (rv) {
+        case ENGINE_KEY_EEXISTS:
+        case ENGINE_KEY_ENOENT:
+        case ENGINE_NOT_MY_VBUCKET:
+            rv = ENGINE_TMPFAIL;
+            break;
+        default:
+            break;
+        }
+    }
+
+    if (rv == ENGINE_KEY_EEXISTS) {
+        rv = ENGINE_LOCKED;
+    }
+
+    return std::make_pair(cb::engine_errc(rv),
+                          cb::unique_item_ptr{nullptr,
+                                              cb::ItemDeleter{handle}});
+}
+
 cb::EngineErrorItemPair EventuallyPersistentEngine::get_if(const void* cookie,
                                                        const DocKey& key,
                                                        uint16_t vbucket,
index e88de64..c3f8506 100644 (file)
@@ -196,6 +196,11 @@ public:
                                    std::function<bool(
                                        const item_info&)> filter);
 
+    cb::EngineErrorItemPair get_and_touch(const void* cookie,
+                                          const DocKey& key,
+                                          uint16_t vbucket,
+                                          uint32_t expiry_time);
+
     ENGINE_ERROR_CODE get_locked(const void* cookie,
                                  item** itm,
                                  const DocKey& key,
index 8ca4092..c9f0181 100644 (file)
@@ -556,18 +556,11 @@ ENGINE_ERROR_CODE seqnoPersistence(ENGINE_HANDLE* h,
     return rv;
 }
 
-void gat(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1, const char* key,
-         uint16_t vb, uint32_t exp, bool quiet) {
-    char ext[4];
-    uint8_t opcode = quiet ? PROTOCOL_BINARY_CMD_GATQ : PROTOCOL_BINARY_CMD_GAT;
-    uint32_t keylen = key ? strlen(key) : 0;
-    protocol_binary_request_header *request;
-    encodeExt(ext, exp);
-    request = createPacket(opcode, vb, 0, ext, 4, key, keylen);
+cb::EngineErrorItemPair gat(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1,
+                            const char* key, uint16_t vb, uint32_t exp) {
 
-    check(h1->unknown_command(h, NULL, request, add_response, testHarness.doc_namespace) == ENGINE_SUCCESS,
-          "Failed to call gat");
-    cb_free(request);
+    return h1->get_and_touch(h, nullptr, DocKey(key, testHarness.doc_namespace),
+                             vb, exp);
 }
 
 bool get_item_info(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1, item_info *info,
@@ -1001,17 +994,22 @@ ENGINE_ERROR_CODE storeCasVb11(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1,
     return rv;
 }
 
-void touch(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1, const char* key,
+ENGINE_ERROR_CODE touch(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1, const char* key,
            uint16_t vb, uint32_t exp) {
-    char ext[4];
-    uint32_t keylen = key ? strlen(key) : 0;
-    protocol_binary_request_header *request;
-    encodeExt(ext, exp);
-    request = createPacket(PROTOCOL_BINARY_CMD_TOUCH, vb, 0, ext, 4, key, keylen);
 
-    check(h1->unknown_command(h, NULL, request, add_response, testHarness.doc_namespace) == ENGINE_SUCCESS,
-          "Failed to call touch");
-    cb_free(request);
+    auto result = h1->get_and_touch(h, nullptr,
+                                    DocKey(key, testHarness.doc_namespace), vb,
+                                    exp);
+
+    // Update the global cas value (used by some tests)
+    if (result.first == cb::engine_errc::success) {
+        item_info info{};
+        check(h1->get_item_info(h, nullptr, result.second.get(), &info),
+              "Failed to get item info");
+        last_cas.store(info.cas);
+    }
+
+    return ENGINE_ERROR_CODE(result.first);
 }
 
 ENGINE_ERROR_CODE unl(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1,
index 6b679e5..0512d8b 100644 (file)
@@ -193,8 +193,8 @@ void evict_key(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1, const char *key,
                bool expectError = false);
 size_t estimateVBucketMove(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1,
                            uint16_t vbid = 0, const char* tap_name = "");
-void gat(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1, const char* key,
-         uint16_t vb, uint32_t exp, bool quiet = false);
+cb::EngineErrorItemPair gat(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1,
+                            const char* key, uint16_t vb, uint32_t exp);
 bool get_item_info(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1, item_info *info,
                    const char* key, uint16_t vb = 0);
 bool get_key(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1, item *i,
@@ -264,8 +264,8 @@ ENGINE_ERROR_CODE storeCasVb11(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1,
                                uint16_t vb, uint32_t exp = 3600,
                                uint8_t datatype = 0x00,
                                DocumentState docState = DocumentState::Alive);
-void touch(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1, const char* key,
-           uint16_t vb, uint32_t exp);
+ENGINE_ERROR_CODE touch(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1,
+                        const char* key, uint16_t vb, uint32_t exp);
 ENGINE_ERROR_CODE unl(ENGINE_HANDLE* h, ENGINE_HANDLE_V1* h1,
                       const void* cookie, const char* key,
                       uint16_t vb, uint64_t cas = 0);
index ea63d7d..3f61630 100644 (file)
@@ -1455,9 +1455,7 @@ static enum test_result test_vbucket_compact(ENGINE_HANDLE *h,
 
     check_key_value(h, h1, "trees", "cleanse", strlen("cleanse"));
 
-    touch(h, h1, key, 0, 11);
-    checkeq(PROTOCOL_BINARY_RESPONSE_SUCCESS, last_status.load(),
-            "touch Carss");
+    checkeq(ENGINE_SUCCESS, touch(h, h1, key, 0, 11), "touch Carss");
 
     testHarness.time_travel(12);
     wait_for_flusher_to_settle(h, h1);
index 4bf313c..42f860b 100644 (file)
@@ -972,30 +972,11 @@ static enum test_result test_replace(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
 }
 
 static enum test_result test_touch(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
-    // key is a mandatory field!
-    touch(h, h1, NULL, 0, (time(NULL) + 10));
-    checkeq(PROTOCOL_BINARY_RESPONSE_EINVAL,
-            last_status.load(), "Testing invalid arguments");
-
-    // extlen is a mandatory field!
-    protocol_binary_request_header *request;
-    request = createPacket(PROTOCOL_BINARY_CMD_TOUCH, 0, 0, NULL, 0, "akey", 4);
-    checkeq(ENGINE_SUCCESS,
-            h1->unknown_command(h, NULL, request, add_response, testHarness.doc_namespace),
-            "Failed to call touch");
-    checkeq(PROTOCOL_BINARY_RESPONSE_EINVAL, last_status.load(),
-            "Testing invalid arguments");
-    cb_free(request);
-
     // Try to touch an unknown item...
-    touch(h, h1, "mykey", 0, (time(NULL) + 10));
-    checkeq(PROTOCOL_BINARY_RESPONSE_KEY_ENOENT, last_status.load(),
-            "Testing unknown key");
+    checkeq(ENGINE_KEY_ENOENT, touch(h, h1, "mykey", 0, 0), "Testing unknown key");
 
     // illegal vbucket
-    touch(h, h1, "mykey", 5, (time(NULL) + 10));
-    checkeq(PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET, last_status.load(),
-            "Testing illegal vbucket");
+    checkeq(ENGINE_NOT_MY_VBUCKET, touch(h, h1, "mykey", 5, 0), "Testing illegal vbucket");
 
     // Store the item!
     item *itm = NULL;
@@ -1014,8 +995,8 @@ static enum test_result test_touch(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
     time_t curr_exptime = last_meta.exptime;
     uint64_t curr_revseqno = last_meta.revSeqno;
 
-    touch(h, h1, "mykey", 0, (time(NULL) + 10));
-    checkeq(PROTOCOL_BINARY_RESPONSE_SUCCESS, last_status.load(),
+    checkeq(ENGINE_SUCCESS,
+            touch(h, h1, "mykey", 0, uint32_t(time(NULL) + 10)),
             "touch mykey");
     check(last_cas != curr_cas, "touch should have returned an updated CAS");
 
@@ -1052,9 +1033,7 @@ static enum test_result test_touch_mb7342(ENGINE_HANDLE *h,
             "Failed set.");
     h1->release(h, NULL, itm);
 
-    touch(h, h1, key, 0, 0);
-    checkeq(PROTOCOL_BINARY_RESPONSE_SUCCESS,
-            last_status.load(), "touch key");
+    checkeq(ENGINE_SUCCESS, touch(h, h1, key, 0, 0), "touch key");
 
     check_key_value(h, h1, key, "v", 1);
 
@@ -1079,39 +1058,23 @@ static enum test_result test_touch_mb10277(ENGINE_HANDLE *h,
     wait_for_flusher_to_settle(h, h1);
     evict_key(h, h1, key, 0, "Ejected.");
 
-    touch(h, h1, key, 0, 3600); // A new expiration time remains in the same.
-    checkeq(PROTOCOL_BINARY_RESPONSE_SUCCESS,
-            last_status.load(),
+    checkeq(ENGINE_SUCCESS,
+            touch(h, h1, key, 0, 3600), // A new expiration time remains in the same.
             "touch key");
 
     return SUCCESS;
 }
 
 static enum test_result test_gat(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
-    // key is a mandatory field!
-    gat(h, h1, NULL, 0, 10);
-    checkeq(PROTOCOL_BINARY_RESPONSE_EINVAL, last_status.load(),
-            "Testing invalid arguments");
-
-    // extlen is a mandatory field!
-    protocol_binary_request_header *request;
-    request = createPacket(PROTOCOL_BINARY_CMD_GAT, 0, 0, NULL, 0, "akey", 4);
-    checkeq(ENGINE_SUCCESS,
-            h1->unknown_command(h, NULL, request, add_response, testHarness.doc_namespace),
-            "Failed to call gat");
-    checkeq(PROTOCOL_BINARY_RESPONSE_EINVAL, last_status.load(),
-            "Testing invalid arguments");
-    cb_free(request);
-
     // Try to gat an unknown item...
-    gat(h, h1, "mykey", 0, 10);
-    checkeq(PROTOCOL_BINARY_RESPONSE_KEY_ENOENT, last_status.load(),
+    auto ret = gat(h, h1, "mykey", 0, 10);
+    checkeq(ENGINE_KEY_ENOENT, ENGINE_ERROR_CODE(ret.first),
             "Testing unknown key");
 
     // illegal vbucket
-    gat(h, h1, "mykey", 5, 10);
-    checkeq(PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET,
-            last_status.load(), "Testing illegal vbucket");
+    ret = gat(h, h1, "mykey", 5, 10);
+    checkeq(ENGINE_NOT_MY_VBUCKET,
+            ENGINE_ERROR_CODE(ret.first), "Testing illegal vbucket");
 
     // Store the item!
     item *itm = NULL;
@@ -1124,78 +1087,21 @@ static enum test_result test_gat(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
     check_key_value(h, h1, "mykey", "{\"some\":\"value\"}",
             strlen("{\"some\":\"value\"}"));
 
-    gat(h, h1, "mykey", 0, 10);
-    checkeq(PROTOCOL_BINARY_RESPONSE_SUCCESS,
-            last_status.load(), "gat mykey");
-    checkeq(static_cast<uint8_t>(PROTOCOL_BINARY_DATATYPE_JSON),
-            last_datatype.load(), "Expected datatype to be JSON");
-    check(last_body.compare(0, sizeof("{\"some\":\"value\"}"),
-                            "{\"some\":\"value\"}") == 0,
-          "Invalid data returned");
-
-    // time-travel 9 secs..
-    testHarness.time_travel(9);
-
-    // The item should still exist
-    check_key_value(h, h1, "mykey", "{\"some\":\"value\"}",
-                    strlen("{\"some\":\"value\"}"));
-
-    // time-travel 2 secs..
-    testHarness.time_travel(2);
-
-    // The item should have expired now...
-    checkeq(ENGINE_KEY_ENOENT,
-            get(h, h1, NULL, &itm, "mykey", 0), "Item should be gone");
-    return SUCCESS;
-}
-
-static enum test_result test_gatq(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
-    // key is a mandatory field!
-    gat(h, h1, NULL, 0, 10, true);
-    checkeq(PROTOCOL_BINARY_RESPONSE_EINVAL,
-            last_status.load(), "Testing invalid arguments");
-
-    // extlen is a mandatory field!
-    protocol_binary_request_header *request;
-    request = createPacket(PROTOCOL_BINARY_CMD_GATQ, 0, 0, NULL, 0, "akey", 4);
-    checkeq(ENGINE_SUCCESS,
-            h1->unknown_command(h, NULL, request, add_response, testHarness.doc_namespace),
-            "Failed to call gatq");
-    checkeq(PROTOCOL_BINARY_RESPONSE_EINVAL,
-            last_status.load(), "Testing invalid arguments");
-    cb_free(request);
-
-    // Try to gatq an unknown item...
-    last_status = static_cast<protocol_binary_response_status>(0xffff);
-    gat(h, h1, "mykey", 0, 10, true);
-
-    // We should not have sent any response!
-    checkeq((protocol_binary_response_status)0xffff,
-            last_status.load(), "Testing unknown key");
-
-    // illegal vbucket
-    gat(h, h1, "mykey", 5, 10, true);
-    checkeq(PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET,
-            last_status.load(),
-            "Testing illegal vbucket");
-
-    // Store the item!
-    item *itm = NULL;
+    ret = gat(h, h1, "mykey", 0, 10);
     checkeq(ENGINE_SUCCESS,
-            store(h, h1, NULL, OPERATION_SET, "mykey", "{\"some\":\"value\"}",
-                  &itm, 0, 0, 3600, PROTOCOL_BINARY_DATATYPE_JSON),
-            "Failed set.");
-    h1->release(h, NULL, itm);
+            ENGINE_ERROR_CODE(ret.first), "gat mykey");
 
-    check_key_value(h, h1, "mykey", "{\"some\":\"value\"}",
-                    strlen("{\"some\":\"value\"}"));
+    item_info info;
+    check(h1->get_item_info(h, nullptr, ret.second.get(), &info),
+          "Getting item info failed");
 
-    gat(h, h1, "mykey", 0, 10, true);
-    checkeq(PROTOCOL_BINARY_RESPONSE_SUCCESS, last_status.load(), "gat mykey");
     checkeq(static_cast<uint8_t>(PROTOCOL_BINARY_DATATYPE_JSON),
-            last_datatype.load(), "Expected datatype to be JSON");
-    check(last_body.compare(0, sizeof("{\"some\":\"value\"}"),
-                            "{\"some\":\"value\"}") == 0,
+            info.datatype, "Expected datatype to be JSON");
+
+    std::string body{static_cast<char*>(info.value[0].iov_base),
+    info.value[0].iov_len};
+    check(body.compare(0, sizeof("{\"some\":\"value\"}"),
+                       "{\"some\":\"value\"}") == 0,
           "Invalid data returned");
 
     // time-travel 9 secs..
@@ -1210,8 +1116,7 @@ static enum test_result test_gatq(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
 
     // The item should have expired now...
     checkeq(ENGINE_KEY_ENOENT,
-            get(h, h1, NULL, &itm, "mykey", 0),
-            "Item should be gone");
+            get(h, h1, NULL, &itm, "mykey", 0), "Item should be gone");
     return SUCCESS;
 }
 
@@ -1230,19 +1135,17 @@ static enum test_result test_gat_locked(ENGINE_HANDLE *h,
           "Expected getl to succeed on key");
     h1->release(h, nullptr, locked);
 
-    gat(h, h1, "key", 0, 10);
-    checkeq(PROTOCOL_BINARY_RESPONSE_ETMPFAIL, last_status.load(), "Expected tmp fail");
-    check(last_body == "Lock Error", "Wrong error message");
+    auto ret = gat(h, h1, "key", 0, 10);
+    checkeq(ENGINE_LOCKED, ENGINE_ERROR_CODE(ret.first), "Expected LOCKED");
 
     testHarness.time_travel(16);
-    gat(h, h1, "key", 0, 10);
-    checkeq(PROTOCOL_BINARY_RESPONSE_SUCCESS, last_status.load(), "Expected success");
+    ret = gat(h, h1, "key", 0, 10);
+    checkeq(ENGINE_SUCCESS, ENGINE_ERROR_CODE(ret.first), "Expected success");
 
     testHarness.time_travel(11);
     checkeq(ENGINE_KEY_ENOENT,
             get(h, h1, NULL, &itm, "key", 0),
             "Expected value to be expired");
-
     return SUCCESS;
 }
 
@@ -1260,13 +1163,11 @@ static enum test_result test_touch_locked(ENGINE_HANDLE *h,
             "Expected getl to succeed on key");
     h1->release(h, nullptr, locked);
 
-    touch(h, h1, "key", 0, 10);
-    checkeq(PROTOCOL_BINARY_RESPONSE_ETMPFAIL, last_status.load(), "Expected tmp fail");
-    check(last_body == "Lock Error", "Wrong error message");
+    checkeq(ENGINE_LOCKED, touch(h, h1, "key", 0, 10),
+            "Expected tmp fail");
 
     testHarness.time_travel(16);
-    touch(h, h1, "key", 0, 10);
-    checkeq(PROTOCOL_BINARY_RESPONSE_SUCCESS, last_status.load(), "Expected success");
+    checkeq(ENGINE_SUCCESS, touch(h, h1, "key", 0, 10), "Expected success");
 
     testHarness.time_travel(11);
     checkeq(ENGINE_KEY_ENOENT,
@@ -1292,9 +1193,7 @@ static enum test_result test_mb5215(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
     // set new exptime to 111
     int expTime = time(NULL) + 111;
 
-    touch(h, h1, "coolkey", 0, expTime);
-    checkeq(PROTOCOL_BINARY_RESPONSE_SUCCESS,
-            last_status.load(),
+    checkeq(ENGINE_SUCCESS, touch(h, h1, "coolkey", 0, expTime),
             "touch coolkey");
 
     //reload engine
@@ -1319,9 +1218,8 @@ static enum test_result test_mb5215(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
     evict_key(h, h1, "coolkey", 0, "Ejected.");
 
     expTime = time(NULL) + 222;
-    touch(h, h1, "coolkey", 0, expTime);
-    checkeq(PROTOCOL_BINARY_RESPONSE_SUCCESS,
-            last_status.load(), "touch coolkey");
+    checkeq(ENGINE_SUCCESS ,touch(h, h1, "coolkey", 0, expTime),
+            "touch coolkey");
 
     testHarness.reload_engine(&h, &h1,
                               testHarness.engine_path,
@@ -2335,8 +2233,6 @@ BaseTestCase testsuite_testcases[] = {
                  teardown, NULL, prepare_ep_bucket, cleanup),
         TestCase("test gat", test_gat, test_setup, teardown,
                  NULL, prepare, cleanup),
-        TestCase("test gatq", test_gatq, test_setup, teardown,
-                 NULL, prepare, cleanup),
         TestCase("test locked gat", test_gat_locked,
                  test_setup, teardown, NULL, prepare, cleanup),
         TestCase("test locked touch", test_touch_locked,