crawler.c C 998 lines View on github.com → Search inside
1/*  Copyright 2016 Netflix.2 *3 *  Use and distribution licensed under the BSD license.  See4 *  the LICENSE file for full text.5 */67/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */8#include "memcached.h"9#include "storage.h"10#include <sys/stat.h>11#include <sys/socket.h>12#include <sys/resource.h>13#include <fcntl.h>14#include <netinet/in.h>15#include <errno.h>16#include <stdlib.h>17#include <stdio.h>18#include <string.h>19#include <time.h>20#include <assert.h>21#include <unistd.h>22#include <poll.h>2324#include "base64.h"2526#define LARGEST_ID POWER_LARGEST2728typedef struct {29    void *c; /* original connection structure. still with source thread attached. */30    int sfd; /* client fd. */31    int buflen;32    int bufused;33    char *buf; /* output buffer */34} crawler_client_t;3536typedef struct _crawler_module_t crawler_module_t;3738typedef void (*crawler_eval_func)(crawler_module_t *cm, item *it, uint32_t hv, int slab_cls);39typedef int (*crawler_init_func)(crawler_module_t *cm, void *data); // TODO: init args?40typedef void (*crawler_deinit_func)(crawler_module_t *cm); // TODO: extra args?41typedef void (*crawler_doneclass_func)(crawler_module_t *cm, int slab_cls);42typedef void (*crawler_finalize_func)(crawler_module_t *cm);4344typedef struct {45    crawler_init_func init; /* run before crawl starts */46    crawler_eval_func eval; /* runs on an item. */47    crawler_doneclass_func doneclass; /* runs once per sub-crawler completion. */48    crawler_finalize_func finalize; /* runs once when all sub-crawlers are done. */49    bool needs_lock; /* whether or not we need the LRU lock held when eval is called */50    bool needs_client; /* whether or not to grab onto the remote client */51} crawler_module_reg_t;5253struct _crawler_module_t {54    void *data; /* opaque data pointer */55    crawler_client_t c;56    crawler_module_reg_t *mod;57    int status; /* flags/code/etc for internal module usage */58};5960static int crawler_expired_init(crawler_module_t *cm, void *data);61static void crawler_expired_doneclass(crawler_module_t *cm, int slab_cls);62static void crawler_expired_finalize(crawler_module_t *cm);63static void crawler_expired_eval(crawler_module_t *cm, item *search, uint32_t hv, int i);6465crawler_module_reg_t crawler_expired_mod = {66    .init = crawler_expired_init,67    .eval = crawler_expired_eval,68    .doneclass = crawler_expired_doneclass,69    .finalize = crawler_expired_finalize,70    .needs_lock = true,71    .needs_client = false,72};7374static int crawler_metadump_init(crawler_module_t *cm, void *data);75static void crawler_metadump_eval(crawler_module_t *cm, item *search, uint32_t hv, int i);76static void crawler_metadump_finalize(crawler_module_t *cm);7778crawler_module_reg_t crawler_metadump_mod = {79    .init = crawler_metadump_init,80    .eval = crawler_metadump_eval,81    .doneclass = NULL,82    .finalize = crawler_metadump_finalize,83    .needs_lock = false,84    .needs_client = true,85};8687static int crawler_mgdump_init(crawler_module_t *cm, void *data);88static void crawler_mgdump_eval(crawler_module_t *cm, item *search, uint32_t hv, int i);89static void crawler_mgdump_finalize(crawler_module_t *cm);9091crawler_module_reg_t crawler_mgdump_mod = {92    .init = crawler_mgdump_init,93    .eval = crawler_mgdump_eval,94    .doneclass = NULL,95    .finalize = crawler_mgdump_finalize,96    .needs_lock = false,97    .needs_client = true,98};99100crawler_module_reg_t *crawler_mod_regs[4] = {101    &crawler_expired_mod,102    &crawler_expired_mod,103    &crawler_metadump_mod,104    &crawler_mgdump_mod,105};106107static int lru_crawler_write(crawler_client_t *c);108crawler_module_t active_crawler_mod;109enum crawler_run_type active_crawler_type;110111static crawler crawlers[LARGEST_ID];112113static int crawler_count = 0;114static volatile int do_run_lru_crawler_thread = 0;115static int lru_crawler_initialized = 0;116static pthread_mutex_t lru_crawler_lock = PTHREAD_MUTEX_INITIALIZER;117static pthread_cond_t  lru_crawler_cond = PTHREAD_COND_INITIALIZER;118#ifdef EXTSTORE119/* TODO: pass this around */120static void *storage;121#endif122123/* Will crawl all slab classes a minimum of once per hour */124#define MAX_MAINTCRAWL_WAIT 60 * 60125126/*** LRU CRAWLER THREAD ***/127128#define LRU_CRAWLER_MINBUFSPACE 8192129130static void lru_crawler_close_client(crawler_client_t *c) {131    //fprintf(stderr, "CRAWLER: Closing client\n");132    sidethread_conn_close(c->c);133    c->c = NULL;134    free(c->buf);135    c->buf = NULL;136}137138static void lru_crawler_release_client(crawler_client_t *c) {139    //fprintf(stderr, "CRAWLER: Closing client\n");140    redispatch_conn(c->c);141    c->c = NULL;142    free(c->buf);143    c->buf = NULL;144}145146static int lru_crawler_expand_buf(crawler_client_t *c) {147    c->buflen *= 2;148    char *nb = realloc(c->buf, c->buflen);149    if (nb == NULL) {150        return -1;151    }152    c->buf = nb;153    return 0;154}155156static int crawler_expired_init(crawler_module_t *cm, void *data) {157    struct crawler_expired_data *d;158    if (data != NULL) {159        d = data;160        d->is_external = true;161        cm->data = data;162    } else {163        // allocate data.164        d = calloc(1, sizeof(struct crawler_expired_data));165        if (d == NULL) {166            return -1;167        }168        // init lock.169        pthread_mutex_init(&d->lock, NULL);170        d->is_external = false;171        d->start_time = current_time;172173        cm->data = d;174    }175    pthread_mutex_lock(&d->lock);176    memset(&d->crawlerstats, 0, sizeof(crawlerstats_t) * POWER_LARGEST);177    for (int x = 0; x < POWER_LARGEST; x++) {178        d->crawlerstats[x].start_time = current_time;179        d->crawlerstats[x].run_complete = false;180    }181    pthread_mutex_unlock(&d->lock);182    return 0;183}184185static void crawler_expired_doneclass(crawler_module_t *cm, int slab_cls) {186    struct crawler_expired_data *d = (struct crawler_expired_data *) cm->data;187    pthread_mutex_lock(&d->lock);188    d->crawlerstats[slab_cls].end_time = current_time;189    d->crawlerstats[slab_cls].run_complete = true;190    pthread_mutex_unlock(&d->lock);191}192193static void crawler_expired_finalize(crawler_module_t *cm) {194    struct crawler_expired_data *d = (struct crawler_expired_data *) cm->data;195    pthread_mutex_lock(&d->lock);196    d->end_time = current_time;197    d->crawl_complete = true;198    pthread_mutex_unlock(&d->lock);199200    if (!d->is_external) {201        free(d);202    }203}204205/* I pulled this out to make the main thread clearer, but it reaches into the206 * main thread's values too much. Should rethink again.207 */208static void crawler_expired_eval(crawler_module_t *cm, item *search, uint32_t hv, int i) {209    struct crawler_expired_data *d = (struct crawler_expired_data *) cm->data;210    pthread_mutex_lock(&d->lock);211    crawlerstats_t *s = &d->crawlerstats[i];212    int is_flushed = item_is_flushed(search);213#ifdef EXTSTORE214    bool is_valid = true;215    if (search->it_flags & ITEM_HDR) {216        is_valid = storage_validate_item(storage, search);217    }218#endif219    if ((search->exptime != 0 && search->exptime < current_time)220        || is_flushed221#ifdef EXTSTORE222        || !is_valid223#endif224        ) {225        crawlers[i].reclaimed++;226        s->reclaimed++;227228        if (settings.verbose > 1) {229            int ii;230            char *key = ITEM_key(search);231            fprintf(stderr, "LRU crawler found an expired item (flags: %d, slab: %d): ",232                search->it_flags, search->slabs_clsid);233            for (ii = 0; ii < search->nkey; ++ii) {234                fprintf(stderr, "%c", key[ii]);235            }236            fprintf(stderr, "\n");237        }238        if ((search->it_flags & ITEM_FETCHED) == 0 && !is_flushed) {239            crawlers[i].unfetched++;240        }241#ifdef EXTSTORE242        STORAGE_delete(storage, search);243#endif244        do_item_unlink_nolock(search, hv);245        do_item_remove(search);246    } else {247        s->seen++;248        refcount_decr(search);249        if (search->exptime == 0) {250            s->noexp++;251        } else if (search->exptime - current_time > 3599) {252            s->ttl_hourplus++;253        } else {254            rel_time_t ttl_remain = search->exptime - current_time;255            int bucket = ttl_remain / 60;256            if (bucket <= 60) {257                s->histo[bucket]++;258            }259        }260    }261    pthread_mutex_unlock(&d->lock);262}263264static int crawler_metadump_init(crawler_module_t *cm, void *data) {265    cm->status = 0;266    if (cm->c.buf != NULL) {267        memcpy(cm->c.buf, "OK\r\n", 4);268        cm->c.bufused += 4;269    }270    return 0;271}272273#define KADD(p, s) { \274    memcpy(p, s, sizeof(s)-1); \275    p += sizeof(s)-1; \276}277#define KSP(p) { \278    *p = ' '; \279    p++; \280}281282static void crawler_metadump_eval(crawler_module_t *cm, item *it, uint32_t hv, int i) {283    int is_flushed = item_is_flushed(it);284#ifdef EXTSTORE285    bool is_valid = true;286    if (it->it_flags & ITEM_HDR) {287        is_valid = storage_validate_item(storage, it);288    }289#endif290    /* Ignore expired content. */291    if ((it->exptime != 0 && it->exptime < current_time)292        || is_flushed293#ifdef EXTSTORE294        || !is_valid295#endif296        ) {297        refcount_decr(it);298        return;299    }300    client_flags_t flags;301    FLAGS_CONV(it, flags);302    assert(it->nkey * 3 < LRU_CRAWLER_MINBUFSPACE/2);303    // unrolled snprintf for ~30% time improvement on fullspeed dump304    // key=%s exp=%ld la=%llu cas=%llu fetch=%s cls=%u size=%lu flags=%llu\n305    // + optional ext_page=%u and ext_offset=%u306307    char *p = cm->c.buf + cm->c.bufused;308    char *start = p;309    KADD(p, "key=");310    p = uriencode_p(ITEM_key(it), p, it->nkey);311    KSP(p);312313    KADD(p, "exp=");314    if (it->exptime == 0) {315        KADD(p, "-1 ");316    } else {317        p = itoa_64((long)(it->exptime + process_started), p);318        KSP(p);319    }320321    KADD(p, "la=");322    p = itoa_u64((unsigned long long)(it->time + process_started), p);323    KSP(p);324325    KADD(p, "cas=");326    p = itoa_u64(ITEM_get_cas(it), p);327    KSP(p);328329    if (it->it_flags & ITEM_FETCHED) {330        KADD(p, "fetch=yes ");331    } else {332        KADD(p, "fetch=no ");333    }334335    KADD(p, "cls=");336    p = itoa_u32(ITEM_clsid(it), p);337    KSP(p);338339    KADD(p, "size=");340    p = itoa_u64(ITEM_ntotal(it), p);341    KSP(p);342343    KADD(p, "flags=");344    p = itoa_u64(flags, p);345    KSP(p);346347#ifdef EXTSTORE348    if (it->it_flags & ITEM_HDR) {349#ifdef NEED_ALIGN350    item_hdr hdr_s;351    memcpy(&hdr_s, ITEM_data(it), sizeof(hdr_s));352    item_hdr *hdr = &hdr_s;353#else354    item_hdr *hdr = (item_hdr *)ITEM_data(it);355#endif356357    KADD(p, "ext_page=");358    p = itoa_u32(hdr->page_id, p);359    KSP(p);360361    KADD(p, "ext_offset=");362    p = itoa_u32(hdr->offset, p);363    KSP(p);364    }365#endif366367    KADD(p, "\n");368369    refcount_decr(it);370    assert(p - start < LRU_CRAWLER_MINBUFSPACE-1);371    cm->c.bufused += p - start;372}373374#undef KADD375#undef KSP376377static void crawler_metadump_finalize(crawler_module_t *cm) {378    if (cm->c.c != NULL) {379        // flush any pending data.380        if (lru_crawler_write(&cm->c) == 0) {381            // Only nonzero status right now means we were locked382            if (cm->status != 0) {383                const char *errstr = "ERROR locked try again later\r\n";384                size_t errlen = strlen(errstr);385                memcpy(cm->c.buf, errstr, errlen);386                cm->c.bufused += errlen;387            } else {388                memcpy(cm->c.buf, "END\r\n", 5);389                cm->c.bufused += 5;390            }391        }392    }393}394395static int crawler_mgdump_init(crawler_module_t *cm, void *data) {396    cm->status = 0;397    if (cm->c.buf != NULL) {398        memcpy(cm->c.buf, "OK\r\n", 4);399        cm->c.bufused += 4;400    }401    return 0;402}403404static void crawler_mgdump_eval(crawler_module_t *cm, item *it, uint32_t hv, int i) {405    int is_flushed = item_is_flushed(it);406    /* Ignore expired content. */407    if ((it->exptime != 0 && it->exptime < current_time)408        || is_flushed) {409        refcount_decr(it);410        return;411    }412413    char *p = cm->c.buf + cm->c.bufused; // buffer offset.414    char *start = p;415    memcpy(p, "mg ", 3);416    p += 3;417    if (it->it_flags & ITEM_KEY_BINARY) {418        p += base64_encode((unsigned char *) ITEM_key(it), it->nkey, (unsigned char*) p, LRU_CRAWLER_MINBUFSPACE/2);419        memcpy(p, " b\r\n", 4);420        p += 4;421    } else {422        memcpy(p, ITEM_key(it), it->nkey);423        p += it->nkey;424        memcpy(p, "\r\n", 2);425        p += 2;426    }427    int total = p - start;428429    refcount_decr(it);430    cm->c.bufused += total;431}432433static void crawler_mgdump_finalize(crawler_module_t *cm) {434    if (cm->c.c != NULL) {435        // flush any pending data.436        if (lru_crawler_write(&cm->c) == 0) {437            // Only nonzero status right now means we were locked438            if (cm->status != 0) {439                const char *errstr = "ERROR locked try again later\r\n";440                size_t errlen = strlen(errstr);441                memcpy(cm->c.buf, errstr, errlen);442                cm->c.bufused += errlen;443            } else {444                memcpy(cm->c.buf, "EN\r\n", 4);445                cm->c.bufused += 4;446            }447        }448    }449}450451// write the whole buffer out to the client socket.452static int lru_crawler_write(crawler_client_t *c) {453    unsigned int data_size = c->bufused;454    unsigned int sent = 0;455    struct pollfd to_poll[1];456    to_poll[0].fd = c->sfd;457    to_poll[0].events = POLLOUT;458459    if (c->c == NULL) return -1;460    if (data_size == 0) return 0;461    // a long backstop so clients can't hang forever.462    // continually timed out stuff used to blow up memory and crash so this is463    // just to avoid replacing one problem with another.464    int tries = 1800;465466    while (sent < data_size) {467        int ret = poll(to_poll, 1, 1000);468469        if (ret < 0) {470            // fatal.471            lru_crawler_close_client(c);472            return -1;473        }474475        // Timed out, retry for a while.476        if (ret == 0) {477            if (--tries == 0) {478                lru_crawler_close_client(c);479                return -1;480            }481            continue;482        }483484        // check if socket was closed on us.485        if (to_poll[0].revents & POLLIN) {486            char buf[1];487            int res = ((conn*)c->c)->read(c->c, buf, 1);488            if (res == 0 || (res == -1 && (errno != EAGAIN && errno != EWOULDBLOCK))) {489                lru_crawler_close_client(c);490                return -1;491            }492        }493494        if (to_poll[0].revents & (POLLHUP|POLLERR)) {495            // got socket hangup.496            lru_crawler_close_client(c);497            return -1;498        } else if (to_poll[0].revents & POLLOUT) {499            // socket is writeable.500            int total = ((conn*)c->c)->write(c->c, c->buf + sent, data_size - sent);501            if (total == -1) {502                if (errno != EAGAIN && errno != EWOULDBLOCK) {503                    lru_crawler_close_client(c);504                    return -1;505                }506            } else if (total == 0) {507                lru_crawler_close_client(c);508                return -1;509            } else {510                sent += total;511            }512        }513    } // while514515    // write buffer now empty.516    c->bufused = 0;517518    return 0;519}520521static void lru_crawler_class_done(int i) {522    crawlers[i].it_flags = 0;523    crawler_count--;524    do_item_unlinktail_q((item *)&crawlers[i]);525    do_item_stats_add_crawl(i, crawlers[i].reclaimed,526            crawlers[i].unfetched, crawlers[i].checked);527    pthread_mutex_unlock(&lru_locks[i]);528    if (active_crawler_mod.mod->doneclass != NULL)529        active_crawler_mod.mod->doneclass(&active_crawler_mod, i);530}531532// ensure we build the buffer a little bit to cut down on poll/write syscalls.533#define MIN_ITEMS_PER_WRITE 16534static void item_crawl_hash(void) {535    // get iterator from assoc. can hang for a long time.536    // - blocks hash expansion537    void *iter = assoc_get_iterator();538    int crawls_persleep = settings.crawls_persleep;539    item *it = NULL;540    int items = 0;541542    // Could not get the iterator: probably locked due to hash expansion.543    if (iter == NULL) {544        active_crawler_mod.status = 1;545        return;546    }547548    // loop while iterator returns something549    // - iterator func handles bucket-walking550    // - iterator returns with bucket locked.551    while (assoc_iterate(iter, &it)) {552        // if iterator returns true but no item, we're inbetween buckets and553        // can do cleanup work without holding an item lock.554        if (it == NULL) {555            if (active_crawler_mod.c.c != NULL) {556                if (items > MIN_ITEMS_PER_WRITE) {557                    int ret = lru_crawler_write(&active_crawler_mod.c);558                    items = 0;559                    if (ret != 0) {560                        // fail out and finalize.561                        break;562                    }563                }564            } else if (active_crawler_mod.mod->needs_client) {565                // fail out and finalize.566                break;567            }568569            // - sleep bits from orig loop570            if (crawls_persleep <= 0 && settings.lru_crawler_sleep) {571                pthread_mutex_unlock(&lru_crawler_lock);572                usleep(settings.lru_crawler_sleep);573                pthread_mutex_lock(&lru_crawler_lock);574                crawls_persleep = settings.crawls_persleep;575            } else if (!settings.lru_crawler_sleep) {576                // TODO: only cycle lock every N?577                pthread_mutex_unlock(&lru_crawler_lock);578                pthread_mutex_lock(&lru_crawler_lock);579            }580            continue;581        }582583        // double check that the item isn't in a transitional state.584        if (refcount_incr(it) < 2) {585            refcount_decr(it);586            continue;587        }588589        // We're presently holding an item lock, so we cannot flush the590        // buffer to the network socket as the syscall is both slow and could591        // hang waiting for POLLOUT. Instead we must expand the buffer.592        if (active_crawler_mod.c.c != NULL) {593            crawler_client_t *c = &active_crawler_mod.c;594            if (c->buflen - c->bufused < LRU_CRAWLER_MINBUFSPACE) {595                if (lru_crawler_expand_buf(c) != 0) {596                    // failed to expand buffer, stop.597                    break;598                }599            }600        }601        // FIXME: missing hv and i are fine for metadump eval, but not fine602        // for expire eval.603        active_crawler_mod.mod->eval(&active_crawler_mod, it, 0, 0);604        crawls_persleep--;605        items++;606    }607608    // must finalize or we leave the hash table expansion blocked.609    assoc_iterate_final(iter);610    return;611}612613static void *item_crawler_thread(void *arg) {614    int i;615    int crawls_persleep = settings.crawls_persleep;616617    pthread_mutex_lock(&lru_crawler_lock);618    pthread_cond_signal(&lru_crawler_cond);619    settings.lru_crawler = true;620    if (settings.verbose > 2)621        fprintf(stderr, "Starting LRU crawler background thread\n");622    while (do_run_lru_crawler_thread) {623    pthread_cond_wait(&lru_crawler_cond, &lru_crawler_lock);624625    if (crawler_count == -1) {626        item_crawl_hash();627        crawler_count = 0;628    } else {629    while (crawler_count) {630        item *search = NULL;631        void *hold_lock = NULL;632633        for (i = POWER_SMALLEST; i < LARGEST_ID; i++) {634            if (crawlers[i].it_flags != 1) {635                continue;636            }637638            if (active_crawler_mod.c.c != NULL) {639                crawler_client_t *c = &active_crawler_mod.c;640                if (c->buflen - c->bufused < LRU_CRAWLER_MINBUFSPACE) {641                    int ret = lru_crawler_write(c);642                    if (ret != 0) {643                        // Must be locked to unlink crawler.644                        pthread_mutex_lock(&lru_locks[i]);645                        lru_crawler_class_done(i);646                        continue;647                    }648                }649            } else if (active_crawler_mod.mod->needs_client) {650                // Must be locked to unlink crawler.651                pthread_mutex_lock(&lru_locks[i]);652                lru_crawler_class_done(i);653                continue;654            }655            pthread_mutex_lock(&lru_locks[i]);656            search = do_item_crawl_q((item *)&crawlers[i]);657            if (search == NULL ||658                (crawlers[i].remaining && --crawlers[i].remaining < 1)) {659                if (settings.verbose > 2)660                    fprintf(stderr, "Nothing left to crawl for %d\n", i);661                lru_crawler_class_done(i);662                continue;663            }664            uint32_t hv = hash(ITEM_key(search), search->nkey);665            /* Attempt to hash item lock the "search" item. If locked, no666             * other callers can incr the refcount667             */668            if ((hold_lock = item_trylock(hv)) == NULL) {669                pthread_mutex_unlock(&lru_locks[i]);670                continue;671            }672            /* Now see if the item is refcount locked */673            if (refcount_incr(search) != 2) {674                refcount_decr(search);675                if (hold_lock)676                    item_trylock_unlock(hold_lock);677                pthread_mutex_unlock(&lru_locks[i]);678                continue;679            }680681            crawlers[i].checked++;682            /* Frees the item or decrements the refcount. */683            /* Interface for this could improve: do the free/decr here684             * instead? */685            if (!active_crawler_mod.mod->needs_lock) {686                pthread_mutex_unlock(&lru_locks[i]);687            }688689            active_crawler_mod.mod->eval(&active_crawler_mod, search, hv, i);690691            if (hold_lock)692                item_trylock_unlock(hold_lock);693            if (active_crawler_mod.mod->needs_lock) {694                pthread_mutex_unlock(&lru_locks[i]);695            }696697            if (crawls_persleep-- <= 0 && settings.lru_crawler_sleep) {698                pthread_mutex_unlock(&lru_crawler_lock);699                usleep(settings.lru_crawler_sleep);700                pthread_mutex_lock(&lru_crawler_lock);701                crawls_persleep = settings.crawls_persleep;702            } else if (!settings.lru_crawler_sleep) {703                // TODO: only cycle lock every N?704                pthread_mutex_unlock(&lru_crawler_lock);705                pthread_mutex_lock(&lru_crawler_lock);706            }707        }708    } // while709    } // if crawler_count710711    if (active_crawler_mod.mod != NULL) {712        if (active_crawler_mod.mod->finalize != NULL)713            active_crawler_mod.mod->finalize(&active_crawler_mod);714        while (active_crawler_mod.c.c != NULL && active_crawler_mod.c.bufused != 0) {715            lru_crawler_write(&active_crawler_mod.c);716        }717        // Double checking in case the client closed during the poll718        if (active_crawler_mod.c.c != NULL) {719            lru_crawler_release_client(&active_crawler_mod.c);720        }721        active_crawler_mod.mod = NULL;722    }723724    if (settings.verbose > 2)725        fprintf(stderr, "LRU crawler thread sleeping\n");726727    STATS_LOCK();728    stats_state.lru_crawler_running = false;729    STATS_UNLOCK();730    }731    pthread_mutex_unlock(&lru_crawler_lock);732    if (settings.verbose > 2)733        fprintf(stderr, "LRU crawler thread stopping\n");734    settings.lru_crawler = false;735736    return NULL;737}738739static pthread_t item_crawler_tid;740741int stop_item_crawler_thread(bool wait) {742    int ret;743    pthread_mutex_lock(&lru_crawler_lock);744    if (do_run_lru_crawler_thread == 0) {745        pthread_mutex_unlock(&lru_crawler_lock);746        return 0;747    }748    do_run_lru_crawler_thread = 0;749    pthread_cond_signal(&lru_crawler_cond);750    pthread_mutex_unlock(&lru_crawler_lock);751    if (wait && (ret = pthread_join(item_crawler_tid, NULL)) != 0) {752        fprintf(stderr, "Failed to stop LRU crawler thread: %s\n", strerror(ret));753        return -1;754    }755    return 0;756}757758/* Lock dance to "block" until thread is waiting on its condition:759 * caller locks mtx. caller spawns thread.760 * thread blocks on mutex.761 * caller waits on condition, releases lock.762 * thread gets lock, sends signal.763 * caller can't wait, as thread has lock.764 * thread waits on condition, releases lock765 * caller wakes on condition, gets lock.766 * caller immediately releases lock.767 * thread is now safely waiting on condition before the caller returns.768 */769int start_item_crawler_thread(void) {770    int ret;771772    if (settings.lru_crawler)773        return -1;774    pthread_mutex_lock(&lru_crawler_lock);775    do_run_lru_crawler_thread = 1;776    if ((ret = pthread_create(&item_crawler_tid, NULL,777        item_crawler_thread, NULL)) != 0) {778        fprintf(stderr, "Can't create LRU crawler thread: %s\n",779            strerror(ret));780        pthread_mutex_unlock(&lru_crawler_lock);781        return -1;782    }783    thread_setname(item_crawler_tid, "mc-itemcrawler");784    /* Avoid returning until the crawler has actually started */785    pthread_cond_wait(&lru_crawler_cond, &lru_crawler_lock);786    pthread_mutex_unlock(&lru_crawler_lock);787788    return 0;789}790791/* 'remaining' is passed in so the LRU maintainer thread can scrub the whole792 * LRU every time.793 */794static int do_lru_crawler_start(uint32_t id, uint32_t remaining) {795    uint32_t sid = id;796    int starts = 0;797798    pthread_mutex_lock(&lru_locks[sid]);799    if (crawlers[sid].it_flags == 0) {800        if (settings.verbose > 2)801            fprintf(stderr, "Kicking LRU crawler off for LRU %u\n", sid);802        crawlers[sid].nbytes = 0;803        crawlers[sid].nkey = 0;804        crawlers[sid].it_flags = 1; /* For a crawler, this means enabled. */805        crawlers[sid].next = 0;806        crawlers[sid].prev = 0;807        crawlers[sid].time = 0;808        if (remaining == LRU_CRAWLER_CAP_REMAINING) {809            remaining = do_get_lru_size(sid);810        }811        /* Values for remaining:812         * remaining = 0813         * - scan all elements, until a NULL is reached814         * - if empty, NULL is reached right away815         * remaining = n + 1816         * - first n elements are parsed (or until a NULL is reached)817         */818        if (remaining) remaining++;819        crawlers[sid].remaining = remaining;820        crawlers[sid].slabs_clsid = sid;821        crawlers[sid].reclaimed = 0;822        crawlers[sid].unfetched = 0;823        crawlers[sid].checked = 0;824        do_item_linktail_q((item *)&crawlers[sid]);825        crawler_count++;826        starts++;827    }828    pthread_mutex_unlock(&lru_locks[sid]);829    return starts;830}831832static int lru_crawler_set_client(crawler_module_t *cm, void *c, const int sfd) {833    crawler_client_t *crawlc = &cm->c;834    if (crawlc->c != NULL) {835        return -1;836    }837    crawlc->c = c;838    crawlc->sfd = sfd;839840    size_t size = LRU_CRAWLER_MINBUFSPACE * 16;841    crawlc->buf = malloc(size);842843    if (crawlc->buf == NULL) {844        return -2;845    }846    crawlc->buflen = size;847    crawlc->bufused = 0;848    return 0;849}850851int lru_crawler_start(uint8_t *ids, uint32_t remaining,852                             const enum crawler_run_type type, void *data,853                             void *c, const int sfd) {854    int starts = 0;855    bool is_running;856    static rel_time_t block_ae_until = 0;857    pthread_mutex_lock(&lru_crawler_lock);858    STATS_LOCK();859    is_running = stats_state.lru_crawler_running;860    STATS_UNLOCK();861    if (do_run_lru_crawler_thread == 0) {862        pthread_mutex_unlock(&lru_crawler_lock);863        return -2;864    }865866    if (is_running &&867            !(type == CRAWLER_AUTOEXPIRE && active_crawler_type == CRAWLER_AUTOEXPIRE)) {868        pthread_mutex_unlock(&lru_crawler_lock);869        block_ae_until = current_time + 60;870        return -1;871    }872873    if (type == CRAWLER_AUTOEXPIRE && block_ae_until > current_time) {874        pthread_mutex_unlock(&lru_crawler_lock);875        return -1;876    }877878    /* hash table walk only supported with metadump for now. */879    if (ids == NULL && type != CRAWLER_METADUMP && type != CRAWLER_MGDUMP) {880        pthread_mutex_unlock(&lru_crawler_lock);881        return -2;882    }883884    /* Configure the module */885    if (!is_running) {886        assert(crawler_mod_regs[type] != NULL);887        active_crawler_mod.mod = crawler_mod_regs[type];888        active_crawler_type = type;889        /* set client, then call init to be able to send OK */890        if (active_crawler_mod.mod->needs_client) {891            if (c == NULL || sfd == 0) {892                pthread_mutex_unlock(&lru_crawler_lock);893                return -2;894            }895            if (lru_crawler_set_client(&active_crawler_mod, c, sfd) != 0) {896                pthread_mutex_unlock(&lru_crawler_lock);897                return -2;898            }899        }900        if (active_crawler_mod.mod->init != NULL) {901            active_crawler_mod.mod->init(&active_crawler_mod, data);902        }903    }904905    if (ids == NULL) {906        /* NULL ids means to walk the hash table instead. */907        starts = 1;908        /* FIXME: hack to signal hash mode to the crawler thread.909         * Something more clear would be nice.910         */911        crawler_count = -1;912    } else {913        /* we allow the autocrawler to restart sub-LRU's before completion */914        for (int sid = POWER_SMALLEST; sid < POWER_LARGEST; sid++) {915            if (ids[sid])916                starts += do_lru_crawler_start(sid, remaining);917        }918    }919    if (starts) {920        STATS_LOCK();921        stats_state.lru_crawler_running = true;922        stats.lru_crawler_starts++;923        STATS_UNLOCK();924        pthread_cond_signal(&lru_crawler_cond);925    }926    pthread_mutex_unlock(&lru_crawler_lock);927    return starts;928}929930/*931 * Also only clear the crawlerstats once per sid.932 */933enum crawler_result_type lru_crawler_crawl(char *slabs, const enum crawler_run_type type,934        void *c, const int sfd, unsigned int remaining) {935    char *b = NULL;936    uint32_t sid = 0;937    int starts = 0;938    uint8_t tocrawl[POWER_LARGEST];939    bool hash_crawl = false;940941    /* FIXME: I added this while debugging. Don't think it's needed? */942    memset(tocrawl, 0, sizeof(uint8_t) * POWER_LARGEST);943    if (strcmp(slabs, "all") == 0) {944        for (sid = 0; sid < POWER_LARGEST; sid++) {945            tocrawl[sid] = 1;946        }947    } else if (strcmp(slabs, "hash") == 0) {948        hash_crawl = true;949    } else {950        for (char *p = strtok_r(slabs, ",", &b);951             p != NULL;952             p = strtok_r(NULL, ",", &b)) {953954            if (!safe_strtoul(p, &sid) || sid < POWER_SMALLEST955                    || sid >= MAX_NUMBER_OF_SLAB_CLASSES) {956                return CRAWLER_BADCLASS;957            }958            tocrawl[sid | TEMP_LRU] = 1;959            tocrawl[sid | HOT_LRU] = 1;960            tocrawl[sid | WARM_LRU] = 1;961            tocrawl[sid | COLD_LRU] = 1;962        }963    }964965    starts = lru_crawler_start(hash_crawl ? NULL : tocrawl, remaining, type, NULL, c, sfd);966    if (starts == -1) {967        return CRAWLER_RUNNING;968    } else if (starts == -2) {969        return CRAWLER_ERROR; /* FIXME: not very helpful. */970    } else if (starts) {971        return CRAWLER_OK;972    } else {973        return CRAWLER_NOTSTARTED;974    }975}976977/* If we hold this lock, crawler can't wake up or move */978void lru_crawler_pause(void) {979    pthread_mutex_lock(&lru_crawler_lock);980}981982void lru_crawler_resume(void) {983    pthread_mutex_unlock(&lru_crawler_lock);984}985986int init_lru_crawler(void *arg) {987    if (lru_crawler_initialized == 0) {988#ifdef EXTSTORE989        storage = arg;990#endif991        active_crawler_mod.c.c = NULL;992        active_crawler_mod.mod = NULL;993        active_crawler_mod.data = NULL;994        lru_crawler_initialized = 1;995    }996    return 0;997}

