Decouple StoredValue and HashTable class
[ep-engine.git] / src / stored-value.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 #include "config.h"
19 #include <platform/cb_malloc.h>
20 #include "stored-value.h"
21
22 double StoredValue::mutation_mem_threshold = 0.9;
23 const int64_t StoredValue::state_deleted_key = -3;
24 const int64_t StoredValue::state_non_existent_key = -4;
25 const int64_t StoredValue::state_temp_init = -5;
26 const int64_t StoredValue::state_collection_open = -6;
27
28 StoredValue::StoredValue(const Item& itm,
29                          UniquePtr n,
30                          EPStats& stats,
31                          bool isOrdered)
32     : value(itm.getValue()),
33       chain_next_or_replacement(std::move(n)),
34       cas(itm.getCas()),
35       revSeqno(itm.getRevSeqno()),
36       bySeqno(itm.getBySeqno()),
37       lock_expiry_or_delete_time(0),
38       exptime(itm.getExptime()),
39       flags(itm.getFlags()),
40       datatype(itm.getDataType()),
41       deleted(itm.isDeleted()),
42       newCacheItem(true),
43       isOrdered(isOrdered),
44       nru(itm.getNRUValue()),
45       stale(false) {
46     // Placement-new the key which lives in memory directly after this
47     // object.
48     new (key()) SerialisedDocKey(itm.getKey());
49
50     if (isTempInitialItem()) {
51         markClean();
52     } else {
53         markDirty();
54     }
55
56     if (isTempItem()) {
57         markNotResident();
58     }
59
60     ObjectRegistry::onCreateStoredValue(this);
61 }
62
63 StoredValue::StoredValue(const StoredValue& other,
64                          UniquePtr n,
65                          EPStats& stats)
66     : value(other.value),
67       chain_next_or_replacement(std::move(n)),
68       cas(other.cas),
69       revSeqno(other.revSeqno),
70       bySeqno(other.bySeqno),
71       lock_expiry_or_delete_time(other.lock_expiry_or_delete_time),
72       exptime(other.exptime),
73       flags(other.flags),
74       datatype(other.datatype),
75       _isDirty(other._isDirty),
76       deleted(other.deleted),
77       newCacheItem(other.newCacheItem),
78       isOrdered(other.isOrdered),
79       nru(other.nru),
80       stale(false) {
81     // Placement-new the key which lives in memory directly after this
82     // object.
83     StoredDocKey sKey(other.getKey());
84     new (key()) SerialisedDocKey(sKey);
85
86     ObjectRegistry::onCreateStoredValue(this);
87 }
88
89 void StoredValue::setValue(const Item& itm) {
90     if (isOrdered) {
91         return static_cast<OrderedStoredValue*>(this)->setValueImpl(itm);
92     } else {
93         return this->setValueImpl(itm);
94     }
95 }
96
97 void StoredValue::ejectValue() {
98     markNotResident();
99     value = nullptr;
100 }
101
102 void StoredValue::referenced() {
103     if (nru > MIN_NRU_VALUE) {
104         --nru;
105     }
106 }
107
108 void StoredValue::setNRUValue(uint8_t nru_val) {
109     if (nru_val <= MAX_NRU_VALUE) {
110         nru = nru_val;
111     }
112 }
113
114 uint8_t StoredValue::incrNRUValue() {
115     uint8_t ret = MAX_NRU_VALUE;
116     if (nru < MAX_NRU_VALUE) {
117         ret = ++nru;
118     }
119     return ret;
120 }
121
122 uint8_t StoredValue::getNRUValue() const {
123     return nru;
124 }
125
126 void StoredValue::restoreValue(const Item& itm) {
127     if (isTempInitialItem()) {
128         cas = itm.getCas();
129         flags = itm.getFlags();
130         exptime = itm.getExptime();
131         revSeqno = itm.getRevSeqno();
132         bySeqno = itm.getBySeqno();
133         nru = INITIAL_NRU_VALUE;
134     }
135     datatype = itm.getDataType();
136     deleted = itm.isDeleted();
137     value = itm.getValue();
138 }
139
140 void StoredValue::restoreMeta(const Item& itm) {
141     cas = itm.getCas();
142     flags = itm.getFlags();
143     datatype = itm.getDataType();
144     exptime = itm.getExptime();
145     revSeqno = itm.getRevSeqno();
146     if (itm.isDeleted()) {
147         setDeleted();
148     } else { /* Regular item with the full eviction */
149         bySeqno = itm.getBySeqno();
150         /* set it back to false as we created a temp item by setting it to true
151            when bg fetch is scheduled (full eviction mode). */
152         newCacheItem = false;
153     }
154     if (nru == MAX_NRU_VALUE) {
155         nru = INITIAL_NRU_VALUE;
156     }
157 }
158
159 bool StoredValue::del() {
160     if (isOrdered) {
161         return static_cast<OrderedStoredValue*>(this)->deleteImpl();
162     } else {
163         return this->deleteImpl();
164     }
165 }
166
167 void StoredValue::setMutationMemoryThreshold(double memThreshold) {
168     if (memThreshold > 0.0 && memThreshold <= 1.0) {
169         mutation_mem_threshold = memThreshold;
170     }
171 }
172
173 /**
174  * Is there enough space for this thing?
175  */
176 bool StoredValue::hasAvailableSpace(EPStats &st, const Item &itm,
177                                     bool isReplication) {
178     double newSize = static_cast<double>(st.getTotalMemoryUsed() +
179                                          sizeof(StoredValue) + itm.getKey().size());
180     double maxSize = static_cast<double>(st.getMaxDataSize());
181     if (isReplication) {
182         return newSize <= (maxSize * st.replicationThrottleThreshold);
183     } else {
184         return newSize <= (maxSize * mutation_mem_threshold);
185     }
186 }
187
188 std::unique_ptr<Item> StoredValue::toItem(bool lck, uint16_t vbucket) const {
189     auto itm =
190             std::make_unique<Item>(getKey(),
191                                    getFlags(),
192                                    getExptime(),
193                                    value,
194                                    lck ? static_cast<uint64_t>(-1) : getCas(),
195                                    bySeqno,
196                                    vbucket,
197                                    getRevSeqno());
198
199     // This is a partial item...
200     if (value.get() == nullptr) {
201         itm->setDataType(datatype);
202     }
203
204     itm->setNRUValue(nru);
205
206     if (deleted) {
207         itm->setDeleted();
208     }
209
210     return itm;
211 }
212
213 std::unique_ptr<Item> StoredValue::toItemWithNoValue(uint16_t vbucket) const {
214     auto itm =
215             std::make_unique<Item>(getKey(),
216                                    getFlags(),
217                                    getExptime(),
218                                    /* valuePtr */ nullptr,
219                                    /* valuelen */ 0,
220                                    /* ext_meta*/ nullptr,
221                                    /* ext_len */ 0,
222                                    getCas(),
223                                    getBySeqno(),
224                                    vbucket,
225                                    getRevSeqno());
226
227     return itm;
228 }
229
230 void StoredValue::reallocate() {
231     // Allocate a new Blob for this stored value; copy the existing Blob to
232     // the new one and free the old.
233     value_t new_val(Blob::Copy(*value));
234     value.reset(new_val);
235 }
236
237 void StoredValue::Deleter::operator()(StoredValue* val) {
238     if (val->isOrdered) {
239         delete static_cast<OrderedStoredValue*>(val);
240     } else {
241         delete val;
242     }
243 }
244
245 OrderedStoredValue* StoredValue::toOrderedStoredValue() {
246     if (isOrdered) {
247         return static_cast<OrderedStoredValue*>(this);
248     }
249     throw std::bad_cast();
250 }
251
252 const OrderedStoredValue* StoredValue::toOrderedStoredValue() const {
253     if (isOrdered) {
254         return static_cast<const OrderedStoredValue*>(this);
255     }
256     throw std::bad_cast();
257 }
258
259 bool StoredValue::operator==(const StoredValue& other) const {
260     return (cas == other.cas && revSeqno == other.revSeqno &&
261             bySeqno == other.bySeqno &&
262             lock_expiry_or_delete_time == other.lock_expiry_or_delete_time &&
263             exptime == other.exptime && flags == other.flags &&
264             _isDirty == other._isDirty && deleted == other.deleted &&
265             newCacheItem == other.newCacheItem &&
266             isOrdered == other.isOrdered && nru == other.nru &&
267             getKey() == other.getKey());
268 }
269
270 bool StoredValue::deleteImpl() {
271     if (isDeleted() && !getValue()) {
272         // SV is already marked as deleted and has no value - no further
273         // deletion possible.
274         return false;
275     }
276
277     markNotResident();
278     // item no longer resident once value is reset
279     deleted = true;
280     markDirty();
281
282     return true;
283 }
284
285 void StoredValue::setValueImpl(const Item& itm) {
286     value = itm.getValue();
287     deleted = itm.isDeleted();
288     flags = itm.getFlags();
289     datatype = itm.getDataType();
290     bySeqno = itm.getBySeqno();
291
292     cas = itm.getCas();
293     lock_expiry_or_delete_time = 0;
294     exptime = itm.getExptime();
295     revSeqno = itm.getRevSeqno();
296
297     nru = itm.getNRUValue();
298
299     if (isTempInitialItem()) {
300         markClean();
301     } else {
302         markDirty();
303     }
304
305     if (isTempItem()) {
306         markNotResident();
307     }
308 }
309
310 std::ostream& operator<<(std::ostream& os, const StoredValue& sv) {
311
312     // type, address
313     os << (sv.isOrdered ? "OSV @" : " SV @") << &sv << " ";
314
315     // datatype: XCJ
316     os << (mcbp::datatype::is_xattr(sv.getDatatype()) ? 'X' : '.');
317     os << (mcbp::datatype::is_snappy(sv.getDatatype()) ? 'C' : '.');
318     os << (mcbp::datatype::is_json(sv.getDatatype()) ? 'J' : '.');
319     os << ' ';
320
321     // dirty (Written), deleted, new
322     os << (sv.isDirty() ? 'W' : '.');
323     os << (sv.isDeleted() ? 'D' : '.');
324     os << (sv.isNewCacheItem() ? 'N' : '.');
325     os << ' ';
326
327     // Temporary states
328     os << "temp:"
329        << (sv.isTempInitialItem() ? 'I' : ' ')
330        << (sv.isTempDeletedItem() ? 'D' : ' ')
331        << (sv.isTempNonExistentItem() ? 'N' : ' ')
332        << ' ';
333
334     // seqno, revid, expiry
335     os << "seq:" << sv.getBySeqno() << " rev:" << sv.getRevSeqno();
336     os << " key:\"" << sv.getKey() << "\"";
337     os << " exp:" << sv.getExptime();
338
339     os << " vallen:" << sv.valuelen();
340     if (sv.getValue().get()) {
341         os << " val:\"";
342         const char* data = sv.getValue()->getData();
343         // print up to first 40 bytes of value.
344         const size_t limit = std::min(size_t(40), sv.getValue()->vlength());
345         for (size_t ii = 0; ii < limit; ii++) {
346             os << data[ii];
347         }
348         if (limit < sv.getValue()->vlength()) {
349             os << " <cut>";
350         }
351         os << "\"";
352     }
353     return os;
354 }
355
356 bool OrderedStoredValue::operator==(const OrderedStoredValue& other) const {
357     return StoredValue::operator==(other);
358 }
359
360 /**
361  * Return the time the item was deleted. Only valid for deleted items.
362  */
363 rel_time_t OrderedStoredValue::getDeletedTime() const {
364     if (isDeleted()) {
365         return lock_expiry_or_delete_time;
366     } else {
367         throw std::logic_error(
368                 "OrderedStoredValue::getDeletedItem: Called on Alive item");
369     }
370 }
371
372 bool OrderedStoredValue::deleteImpl() {
373     if (StoredValue::deleteImpl()) {
374         // Need to record the time when an item is deleted for subsequent
375         //purging (ephemeral_metadata_purge_age).
376         setDeletedTime(ep_current_time());
377         return true;
378     }
379     return false;
380 }
381
382 void OrderedStoredValue::setValueImpl(const Item& itm) {
383     StoredValue::setValueImpl(itm);
384
385     // Update the deleted time (note - even if it was already deleted we should
386     // refresh this).
387     if (isDeleted()) {
388         setDeletedTime(ep_current_time());
389     }
390 }
391
392 void OrderedStoredValue::setDeletedTime(rel_time_t time) {
393     if (!isDeleted()) {
394         throw std::logic_error(
395                 "OrderedStoredValue::setDeletedTime: Called on Alive item");
396     }
397     lock_expiry_or_delete_time = time;
398 }