<commit_before>
#include "ngx_http_tcache_module.h"


typedef struct {
    ngx_rbtree_t                     rbtree;
    ngx_rbtree_node_t                sentinel;
    ngx_queue_t                      queue;
} ngx_http_tcache_slab_sh_t;


typedef struct {
    ngx_rbtree_node_t                node;
    ngx_queue_t                      queue;

    u_char                           key[NGX_HTTP_CACHE_KEY_LEN
                                         - sizeof(ngx_rbtree_key_t)];
    ngx_http_tcache_node_t          *data;
} ngx_http_tcache_slab_node_index_t;


static ngx_int_t ngx_http_tcache_slab_init(ngx_http_tcache_t *cache);
static ngx_int_t ngx_http_tcache_slab_get(
    ngx_http_tcache_t *cache, ngx_http_tcache_ctx_t *ctx, ngx_flag_t lookup);
static ngx_http_tcache_node_t * ngx_http_tcache_slab_create(
    ngx_http_tcache_t *cache, ngx_http_tcache_ctx_t *ctx);
static u_char * ngx_http_tcache_slab_alloc(ngx_http_tcache_t *cache,
    size_t size);
static ngx_int_t ngx_http_tcache_slab_put(ngx_http_tcache_t *cache, 
    ngx_http_tcache_ctx_t *ctx);
static void ngx_http_tcache_slab_delete(ngx_http_tcache_t *cache,
    ngx_http_tcache_node_t *tn);
static void ngx_http_tcache_slab_expire(ngx_http_tcache_t *cache);
static void ngx_http_tcache_slab_force_expire(ngx_http_tcache_t *cache);
static void ngx_http_tcache_slab_cleanup(ngx_http_tcache_t *cache);

static ngx_http_tcache_node_t * ngx_http_tcache_slab_alloc_node(
    ngx_slab_pool_t *shpool);
static void ngx_http_tcache_slab_free_node(ngx_slab_pool_t *shpool,
    ngx_http_tcache_node_t *tn);

static void ngx_http_tcache_rbtree_insert_value(ngx_rbtree_node_t *temp,
    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);


ngx_http_tcache_storage_t tcache_slab = {
    ngx_http_tcache_slab_init,
    ngx_http_tcache_slab_get,
    ngx_http_tcache_slab_put,
    NULL,
    ngx_http_tcache_slab_delete,
    ngx_http_tcache_slab_expire,
    ngx_http_tcache_slab_force_expire,
    ngx_http_tcache_slab_cleanup,
};


static ngx_int_t
ngx_http_tcache_slab_init(ngx_http_tcache_t *cache)
{
    ngx_slab_pool_t            *shpool;
    ngx_http_tcache_slab_sh_t  *sh;

    shpool = cache->shpool;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cache->log, 0,
                   "tcache slab init");

    cache->sh = ngx_slab_alloc_locked(shpool,
                                      sizeof(ngx_http_tcache_slab_sh_t));
    if (cache->sh == NULL) {
        return NGX_ERROR;
    }

    sh = cache->sh;

    ngx_rbtree_init(&sh->rbtree, &sh->sentinel,
                    ngx_http_tcache_rbtree_insert_value);

    ngx_queue_init(&sh->queue);

    return NGX_OK;
}


static ngx_http_tcache_node_t *
ngx_http_tcache_slab_lookup(ngx_http_tcache_t *cache, u_char *key)
{
    ngx_int_t                          rc;
    ngx_rbtree_key_t                   node_key;
    ngx_rbtree_node_t                 *node, *sentinel;
    ngx_http_tcache_slab_sh_t         *sh;
    ngx_http_tcache_slab_node_index_t *index;

    ngx_memcpy((u_char *) &node_key, key, sizeof(ngx_rbtree_key_t));

    sh = cache->sh;
    node = sh->rbtree.root;
    sentinel = sh->rbtree.sentinel;

    while (node != sentinel) {

        if (node_key < node->key) {
            node = node->left;
            continue;
        }

        if (node_key > node->key) {
            node = node->right;
            continue;
        }

        /* node_key == node->key */

        index = (ngx_http_tcache_slab_node_index_t *) node;

        rc = ngx_memcmp(&key[sizeof(ngx_rbtree_key_t)], index->key,
                        NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));

        if (rc == 0) {
            return (ngx_http_tcache_node_t *) index->data;
        }

        node = (rc < 0) ? node->left : node->right;
    }

    return NULL;
}


