MB-23906: Implement delete-with-value with store() instead of delete()
[ep-engine.git] / tests / module_tests / stats_test.cc
1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2016 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 /*
19  * Unit test for stats
20  */
21
22 #include "stats_test.h"
23 #include "evp_store_single_threaded_test.h"
24 #include "test_helpers.h"
25
26 #include <gmock/gmock.h>
27
28 void StatTest::SetUp() {
29     SingleThreadedEPBucketTest::SetUp();
30     store->setVBucketState(vbid, vbucket_state_active, false);
31 }
32
33 std::map<std::string, std::string> StatTest::get_stat(const char* statkey) {
34     // Define a lambda to use as the ADD_STAT callback. Note we cannot use
35     // a capture for the statistics map (as it's a C-style callback), so
36     // instead pass via the cookie.
37     using StatMap = std::map<std::string, std::string>;
38     StatMap stats;
39     auto add_stats = [](const char* key,
40                         const uint16_t klen,
41                         const char* val,
42                         const uint32_t vlen,
43                         const void* cookie) {
44         auto* stats = reinterpret_cast<StatMap*>(const_cast<void*>(cookie));
45         std::string k(key, klen);
46         std::string v(val, vlen);
47         (*stats)[k] = v;
48     };
49
50     ENGINE_HANDLE* handle = reinterpret_cast<ENGINE_HANDLE*>(engine.get());
51     EXPECT_EQ(ENGINE_SUCCESS,
52               engine->get_stats(handle,
53                                 &stats,
54                                 statkey,
55                                 statkey == NULL ? 0 : strlen(statkey),
56                                 add_stats))
57         << "Failed to get stats.";
58
59     return stats;
60 }
61
62 class DatatypeStatTest : public StatTest,
63                          public ::testing::WithParamInterface<std::string> {
64 protected:
65     void SetUp() override {
66         config_string += std::string{"item_eviction_policy="} + GetParam();
67         StatTest::SetUp();
68     }
69 };
70
71 TEST_F(StatTest, vbucket_seqno_stats_test) {
72     using namespace testing;
73     const std::string vbucket = "vb_" + std::to_string(vbid);
74     auto vals = get_stat("vbucket-seqno");
75
76     EXPECT_THAT(vals, UnorderedElementsAre(
77             Key(vbucket + ":uuid"),
78             Pair(vbucket + ":high_seqno", "0"),
79             Pair(vbucket + ":abs_high_seqno", "0"),
80             Pair(vbucket + ":last_persisted_seqno", "0"),
81             Pair(vbucket + ":purge_seqno", "0"),
82             Pair(vbucket + ":last_persisted_snap_start", "0"),
83             Pair(vbucket + ":last_persisted_snap_end", "0")));
84 }
85
86 TEST_P(DatatypeStatTest, datatypesInitiallyZero) {
87     // Check that the datatype stats initialise to 0
88     auto vals = get_stat(nullptr);
89     EXPECT_EQ(0, std::stoi(vals["ep_active_datatype_snappy"]));
90     EXPECT_EQ(0, std::stoi(vals["ep_active_datatype_snappy,json"]));
91     EXPECT_EQ(0, std::stoi(vals["ep_active_datatype_snappy,xattr"]));
92     EXPECT_EQ(0, std::stoi(vals["ep_active_datatype_json"]));
93     EXPECT_EQ(0, std::stoi(vals["ep_active_datatype_json,xattr"]));
94     EXPECT_EQ(0, std::stoi(vals["ep_active_datatype_raw"]));
95     EXPECT_EQ(0, std::stoi(vals["ep_active_datatype_xattr"]));
96     EXPECT_EQ(0, std::stoi(vals["ep_active_datatype_snappy,json,xattr"]));
97
98     EXPECT_EQ(0, std::stoi(vals["ep_replica_datatype_snappy"]));
99     EXPECT_EQ(0, std::stoi(vals["ep_replica_datatype_snappy,json"]));
100     EXPECT_EQ(0, std::stoi(vals["ep_replica_datatype_snappy,xattr"]));
101     EXPECT_EQ(0, std::stoi(vals["ep_replica_datatype_json"]));
102     EXPECT_EQ(0, std::stoi(vals["ep_replica_datatype_json,xattr"]));
103     EXPECT_EQ(0, std::stoi(vals["ep_replica_datatype_raw"]));
104     EXPECT_EQ(0, std::stoi(vals["ep_replica_datatype_xattr"]));
105     EXPECT_EQ(0, std::stoi(vals["ep_replica_datatype_snappy,json,xattr"]));
106 }
107
108 void setDatatypeItem(KVBucket* store,
109                      const void* cookie,
110                      protocol_binary_datatype_t datatype,
111                      std::string name, std::string val = "[0]") {
112     Item item(make_item(
113             0, {name, DocNamespace::DefaultCollection}, val, 0, datatype));
114     store->set(item, cookie);
115 }
116
117 TEST_P(DatatypeStatTest, datatypeJsonToXattr) {
118     setDatatypeItem(store, cookie, PROTOCOL_BINARY_DATATYPE_JSON, "jsonDoc");
119     auto vals = get_stat(nullptr);
120     EXPECT_EQ(1, std::stoi(vals["ep_active_datatype_json"]));
121
122     // Check that updating an items datatype works
123     setDatatypeItem(store, cookie, PROTOCOL_BINARY_DATATYPE_XATTR, "jsonDoc");
124     vals = get_stat(nullptr);
125
126     EXPECT_EQ(1, std::stoi(vals["ep_active_datatype_xattr"]));
127     EXPECT_EQ(0, std::stoi(vals["ep_active_datatype_json"]));
128 }
129
130 TEST_P(DatatypeStatTest, datatypeRawStatTest) {
131     setDatatypeItem(store, cookie, 0, "rawDoc");
132     auto vals = get_stat(nullptr);
133     EXPECT_EQ(1, std::stoi(vals["ep_active_datatype_raw"]));
134 }
135
136 TEST_P(DatatypeStatTest, datatypeXattrStatTest) {
137     setDatatypeItem(store, cookie, PROTOCOL_BINARY_DATATYPE_XATTR, "xattrDoc");
138     auto vals = get_stat(nullptr);
139     EXPECT_EQ(1, std::stoi(vals["ep_active_datatype_xattr"]));
140     // Update the same key with a different value. The datatype stat should
141     // stay the same
142     setDatatypeItem(store, cookie, PROTOCOL_BINARY_DATATYPE_XATTR,
143                     "xattrDoc", "[2]");
144     vals = get_stat(nullptr);
145     EXPECT_EQ(1, std::stoi(vals["ep_active_datatype_xattr"]));
146 }
147
148 TEST_P(DatatypeStatTest, datatypeCompressedStatTest) {
149     setDatatypeItem(store,
150                     cookie,
151                     PROTOCOL_BINARY_DATATYPE_SNAPPY,
152                     "compressedDoc");
153     auto vals = get_stat(nullptr);
154     EXPECT_EQ(1, std::stoi(vals["ep_active_datatype_snappy"]));
155 }
156
157 TEST_P(DatatypeStatTest, datatypeCompressedJson) {
158     setDatatypeItem(
159             store,
160             cookie,
161             PROTOCOL_BINARY_DATATYPE_JSON | PROTOCOL_BINARY_DATATYPE_SNAPPY,
162             "jsonCompressedDoc");
163     auto vals = get_stat(nullptr);
164     EXPECT_EQ(1, std::stoi(vals["ep_active_datatype_snappy,json"]));
165 }
166
167 TEST_P(DatatypeStatTest, datatypeCompressedXattr) {
168     setDatatypeItem(store,
169                     cookie,
170                     PROTOCOL_BINARY_DATATYPE_XATTR |
171                             PROTOCOL_BINARY_DATATYPE_SNAPPY,
172                     "xattrCompressedDoc");
173     auto vals = get_stat(nullptr);
174     EXPECT_EQ(1, std::stoi(vals["ep_active_datatype_snappy,xattr"]));
175 }
176
177 TEST_P(DatatypeStatTest, datatypeJsonXattr) {
178     setDatatypeItem(
179             store,
180             cookie,
181             PROTOCOL_BINARY_DATATYPE_JSON | PROTOCOL_BINARY_DATATYPE_XATTR,
182             "jsonXattrDoc");
183     auto vals = get_stat(nullptr);
184     EXPECT_EQ(1, std::stoi(vals["ep_active_datatype_json,xattr"]));
185 }
186
187 TEST_P(DatatypeStatTest, datatypeDeletion) {
188     setDatatypeItem(
189             store,
190             cookie,
191             PROTOCOL_BINARY_DATATYPE_JSON | PROTOCOL_BINARY_DATATYPE_XATTR,
192             "jsonXattrDoc");
193     auto vals = get_stat(nullptr);
194     EXPECT_EQ(1, std::stoi(vals["ep_active_datatype_json,xattr"]));
195     uint64_t cas = 0;
196     store->deleteItem({"jsonXattrDoc", DocNamespace::DefaultCollection},
197                       cas,
198                       0,
199                       cookie,
200                       nullptr,
201                       nullptr);
202     vals = get_stat(nullptr);
203     EXPECT_EQ(0, std::stoi(vals["ep_active_datatype_json,xattr"]));
204 }
205
206 TEST_P(DatatypeStatTest, datatypeCompressedJsonXattr) {
207     setDatatypeItem(store,
208                     cookie,
209                     PROTOCOL_BINARY_DATATYPE_JSON |
210                             PROTOCOL_BINARY_DATATYPE_SNAPPY |
211                             PROTOCOL_BINARY_DATATYPE_XATTR,
212                     "jsonCompressedXattrDoc");
213     auto vals = get_stat(nullptr);
214     EXPECT_EQ(1, std::stoi(vals["ep_active_datatype_snappy,json,xattr"]));
215 }
216
217 TEST_P(DatatypeStatTest, datatypeExpireItem) {
218     Item item(make_item(
219             0, {"expiryDoc", DocNamespace::DefaultCollection}, "[0]", 1,
220             PROTOCOL_BINARY_DATATYPE_JSON));
221     store->set(item, cookie);
222     store->get({"expiryDoc", DocNamespace::DefaultCollection}, 0, cookie, NONE);
223     auto vals = get_stat(nullptr);
224
225     //Should be 0, becuase the doc should have expired
226     EXPECT_EQ(0, std::stoi(vals["ep_active_datatype_json"]));
227 }
228
229
230 TEST_P(DatatypeStatTest, datatypeEviction) {
231     const DocKey key = {"jsonXattrDoc", DocNamespace::DefaultCollection};
232     setDatatypeItem(
233             store,
234             cookie,
235             PROTOCOL_BINARY_DATATYPE_JSON | PROTOCOL_BINARY_DATATYPE_XATTR,
236             "jsonXattrDoc");
237     auto vals = get_stat(nullptr);
238     EXPECT_EQ(1, std::stoi(vals["ep_active_datatype_json,xattr"]));
239     store->flushVBucket(0);
240     const char* msg;
241     store->evictKey(key, 0, &msg);
242     vals = get_stat(nullptr);
243     if (GetParam() == "value_only"){
244         // Should still be 1 as only value is evicted
245         EXPECT_EQ(1, std::stoi(vals["ep_active_datatype_json,xattr"]));
246     } else {
247         // Should be 0 as everything is evicted
248         EXPECT_EQ(0, std::stoi(vals["ep_active_datatype_json,xattr"]));
249     }
250
251     store->get(key, 0, cookie, QUEUE_BG_FETCH);
252     if (GetParam() == "full_eviction") {
253         // Run the bgfetch to restore the item from disk
254         ExTask task = new SingleBGFetcherTask(
255                 engine.get(), key, 0, cookie, false, 0, false);
256         task_executor->schedule(task);
257         runNextTask(*task_executor->getLpTaskQ()[READER_TASK_IDX]);
258     }
259     vals = get_stat(nullptr);
260     // The item should be restored to memory, hence added back to the stats
261     EXPECT_EQ(1, std::stoi(vals["ep_active_datatype_json,xattr"]));
262 }
263
264 TEST_P(DatatypeStatTest, MB23892) {
265     // This test checks that updating a document with a different datatype is
266     // safe to do after an eviction (where the blob is now null)
267     const DocKey key = {"jsonXattrDoc", DocNamespace::DefaultCollection};
268     setDatatypeItem(
269             store,
270             cookie,
271             PROTOCOL_BINARY_DATATYPE_JSON | PROTOCOL_BINARY_DATATYPE_XATTR,
272             "jsonXattrDoc");
273     auto vals = get_stat(nullptr);
274     EXPECT_EQ(1, std::stoi(vals["ep_active_datatype_json,xattr"]));
275     store->flushVBucket(0);
276     const char* msg;
277     store->evictKey(key, 0, &msg);
278     store->flushVBucket(0);
279     setDatatypeItem(store, cookie, PROTOCOL_BINARY_DATATYPE_JSON, "jsonXattrDoc", "[1]");
280 }
281
282 INSTANTIATE_TEST_CASE_P(FullAndValueEviction, DatatypeStatTest,
283                         ::testing::Values("value_only", "full_eviction"), []
284                                 (const ::testing::TestParamInfo<std::string>&
285                                 info) {return info.param;});