Decouple StoredValue and HashTable class
[ep-engine.git] / src / stored-value.h
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 #pragma once
19
20 #include "config.h"
21
22 #include "item.h"
23 #include "item_pager.h"
24 #include "utility.h"
25
26 #include <boost/intrusive/list.hpp>
27
28 class OrderedStoredValue;
29
30 /**
31  * In-memory storage for an item.
32  *
33  * This class represents a single document which is present in the HashTable -
34  * essentially this is value_type used by HashTable.
35  *
36  * It contains the documents' key, related metadata (CAS, rev, seqno, ...).
37  * It also has a pointer to the documents' value - which may be null if the
38  * value of the item is not currently resident (for example it's been evicted to
39  * save memory).
40  * Additionally it contains flags to help HashTable manage the state of the
41  * item - such as dirty flag, NRU bits, and a `next` pointer to support
42  * chaining of StoredValues which hash to the same hash bucket.
43  *
44  * The key of the item is of variable length (from 1 to ~256 bytes). As an
45  * optimization, we allocate the key directly after the fixed size of
46  * StoredValue, so StoredValue and its key are contiguous in memory. This saves
47  * us the cost of an indirection compared to storing the key out-of-line, and
48  * the space of a pointer in StoredValue to point to the out-of-line
49  * allocation. It does, however complicate the management of StoredValue
50  * objects as they are now variable-sized - they must be created using a
51  * factory method (StoredValueFactory) and must be heap-allocated, managed
52  * using a unique_ptr (StoredValue::UniquePtr).
53  *
54  * Graphically the looks like:
55  *
56  *              StoredValue::UniquePtr
57  *                          |
58  *                          V
59  *               .-------------------.
60  *               | StoredValue       |
61  *               +-------------------+
62  *           {   | value [ptr]       | ======> Blob (nullptr if evicted)
63  *           {   | next  [ptr]       | ======> StoredValue (next in hash chain).
64  *     fixed {   | CAS               |
65  *    length {   | revSeqno          |
66  *           {   | ...               |
67  *           {   | datatype          |
68  *           {   | internal flags: isDirty, deleted, isOrderedStoredValue ...
69  *               + - - - - - - - - - +
70  *  variable {   | key[]             |
71  *   length  {   | ...               |
72  *               +-------------------+
73  *
74  * OrderedStoredValue is a "subclass" of StoredValue, which is used by
75  * Ephemeral buckets as it supports maintaining a seqno ordering of items in
76  * memory (for Persistent buckets this ordering is maintained on-disk).
77  *
78  * The implementation of OrderedStoredValue is tightly coupled to StoredValue
79  * so it will be described here:
80  *
81  * OrderedStoredValue has the fixed length members of StoredValue, then it's
82  * own fixed length fields (seqno list), followed finally by the variable
83  * length key (again, allocated contiguously):
84  *
85  *              StoredValue::UniquePtr
86  *                          |
87  *                          V
88  *               .--------------------.
89  *               | OrderedStoredValue |
90  *               +--------------------+
91  *           {   | value [ptr]        | ======> Blob (nullptr if evicted)
92  *           {   | next  [ptr]        | ======> StoredValue (next in hash chain).
93  *     fixed {   | StoredValue fixed ...
94  *    length {   + - - - - - - - - - -+
95  *           {   | seqno next [ptr]   |
96  *           {   | seqno prev [ptr]   |
97  *               + - - - - - - - - - -+
98  *  variable {   | key[]              |
99  *   length  {   | ...                |
100  *               +--------------------+
101  *
102  * To support dynamic dispatch (for example to lookup the key, whose location
103  * varies depending if it's StoredValue or OrderedStoredValue), we choose to
104  * use a manual flag-based dispatching (as opposed to a normal vTable based
105  * approach) as the per-object costs are much cheaper - 1 bit for the flag vs.
106  * 8 bytes for a vTable ptr.
107  * StoredValue::isOrderedStoredValue is set to false for StoredValue objects,
108  * and true for OrderedStoredValue objects, and then any methods
109  * needing dynamic dispatch read the value of the flag. Note this means that
110  * the 'base' class (StoredValue) needs to know about all possible subclasses
111  * (only one currently) and what class-specific code to call.
112  * Similary, deletion of OrderedStoredValue objects is delicate - we cannot
113  * safely delete via the base-class pointer directly, as that would only run
114  * ~StoredValue and not the members of the derived class. Instead a custom
115  * deleter is associated with StoredValue::UniquePtr, which checks the flag
116  * and dispatches to the correct destructor.
117  */
118 class StoredValue {
119 public:
120     // Custom deleter for StoredValue objects.
121     struct Deleter {
122         void operator()(StoredValue* val);
123     };
124
125     // Owning pointer type for StoredValue objects.
126     using UniquePtr = std::unique_ptr<StoredValue, Deleter>;
127
128     uint8_t getNRUValue() const;
129
130     void setNRUValue(uint8_t nru_val);
131
132     uint8_t incrNRUValue();
133
134     void referenced();
135
136     /**
137      * Mark this item as needing to be persisted.
138      */
139     void markDirty() {
140         _isDirty = 1;
141     }
142
143     /**
144      * Mark this item as clean.
145      */
146     void markClean() {
147         _isDirty = 0;
148     }
149
150     /**
151      * True if this object is dirty.
152      */
153     bool isDirty() const {
154         return _isDirty;
155     }
156
157     bool eligibleForEviction(item_eviction_policy_t policy) {
158         if (policy == VALUE_ONLY) {
159             return isResident() && !isDirty() && !isDeleted();
160         } else {
161             return !isDirty() && !isDeleted();
162         }
163     }
164
165     /**
166      * Check if this item is expired or not.
167      *
168      * @param asOf the time to be compared with this item's expiry time
169      * @return true if this item's expiry time < asOf
170      */
171     bool isExpired(time_t asOf) const {
172         if (getExptime() != 0 && getExptime() < asOf) {
173             return true;
174         }
175         return false;
176     }
177
178     /**
179      * True if this item is for the given key.
180      *
181      * @param k the key we're checking
182      * @return true if this item's key is equal to k
183      */
184     bool hasKey(const DocKey& k) const {
185         return getKey() == k;
186     }
187
188     /**
189      * Get this item's key.
190      */
191     const SerialisedDocKey& getKey() const {
192         return *const_cast<const SerialisedDocKey*>(
193                 const_cast<StoredValue&>(*this).key());
194     }
195
196     /**
197      * Get this item's value.
198      */
199     const value_t &getValue() const {
200         return value;
201     }
202
203     /**
204      * Get the expiration time of this item.
205      *
206      * @return the expiration time.
207      */
208     time_t getExptime() const {
209         return exptime;
210     }
211
212     void setExptime(time_t tim) {
213         exptime = tim;
214         markDirty();
215     }
216
217     /**
218      * Get the client-defined flags of this item.
219      *
220      * @return the flags.
221      */
222     uint32_t getFlags() const {
223         return flags;
224     }
225
226     /**
227      * Set the client-defined flags for this item.
228      */
229     void setFlags(uint32_t fl) {
230         flags = fl;
231     }
232
233     /**
234      * get the items datatype
235      */
236     protocol_binary_datatype_t getDatatype() const {
237         return datatype;
238     }
239
240     /**
241      * Set the items datatype
242      */
243     void setDatatype(protocol_binary_datatype_t type) {
244         datatype = type;
245     }
246
247     /**
248      * Set a new value for this item.
249      *
250      * @param itm the item with a new value
251      */
252     void setValue(const Item& itm);
253
254     void markDeleted() {
255         deleted = true;
256         markDirty();
257     }
258
259     /**
260      * Eject an item value from memory.
261      */
262     void ejectValue();
263
264     /**
265      * Restore the value for this item.
266      *
267      * @param itm the item to be restored
268      */
269     void restoreValue(const Item& itm);
270
271     /**
272      * Restore the metadata of of a temporary item upon completion of a
273      * background fetch assuming the hashtable bucket is locked.
274      *
275      * @param itm the Item whose metadata is being restored
276      */
277     void restoreMeta(const Item& itm);
278
279     /**
280      * Get this item's CAS identifier.
281      *
282      * @return the cas ID
283      */
284     uint64_t getCas() const {
285         return cas;
286     }
287
288     /**
289      * Set a new CAS ID.
290      */
291     void setCas(uint64_t c) {
292         cas = c;
293     }
294
295     /**
296      * Lock this item until the given time.
297      */
298     void lock(rel_time_t expiry) {
299         if (isDeleted()) {
300             // Cannot lock Deleted items.
301             throw std::logic_error(
302                     "StoredValue::lock: Called on Deleted item");
303         }
304         lock_expiry_or_delete_time = expiry;
305     }
306
307     /**
308      * Unlock this item.
309      */
310     void unlock() {
311         if (isDeleted()) {
312             // Deleted items are not locked - just skip.
313             return;
314         }
315         lock_expiry_or_delete_time = 0;
316     }
317
318     /**
319      * True if this item has an ID.
320      *
321      * An item always has an ID after it's been persisted.
322      */
323     bool hasBySeqno() {
324         return bySeqno > 0;
325     }
326
327     /**
328      * Get this item's ID.
329      *
330      * @return the ID for the item; 0 if the item has no ID
331      */
332     int64_t getBySeqno() const {
333         return bySeqno;
334     }
335
336     /**
337      * Set the ID for this item.
338      *
339      * This is used by the persistene layer.
340      *
341      * It is an error to set an ID on an item that already has one.
342      */
343     void setBySeqno(int64_t to) {
344         if (to <= 0) {
345             throw std::invalid_argument("StoredValue::setBySeqno: to "
346                     "(which is " + std::to_string(to) + ") must be positive");
347         }
348         bySeqno = to;
349     }
350
351     // Marks the stored item as deleted.
352     void setDeleted()
353     {
354         bySeqno = state_deleted_key;
355     }
356
357     // Marks the stored item as non-existent.
358     void setNonExistent()
359     {
360         bySeqno = state_non_existent_key;
361     }
362
363     /**
364      * Is this a temporary item created for processing a get-meta request?
365      */
366     bool isTempItem() const {
367         return (isTempNonExistentItem() || isTempDeletedItem() ||
368                 isTempInitialItem());
369
370      }
371
372     /**
373      * Is this an initial temporary item?
374      */
375      bool isTempInitialItem() const {
376          return bySeqno == state_temp_init;
377     }
378
379     /**
380      * Is this a temporary item created for a non-existent key?
381      */
382     bool isTempNonExistentItem() const {
383          return bySeqno == state_non_existent_key;
384     }
385
386     /**
387      * Is this a temporary item created for a deleted key?
388      */
389     bool isTempDeletedItem() const {
390         return bySeqno == state_deleted_key;
391
392      }
393
394     size_t valuelen() const {
395         if (!isResident()) {
396             return 0;
397         }
398         return value->length();
399     }
400
401     /**
402      * Get the total size of this item.
403      *
404      * @return the amount of memory used by this item.
405      */
406     size_t size() const {
407         return getObjectSize() + valuelen();
408     }
409
410     size_t metaDataSize() const {
411         return getObjectSize();
412     }
413
414     /**
415      * Return true if this item is locked as of the given timestamp.
416      *
417      * @param curtime lock expiration marker (usually the current time)
418      * @return true if the item is locked
419      */
420     bool isLocked(rel_time_t curtime) const {
421         if (isDeleted()) {
422             // Deleted items cannot be locked.
423             return false;
424         }
425
426         if (lock_expiry_or_delete_time == 0 ||
427             (curtime > lock_expiry_or_delete_time)) {
428             return false;
429         }
430         return true;
431     }
432
433     /**
434      * True if this value is resident in memory currently.
435      */
436     bool isResident() const {
437         return value.get() != NULL;
438     }
439
440     void markNotResident() {
441         value.reset();
442     }
443
444     /**
445      * True if this object is logically deleted.
446      */
447     bool isDeleted() const {
448         return deleted;
449     }
450
451     /**
452      * Logically delete this object
453      * @return true if the item was deleted
454      */
455     bool del();
456
457     uint64_t getRevSeqno() const {
458         return revSeqno;
459     }
460
461     /**
462      * Set a new revision sequence number.
463      */
464     void setRevSeqno(uint64_t s) {
465         revSeqno = s;
466     }
467
468     /**
469      * Return true if this is a new cache item.
470      */
471     bool isNewCacheItem() const {
472         return newCacheItem;
473     }
474
475     /**
476      * Set / reset a new cache item flag.
477      */
478     void setNewCacheItem(bool newitem) {
479         newCacheItem = newitem;
480     }
481
482     /**
483      * Generate a new Item out of this object.
484      *
485      * @param lck if true, the new item will return a locked CAS ID.
486      * @param vbucket the vbucket containing this item.
487      */
488     std::unique_ptr<Item> toItem(bool lck, uint16_t vbucket) const;
489
490     /**
491      * Generate a new Item with only key and metadata out of this object.
492      * The item generated will not contain value
493      *
494      * @param vbucket the vbucket containing this item.
495      */
496     std::unique_ptr<Item> toItemWithNoValue(uint16_t vbucket) const;
497
498     void setNext(UniquePtr&& nextSv) {
499         if (stale) {
500             throw std::logic_error(
501                     "StoredValue::setNext: StoredValue is stale,"
502                     "cannot set chain next value");
503         }
504         chain_next_or_replacement = std::move(nextSv);
505     }
506
507     UniquePtr& getNext() {
508         if (stale) {
509             throw std::logic_error(
510                     "StoredValue::getNext: StoredValue is stale,"
511                     "cannot get chain next value");
512         }
513         return chain_next_or_replacement;
514     }
515
516     /**
517      * Set the memory threshold on the current bucket quota for accepting a new mutation
518      */
519     static void setMutationMemoryThreshold(double memThreshold);
520
521     /**
522      * Return the memory threshold for accepting a new mutation
523      */
524     static double getMutationMemThreshold() {
525         return mutation_mem_threshold;
526     }
527
528     /*
529      * Values of the bySeqno attribute used by temporarily created StoredValue
530      * objects.
531      * state_deleted_key: represents an item that's deleted from memory but
532      *                    present in the persistent store.
533      * state_non_existent_key: represents a non existent item
534      * state_collection_open: a special value used by collections to help
535      *  represent a collections life-time in sequence-numbers (start to end).
536      *  If a collection has no end, it's termed open and has an end
537      *  sequence-number of StoredValue::state_collection_open. We do not
538      *  actually assign this value to StoredValue objects, but it's here in
539      *  this "number space" of special sequence numbers to help ensure it's
540      *  different to the other special sequence numbers we define.
541      *
542      */
543     static const int64_t state_deleted_key;
544     static const int64_t state_non_existent_key;
545     static const int64_t state_temp_init;
546     static const int64_t state_collection_open;
547
548     /**
549      * Return the size in byte of this object; both the fixed fields and the
550      * variable-length key. Doesn't include value size (allocated externally).
551      */
552     inline size_t getObjectSize() const;
553
554     /**
555      * Reallocates the dynamic members of StoredValue. Used as part of
556      * defragmentation.
557      */
558     void reallocate();
559
560     /**
561      * Returns pointer to the subclass OrderedStoredValue if it the object is
562      * of the type, if not throws a bad_cast.
563      *
564      * Equivalent to dynamic cast, but done manually as we wanted to avoid
565      * vptr per object.
566      */
567     OrderedStoredValue* toOrderedStoredValue();
568     const OrderedStoredValue* toOrderedStoredValue() const;
569
570     /**
571      * Check if the contents of the StoredValue is same as that of the other
572      * one. Does not consider the intrusive hash bucket link.
573      *
574      * @param other The StoredValue to be compared with
575      */
576     bool operator==(const StoredValue& other) const;
577
578     /* [TBD] : Move this function out of StoredValue class */
579     static bool hasAvailableSpace(EPStats&,
580                                   const Item& item,
581                                   bool isReplication = false);
582
583     /// Return how many bytes are need to store Item as a StoredValue
584     static size_t getRequiredStorage(const Item& item) {
585         return sizeof(StoredValue) +
586                SerialisedDocKey::getObjectSize(item.getKey().size());
587     }
588
589 protected:
590     /**
591      * Constructor - protected as allocation needs to be done via
592      * StoredValueFactory.
593      *
594      * @param itm Item to base this StoredValue on.
595      * @param n The StoredValue which will follow the new stored value in
596      *           the hash bucket chain, which this new item will take
597      *           ownership of. (Typically the top of the hash bucket into
598      *           which the new item is being inserted).
599      * @param stats EPStats to update for this new StoredValue
600      * @param isOrdered Are we constructing an OrderedStoredValue?
601      */
602     StoredValue(const Item& itm,
603                 UniquePtr n,
604                 EPStats& stats,
605                 bool isOrdered);
606
607     // Destructor. protected, as needs to be carefully deleted (via
608     // StoredValue::Destructor) depending on the value of isOrdered flag.
609     ~StoredValue() {
610         ObjectRegistry::onDeleteStoredValue(this);
611     }
612
613     /**
614      * Copy constructor - protected as allocation needs to be done via
615      * StoredValueFactory.
616      *
617      * @param other StoredValue being copied
618      * @param n The StoredValue which will follow the new stored value in
619      *           the hash bucket chain, which this new item will take
620      *           ownership of. (Typically the top of the hash bucket into
621      *           which the new item is being inserted).
622      * @param stats EPStats to update for this new StoredValue
623      */
624     StoredValue(const StoredValue& other,
625                 UniquePtr n,
626                 EPStats& stats);
627
628     /* Do not allow assignment */
629     StoredValue& operator=(const StoredValue& other) = delete;
630
631     /**
632      * Get the address of item's key .
633      */
634     inline SerialisedDocKey* key();
635
636     /**
637      * Logically mark this SV as deleted.
638      * Implementation for StoredValue instances (dispatched to by del() based
639      * on isOrdered==false).
640      */
641     bool deleteImpl();
642
643     /* Update the value for this SV from the given item.
644      * Implementation for StoredValue instances (dispatched to by setValue()).
645      */
646     void setValueImpl(const Item& itm);
647
648     friend class StoredValueFactory;
649
650     value_t            value;          // 8 bytes
651
652     // Serves two purposes -
653     // 1. Used to implement HashTable chaining (for elements hashing to the same
654     // bucket).
655     // 2. Once the stored value has been marked stale, this is used to point at
656     // the replacement stored value. In this case, *we do not have ownership*,
657     // so we release the ptr in the destructor. The replacement is needed to
658     // determine if it would also appear in a given rangeRead - we should return
659     // only the newer version if so.
660     UniquePtr chain_next_or_replacement; // 8 bytes
661     uint64_t           cas;            //!< CAS identifier.
662     uint64_t           revSeqno;       //!< Revision id sequence number
663     int64_t            bySeqno;        //!< By sequence id number
664     /// For alive items: GETL lock expiration. For deleted items: delete time.
665     rel_time_t         lock_expiry_or_delete_time;
666     uint32_t           exptime;        //!< Expiration time of this item.
667     uint32_t           flags;          // 4 bytes
668     protocol_binary_datatype_t datatype; // 1 byte
669     bool               _isDirty  :  1; // 1 bit
670     bool               deleted   :  1;
671     bool               newCacheItem : 1;
672     const bool isOrdered : 1; //!< Is this an instance of OrderedStoredValue?
673     uint8_t            nru       :  2; //!< True if referenced since last sweep
674     bool unused : 2; // Unused bits in first byte of bitfields.
675
676     // Indicates if a newer instance of the item is added. Logically part of
677     // OSV, but is physically located in SV as there are spare bytes here.
678     // Guarded by the SequenceList's writeLock.
679     // NOTE: As this is guarded by a different lock to the rest of the SV,
680     // it *must* be in a different byte than any other data not guarded by
681     // writeLock (Hence why this isn't in the same byte as _isDirty, deleted,
682     // newCacheItem etc). To achieve this std::atomic is used to ensure accesses
683     // are not "optimized" and merged with the previous byte. The thread-safety
684     // of std::atomic is not actually used/needed; we just need the no-merge
685     // guarantee.
686     // Note (2): Only 1 bit of this is currently used; rest is "spare".
687     std::atomic<bool> stale;
688
689     static double mutation_mem_threshold;
690
691     friend std::ostream& operator<<(std::ostream& os, const StoredValue& sv);
692 };
693
694 std::ostream& operator<<(std::ostream& os, const StoredValue& sv);
695
696 /**
697  * Subclass of StoredValue which additionally supports sequence number ordering.
698  *
699  * See StoredValue for implementation details.
700  */
701 class OrderedStoredValue : public StoredValue {
702 public:
703     // Intrusive linked-list for sequence number ordering.
704     // Guarded by the SequenceList's writeLock.
705     boost::intrusive::list_member_hook<> seqno_hook;
706
707     ~OrderedStoredValue() {
708         if (stale) {
709             // This points to the replacement OSV which we do not actually own.
710             // We are reusing a unique_ptr so we explicitly release it in this
711             // case. We /do/ own the chain_next if we are not stale.
712             chain_next_or_replacement.release();
713         }
714     }
715
716     /**
717      * True if a newer version of the same key exists in the HashTable.
718      * Note: Only true for OrderedStoredValues which are no longer in the
719      *       HashTable (and only live in SequenceList)
720      * @param writeGuard The locked SeqList writeLock which guards the stale
721      * param.
722      */
723     bool isStale(std::lock_guard<std::mutex>& writeGuard) const {
724         return stale;
725     }
726
727     /**
728      * Marks that newer instance of this item is added in the HashTable
729      * @param writeLock The SeqList writeLock which guards the stale param.
730      */
731     void markStale(std::lock_guard<std::mutex>& writeGuard,
732                    StoredValue* newSv) {
733         // next is a UniquePtr which is up to this point was used for chaining
734         // in the HashTable. Now this item is stale, we are reusing this to
735         // point to the updated version of this StoredValue. _BUT_ we do not
736         // own the new SV. At destruction, we must release this ptr if
737         // we are stale.
738         chain_next_or_replacement.reset(newSv);
739         stale = true;
740     }
741
742     StoredValue* getReplacementIfStale(
743             std::lock_guard<std::mutex>& writeGuard) const {
744         if (!stale) {
745             return nullptr;
746         }
747
748         return chain_next_or_replacement.get();
749     }
750
751     /**
752      * Check if the contents of the StoredValue is same as that of the other
753      * one. Does not consider the intrusive hash bucket link.
754      *
755      * @param other The StoredValue to be compared with
756      */
757     bool operator==(const OrderedStoredValue& other) const;
758
759     /// Return how many bytes are need to store Item as an OrderedStoredValue
760     static size_t getRequiredStorage(const Item& item) {
761         return sizeof(OrderedStoredValue) +
762                SerialisedDocKey::getObjectSize(item.getKey());
763     }
764
765     /**
766      * Return the time the item was deleted. Only valid for deleted items.
767      */
768     rel_time_t getDeletedTime() const;
769
770 protected:
771     SerialisedDocKey* key() {
772         return reinterpret_cast<SerialisedDocKey*>(this + 1);
773     }
774
775     /**
776      * Logically mark this OSV as deleted. Implementation for
777      * OrderedStoredValue instances (dispatched to by del() based on
778      * isOrdered==true).
779      */
780     bool deleteImpl();
781
782     /* Update the value for this OSV from the given item.
783      * Implementation for OrderedStoredValue instances (dispatched to by
784      *  setValue()).
785      */
786     void setValueImpl(const Item& itm);
787
788     /**
789      * Set the time the item was deleted to the specified time.
790      */
791     inline void setDeletedTime(rel_time_t time);
792
793 private:
794     // Constructor. Private, as needs to be carefully created via
795     // OrderedStoredValueFactory.
796     OrderedStoredValue(const Item& itm,
797                        UniquePtr n,
798                        EPStats& stats)
799         : StoredValue(itm, std::move(n), stats, /*isOrdered*/ true) {
800     }
801
802     // Copy Constructor. Private, as needs to be carefully created via
803     // OrderedStoredValueFactory.
804     //
805     // Only StoredValue part (Hash Chain included) is copied. Hence the copied
806     // StoredValue will be in the HashTable, but not in the ordered
807     // data structure.
808     OrderedStoredValue(const StoredValue& other,
809                        UniquePtr n,
810                        EPStats& stats)
811         : StoredValue(other, std::move(n), stats) {
812     }
813
814     /* Do not allow assignment */
815     OrderedStoredValue& operator=(const OrderedStoredValue& other) = delete;
816
817     // Grant friendship so our factory can call our (private) constructor.
818     friend class OrderedStoredValueFactory;
819
820     // Grant friendship to base class so it can perform flag dispatch to our
821     // overridden protected methods.
822     friend class StoredValue;
823 };
824
825 SerialisedDocKey* StoredValue::key() {
826     // key is located immediately following the object.
827     if (isOrdered) {
828         return static_cast<OrderedStoredValue*>(this)->key();
829     } else {
830         return reinterpret_cast<SerialisedDocKey*>(this + 1);
831     }
832 }
833
834 size_t StoredValue::getObjectSize() const {
835     // Size of fixed part of OrderedStoredValue or StoredValue, plus size of
836     // (variable) key.
837     if (isOrdered) {
838         return sizeof(OrderedStoredValue) + getKey().getObjectSize();
839     }
840     return sizeof(*this) + getKey().getObjectSize();
841 }