b6b59c889441693740c526620570e978dc19a37b
[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 void StoredValue::setValue(const Item& itm, HashTable& ht) {
32     if (isOrdered) {
33         return static_cast<OrderedStoredValue*>(this)->setValueImpl(itm, ht);
34     } else {
35         return this->setValueImpl(itm, ht);
36     }
37 }
38
39 bool StoredValue::ejectValue(HashTable &ht, item_eviction_policy_t policy) {
40     if (eligibleForEviction(policy)) {
41         reduceCacheSize(ht, value->length());
42         markNotResident();
43         value = NULL;
44         return true;
45     }
46     return false;
47 }
48
49 void StoredValue::referenced() {
50     if (nru > MIN_NRU_VALUE) {
51         --nru;
52     }
53 }
54
55 void StoredValue::setNRUValue(uint8_t nru_val) {
56     if (nru_val <= MAX_NRU_VALUE) {
57         nru = nru_val;
58     }
59 }
60
61 uint8_t StoredValue::incrNRUValue() {
62     uint8_t ret = MAX_NRU_VALUE;
63     if (nru < MAX_NRU_VALUE) {
64         ret = ++nru;
65     }
66     return ret;
67 }
68
69 uint8_t StoredValue::getNRUValue() const {
70     return nru;
71 }
72
73 void StoredValue::restoreValue(const Item& itm) {
74     if (isTempInitialItem()) {
75         cas = itm.getCas();
76         flags = itm.getFlags();
77         exptime = itm.getExptime();
78         revSeqno = itm.getRevSeqno();
79         bySeqno = itm.getBySeqno();
80         nru = INITIAL_NRU_VALUE;
81     }
82     datatype = itm.getDataType();
83     deleted = itm.isDeleted();
84     value = itm.getValue();
85 }
86
87 void StoredValue::restoreMeta(const Item& itm) {
88     cas = itm.getCas();
89     flags = itm.getFlags();
90     datatype = itm.getDataType();
91     exptime = itm.getExptime();
92     revSeqno = itm.getRevSeqno();
93     if (itm.isDeleted()) {
94         setDeleted();
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). */
99         newCacheItem = false;
100     }
101     if (nru == MAX_NRU_VALUE) {
102         nru = INITIAL_NRU_VALUE;
103     }
104 }
105
106 void StoredValue::del(HashTable& ht) {
107     if (isOrdered) {
108         return static_cast<OrderedStoredValue*>(this)->deleteImpl(ht);
109     } else {
110         return this->deleteImpl(ht);
111     }
112 }
113
114 void StoredValue::setMutationMemoryThreshold(double memThreshold) {
115     if (memThreshold > 0.0 && memThreshold <= 1.0) {
116         mutation_mem_threshold = memThreshold;
117     }
118 }
119
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);
125 }
126
127 void StoredValue::reduceCacheSize(HashTable &ht, size_t by) {
128     ht.cacheSize.fetch_sub(by);
129     ht.memSize.fetch_sub(by);
130 }
131
132 void StoredValue::increaseMetaDataSize(HashTable &ht, EPStats &st, size_t by) {
133     ht.metaDataMemory.fetch_add(by);
134     st.currentSize.fetch_add(by);
135 }
136
137 void StoredValue::reduceMetaDataSize(HashTable &ht, EPStats &st, size_t by) {
138     ht.metaDataMemory.fetch_sub(by);
139     st.currentSize.fetch_sub(by);
140 }
141
142 /**
143  * Is there enough space for this thing?
144  */
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());
150     if (isReplication) {
151         return newSize <= (maxSize * st.replicationThrottleThreshold);
152     } else {
153         return newSize <= (maxSize * mutation_mem_threshold);
154     }
155 }
156
157 std::unique_ptr<Item> StoredValue::toItem(bool lck, uint16_t vbucket) const {
158     auto itm =
159             std::make_unique<Item>(getKey(),
160                                    getFlags(),
161                                    getExptime(),
162                                    value,
163                                    lck ? static_cast<uint64_t>(-1) : getCas(),
164                                    bySeqno,
165                                    vbucket,
166                                    getRevSeqno());
167
168     // This is a partial item...
169     if (value.get() == nullptr) {
170         itm->setDataType(datatype);
171     }
172
173     itm->setNRUValue(nru);
174
175     if (deleted) {
176         itm->setDeleted();
177     }
178
179     return itm;
180 }
181
182 std::unique_ptr<Item> StoredValue::toItemWithNoValue(uint16_t vbucket) const {
183     auto itm =
184             std::make_unique<Item>(getKey(),
185                                    getFlags(),
186                                    getExptime(),
187                                    /* valuePtr */ nullptr,
188                                    /* valuelen */ 0,
189                                    /* ext_meta*/ nullptr,
190                                    /* ext_len */ 0,
191                                    getCas(),
192                                    getBySeqno(),
193                                    vbucket,
194                                    getRevSeqno());
195
196     return itm;
197 }
198
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);
204 }
205
206 void StoredValue::Deleter::operator()(StoredValue* val) {
207     if (val->isOrdered) {
208         delete static_cast<OrderedStoredValue*>(val);
209     } else {
210         delete val;
211     }
212 }
213
214 OrderedStoredValue* StoredValue::toOrderedStoredValue() {
215     if (isOrdered) {
216         return static_cast<OrderedStoredValue*>(this);
217     }
218     throw std::bad_cast();
219 }
220
221 const OrderedStoredValue* StoredValue::toOrderedStoredValue() const {
222     if (isOrdered) {
223         return static_cast<const OrderedStoredValue*>(this);
224     }
225     throw std::bad_cast();
226 }
227
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());
237 }
238
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.
243         return;
244     }
245
246     reduceCacheSize(ht, valuelen());
247     markNotResident();
248     // item no longer resident once value is reset
249     deleted = true;
250     markDirty();
251 }
252
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();
261
262     cas = itm.getCas();
263     lock_expiry_or_delete_time = 0;
264     exptime = itm.getExptime();
265     revSeqno = itm.getRevSeqno();
266
267     nru = itm.getNRUValue();
268
269     if (isTempInitialItem()) {
270         markClean();
271     } else {
272         markDirty();
273     }
274
275     if (isTempItem()) {
276         markNotResident();
277     }
278
279     size_t newSize = size();
280     increaseCacheSize(ht, newSize);
281 }
282
283 std::ostream& operator<<(std::ostream& os, const StoredValue& sv) {
284
285     // type, address
286     os << (sv.isOrdered ? "OSV @" : " SV @") << &sv << " ";
287
288     // datatype: XCJ
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' : '.');
292     os << ' ';
293
294     // dirty (Written), deleted, new
295     os << (sv.isDirty() ? 'W' : '.');
296     os << (sv.isDeleted() ? 'D' : '.');
297     os << (sv.isNewCacheItem() ? 'N' : '.');
298     os << ' ';
299
300     // Temporary states
301     os << "temp:"
302        << (sv.isTempInitialItem() ? 'I' : ' ')
303        << (sv.isTempDeletedItem() ? 'D' : ' ')
304        << (sv.isTempNonExistentItem() ? 'N' : ' ')
305        << ' ';
306
307     // seqno, revid, expiry
308     os << "seq:" << sv.getBySeqno() << " rev:" << sv.getRevSeqno();
309     os << " key:\"" << sv.getKey() << "\"";
310     os << " exp:" << sv.getExptime();
311
312     os << " vallen:" << sv.valuelen();
313     if (sv.getValue().get()) {
314         os << " val:\"";
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++) {
319             os << data[ii];
320         }
321         if (limit < sv.getValue()->vlength()) {
322             os << " <cut>";
323         }
324         os << "\"";
325     }
326     return os;
327 }
328
329 bool OrderedStoredValue::operator==(const OrderedStoredValue& other) const {
330     return StoredValue::operator==(other);
331 }
332
333 /**
334  * Return the time the item was deleted. Only valid for deleted items.
335  */
336 rel_time_t OrderedStoredValue::getDeletedTime() const {
337     if (isDeleted()) {
338         return lock_expiry_or_delete_time;
339     } else {
340         throw std::logic_error(
341                 "OrderedStoredValue::getDeletedItem: Called on Alive item");
342     }
343 }
344
345 void OrderedStoredValue::deleteImpl(HashTable& ht) {
346     StoredValue::deleteImpl(ht);
347
348     // Need to record the time when an item is deleted for subsequent purging
349     // (ephemeral_metadata_purge_age).
350     setDeletedTime(ep_current_time());
351 }
352
353 void OrderedStoredValue::setValueImpl(const Item& itm, HashTable& ht) {
354     StoredValue::setValueImpl(itm, ht);
355
356     // Update the deleted time (note - even if it was already deleted we should
357     // refresh this).
358     if (isDeleted()) {
359         setDeletedTime(ep_current_time());
360     }
361 }
362
363 void OrderedStoredValue::setDeletedTime(rel_time_t time) {
364     if (!isDeleted()) {
365         throw std::logic_error(
366                 "OrderedStoredValue::setDeletedTime: Called on Alive item");
367     }
368     lock_expiry_or_delete_time = time;
369 }