xref: /5.5.2/kv_engine/engines/ep/src/stored-value.cc (revision bb60376f)
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
20#include "stored-value.h"
21
22#include "ep_time.h"
23#include "item.h"
24#include "objectregistry.h"
25#include "stats.h"
26
27#include <platform/cb_malloc.h>
28#include <platform/compress.h>
29
30const int64_t StoredValue::state_pending_seqno = -2;
31const int64_t StoredValue::state_deleted_key = -3;
32const int64_t StoredValue::state_non_existent_key = -4;
33const int64_t StoredValue::state_temp_init = -5;
34const int64_t StoredValue::state_collection_open = -6;
35
36StoredValue::StoredValue(const Item& itm,
37                         UniquePtr n,
38                         EPStats& stats,
39                         bool isOrdered)
40    : value(itm.getValue()),
41      chain_next_or_replacement(std::move(n)),
42      cas(itm.getCas()),
43      bySeqno(itm.getBySeqno()),
44      lock_expiry_or_delete_time(0),
45      exptime(itm.getExptime()),
46      flags(itm.getFlags()),
47      revSeqno(itm.getRevSeqno()),
48      datatype(itm.getDataType()) {
49    // Initialise bit fields
50    setDeletedPriv(itm.isDeleted());
51    setNewCacheItem(true);
52    setOrdered(isOrdered);
53    setNru(itm.getNRUValue());
54    setResident(!isTempItem());
55    setStale(false);
56    // dirty initialised below
57
58    // Placement-new the key which lives in memory directly after this
59    // object.
60    new (key()) SerialisedDocKey(itm.getKey());
61
62    if (isTempInitialItem()) {
63        markClean();
64    } else {
65        markDirty();
66    }
67
68    if (isTempItem()) {
69        resetValue();
70    }
71
72    ObjectRegistry::onCreateStoredValue(this);
73}
74
75StoredValue::~StoredValue() {
76    ObjectRegistry::onDeleteStoredValue(this);
77}
78
79StoredValue::StoredValue(const StoredValue& other, UniquePtr n, EPStats& stats)
80    : value(other.value), // Implicitly also copies the frequency counter
81      chain_next_or_replacement(std::move(n)),
82      cas(other.cas),
83      bySeqno(other.bySeqno),
84      lock_expiry_or_delete_time(other.lock_expiry_or_delete_time),
85      exptime(other.exptime),
86      flags(other.flags),
87      revSeqno(other.revSeqno),
88      datatype(other.datatype) {
89    setDirty(other.isDirty());
90    setDeletedPriv(other.isDeleted());
91    setNewCacheItem(other.isNewCacheItem());
92    setOrdered(other.isOrdered());
93    setNru(other.getNru());
94    setResident(other.isResident());
95    setStale(false);
96    // Placement-new the key which lives in memory directly after this
97    // object.
98    StoredDocKey sKey(other.getKey());
99    new (key()) SerialisedDocKey(sKey);
100
101    ObjectRegistry::onCreateStoredValue(this);
102}
103
104void StoredValue::setValue(const Item& itm) {
105    if (isOrdered()) {
106        return static_cast<OrderedStoredValue*>(this)->setValueImpl(itm);
107    } else {
108        return this->setValueImpl(itm);
109    }
110}
111
112void StoredValue::ejectValue() {
113    markNotResident();
114}
115
116void StoredValue::referenced() {
117    uint8_t nru = getNru();
118    if (nru > MIN_NRU_VALUE) {
119        setNru(--nru);
120    }
121}
122
123void StoredValue::setNRUValue(uint8_t nru_val) {
124    if (nru_val <= MAX_NRU_VALUE) {
125        setNru(nru_val);
126    }
127}
128
129uint8_t StoredValue::incrNRUValue() {
130    uint8_t ret = MAX_NRU_VALUE;
131    uint8_t nru = getNru();
132    if (nru < MAX_NRU_VALUE) {
133        ret = ++nru;
134        setNru(nru);
135    }
136    return ret;
137}
138
139uint8_t StoredValue::getNRUValue() const {
140    return getNru();
141}
142
143void StoredValue::setFreqCounterValue(uint16_t newValue) {
144    auto taggedPtr = value.get();
145    taggedPtr.setTag(newValue);
146    value.reset(taggedPtr);
147}
148
149uint16_t StoredValue::getFreqCounterValue() const {
150    return value.get().getTag();
151}
152
153void StoredValue::restoreValue(const Item& itm) {
154    if (isTempInitialItem() || isTempDeletedItem()) {
155        cas = itm.getCas();
156        flags = itm.getFlags();
157        exptime = itm.getExptime();
158        revSeqno = itm.getRevSeqno();
159        bySeqno = itm.getBySeqno();
160        setNru(INITIAL_NRU_VALUE);
161    }
162    datatype = itm.getDataType();
163    setDeletedPriv(itm.isDeleted());
164    value = itm.getValue(); // Implicitly also copies the frequency counter
165    setResident(true);
166}
167
168void StoredValue::restoreMeta(const Item& itm) {
169    cas = itm.getCas();
170    flags = itm.getFlags();
171    datatype = itm.getDataType();
172    exptime = itm.getExptime();
173    revSeqno = itm.getRevSeqno();
174    if (itm.isDeleted()) {
175        setTempDeleted();
176    } else { /* Regular item with the full eviction */
177        bySeqno = itm.getBySeqno();
178        /* set it back to false as we created a temp item by setting it to true
179           when bg fetch is scheduled (full eviction mode). */
180        setNewCacheItem(false);
181    }
182    if (getNru() == MAX_NRU_VALUE) {
183        setNru(INITIAL_NRU_VALUE);
184    }
185    setFreqCounterValue(itm.getFreqCounterValue());
186}
187
188size_t StoredValue::uncompressedValuelen() const {
189    if (!value) {
190        return 0;
191    }
192    if (mcbp::datatype::is_snappy(datatype)) {
193        return cb::compression::get_uncompressed_length(
194                cb::compression::Algorithm::Snappy,
195                {value->getData(), value->valueSize()});
196    }
197    return valuelen();
198}
199
200bool StoredValue::del() {
201    if (isOrdered()) {
202        return static_cast<OrderedStoredValue*>(this)->deleteImpl();
203    } else {
204        return this->deleteImpl();
205    }
206}
207
208size_t StoredValue::getRequiredStorage(const Item& item) {
209    return sizeof(StoredValue) +
210           SerialisedDocKey::getObjectSize(item.getKey().size());
211}
212
213std::unique_ptr<Item> StoredValue::toItem(bool lck, uint16_t vbucket) const {
214    auto itm =
215            std::make_unique<Item>(getKey(),
216                                   getFlags(),
217                                   getExptime(),
218                                   value,
219                                   datatype,
220                                   lck ? static_cast<uint64_t>(-1) : getCas(),
221                                   bySeqno,
222                                   vbucket,
223                                   getRevSeqno());
224
225    itm->setNRUValue(getNru());
226    itm->setFreqCounterValue(getFreqCounterValue());
227
228    if (isDeleted()) {
229        itm->setDeleted();
230    }
231
232    return itm;
233}
234
235std::unique_ptr<Item> StoredValue::toItemKeyOnly(uint16_t vbucket) const {
236    auto itm =
237            std::make_unique<Item>(getKey(),
238                                   getFlags(),
239                                   getExptime(),
240                                   value_t{},
241                                   datatype,
242                                   getCas(),
243                                   getBySeqno(),
244                                   vbucket,
245                                   getRevSeqno());
246
247    itm->setNRUValue(getNru());
248    itm->setFreqCounterValue(getFreqCounterValue());
249
250    if (isDeleted()) {
251        itm->setDeleted();
252    }
253
254    return itm;
255}
256
257void StoredValue::reallocate() {
258    // Allocate a new Blob for this stored value; copy the existing Blob to
259    // the new one and free the old.
260    value_t new_val(Blob::Copy(*value));
261    replaceValue(new_val.get());
262}
263
264void StoredValue::Deleter::operator()(StoredValue* val) {
265    if (val->isOrdered()) {
266        delete static_cast<OrderedStoredValue*>(val);
267    } else {
268        delete val;
269    }
270}
271
272OrderedStoredValue* StoredValue::toOrderedStoredValue() {
273    if (isOrdered()) {
274        return static_cast<OrderedStoredValue*>(this);
275    }
276    throw std::bad_cast();
277}
278
279const OrderedStoredValue* StoredValue::toOrderedStoredValue() const {
280    if (isOrdered()) {
281        return static_cast<const OrderedStoredValue*>(this);
282    }
283    throw std::bad_cast();
284}
285
286bool StoredValue::operator==(const StoredValue& other) const {
287    return (cas == other.cas && revSeqno == other.revSeqno &&
288            bySeqno == other.bySeqno &&
289            lock_expiry_or_delete_time == other.lock_expiry_or_delete_time &&
290            exptime == other.exptime && flags == other.flags &&
291            isDirty() == other.isDirty() && isDeleted() == other.isDeleted() &&
292            isNewCacheItem() == other.isNewCacheItem() &&
293            isOrdered() == other.isOrdered() && getNru() == other.getNru() &&
294            isResident() == other.isResident() && getKey() == other.getKey());
295}
296
297bool StoredValue::deleteImpl() {
298    if (isDeleted() && !getValue()) {
299        // SV is already marked as deleted and has no value - no further
300        // deletion possible.
301        return false;
302    }
303
304    resetValue();
305    setDatatype(PROTOCOL_BINARY_RAW_BYTES);
306    setPendingSeqno();
307
308    setDeletedPriv(true);
309    markDirty();
310
311    return true;
312}
313
314void StoredValue::setValueImpl(const Item& itm) {
315    if (isDeleted() && !itm.isDeleted()) {
316        // Transitioning from deleted -> alive - this should be considered
317        // a new cache item as it is increasing the number of (alive) items
318        // in the vBucket.
319        setNewCacheItem(true);
320    }
321
322    setDeletedPriv(itm.isDeleted());
323    flags = itm.getFlags();
324    datatype = itm.getDataType();
325    bySeqno = itm.getBySeqno();
326
327    cas = itm.getCas();
328    lock_expiry_or_delete_time = 0;
329    exptime = itm.getExptime();
330    revSeqno = itm.getRevSeqno();
331
332    if (isTempInitialItem()) {
333        markClean();
334    } else {
335        markDirty();
336    }
337
338    if (isTempItem()) {
339        setResident(false);
340    } else {
341        setResident(true);
342        value = itm.getValue();
343    }
344}
345
346bool StoredValue::compressValue() {
347    if (!mcbp::datatype::is_snappy(datatype)) {
348        // Attempt compression only if datatype indicates
349        // that the value is not compressed already
350        cb::compression::Buffer deflated;
351        if (cb::compression::deflate(cb::compression::Algorithm::Snappy,
352                                     {value->getData(), value->valueSize()},
353                                     deflated)) {
354            if (deflated.size() > value->valueSize()) {
355                // No point of keeping it compressed if the deflated length
356                // is greater than the original length
357                return true;
358            }
359            Blob* data = nullptr;
360            data = Blob::New(deflated.data(), deflated.size());
361            datatype |= PROTOCOL_BINARY_DATATYPE_SNAPPY;
362            replaceValue(TaggedPtr<Blob>(data));
363        } else {
364            return false;
365        }
366    }
367
368    return true;
369}
370
371void StoredValue::storeCompressedBuffer(cb::const_char_buffer deflated) {
372    Blob* data = Blob::New(deflated.data(), deflated.size());
373    datatype |= PROTOCOL_BINARY_DATATYPE_SNAPPY;
374    replaceValue(TaggedPtr<Blob>(data));
375}
376
377/**
378 * Get an item_info from the StoredValue
379 */
380boost::optional<item_info> StoredValue::getItemInfo(uint64_t vbuuid) const {
381    if (isTempItem()) {
382        return boost::none;
383    }
384
385    item_info info;
386    info.cas = cas;
387    info.vbucket_uuid = vbuuid;
388    info.seqno = bySeqno;
389    info.exptime = exptime;
390    info.nbytes = 0;
391    info.flags = flags;
392    info.datatype = datatype;
393    info.document_state =
394            isDeleted() ? DocumentState::Deleted : DocumentState::Alive;
395    info.nkey = getKey().size();
396    info.key = getKey().data();
397    if (getValue()) {
398        info.value[0].iov_base = const_cast<char*>(getValue()->getData());
399        info.value[0].iov_len = getValue()->valueSize();
400    }
401    return info;
402}
403
404std::ostream& operator<<(std::ostream& os, const StoredValue& sv) {
405
406    // type, address
407    os << (sv.isOrdered() ? "OSV @" : " SV @") << &sv << " ";
408
409    // datatype: XCJ
410    os << (mcbp::datatype::is_xattr(sv.getDatatype()) ? 'X' : '.');
411    os << (mcbp::datatype::is_snappy(sv.getDatatype()) ? 'C' : '.');
412    os << (mcbp::datatype::is_json(sv.getDatatype()) ? 'J' : '.');
413    os << ' ';
414
415    // dirty (Written), deleted, new, locked
416    os << (sv.isDirty() ? 'W' : '.');
417    os << (sv.isDeleted() ? 'D' : '.');
418    os << (sv.isNewCacheItem() ? 'N' : '.');
419    os << (sv.isResident() ? 'R' : '.');
420    os << (sv.isLocked(ep_current_time()) ? 'L' : '.');
421    if (sv.isOrdered()) {
422        const auto* osv = sv.toOrderedStoredValue();
423        os << (osv->isStalePriv() ? 'S' : '.');
424    }
425    os << ' ';
426
427    // Temporary states
428    os << "temp:"
429       << (sv.isTempInitialItem() ? 'I' : ' ')
430       << (sv.isTempDeletedItem() ? 'D' : ' ')
431       << (sv.isTempNonExistentItem() ? 'N' : ' ')
432       << ' ';
433
434    // seqno, revid, expiry / purge time
435    os << "seq:" << sv.getBySeqno() << " rev:" << sv.getRevSeqno();
436    os << " cas:" << sv.getCas();
437    os << " key:\"" << sv.getKey() << "\"";
438    if (sv.isOrdered() && sv.isDeleted()) {
439        os << " del_time:" << sv.lock_expiry_or_delete_time;
440    } else {
441        os << " exp:" << sv.getExptime();
442    }
443
444    os << " vallen:" << sv.valuelen();
445    if (sv.getValue().get()) {
446        os << " val:\"";
447        const char* data = sv.getValue()->getData();
448        // print up to first 40 bytes of value.
449        const size_t limit = std::min(size_t(40), sv.getValue()->valueSize());
450        for (size_t ii = 0; ii < limit; ii++) {
451            os << data[ii];
452        }
453        if (limit < sv.getValue()->valueSize()) {
454            os << " <cut>";
455        }
456        os << "\"";
457    }
458    return os;
459}
460
461bool OrderedStoredValue::operator==(const OrderedStoredValue& other) const {
462    return StoredValue::operator==(other);
463}
464
465size_t OrderedStoredValue::getRequiredStorage(const Item& item) {
466    return sizeof(OrderedStoredValue) +
467           SerialisedDocKey::getObjectSize(item.getKey());
468}
469
470/**
471 * Return the time the item was deleted. Only valid for deleted items.
472 */
473rel_time_t OrderedStoredValue::getDeletedTime() const {
474    if (isDeleted()) {
475        return lock_expiry_or_delete_time;
476    } else {
477        throw std::logic_error(
478                "OrderedStoredValue::getDeletedItem: Called on Alive item");
479    }
480}
481
482bool OrderedStoredValue::deleteImpl() {
483    if (StoredValue::deleteImpl()) {
484        // Need to record the time when an item is deleted for subsequent
485        //purging (ephemeral_metadata_purge_age).
486        setDeletedTime(ep_current_time());
487        return true;
488    }
489    return false;
490}
491
492void OrderedStoredValue::setValueImpl(const Item& itm) {
493    StoredValue::setValueImpl(itm);
494
495    // Update the deleted time (note - even if it was already deleted we should
496    // refresh this).
497    if (isDeleted()) {
498        setDeletedTime(ep_current_time());
499    }
500}
501
502void OrderedStoredValue::setDeletedTime(rel_time_t time) {
503    if (!isDeleted()) {
504        throw std::logic_error(
505                "OrderedStoredValue::setDeletedTime: Called on Alive item");
506    }
507    lock_expiry_or_delete_time = time;
508}
509