static ngx_int_t
ngx_http_tcache_slab_get(ngx_http_tcache_t *cache,
    ngx_http_tcache_ctx_t *ctx, ngx_flag_t lookup)
{
    time_t                  now;
    ngx_buf_t              *buf;
    ngx_http_tcache_node_t *tn;

    tn = ngx_http_tcache_slab_lookup(cache, ctx->key);
    if (tn == NULL) {
        return NGX_DECLINED;
    }

    now = ngx_time();

    if (lookup) {

        if (tn->status != ctx->status) {

            if (ctx->can_use_stale) {

                if (tn->use_stale) {

                    /*TODO: try interval*/
                    if ((now - tn->last_try) > 3) {

                        tn->last_try = now;
                        return NGX_DECLINED;
                    }

                } else {
                    tn->last_try = now;
                    tn->use_stale = 1;
                }

                return NGX_AGAIN;

            } else {
                return NGX_DECLINED;
            }
        }

        return NGX_OK;
    }

    if (tn->expires < now) {
        if (tn->stale > now) {
            if (tn->use_stale) {
                /* Try once again */
                if ((now - tn->last_try) > 3) {
                    tn->last_try = now;
                    return NGX_DECLINED;
                }

                goto use_cache;
            }

            if (ctx->updating_use_stale) {
                if (!tn->updating) {
                    tn->last_try = now;
                    tn->updating = 1;
                    return NGX_DECLINED;

                } else {

                    goto use_cache;
                }
            }
        }

        return NGX_DECLINED;
    }

use_cache:
    
    ctx->node = tn;
    ctx->cache_length = tn->length;
    ctx->valid = tn->expires - now;
    ctx->age = now - tn->date;

    buf = ngx_create_temp_buf(ctx->pool, tn->length);
    if (buf == NULL) {
        return NGX_ERROR;
    }

    buf->last = ngx_copy(buf->pos, tn->payload, tn->length);
<commit_msg>added use_stale_cache for updating cache<commit_after>
#include "ngx_http_tcache_module.h"


typedef struct {
    ngx_rbtree_t                     rbtree;
    ngx_rbtree_node_t                sentinel;
    ngx_queue_t                      queue;
} ngx_http_tcache_slab_sh_t;


typedef struct {
    ngx_rbtree_node_t                node;
    ngx_queue_t                      queue;

    u_char                           key[NGX_HTTP_CACHE_KEY_LEN
                                         - sizeof(ngx_rbtree_key_t)];
    ngx_http_tcache_node_t          *data;
} ngx_http_tcache_slab_node_index_t;


static ngx_int_t ngx_http_tcache_slab_init(ngx_http_tcache_t *cache);
static ngx_int_t ngx_http_tcache_slab_get(
    ngx_http_tcache_t *cache, ngx_http_tcache_ctx_t *ctx, ngx_flag_t lookup);
static ngx_http_tcache_node_t * ngx_http_tcache_slab_create(
    ngx_http_tcache_t *cache, ngx_http_tcache_ctx_t *ctx);
static u_char * ngx_http_tcache_slab_alloc(ngx_http_tcache_t *cache,
    size_t size);
static ngx_int_t ngx_http_tcache_slab_put(ngx_http_tcache_t *cache, 
    ngx_http_tcache_ctx_t *ctx);
static void ngx_http_tcache_slab_delete(ngx_http_tcache_t *cache,
    ngx_http_tcache_node_t *tn);
static void ngx_http_tcache_slab_expire(ngx_http_tcache_t *cache);
static void ngx_http_tcache_slab_force_expire(ngx_http_tcache_t *cache);
static void ngx_http_tcache_slab_cleanup(ngx_http_tcache_t *cache);

static ngx_http_tcache_node_t * ngx_http_tcache_slab_alloc_node(
    ngx_slab_pool_t *shpool);
static void ngx_http_tcache_slab_free_node(ngx_slab_pool_t *shpool,
    ngx_http_tcache_node_t *tn);

static void ngx_http_tcache_rbtree_insert_value(ngx_rbtree_node_t *temp,
    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);


ngx_http_tcache_storage_t tcache_slab = {
    ngx_http_tcache_slab_init,
    ngx_http_tcache_slab_get,
    ngx_http_tcache_slab_put,
    NULL,
    ngx_http_tcache_slab_delete,
    ngx_http_tcache_slab_expire,
    ngx_http_tcache_slab_force_expire,
    ngx_http_tcache_slab_cleanup,
};


static ngx_int_t
ngx_http_tcache_slab_init(ngx_http_tcache_t *cache)
{
    ngx_slab_pool_t            *shpool;
    ngx_http_tcache_slab_sh_t  *sh;

    shpool = cache->shpool;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cache->log, 0,
                   "tcache slab init");

    cache->sh = ngx_slab_alloc_locked(shpool,
                                      sizeof(ngx_http_tcache_slab_sh_t));
    if (cache->sh == NULL) {
        return NGX_ERROR;
    }

    sh = cache->sh;

    ngx_rbtree_init(&sh->rbtree, &sh->sentinel,
                    ngx_http_tcache_rbtree_insert_value);

    ngx_queue_init(&sh->queue);

    return NGX_OK;
}


