<commit_before>// tile cache policies
// it exports two cache types:
// 'nocache' implements a pass-troguth cache
// 'lru' implements a LRU cache

var LRUCache       = require('./lru'),
    CacheValidator = require('./cache_validator'),
    TTL            = require('./ttl');

var BLANK_TILE_SIZE = 334; // totally transparent tile size in bytes

module.exports.NoCache = function() {

    var me = {}

    me.beforeTileRender = function(req, res, callback) {
        callback(null);
    }

    me.afterTileRender = function(req, res, tile, headers, callback) {
        callback(null, tile, headers);
    }

    return me;

}

module.exports.LRUcache = function(max_items, redis_opts, ttl_timeout) {
    var cache_validator = CacheValidator(redis_opts);
    return GenericCache(new LRUCache(max_items), cache_validator, ttl_timeout || 600);
}

// implements a generic cache for tile
// cache_policy should implement set and get methods and optionally getStats
function GenericCache (cache_policy, cache_validator, ttl_timeout) {

    var me = {
        cache: cache_policy,
        cache_validator: cache_validator,
        cache_hits: 0,
        cache_misses: 0,
        cache_not_modified: 0,
        current_items: 0,
        expired: 0,
        cache_invalidated: 0,
        max_items: 0
    };

    // enable ttl
    var ttl = TTL(function(key) {
        me.remove(key);
        me.expired++;
    }, ttl_timeout || 60);

    function cache_key(req) {
        return req.url;
    }

    function update_items(n) {
        me.current_items = n;
        if(n > me.max_items) {
            me.max_items = n;
        }
    }

    me.remove = function(key) {
        ttl.remove(key);
        me.cache.remove(key);
        update_items(me.cache.size || 0);
    }

    me.beforeTileRender = function(req, res, callback) {
        req.windshaft_start = new Date().getTime();
        var key = cache_key(req);
        var tile = me.cache.get(key);
        if(tile) {
            // validate the cache
            me.cache_validator.getTimestamp(req.params.dbname, req.params.table, function(err, t) {
                if(t !== null && tile.timestamp < t) {
                    me.cache_misses++;
                    me.cache_invalidated++;
                    callback(null);
                } else {
                    // stats
                    me.cache_hits++;
                    var timestamp = new Date().getTime();
                    var delta = timestamp - req.windshaft_start;
                    tile.cache_time = delta/1000.0;
                    tile.hits++;
                    res.header('X-Cache-hit', 'true');
                    res.header('Last-Modified', new Date(tile.timestamp*1000).toGMTString());
                    // check 304
                    var modified_since = req.header('if-modified-since');
                    if (modified_since) {
                        modified_since = Date.parse(modified_since);
                        if(modified_since && modified_since <= timestamp*1000) {
                            me.cache_not_modified++;
                            res.send(304);
                            return;
                        }
                    }
                    res.send(tile.tile, tile.headers, 200);
                }
            });
        } else {
            me.cache_misses++;
            callback(null);
        }
    }

    me.afterTileRender = function(req, res, tile, headers, callback) {
        var timestamp = new Date().getTime();
        var delta = timestamp - req.windshaft_start;<commit_msg>explicitly set http status for 304<commit_after>// tile cache policies
// it exports two cache types:
// 'nocache' implements a pass-troguth cache
// 'lru' implements a LRU cache

var LRUCache       = require('./lru'),
    CacheValidator = require('./cache_validator'),
    TTL            = require('./ttl');

var BLANK_TILE_SIZE = 334; // totally transparent tile size in bytes

module.exports.NoCache = function() {

    var me = {}

    me.beforeTileRender = function(req, res, callback) {
        callback(null);
    }

    me.afterTileRender = function(req, res, tile, headers, callback) {
        callback(null, tile, headers);
    }

    return me;

}

module.exports.LRUcache = function(max_items, redis_opts, ttl_timeout) {
    var cache_validator = CacheValidator(redis_opts);
    return GenericCache(new LRUCache(max_items), cache_validator, ttl_timeout || 600);
}

// implements a generic cache for tile
// cache_policy should implement set and get methods and optionally getStats
function GenericCache (cache_policy, cache_validator, ttl_timeout) {

    var me = {
        cache: cache_policy,
        cache_validator: cache_validator,
        cache_hits: 0,
        cache_misses: 0,
        cache_not_modified: 0,
        current_items: 0,
        expired: 0,
        cache_invalidated: 0,
        max_items: 0
    };

    // enable ttl
    var ttl = TTL(function(key) {
        me.remove(key);
        me.expired++;
    }, ttl_timeout || 60);

    function cache_key(req) {
        return req.url;
    }

    function update_items(n) {
        me.current_items = n;
        if(n > me.max_items) {
            me.max_items = n;
        }
    }

    me.remove = function(key) {
        ttl.remove(key);
        me.cache.remove(key);
        update_items(me.cache.size || 0);
    }

    me.beforeTileRender = function(req, res, callback) {
        req.windshaft_start = new Date().getTime();
        var key = cache_key(req);
        var tile = me.cache.get(key);
        if(tile) {
            // validate the cache
            me.cache_validator.getTimestamp(req.params.dbname, req.params.table, function(err, t) {
                if(t !== null && tile.timestamp < t) {
                    me.cache_misses++;
                    me.cache_invalidated++;
                    callback(null);
                } else {
                    // stats
                    me.cache_hits++;
                    var timestamp = new Date().getTime();
                    var delta = timestamp - req.windshaft_start;
                    tile.cache_time = delta/1000.0;
                    tile.hits++;
                    res.header('X-Cache-hit', 'true');
                    res.header('Last-Modified', new Date(tile.timestamp*1000).toGMTString());
                    // check 304
                    var modified_since = req.header('if-modified-since');
                    if (modified_since) {
                        modified_since = Date.parse(modified_since);
                        if(modified_since && modified_since <= timestamp*1000) {
                            me.cache_not_modified++;
                            res.statusCode = 304;
                            res.end();
                            return;
                        }
                    }
                    res.send(tile.tile, tile.headers, 200);
                }
            });
        } else {
            me.cache_misses++;
            callback(null);
        }
    }

    me.afterTileRender = function(req, res, tile, headers, callback) {
        var timestamp = new Date().getTime();
        var delta = timestamp - req.windshaft_start;
{'fraction_non_alphanumeric': 0.1110184799221898, 'fraction_numerical': 0.010976795887175211, 'mean_word_length': 1.640498899486427, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4118740', 'n_tokens_mistral': 2120, 'n_tokens_neox': 1986, 'n_words': 442}