1 #include "memcached/types.h"
2 
3 #include <atomic>
4 #include <cstddef>
5 #include <cstring>
6 
7 #include "default_engine_internal.h"
8 
9 #ifndef ITEMS_H
10 #define ITEMS_H
11 
12 /*
13  * You should not try to aquire any of the item locks before calling these
14  * functions.
15  */
16 typedef struct _hash_item {
17     struct _hash_item* next;
18     struct _hash_item* prev;
19     struct _hash_item* h_next; /* hash chain next */
20     /**
21      * The unique identifier for this item (it is guaranteed to be unique
22      * per key, which means that a two different version of a document
23      * cannot have the same CAS value (This is not true after a server
24      * restart given that default_bucket is an in-memory bucket).
25      */
26     uint64_t cas;
27 
28     /** least recent access */
29     rel_time_t time;
30 
31     /** When the item will expire (relative to process startup) */
32     rel_time_t exptime;
33 
34     /**
35      * When the current lock for the object expire. If locktime < "current
36      * time" the item isn't locked anymore (timed out). If locktime >=
37      * "current time" the object is locked.
38      */
39     rel_time_t locktime;
40 
41     /** The total size of the data (in bytes) */
42     uint32_t nbytes;
43 
44     /** Flags associated with the item (in network byte order) */
45     uint32_t flags;
46 
47     /**
48      * The number of entities holding a reference to this item object (we
49      * operate in a copy'n'write context so it is always safe for all of
50      * our clients to share an existing object, but we need the refcount
51      * so that we know when we can release the object.
52      */
53     uint16_t refcount;
54 
55     /** Intermal flags used by the engine.*/
56     std::atomic<uint8_t> iflag;
57 
58     /** which slab class we're in */
59     uint8_t slabs_clsid;
60 
61     /** to identify the type of the data */
62     uint8_t datatype;
63 
64     // There is 3 spare bytes due to alignment
65 } hash_item;
66 
67 /*
68     The structure of the key we hash with.
69 
70     This is a combination of the bucket index and the client's key.
71 
72     To respect the memcached protocol we support keys > 250, even
73     though the current frontend doesn't.
74 
75     Keys upto 128 bytes long will be carried wholly on the stack,
76     larger keys go on the heap.
77 */
78 typedef struct _hash_key_sized {
79     bucket_id_t bucket_index;
80     uint8_t client_key[128];
81 } hash_key_sized;
82 
83 typedef struct _hash_key_data {
84     bucket_id_t bucket_index;
85     uint8_t client_key[1];
86 } hash_key_data;
87 
88 typedef struct _hash_key_header {
89     uint16_t len; /* length of the hash key (bucket_index+client) */
90     hash_key_data* full_key; /* points to hash_key::key_storage or a malloc blob*/
91 } hash_key_header;
92 
93 typedef struct _hash_key {
94     hash_key_header header;
95     hash_key_sized key_storage;
96 } hash_key;
97 
hash_key_get_key(const hash_key* key)98 static inline uint8_t* hash_key_get_key(const hash_key* key) {
99     return (uint8_t*)key->header.full_key;
100 }
101 
hash_key_get_bucket_index(const hash_key* key)102 static inline bucket_id_t hash_key_get_bucket_index(const hash_key* key) {
103     return key->header.full_key->bucket_index;
104 }
105 
hash_key_set_bucket_index(hash_key* key, bucket_id_t bucket_index)106 static inline void hash_key_set_bucket_index(hash_key* key,
107                                              bucket_id_t bucket_index) {
108     key->header.full_key->bucket_index = bucket_index;
109 }
110 
hash_key_get_key_len(const hash_key* key)111 static inline uint16_t hash_key_get_key_len(const hash_key* key) {
112     return key->header.len;
113 }
114 
hash_key_set_len(hash_key* key, uint16_t len)115 static inline void hash_key_set_len(hash_key* key, uint16_t len) {
116     key->header.len = len;
117 }
118 
hash_key_get_client_key(const hash_key* key)119 static inline uint8_t* hash_key_get_client_key(const hash_key* key) {
120     return key->header.full_key->client_key;
121 }
122 
hash_key_get_client_key_len(const hash_key* key)123 static inline uint16_t hash_key_get_client_key_len(const hash_key* key) {
124     return hash_key_get_key_len(key) -
125            gsl::narrow<uint16_t>(sizeof(key->header.full_key->bucket_index));
126 }
127 
hash_key_set_client_key(hash_key* key, const void* client_key, const size_t client_key_len)128 static inline void hash_key_set_client_key(hash_key* key,
129                                            const void* client_key,
130                                            const size_t client_key_len) {
131     memcpy(key->header.full_key->client_key, client_key, client_key_len);
132 }
133 
134 /*
135  * return the bytes needed to store the hash_key structure
136  * in a single contiguous allocation.
137  */
hash_key_get_alloc_size(const hash_key* key)138 static inline size_t hash_key_get_alloc_size(const hash_key* key) {
139     return offsetof(hash_key, key_storage) + hash_key_get_key_len(key);
140 }
141 
142 typedef struct {
143     unsigned int evicted;
144     unsigned int evicted_nonzero;
145     rel_time_t evicted_time;
146     unsigned int outofmemory;
147     unsigned int tailrepairs;
148     unsigned int reclaimed;
149 } itemstats_t;
150 
151 struct items {
152    hash_item *heads[POWER_LARGEST];
153    hash_item *tails[POWER_LARGEST];
154    itemstats_t itemstats[POWER_LARGEST];
155    unsigned int sizes[POWER_LARGEST];
156    /*
157     * serialise access to the items data
158    */
159    cb_mutex_t lock;
160 };
161 
162 
163 /**
164  * Allocate and initialize a new item structure
165  * @param engine handle to the storage engine
166  * @param key the key for the new item
167  * @param nkey the number of bytes in the key
168  * @param flags the flags in the new item
169  * @param exptime when the object should expire
170  * @param nbytes the number of bytes in the body for the item
171  * @return a pointer to an item on success NULL otherwise
172  */
173 hash_item *item_alloc(struct default_engine *engine,
174                       const void *key, const size_t nkey, int flags,
175                       rel_time_t exptime, int nbytes, const void *cookie,
176                       uint8_t datatype);
177 
178 /**
179  * Get an item from the cache
180  *
181  * @param engine handle to the storage engine
182  * @param cookie connection cookie
183  * @param key the key for the item to get
184  * @param nkey the number of bytes in the key
185  * @param state Only return documents in this state
186  * @return pointer to the item if it exists or NULL otherwise
187  */
188 hash_item* item_get(struct default_engine* engine,
189                     const void* cookie,
190                     const void* key,
191                     const size_t nkey,
192                     const DocStateFilter state);
193 
194 /**
195  * Get an item from the cache using a hash_key
196  *
197  * @param engine handle to the storage engine
198  * @param cookie connection cookie
199  * @param key to lookup
200  * @param state Only return documents in this state
201  * @return pointer to the item if it exists or NULL otherwise
202  */
203 hash_item* item_get(struct default_engine* engine,
204                     const void* cookie,
205                     const hash_key& key,
206                     const DocStateFilter state);
207 
208 /**
209  * Get an item from the cache and acquire the lock.
210  *
211  * @param engine handle to the storage engine
212  * @param cookie connection cookie
213  * @param where to return the item (if found)
214  * @param key the key for the item to get
215  * @param nkey the number of bytes in the key
216  * @param locktime when the item expire
217  * @return ENGINE_SUCCESS for success
218  */
219 ENGINE_ERROR_CODE item_get_locked(struct default_engine* engine,
220                                   const void* cookie,
221                                   hash_item** it,
222                                   const void* key,
223                                   const size_t nkey,
224                                   rel_time_t locktime);
225 
226 /**
227  * Get and touch an item
228  *
229  * @param engine handle to the storage engine
230  * @param cookie connection cookie
231  * @param where to return the item (if found)
232  * @param key the key for the item to get
233  * @param nkey the number of bytes in the key
234  * @param exptime The new expiry time
235  * @return ENGINE_SUCCESS for success
236  */
237 ENGINE_ERROR_CODE item_get_and_touch(struct default_engine* engine,
238                                      const void* cookie,
239                                      hash_item** it,
240                                      const void* key,
241                                      const size_t nkey,
242                                      rel_time_t exptime);
243 
244 
245 /**
246  * Unlock an item in the cache
247  *
248  * @param engine handle to the storage engine
249  * @param cookie connection cookie
250  * @param key the key for the item to unlock
251  * @param nkey the number of bytes in the key
252  * @param cas value for the locked value
253  * @return ENGINE_SUCCESS for success
254  */
255 ENGINE_ERROR_CODE item_unlock(struct default_engine* engine,
256                               const void* cookie,
257                               const void* key,
258                               const size_t nkey,
259                               uint64_t cas);
260 
261 /**
262  * Reset the item statistics
263  * @param engine handle to the storage engine
264  */
265 void item_stats_reset(struct default_engine *engine);
266 
267 /**
268  * Get item statitistics
269  * @param engine handle to the storage engine
270  * @param add_stat callback provided by the core used to
271  *                 push statistics into the response
272  * @param cookie cookie provided by the core to identify the client
273  */
274 void item_stats(struct default_engine *engine,
275                 ADD_STAT add_stat,
276                 const void *cookie);
277 
278 /**
279  * Get detaild item statitistics
280  * @param engine handle to the storage engine
281  * @param add_stat callback provided by the core used to
282  *                 push statistics into the response
283  * @param cookie cookie provided by the core to identify the client
284  */
285 void item_stats_sizes(struct default_engine *engine,
286                       ADD_STAT add_stat, const void *cookie);
287 
288 /**
289  * Flush expired items from the cache
290  * @param engine handle to the storage engine
291  */
292 void  item_flush_expired(struct default_engine *engine);
293 
294 /**
295  * Release our reference to the current item
296  * @param engine handle to the storage engine
297  * @param it the item to release
298  */
299 void item_release(struct default_engine *engine, hash_item *it);
300 
301 /**
302  * Unlink the item from the hash table (make it inaccessible)
303  * @param engine handle to the storage engine
304  * @param it the item to unlink
305  */
306 void item_unlink(struct default_engine *engine, hash_item *it);
307 
308 /**
309  * Unlink the item from the hash table (make it inaccessible),
310  * but only if the CAS value in the item is the same as the
311  * one in the hash table (two different connections may operate
312  * on the same objects, so the cas value for the value in the
313  * hashtable may be different than the items value. We need
314  * to have exclusive access to the hashtable to do the actual
315  * unlink)
316  *
317  * @param engine handle to the storage engine
318  * @param it the item to unlink
319  */
320 ENGINE_ERROR_CODE safe_item_unlink(struct default_engine *engine,
321                                    hash_item *it);
322 
323 /**
324  * Store an item in the cache
325  * @param engine handle to the storage engine
326  * @param item the item to store
327  * @param cas the cas value (OUT)
328  * @param operation what kind of store operation is this (ADD/SET etc)
329  * @param document_state the state of the document to store
330  * @return ENGINE_SUCCESS on success
331  *
332  * @todo should we refactor this into hash_item ** and remove the cas
333  *       there so that we can get it from the item instead?
334  */
335 ENGINE_ERROR_CODE store_item(struct default_engine *engine,
336                              hash_item *item,
337                              uint64_t *cas,
338                              ENGINE_STORE_OPERATION operation,
339                              const void *cookie,
340                              const DocumentState document_state);
341 
342 /**
343  * Run a single scrub loop for the engine.
344  * @param engine handle to the storage engine
345  */
346 void item_scrubber_main(struct default_engine *engine);
347 
348 /**
349  * Start the item scrubber for the engine
350  * @param engine handle to the storage engine
351  * @return true if the scrubber has been invoked
352  */
353 bool item_start_scrub(struct default_engine *engine);
354 
355 #endif
356