Code quality findings 14

Warning: Excessive nesting depth (>= 5 levels) detected. Deeply nested code is hard to read and maintain. Consider refactoring into separate functions.
warning maintainability deep-nesting
while (assoc_iterate(iter, &it)) {
Warning: Excessive nesting depth (>= 5 levels) detected. Deeply nested code is hard to read and maintain. Consider refactoring into separate functions.
warning maintainability deep-nesting
if (it == NULL) {
Warning: Excessive nesting depth (>= 5 levels) detected. Deeply nested code is hard to read and maintain. Consider refactoring into separate functions.
warning maintainability deep-nesting
if (active_crawler_mod.c.c != NULL) {
Warning: Excessive nesting depth (>= 5 levels) detected. Deeply nested code is hard to read and maintain. Consider refactoring into separate functions.
warning maintainability deep-nesting
if (items > MIN_ITEMS_PER_WRITE) {
Warning: Excessive nesting depth (>= 5 levels) detected. Deeply nested code is hard to read and maintain. Consider refactoring into separate functions.
warning maintainability deep-nesting
if (ret != 0) {
Warning: Excessive nesting depth (>= 5 levels) detected. Deeply nested code is hard to read and maintain. Consider refactoring into separate functions.
warning maintainability deep-nesting
if (crawler_count == -1) {
Warning: Excessive nesting depth (>= 5 levels) detected. Deeply nested code is hard to read and maintain. Consider refactoring into separate functions.
warning maintainability deep-nesting
} else {
Warning: Excessive nesting depth (>= 5 levels) detected. Deeply nested code is hard to read and maintain. Consider refactoring into separate functions.
warning maintainability deep-nesting
while (crawler_count) {
Warning: Excessive nesting depth (>= 5 levels) detected. Deeply nested code is hard to read and maintain. Consider refactoring into separate functions.
warning maintainability deep-nesting
for (i = POWER_SMALLEST; i < LARGEST_ID; i++) {
Warning: Excessive nesting depth (>= 5 levels) detected. Deeply nested code is hard to read and maintain. Consider refactoring into separate functions.
warning maintainability deep-nesting
if (crawlers[i].it_flags != 1) {
Warning: Excessive nesting depth (>= 5 levels) detected. Deeply nested code is hard to read and maintain. Consider refactoring into separate functions.
warning maintainability deep-nesting
if (active_crawler_mod.c.c != NULL) {
Warning: Possible assignment ('=') used in conditional context instead of comparison ('=='). Verify intent.
warning correctness assignment-in-condition
if ((ret = pthread_create(&item_crawler_tid, NULL,
Info: Magic number detected in condition or control flow. Use named constants via #define or const for better readability and maintainability.
info maintainability magic-number
} else if (search->exptime - current_time > 3599) {
Info: Magic number detected in condition or control flow. Use named constants via #define or const for better readability and maintainability.
info maintainability magic-number
if (bucket <= 60) {

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.

Generated by searchcode.com · powered by code_get_file + code_get_findings