今天主要總結items相關的操作,items的操作分佈比較多,定義和實現在memcachd.h/c、thread.h/c、items.h/c都有,感覺完全可以放在items.h/c中。這裏就對所有的這些操作(除去stats部分)進行一個簡單的總結。
首先對數據結構、ITEM_*宏和一些變量進行一個簡單的說明,這裏先建立一個宏觀的概念,理解了它們的用途對後續閱讀程序有很大的幫助。
- /**
- * Structure for storing items within memcached.
- */
- typedef struct _stritem {
- struct _stritem *next; // next, prev在LRU隊列中使用
- struct _stritem *prev;
- struct _stritem *h_next; /* hash chain next */ // 用於hashtable中
- rel_time_t time; /* least recent access */ // 最近訪問時間
- rel_time_t exptime; /* expire time */ // 過期時間
- int nbytes; /* size of data */ // 數據的長度(數據格式等下面介紹)
- unsigned short refcount; // 引用計數
- uint8_t nsuffix; /* length of flags-and-length string */
- uint8_t it_flags; /* ITEM_* above */
- uint8_t slabs_clsid;/* which slab class we're in */
- uint8_t nkey; /* key length, w/terminating null and padding */
- /* this odd type prevents type-punning issues when we do
- * the little shuffle to save space when not using CAS. */
- union {
- uint64_t cas;
- char end;
- } data[];
- /* if it_flags & ITEM_CAS we have 8 bytes CAS */
- /* then null-terminated key */
- /* then " flags length\r\n" (no terminating null) */
- /* then data with terminating \r\n (no terminating null; it's binary!) */
- } item;
從上面的定義可以知道data字段的格式如下:
if it_flags & ITEM_CAS, 8bytes # 如果ITEM_CAS標誌設置時,這裏有8字節的數據
key<null> # 鍵值字符串,以null結尾,nkey長度不包括null字符
flags-length\r\n # flags-and-length字符串,以\r\n結尾,nsuffix長度包括\r\n
value\r\n # 數據域,以\r\n結尾,nbytes長度包括\r\n
知道了具體的data字段的格式,ITEM_*宏就很容易理解了,主要考慮ITEM_CAS標誌設置時,後面的key,flags-length及value字段的存取都需要後移8字節。
- // 獲取cas值,即當<span style="font-size:16px;">ITEM_CAS標誌設置時</span>,返回8字節的cas值,否則返回0
- #define ITEM_get_cas(i) (((i)->it_flags & ITEM_CAS) ? \
- (i)->data->cas : (uint64_t)0)
- // 設置cas值,同上類似
- #define ITEM_set_cas(i,v) { \
- if ((i)->it_flags & ITEM_CAS) { \
- (i)->data->cas = v; \
- } \
- }
- // 獲得key值,<span style="font-size:16px;">如果ITEM_CAS標誌設置時</span>,需要後移8字節
- #define ITEM_key(item) (((char*)&((item)->data)) \
- + (((item)->it_flags & ITEM_CAS) ? sizeof(uint64_t) : 0))
- // 獲得suffix字段,同上類似
- #define ITEM_suffix(item) ((char*) &((item)->data) + (item)->nkey + 1 \
- + (((item)->it_flags & ITEM_CAS) ? sizeof(uint64_t) : 0))
- // 獲得數據域
- #define ITEM_data(item) ((char*) &((item)->data) + (item)->nkey + 1 \
- + (item)->nsuffix \
- + (((item)->it_flags & ITEM_CAS) ? sizeof(uint64_t) : 0))
- // 獲得總結構的大小
- #define ITEM_ntotal(item) (sizeof(struct _stritem) + (item)->nkey + 1 \
- + (item)->nsuffix + (item)->nbytes \
- + (((item)->it_flags & ITEM_CAS) ? sizeof(uint64_t) : 0))
下面3個宏,定義了item的狀態:
- #define ITEM_LINKED 1
- #define ITEM_CAS 2
- #define ITEM_SLABBED 4
ITEM_LINKED表示item處於hashtable和LRU隊列中,分別在do_item_link(將item放入hashtable和LRU隊列)和do_item_unlink(將item從hashtable和LRU隊列移除)中設置和置位,後續的對it_flags &ITEM_LINKED的判斷,根據是否在hashtable和LRU隊列中,從而能夠進行或禁止某些操作,如:如果item在hashtable和LRU隊列,就不能執行free_item操作(assert((it->it_flags & ITEM_LINKED) == 0)保證),必須首先do_item_unlink。
Item_CAS是根據用戶命令行參數決定的,CAS表示check and set,只有cas字段一致時(check)才執行後續操作(set),否則操作失敗。
ITEM_SLABBED,這個標誌只有在item_free函數中,slabs_free函數調用前設置,說明只有將其放入slabs對應的slabclass_t的freelist指針數組中時,才設置此標誌,即設置此標誌的item應該爲free狀態,it_flags & ITEM_SLABBED即可以以此來理解。
下面說明LRU隊列,主要包括heads和tails,sizes數組只是爲了stats時使用,記錄了每個對應的heads中item的數量:
- #define LARGEST_ID POWER_LARGEST
- static item *heads[LARGEST_ID]; // LRU head buckets, 每個與slabclass對應,雙向鏈表(每次插入放最前面)
- static item *tails[LARGEST_ID]; // LRU tail buckets, 雙向鏈表,記錄了LRU隊列的最後一個元素(oldest)
slabclass定義爲:
- #define MAX_NUMBER_OF_SLAB_CLASSES (POWER_LARGEST + 1)
- static slabclass_t slabclass[MAX_NUMBER_OF_SLAB_CLASSES]; // slabclass數組
感覺 #define LARGEST_ID POWER_LARGEST 改爲 #define LARGEST_ID POWER_LARGEST+1 更合理一些,這樣heads就與slabclass一一對應起來了,雖然slabclass及heads並不是每個位置都使用了。因爲slabclass[0]並沒有使用,所以heads[0]和tails[0]也沒有使用(使用方法heads[slabs_clsid],slabs_clsid非0)。
還有就是對memcached中引用計數的理解,只有do_item_get(或do_item_get_nocheck)時引用計數++,do_item_remove時引用計數--,其它地方不涉及具體的操作。
只有item_link_q和item_unlink_q(名字後面有個_q)兩個函數才操作heads和tails數組,完成對LRU隊列的維護。
下面貼出代碼,並進行相應的註釋。
總結:感覺items相關的操作並沒有很高的複用價值,這個主要是理解slab和hashtable的接口及使用方法,設計的算法和數據結構並不多,本身邏輯也並不是很複雜。
- /**
- * Generates the variable-sized part of the header for an object.
- *
- * key - The key
- * nkey - The length of the key(include the null terminator)
- * flags - key flags
- * nbytes - Number of bytes to hold value and addition CRLF terminator
- * suffix - Buffer for the "VALUE" line suffix (flags, size).
- * nsuffix - The length of the suffix is stored here.
- *
- * Returns the total size of the header.
- */
- static size_t item_make_header(const uint8_t nkey, const int flags, const int nbytes,
- char *suffix, uint8_t *nsuffix) {
- /* suffix is defined at 40 chars elsewhere.. */
- *nsuffix = (uint8_t) snprintf(suffix, 40, " %d %d\r\n", flags, nbytes - 2);
- return sizeof(item) + nkey + *nsuffix + nbytes;
- }
- /* 分配一個item結構 */
- item *do_item_alloc(char *key, const size_t nkey, const int flags, const rel_time_t exptime, const int nbytes) {
- uint8_t nsuffix;
- item *it = NULL;
- char suffix[40];
- size_t ntotal = item_make_header(nkey + 1, flags, nbytes, suffix, &nsuffix);
- if (settings.use_cas) {
- ntotal += sizeof(uint64_t); // if( (it_flags & ITEM_CAS)!=0), 8bytes CAS
- }
- unsigned int id = slabs_clsid(ntotal); // 返回對應的slab的clsid
- if (id == 0) // 查找失敗
- return 0;
- /* do a quick check if we have any expired items in the tail.. */
- int tries = 50;
- item *search;
- rel_time_t oldest_live = settings.oldest_live;
- for (search = tails[id];
- tries > 0 && search != NULL;
- tries--, search=search->prev) {
- if (search->refcount == 0 && // 引用計數爲0
- ((search->time < oldest_live) || // dead by flush,比oldest_live舊的已失效
- (search->exptime != 0 && search->exptime < current_time))) { // 過期
- it = search;
- /* I don't want to actually free the object, just steal
- * the item to avoid to grab the slab mutex twice ;-)
- */
- STATS_LOCK();
- stats.reclaimed++;
- STATS_UNLOCK();
- itemstats[id].reclaimed++;
- it->refcount = 1; // 後面會重用此item
- // 調整old item與新的item之間的差值,ITEM_ntotal(it)與ntotal計算方式相同
- slabs_adjust_mem_requested(it->slabs_clsid, ITEM_ntotal(it), ntotal);
- do_item_unlink(it); // 從hashtable和LRU隊列摘除item
- /* Initialize the item block: */
- it->slabs_clsid = 0;
- it->refcount = 0;
- break;
- }
- }
- // 上面查找過程沒有找到,則首先分配一個,如果分配失敗,則通過LRU算法嘗試摘除一些item
- // slabs_alloc:從slabclass[id]中分配一個size大小的trunk,錯誤時返回NULL(0)
- if (it == NULL && (it = slabs_alloc(ntotal, id)) == NULL) {
- /*
- ** Could not find an expired item at the tail, and memory allocation
- ** failed. Try to evict some items!
- */
- /* If requested to not push old items out of cache when memory runs out,
- * we're out of luck at this point...
- */
- // 通過M指定不使用LRU算法淘汰old item
- // -M: return error on memory exhausted (rather than removing items)
- if (settings.evict_to_free == 0) {
- itemstats[id].outofmemory++;
- return NULL;
- }
- /*
- * try to get one off the right LRU
- * don't necessarily unlink the tail because it may be locked: refcount>0
- * search up from tail an item with refcount==0 and unlink it; give up after 50
- * tries
- */
- if (tails[id] == 0) {
- itemstats[id].outofmemory++;
- return NULL;
- }
- tries = 50; // 最多嘗試50次
- for (search = tails[id]; tries > 0 && search != NULL; tries--, search=search->prev)
- {
- if (search->refcount == 0) // 引用計數爲0
- {
- if (search->exptime == 0 || search->exptime > current_time)
- {
- itemstats[id].evicted++;
- itemstats[id].evicted_time = current_time - search->time;
- if (search->exptime != 0)
- itemstats[id].evicted_nonzero++;
- STATS_LOCK();
- stats.evictions++;
- STATS_UNLOCK();
- }
- else
- {
- itemstats[id].reclaimed++;
- STATS_LOCK();
- stats.reclaimed++;
- STATS_UNLOCK();
- }
- do_item_unlink(search); // 從hashtable及LRU隊列摘除item項
- break; // 找到第一個引用計數爲0的就結束此循環
- }
- }
- // 這裏重新分配,如果上面循環回收了item結構,這裏能夠分配成功
- // 否則,分配會失敗,然後強制回收時間鎖定(refcount>0)時間過長(超過3小時)的item
- it = slabs_alloc(ntotal, id);
- if (it == 0)
- {
- itemstats[id].outofmemory++;
- /* Last ditch effort. There is a very rare bug which causes
- * refcount leaks. We've fixed most of them, but it still happens,
- * and it may happen in the future.
- * We can reasonably assume no item can stay locked for more than
- * three hours, so if we find one in the tail which is that old,
- * free it anyway.
- */
- tries = 50;
- for (search = tails[id]; tries > 0 && search != NULL; tries--, search=search->prev)
- {
- if (search->refcount != 0 && search->time + TAIL_REPAIR_TIME < current_time)
- {
- itemstats[id].tailrepairs++;
- search->refcount = 0;
- do_item_unlink(search);
- break;
- }
- }
- it = slabs_alloc(ntotal, id);
- if (it == 0) {
- return NULL;
- }
- }
- }
- assert(it->slabs_clsid == 0);
- it->slabs_clsid = id;
- assert(it != heads[it->slabs_clsid]);
- it->next = it->prev = it->h_next = 0;
- it->refcount = 1; /* the caller will have a reference */
- DEBUG_REFCNT(it, '*');
- it->it_flags = settings.use_cas ? ITEM_CAS : 0; //? 0
- it->nkey = nkey; // key由null結尾,nkey長度不包括null字符
- it->nbytes = nbytes; // value由\r\n結尾,nbytes長度包括\r\n
- memcpy(ITEM_key(it), key, nkey);
- it->exptime = exptime;
- memcpy(ITEM_suffix(it), suffix, (size_t)nsuffix);
- it->nsuffix = nsuffix; // suffix由\r\n結尾,nsuffix長度包括\r\n
- return it;
- }
- void item_free(item *it) {
- size_t ntotal = ITEM_ntotal(it);
- unsigned int clsid;
- assert((it->it_flags & ITEM_LINKED) == 0);
- assert(it != heads[it->slabs_clsid]);
- assert(it != tails[it->slabs_clsid]);
- assert(it->refcount == 0);
- /* so slab size changer can tell later if item is already free or not */
- clsid = it->slabs_clsid;
- it->slabs_clsid = 0;
- it->it_flags |= ITEM_SLABBED;
- DEBUG_REFCNT(it, 'F');
- // 將ptr指向的item結構放入slabclass[clsid]的slabclass_t的freelist數組中
- slabs_free(it, ntotal, clsid);
- }
- /**
- * Returns true if an item will fit in the cache (its size does not exceed
- * the maximum for a cache entry.)
- */
- bool item_size_ok(const size_t nkey, const int flags, const int nbytes) {
- char prefix[40];
- uint8_t nsuffix;
- size_t ntotal = item_make_header(nkey + 1, flags, nbytes,
- prefix, &nsuffix);
- if (settings.use_cas) {
- ntotal += sizeof(uint64_t);
- }
- return slabs_clsid(ntotal) != 0;
- }
- // 把該item插入LRU隊列
- static void item_link_q(item *it) { /* item is the new head */
- item **head, **tail;
- assert(it->slabs_clsid < LARGEST_ID);
- assert((it->it_flags & ITEM_SLABBED) == 0);
- head = &heads[it->slabs_clsid];
- tail = &tails[it->slabs_clsid];
- assert(it != *head);
- assert((*head && *tail) || (*head == 0 && *tail == 0));
- it->prev = 0;
- it->next = *head;
- if (it->next) it->next->prev = it;
- *head = it;
- if (*tail == 0) *tail = it;
- sizes[it->slabs_clsid]++;
- return;
- }
- // 從LRU隊列刪除此item
- static void item_unlink_q(item *it) {
- item **head, **tail;
- assert(it->slabs_clsid < LARGEST_ID);
- head = &heads[it->slabs_clsid];
- tail = &tails[it->slabs_clsid];
- // 頭部
- if (*head == it) {
- assert(it->prev == 0);
- *head = it->next;
- }
- // 尾部
- if (*tail == it) {
- assert(it->next == 0);
- *tail = it->prev;
- }
- assert(it->next != it);
- assert(it->prev != it);
- // 改變雙向鏈表指針
- if (it->next) it->next->prev = it->prev;
- if (it->prev) it->prev->next = it->next;
- sizes[it->slabs_clsid]--;
- return;
- }
- // 將item放入hashtable和LRU隊列
- int do_item_link(item *it) {
- MEMCACHED_ITEM_LINK(ITEM_key(it), it->nkey, it->nbytes);
- assert((it->it_flags & (ITEM_LINKED|ITEM_SLABBED)) == 0);
- it->it_flags |= ITEM_LINKED; // 設置ITEM_LINKED標誌
- it->time = current_time; // 最近訪問時間爲當前時間
- assoc_insert(it); // 將item指針插入hash表中
- STATS_LOCK();
- stats.curr_bytes += ITEM_ntotal(it);
- stats.curr_items += 1;
- stats.total_items += 1;
- STATS_UNLOCK();
- /* Allocate a new CAS ID on link. */
- ITEM_set_cas(it, (settings.use_cas) ? get_cas_id() : 0);// 調用get_cas_id()給item的cas_id賦值
- // 把該item插入LRU隊列(heads, tails, sizes)
- item_link_q(it);
- return 1;
- }
- // 從hashtable及LRU隊列摘除item項
- void do_item_unlink(item *it) {
- MEMCACHED_ITEM_UNLINK(ITEM_key(it), it->nkey, it->nbytes);
- if ((it->it_flags & ITEM_LINKED) != 0)
- {
- it->it_flags &= ~ITEM_LINKED;
- STATS_LOCK();
- stats.curr_bytes -= ITEM_ntotal(it);
- stats.curr_items -= 1;
- STATS_UNLOCK();
- assoc_delete(ITEM_key(it), it->nkey);// 從hashtable中刪除key對應的item
- item_unlink_q(it); // 從LRU隊列刪除此item
- if (it->refcount == 0) // 引用計數爲1時,刪除此item
- item_free(it);
- }
- }
- // 減少引用計數refcount,引用計數爲0的時候,就將其釋放
- void do_item_remove(item *it)
- {
- MEMCACHED_ITEM_REMOVE(ITEM_key(it), it->nkey, it->nbytes);
- assert((it->it_flags & ITEM_SLABBED) == 0);
- if (it->refcount != 0)
- {
- it->refcount--; // 減少引用計數
- DEBUG_REFCNT(it, '-');
- }
- if (it->refcount == 0 && (it->it_flags & ITEM_LINKED) == 0)
- {
- item_free(it);
- }
- }
- // 更新item時間戳
- // 先調用item_unlink_q(),更新了時間以後,再調用item_link_q(),
- // 將其重新連接到LRU隊列之中,即讓該item移到LRU隊列的最前
- void do_item_update(item *it) {
- MEMCACHED_ITEM_UPDATE(ITEM_key(it), it->nkey, it->nbytes);
- if (it->time < current_time - ITEM_UPDATE_INTERVAL) {
- assert((it->it_flags & ITEM_SLABBED) == 0);
- if ((it->it_flags & ITEM_LINKED) != 0) {
- item_unlink_q(it);
- it->time = current_time;
- item_link_q(it);
- }
- }
- }
- // 調用do_item_unlink()解除原有it的連接,再調用do_item_link()連接到新的new_it
- int do_item_replace(item *it, item *new_it) {
- MEMCACHED_ITEM_REPLACE(ITEM_key(it), it->nkey, it->nbytes,
- ITEM_key(new_it), new_it->nkey, new_it->nbytes);
- assert((it->it_flags & ITEM_SLABBED) == 0);
- do_item_unlink(it); // 從hashtable及LRU隊列摘除it
- return do_item_link(new_it); // 將new_it插入hashtable和LRU隊列
- }
- /** wrapper around assoc_find which does the lazy expiration logic */
- item *do_item_get(const char *key, const size_t nkey) {
- item *it = assoc_find(key, nkey); // 從hashtable查找key
- int was_found = 0;
- if (settings.verbose > 2) {
- if (it == NULL) {
- fprintf(stderr, "> NOT FOUND %s", key);
- } else {
- fprintf(stderr, "> FOUND KEY %s", ITEM_key(it));
- was_found++;
- }
- }
- // 命中,且item存取時間比截止時間早,已失效
- if (it != NULL && settings.oldest_live != 0 && settings.oldest_live <= current_time &&
- it->time <= settings.oldest_live) {
- do_item_unlink(it); /* MTSAFE - cache_lock held */
- it = NULL;
- }
- if (it == NULL && was_found) {
- fprintf(stderr, " -nuked by flush");
- was_found--;
- }
- // 命中,且item已過期
- if (it != NULL && it->exptime != 0 && it->exptime <= current_time) {
- do_item_unlink(it); /* MTSAFE - cache_lock held */
- it = NULL;
- }
- if (it == NULL && was_found) {
- fprintf(stderr, " -nuked by expire");
- was_found--;
- }
- if (it != NULL) {
- it->refcount++;
- DEBUG_REFCNT(it, '+');
- }
- if (settings.verbose > 2)
- fprintf(stderr, "\n");
- return it;
- }
- /** returns an item whether or not it's expired. */
- item *do_item_get_nocheck(const char *key, const size_t nkey) {
- item *it = assoc_find(key, nkey);
- if (it) {
- it->refcount++;
- DEBUG_REFCNT(it, '+');
- }
- return it;
- }
- /* expires items that are more recent than the oldest_live setting. */// 執行flush操作,即將所有當前有效的item清空(flush)
- void do_item_flush_expired(void) {
- int i;
- item *iter, *next;
- if (settings.oldest_live == 0)
- return;
- for (i = 0; i < LARGEST_ID; i++) {
- /* The LRU is sorted in decreasing time order, and an item's timestamp
- * is never newer than its last access time, so we only need to walk
- * back until we hit an item older than the oldest_live time.
- * The oldest_live checking will auto-expire the remaining items.
- */
- for (iter = heads[i]; iter != NULL; iter = next) {
- if (iter->time >= settings.oldest_live) { // 比截止時間新的數據
- next = iter->next;
- if ((iter->it_flags & ITEM_SLABBED) == 0) {
- do_item_unlink(iter);
- }
- } else { <pre name="code" class="cpp"> // 比截止時間舊的數據已經在之前回收了,</pre> // 由於是按照時間降序排列的(越新越靠前),所以碰到第一個older的數據就停止 /* We've hit the first old item. Continue to the next queue. */ break; } } }}
- <pre></pre>
- <p><span style="font-size:16px">下面的函數都是上層調用的,主要與memcached的協議關係比較緊密,同時保證了臨界資源的互斥訪問(cache_lock),最好提前熟悉一下memcached的<a target="_blank" href="http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt">文本協議</a>。</span></p>
- <p></p>
- <pre name="code" class="html">/*
- * Allocates a new item.
- */
- // 新分配一個item結構
- item *item_alloc(char *key, size_t nkey, int flags, rel_time_t exptime, int nbytes) {
- item *it;
- pthread_mutex_lock(&cache_lock);
- // do_item_alloc:新分配一個item結構,首先快速掃描是否存在過期,存在則複用item結構,
- // 如果沒有,就新分配一個,如果分配失敗,則使用LRU算法進行回收
- it = do_item_alloc(key, nkey, flags, exptime, nbytes);
- pthread_mutex_unlock(&cache_lock);
- return it;
- }
- /*
- * Returns an item if it hasn't been marked as expired,
- * lazy-expiring as needed.
- */
- // 查詢key值的item
- item *item_get(const char *key, const size_t nkey) {
- item *it;
- pthread_mutex_lock(&cache_lock);
- it = do_item_get(key, nkey); // 查詢item,如果成功,這裏會增加引用計數
- pthread_mutex_unlock(&cache_lock);
- return it;
- }
- /*
- * Decrements the reference count on an item and adds it to the freelist if
- * needed.
- */
- void item_remove(item *item) {
- pthread_mutex_lock(&cache_lock);
- do_item_remove(item); // 減少item的引用計數,如果降爲0,則釋放
- pthread_mutex_unlock(&cache_lock);
- }
- /*
- * Links an item into the LRU and hashtable.
- */
- // 將item放入hashtable和LRU隊列
- int item_link(item *item) {
- int ret;
- pthread_mutex_lock(&cache_lock);
- ret = do_item_link(item);
- pthread_mutex_unlock(&cache_lock);
- return ret;
- }
- /*
- * Unlinks an item from the LRU and hashtable.
- */
- // 從hashtable和LRU隊列去除item
- void item_unlink(item *item) {
- pthread_mutex_lock(&cache_lock);
- do_item_unlink(item);
- pthread_mutex_unlock(&cache_lock);
- }
- /*
- * Replaces one item with another in the hashtable.
- * Unprotected by a mutex lock since the core server does not require
- * it to be thread-safe.
- */
- int item_replace(item *old_it, item *new_it) {
- // 替換,首先將old_it從hashtable和LRU刪除,再講new_it加入hashtable和LRU
- return do_item_replace(old_it, new_it);
- }
- /*
- * Moves an item to the back of the LRU queue.
- */
- void item_update(item *item) {
- pthread_mutex_lock(&cache_lock);
- do_item_update(item);
- pthread_mutex_unlock(&cache_lock);
- }
- /*
- * Stores an item in the cache (high level, obeys set/add/replace semantics)
- */
- enum store_item_type store_item(item *item, int comm, conn* c) {
- enum store_item_type ret;
- pthread_mutex_lock(&cache_lock);
- ret = do_store_item(item, comm, c);
- pthread_mutex_unlock(&cache_lock);
- return ret;
- }
- /*
- * Stores an item in the cache according to the semantics of one of the set
- * commands. In threaded mode, this is protected by the cache lock.
- *
- * Returns the state of storage.
- */
- enum store_item_type do_store_item(item *it, int comm, conn *c) {
- char *key = ITEM_key(it);
- item *old_it = do_item_get(key, it->nkey); // 查詢key值的item
- enum store_item_type stored = NOT_STORED;
- item *new_it = NULL;
- int flags;
- if (old_it != NULL && comm == NREAD_ADD) { // 已經存在
- /* add only adds a nonexistent item, but promote to head of LRU */
- do_item_update(old_it);
- } else if (!old_it && (comm == NREAD_REPLACE
- || comm == NREAD_APPEND || comm == NREAD_PREPEND))
- {
- /* replace only replaces an existing value; don't store */
- } else if (comm == NREAD_CAS) {
- /* validate cas operation */
- if(old_it == NULL) {
- // LRU expired
- stored = NOT_FOUND;
- pthread_mutex_lock(&c->thread->stats.mutex);
- c->thread->stats.cas_misses++;
- pthread_mutex_unlock(&c->thread->stats.mutex);
- }
- else if (ITEM_get_cas(it) == ITEM_get_cas(old_it)) {// cas(check and set)一致時,才操作
- // cas validates
- // it and old_it may belong to different classes.
- // I'm updating the stats for the one that's getting pushed out
- pthread_mutex_lock(&c->thread->stats.mutex);
- c->thread->stats.slab_stats[old_it->slabs_clsid].cas_hits++;
- pthread_mutex_unlock(&c->thread->stats.mutex);
- item_replace(old_it, it);
- stored = STORED;
- } else {
- pthread_mutex_lock(&c->thread->stats.mutex);
- c->thread->stats.slab_stats[old_it->slabs_clsid].cas_badval++;
- pthread_mutex_unlock(&c->thread->stats.mutex);
- if(settings.verbose > 1) {
- fprintf(stderr, "CAS: failure: expected %llu, got %llu\n",
- (unsigned long long)ITEM_get_cas(old_it),
- (unsigned long long)ITEM_get_cas(it));
- }
- stored = EXISTS;
- }
- } else {
- /*
- * Append - combine new and old record into single one. Here it's
- * atomic and thread-safe.
- */
- if (comm == NREAD_APPEND || comm == NREAD_PREPEND) {
- /*
- * Validate CAS
- */
- if (ITEM_get_cas(it) != 0) {
- // CAS much be equal
- if (ITEM_get_cas(it) != ITEM_get_cas(old_it)) {
- stored = EXISTS;
- }
- }
- if (stored == NOT_STORED) {
- /* we have it and old_it here - alloc memory to hold both */
- /* flags was already lost - so recover them from ITEM_suffix(it) */
- flags = (int) strtol(ITEM_suffix(old_it), (char **) NULL, 10);
- // 新建一個item
- new_it = do_item_alloc(key, it->nkey, flags, old_it->exptime, it->nbytes + old_it->nbytes - 2 /* CRLF */);
- if (new_it == NULL) {
- /* SERVER_ERROR out of memory */
- if (old_it != NULL)
- do_item_remove(old_it);
- return NOT_STORED;
- }
- /* copy data from it and old_it to new_it */
- if (comm == NREAD_APPEND) {
- memcpy(ITEM_data(new_it), ITEM_data(old_it), old_it->nbytes);
- memcpy(ITEM_data(new_it) + old_it->nbytes - 2 /* CRLF */, ITEM_data(it), it->nbytes);
- } else {
- /* NREAD_PREPEND */
- memcpy(ITEM_data(new_it), ITEM_data(it), it->nbytes);
- memcpy(ITEM_data(new_it) + it->nbytes - 2 /* CRLF */, ITEM_data(old_it), old_it->nbytes);
- }
- it = new_it;
- }
- }
- if (stored == NOT_STORED) {
- if (old_it != NULL)
- item_replace(old_it, it);
- else
- do_item_link(it);
- c->cas = ITEM_get_cas(it);
- stored = STORED;
- }
- }
- if (old_it != NULL)
- do_item_remove(old_it); /* release our reference */
- if (new_it != NULL)
- do_item_remove(new_it);
- if (stored == STORED) {
- c->cas = ITEM_get_cas(it);
- }
- return stored;
- }
- /*
- * Flushes expired items after a flush_all call
- */
- // 執行flush_all操作,清空所有items
- void item_flush_expired() {
- pthread_mutex_lock(&cache_lock);
- do_item_flush_expired();
- pthread_mutex_unlock(&cache_lock);
- }
- </pre>
- <pre></pre>