static ngx_http_tcache_node_t *
ngx_http_tcache_slab_lookup(ngx_http_tcache_t *cache, u_char *key)
{
    ngx_int_t                          rc;
    ngx_rbtree_key_t                   node_key;
    ngx_rbtree_node_t                 *node, *sentinel;
    ngx_http_tcache_slab_sh_t         *sh;
    ngx_http_tcache_slab_node_index_t *index;

    ngx_memcpy((u_char *) &node_key, key, sizeof(ngx_rbtree_key_t));

    sh = cache->sh;
    node = sh->rbtree.root;
    sentinel = sh->rbtree.sentinel;

    while (node != sentinel) {

        if (node_key < node->key) {
            node = node->left;
            continue;
        }

        if (node_key > node->key) {
            node = node->right;
            continue;
        }

        /* node_key == node->key */

        index = (ngx_http_tcache_slab_node_index_t *) node;

        rc = ngx_memcmp(&key[sizeof(ngx_rbtree_key_t)], index->key,
                        NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));

        if (rc == 0) {
            return (ngx_http_tcache_node_t *) index->data;
        }

        node = (rc < 0) ? node->left : node->right;
    }

    return NULL;
}


static ngx_int_t
ngx_http_tcache_slab_get(ngx_http_tcache_t *cache,
    ngx_http_tcache_ctx_t *ctx, ngx_flag_t lookup)
{
    time_t                  now;
    ngx_buf_t              *buf;
    ngx_http_tcache_node_t *tn;

    tn = ngx_http_tcache_slab_lookup(cache, ctx->key);
    if (tn == NULL) {
        return NGX_DECLINED;
    }

    now = ngx_time();

    if (lookup) {

        if (tn->status != ctx->status) {

            if (ctx->can_use_stale) {

                if (tn->use_stale) {

                    /*TODO: try interval*/
                    if ((now - tn->last_try) > 3) {

                        tn->last_try = now;
                        return NGX_DECLINED;
                    }

                } else {
                    tn->last_try = now;
                    tn->use_stale = 1;
                }

                return NGX_AGAIN;

            } else {
                return NGX_DECLINED;
            }
        }

        return NGX_OK;
    }

    if (tn->expires < now) {
        if (tn->stale > now) {
            if (tn->use_stale) {
                /* Try once again */
                if ((now - tn->last_try) > 3) {
                    tn->last_try = now;
                    return NGX_DECLINED;
                }

                ctx->use_stale_cache = 1;
                goto use_cache;
            }

            if (ctx->updating_use_stale) {
                if (!tn->updating) {
                    tn->last_try = now;
                    tn->updating = 1;
                    return NGX_DECLINED;

                } else {

                    ctx->use_stale_cache = 1;
                    goto use_cache;
                }
            }
        }

        return NGX_DECLINED;
    }

use_cache:
    
    ctx->node = tn;
    ctx->cache_length = tn->length;
    ctx->valid = tn->expires - now;
    ctx->age = now - tn->date;

    buf = ngx_create_temp_buf(ctx->pool, tn->length);
    if (buf == NULL) {
        return NGX_ERROR;
    }

    buf->last = ngx_copy(buf->pos, tn->payload, tn->length);

{'fraction_non_alphanumeric': 0.08815141301529686, 'fraction_numerical': 0.00155561317085818, 'mean_word_length': 1.9259165613147915, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 115, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3929003', 'n_tokens_mistral': 4554, 'n_tokens_neox': 4303, 'n_words': 638}