d6d937ba0640b78f52058101d441ca9787a64c2a
[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
21 #include "stored-value.h"
22
23 #include "hash_table.h"
24
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;
30
31 bool StoredValue::ejectValue(HashTable &ht, item_eviction_policy_t policy) {
32     if (eligibleForEviction(policy)) {
33         reduceCacheSize(ht, value->length());
34         markNotResident();
35         value = NULL;
36         return true;
37     }
38     return false;
39 }
40
41 void StoredValue::referenced() {
42     if (nru > MIN_NRU_VALUE) {
43         --nru;
44     }
45 }
46
47 void StoredValue::setNRUValue(uint8_t nru_val) {
48     if (nru_val <= MAX_NRU_VALUE) {
49         nru = nru_val;
50     }
51 }
52
53 uint8_t StoredValue::incrNRUValue() {
54     uint8_t ret = MAX_NRU_VALUE;
55     if (nru < MAX_NRU_VALUE) {
56         ret = ++nru;
57     }
58     return ret;
59 }
60
61 uint8_t StoredValue::getNRUValue() const {
62     return nru;
63 }
64
65 void StoredValue::restoreValue(const Item& itm) {
66     if (isTempInitialItem()) {
67         cas = itm.getCas();
68         flags = itm.getFlags();
69         exptime = itm.getExptime();
70         revSeqno = itm.getRevSeqno();
71         bySeqno = itm.getBySeqno();
72         nru = INITIAL_NRU_VALUE;
73     }
74     datatype = itm.getDataType();
75     deleted = itm.isDeleted();
76     value = itm.getValue();
77 }
78
79 void StoredValue::restoreMeta(const Item& itm) {
80     cas = itm.getCas();
81     flags = itm.getFlags();
82     datatype = itm.getDataType();
83     exptime = itm.getExptime();
84     revSeqno = itm.getRevSeqno();
85     if (itm.isDeleted()) {
86         setDeleted();
87     } else { /* Regular item with the full eviction */
88         bySeqno = itm.getBySeqno();
89         /* set it back to false as we created a temp item by setting it to true
90            when bg fetch is scheduled (full eviction mode). */
91         newCacheItem = false;
92     }
93     if (nru == MAX_NRU_VALUE) {
94         nru = INITIAL_NRU_VALUE;
95     }
96 }
97
98 void StoredValue::del(HashTable& ht) {
99     if (isOrdered) {
100         return static_cast<OrderedStoredValue*>(this)->deleteImpl(ht);
101     } else {
102         return this->deleteImpl(ht);
103     }
104 }
105
106 void StoredValue::setMutationMemoryThreshold(double memThreshold) {
107     if (memThreshold > 0.0 && memThreshold <= 1.0) {
108         mutation_mem_threshold = memThreshold;
109     }
110 }
111
112 // TODO: Move these two methods to HashTable (it doesn't do anything with
113 // StoredValue objects).
114 void StoredValue::increaseCacheSize(HashTable &ht, size_t by) {
115     ht.cacheSize.fetch_add(by);
116     ht.memSize.fetch_add(by);
117 }
118
119 void StoredValue::reduceCacheSize(HashTable &ht, size_t by) {
120     ht.cacheSize.fetch_sub(by);
121     ht.memSize.fetch_sub(by);
122 }
123
124 void StoredValue::increaseMetaDataSize(HashTable &ht, EPStats &st, size_t by) {
125     ht.metaDataMemory.fetch_add(by);
126     st.currentSize.fetch_add(by);
127 }
128
129 void StoredValue::reduceMetaDataSize(HashTable &ht, EPStats &st, size_t by) {
130     ht.metaDataMemory.fetch_sub(by);
131     st.currentSize.fetch_sub(by);
132 }
133
134 /**
135  * Is there enough space for this thing?
136  */
137 bool StoredValue::hasAvailableSpace(EPStats &st, const Item &itm,
138                                     bool isReplication) {
139     double newSize = static_cast<double>(st.getTotalMemoryUsed() +
140                                          sizeof(StoredValue) + itm.getKey().size());
141     double maxSize = static_cast<double>(st.getMaxDataSize());
142     if (isReplication) {
143         return newSize <= (maxSize * st.replicationThrottleThreshold);
144     } else {
145         return newSize <= (maxSize * mutation_mem_threshold);
146     }
147 }
148
149 std::unique_ptr<Item> StoredValue::toItem(bool lck, uint16_t vbucket) const {
150     auto itm =
151             std::make_unique<Item>(getKey(),
152                                    getFlags(),
153                                    getExptime(),
154                                    value,
155                                    lck ? static_cast<uint64_t>(-1) : getCas(),
156                                    bySeqno,
157                                    vbucket,
158                                    getRevSeqno());
159
160     // This is a partial item...
161     if (value.get() == nullptr) {
162         itm->setDataType(datatype);
163     }
164
165     itm->setNRUValue(nru);
166
167     if (deleted) {
168         itm->setDeleted();
169     }
170
171     return itm;
172 }
173
174 std::unique_ptr<Item> StoredValue::toItemWithNoValue(uint16_t vbucket) const {
175     auto itm =
176             std::make_unique<Item>(getKey(),
177                                    getFlags(),
178                                    getExptime(),
179                                    /* valuePtr */ nullptr,
180                                    /* valuelen */ 0,
181                                    /* ext_meta*/ nullptr,
182                                    /* ext_len */ 0,
183                                    getCas(),
184                                    getBySeqno(),
185                                    vbucket,
186                                    getRevSeqno());
187
188     return itm;
189 }
190
191 void StoredValue::reallocate() {
192     // Allocate a new Blob for this stored value; copy the existing Blob to
193     // the new one and free the old.
194     value_t new_val(Blob::Copy(*value));
195     value.reset(new_val);
196 }
197
198 void StoredValue::Deleter::operator()(StoredValue* val) {
199     if (val->isOrdered) {
200         delete static_cast<OrderedStoredValue*>(val);
201     } else {
202         delete val;
203     }
204 }
205
206 OrderedStoredValue* StoredValue::toOrderedStoredValue() {
207     if (isOrdered) {
208         return static_cast<OrderedStoredValue*>(this);
209     }
210     throw std::bad_cast();
211 }
212
213 const OrderedStoredValue* StoredValue::toOrderedStoredValue() const {
214     if (isOrdered) {
215         return static_cast<const OrderedStoredValue*>(this);
216     }
217     throw std::bad_cast();
218 }
219
220 bool StoredValue::operator==(const StoredValue& other) const {
221     return (cas == other.cas && revSeqno == other.revSeqno &&
222             bySeqno == other.bySeqno &&
223             lock_expiry_or_delete_time == other.lock_expiry_or_delete_time &&
224             exptime == other.exptime && flags == other.flags &&
225             _isDirty == other._isDirty && deleted == other.deleted &&
226             newCacheItem == other.newCacheItem &&
227             isOrdered == other.isOrdered && nru == other.nru &&
228             getKey() == other.getKey());
229 }
230
231 void StoredValue::deleteImpl(HashTable& ht) {
232     if (isDeleted()) {
233         return;
234     }
235
236     reduceCacheSize(ht, valuelen());
237     resetValue();
238     markDirty();
239 }
240
241 std::ostream& operator<<(std::ostream& os, const StoredValue& sv) {
242
243     // type, address
244     os << (sv.isOrdered ? "OSV @" : " SV @") << &sv << " ";
245
246     // datatype: XCJ
247     os << (mcbp::datatype::is_xattr(sv.getDatatype()) ? 'X' : '.');
248     os << (mcbp::datatype::is_snappy(sv.getDatatype()) ? 'C' : '.');
249     os << (mcbp::datatype::is_json(sv.getDatatype()) ? 'J' : '.');
250     os << ' ';
251
252     // dirty (Written), deleted, new
253     os << (sv.isDirty() ? 'W' : '.');
254     os << (sv.isDeleted() ? 'D' : '.');
255     os << (sv.isNewCacheItem() ? 'N' : '.');
256     os << ' ';
257
258     // seqno, revid
259     os << "seq:" << sv.getBySeqno() << " rev:" << sv.getRevSeqno();
260     os << " key:\"" << sv.getKey();
261     os << "\" val:[";
262     if (sv.getValue().get()) {
263         const char* data = sv.getValue()->getData();
264         // print up to first 40 bytes of value.
265         const size_t limit = std::min(size_t(40), sv.getValue()->vlength());
266         for (size_t ii = 0; ii < limit; ii++) {
267             os << data[ii];
268         }
269         if (limit < sv.getValue()->vlength()) {
270             os << " <cut>";
271         }
272     } else {
273         os << "<null>";
274     }
275     os << "]";
276     return os;
277 }
278
279 bool OrderedStoredValue::operator==(const OrderedStoredValue& other) const {
280     return StoredValue::operator==(other);
281 }
282
283 /**
284  * Return the time the item was deleted. Only valid for deleted items.
285  */
286 rel_time_t OrderedStoredValue::getDeletedTime() const {
287     if (isDeleted()) {
288         return lock_expiry_or_delete_time;
289     } else {
290         throw std::logic_error(
291                 "OrderedStoredValue::getDeletedItem: Called on Alive item");
292     }
293 }
294
295 void OrderedStoredValue::deleteImpl(HashTable& ht) {
296     StoredValue::deleteImpl(ht);
297
298     // Need to record the time when an item is deleted for subsequent purging
299     // (ephemeral_metadata_purge_age).
300     setDeletedTime(ep_current_time());
301 }
302
303 void OrderedStoredValue::setDeletedTime(rel_time_t time) {
304     if (!isDeleted()) {
305         throw std::logic_error(
306                 "OrderedStoredValue::setDeletedTime: Called on Alive item");
307     }
308     lock_expiry_or_delete_time = time;
309 }