1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
3 * Copyright 2016 Couchbase, Inc
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <platform/cb_malloc.h>
21 #include "stored-value.h"
23 #include "hash_table.h"
25 double StoredValue::mutation_mem_threshold = 0.9;
26 const int64_t StoredValue::state_deleted_key = -3;
27 const int64_t StoredValue::state_non_existent_key = -4;
28 const int64_t StoredValue::state_temp_init = -5;
29 const int64_t StoredValue::state_collection_open = -6;
31 void StoredValue::setValue(const Item& itm, HashTable& ht) {
33 return static_cast<OrderedStoredValue*>(this)->setValueImpl(itm, ht);
35 return this->setValueImpl(itm, ht);
39 bool StoredValue::ejectValue(HashTable &ht, item_eviction_policy_t policy) {
40 if (eligibleForEviction(policy)) {
41 reduceCacheSize(ht, value->length());
49 void StoredValue::referenced() {
50 if (nru > MIN_NRU_VALUE) {
55 void StoredValue::setNRUValue(uint8_t nru_val) {
56 if (nru_val <= MAX_NRU_VALUE) {
61 uint8_t StoredValue::incrNRUValue() {
62 uint8_t ret = MAX_NRU_VALUE;
63 if (nru < MAX_NRU_VALUE) {
69 uint8_t StoredValue::getNRUValue() const {
73 void StoredValue::restoreValue(const Item& itm) {
74 if (isTempInitialItem()) {
76 flags = itm.getFlags();
77 exptime = itm.getExptime();
78 revSeqno = itm.getRevSeqno();
79 bySeqno = itm.getBySeqno();
80 nru = INITIAL_NRU_VALUE;
82 datatype = itm.getDataType();
83 deleted = itm.isDeleted();
84 value = itm.getValue();
87 void StoredValue::restoreMeta(const Item& itm) {
89 flags = itm.getFlags();
90 datatype = itm.getDataType();
91 exptime = itm.getExptime();
92 revSeqno = itm.getRevSeqno();
93 if (itm.isDeleted()) {
95 } else { /* Regular item with the full eviction */
96 bySeqno = itm.getBySeqno();
97 /* set it back to false as we created a temp item by setting it to true
98 when bg fetch is scheduled (full eviction mode). */
101 if (nru == MAX_NRU_VALUE) {
102 nru = INITIAL_NRU_VALUE;
106 void StoredValue::del(HashTable& ht) {
108 return static_cast<OrderedStoredValue*>(this)->deleteImpl(ht);
110 return this->deleteImpl(ht);
114 void StoredValue::setMutationMemoryThreshold(double memThreshold) {
115 if (memThreshold > 0.0 && memThreshold <= 1.0) {
116 mutation_mem_threshold = memThreshold;
120 // TODO: Move these two methods to HashTable (it doesn't do anything with
121 // StoredValue objects).
122 void StoredValue::increaseCacheSize(HashTable &ht, size_t by) {
123 ht.cacheSize.fetch_add(by);
124 ht.memSize.fetch_add(by);
127 void StoredValue::reduceCacheSize(HashTable &ht, size_t by) {
128 ht.cacheSize.fetch_sub(by);
129 ht.memSize.fetch_sub(by);
132 void StoredValue::increaseMetaDataSize(HashTable &ht, EPStats &st, size_t by) {
133 ht.metaDataMemory.fetch_add(by);
134 st.currentSize.fetch_add(by);
137 void StoredValue::reduceMetaDataSize(HashTable &ht, EPStats &st, size_t by) {
138 ht.metaDataMemory.fetch_sub(by);
139 st.currentSize.fetch_sub(by);
143 * Is there enough space for this thing?
145 bool StoredValue::hasAvailableSpace(EPStats &st, const Item &itm,
146 bool isReplication) {
147 double newSize = static_cast<double>(st.getTotalMemoryUsed() +
148 sizeof(StoredValue) + itm.getKey().size());
149 double maxSize = static_cast<double>(st.getMaxDataSize());
151 return newSize <= (maxSize * st.replicationThrottleThreshold);
153 return newSize <= (maxSize * mutation_mem_threshold);
157 std::unique_ptr<Item> StoredValue::toItem(bool lck, uint16_t vbucket) const {
159 std::make_unique<Item>(getKey(),
163 lck ? static_cast<uint64_t>(-1) : getCas(),
168 // This is a partial item...
169 if (value.get() == nullptr) {
170 itm->setDataType(datatype);
173 itm->setNRUValue(nru);
182 std::unique_ptr<Item> StoredValue::toItemWithNoValue(uint16_t vbucket) const {
184 std::make_unique<Item>(getKey(),
187 /* valuePtr */ nullptr,
189 /* ext_meta*/ nullptr,
199 void StoredValue::reallocate() {
200 // Allocate a new Blob for this stored value; copy the existing Blob to
201 // the new one and free the old.
202 value_t new_val(Blob::Copy(*value));
203 value.reset(new_val);
206 void StoredValue::Deleter::operator()(StoredValue* val) {
207 if (val->isOrdered) {
208 delete static_cast<OrderedStoredValue*>(val);
214 OrderedStoredValue* StoredValue::toOrderedStoredValue() {
216 return static_cast<OrderedStoredValue*>(this);
218 throw std::bad_cast();
221 const OrderedStoredValue* StoredValue::toOrderedStoredValue() const {
223 return static_cast<const OrderedStoredValue*>(this);
225 throw std::bad_cast();
228 bool StoredValue::operator==(const StoredValue& other) const {
229 return (cas == other.cas && revSeqno == other.revSeqno &&
230 bySeqno == other.bySeqno &&
231 lock_expiry_or_delete_time == other.lock_expiry_or_delete_time &&
232 exptime == other.exptime && flags == other.flags &&
233 _isDirty == other._isDirty && deleted == other.deleted &&
234 newCacheItem == other.newCacheItem &&
235 isOrdered == other.isOrdered && nru == other.nru &&
236 getKey() == other.getKey());
239 void StoredValue::deleteImpl(HashTable& ht) {
240 if (isDeleted() && !getValue()) {
241 // SV is already marked as deleted and has no value - no further
242 // deletion possible.
246 reduceCacheSize(ht, valuelen());
248 // item no longer resident once value is reset
253 void StoredValue::setValueImpl(const Item& itm, HashTable& ht) {
254 size_t currSize = size();
255 reduceCacheSize(ht, currSize);
256 value = itm.getValue();
257 deleted = itm.isDeleted();
258 flags = itm.getFlags();
259 datatype = itm.getDataType();
260 bySeqno = itm.getBySeqno();
263 lock_expiry_or_delete_time = 0;
264 exptime = itm.getExptime();
265 revSeqno = itm.getRevSeqno();
267 nru = itm.getNRUValue();
269 if (isTempInitialItem()) {
279 size_t newSize = size();
280 increaseCacheSize(ht, newSize);
283 std::ostream& operator<<(std::ostream& os, const StoredValue& sv) {
286 os << (sv.isOrdered ? "OSV @" : " SV @") << &sv << " ";
289 os << (mcbp::datatype::is_xattr(sv.getDatatype()) ? 'X' : '.');
290 os << (mcbp::datatype::is_snappy(sv.getDatatype()) ? 'C' : '.');
291 os << (mcbp::datatype::is_json(sv.getDatatype()) ? 'J' : '.');
294 // dirty (Written), deleted, new
295 os << (sv.isDirty() ? 'W' : '.');
296 os << (sv.isDeleted() ? 'D' : '.');
297 os << (sv.isNewCacheItem() ? 'N' : '.');
302 << (sv.isTempInitialItem() ? 'I' : ' ')
303 << (sv.isTempDeletedItem() ? 'D' : ' ')
304 << (sv.isTempNonExistentItem() ? 'N' : ' ')
307 // seqno, revid, expiry
308 os << "seq:" << sv.getBySeqno() << " rev:" << sv.getRevSeqno();
309 os << " key:\"" << sv.getKey() << "\"";
310 os << " exp:" << sv.getExptime();
312 os << " vallen:" << sv.valuelen();
313 if (sv.getValue().get()) {
315 const char* data = sv.getValue()->getData();
316 // print up to first 40 bytes of value.
317 const size_t limit = std::min(size_t(40), sv.getValue()->vlength());
318 for (size_t ii = 0; ii < limit; ii++) {
321 if (limit < sv.getValue()->vlength()) {
329 bool OrderedStoredValue::operator==(const OrderedStoredValue& other) const {
330 return StoredValue::operator==(other);
334 * Return the time the item was deleted. Only valid for deleted items.
336 rel_time_t OrderedStoredValue::getDeletedTime() const {
338 return lock_expiry_or_delete_time;
340 throw std::logic_error(
341 "OrderedStoredValue::getDeletedItem: Called on Alive item");
345 void OrderedStoredValue::deleteImpl(HashTable& ht) {
346 StoredValue::deleteImpl(ht);
348 // Need to record the time when an item is deleted for subsequent purging
349 // (ephemeral_metadata_purge_age).
350 setDeletedTime(ep_current_time());
353 void OrderedStoredValue::setValueImpl(const Item& itm, HashTable& ht) {
354 StoredValue::setValueImpl(itm, ht);
356 // Update the deleted time (note - even if it was already deleted we should
359 setDeletedTime(ep_current_time());
363 void OrderedStoredValue::setDeletedTime(rel_time_t time) {
365 throw std::logic_error(
366 "OrderedStoredValue::setDeletedTime: Called on Alive item");
368 lock_expiry_or_delete_time = time;