<commit_before>'use strict';

const RedisPool = require('redis-mpool');
const MapConfig = require('../models/mapconfig');

module.exports = class MapStore {
    constructor (options = {}) {
        Object.assign(this, {
            redis_host: '127.0.0.1',
            redis_port: 6379,
            redis_db: 0,
            redis_key_mapcfg_prefix: 'map_cfg|',
            expire_time: 300, // in seconds
            pool_max: 50,
            pool_idleTimeout: 10000, // in milliseconds
            pool_reapInterval: 1000, // in milliseconds
            pool_log: false
        }, options);

        if (!this.pool) {
            this.pool = new RedisPool({
                host: this._get('redis_host'),
                port: this._get('redis_port'),
                max: this._get('pool_max'),
                idleTimeoutMillis: this._get('pool_idleTimeout'),
                reapIntervalMillis: this._get('pool_reapInterval'),
                log: this._get('pool_log')
            });
        }

        this.logger = options.logger;
    }

    _get (key) {
        return this[key];
    }

    _redisCmd (func, args, callback) {
        const db = this._get('redis_db');

        this.pool.acquire(db, (err, client) => {
            if (err) {
                this.log(err, 'adquiring client');
                return callback(err);
            }

            client[func](...args, (err, data) => {
                this.pool.release(db, client);

                if (err) {
                    this.log(err, func, args);
                }

                if (callback) {
                    if (err) {
                        return callback(err);
                    }

                    return callback(null, data);
                }
            });
        });
    }

    // API: Load a saved MapStore object, renewing expiration time
    load (id, callback) {
        const key = this._get('redis_key_mapcfg_prefix') + id;
        const exp = this._get('expire_time');

        this._redisCmd('GET', [key], (err, json) => {
            if (err) {
                return callback(err);
            }

            if (!json) {
                const mapConfigError = new Error(`Invalid or nonexistent map configuration token '${id}'`);
                return callback(mapConfigError);
            }

            let mapConfig;
            try {
                const serializedMapConfig = JSON.parse(json);
                mapConfig = MapConfig.create(serializedMapConfig);
            } catch (error) {
                return callback(error);
            }

            // Postpone expiration for the key
            // not waiting for response
            this._redisCmd('EXPIRE', [key, exp]);

            return callback(null, mapConfig);
        });
    }

    // API: store map to redis
    save (map, callback) {
        const id = map.id();
        const key = this._get('redis_key_mapcfg_prefix') + id;
        const exp = this._get('expire_time');

        this._redisCmd('SETNX', [key, map.serialize()], (err, wasNew) => {
            if (err) {
                return callback(err);
            }

            this._redisCmd('EXPIRE', [key, exp]); // not waiting for response

            return callback(null, id, !wasNew);
        });
    }

    // API: delete map from store
    del (id, callback) {
        const key = this._get('redis_key_mapcfg_prefix') + id;
        this._redisCmd('DEL', [key], callback);
    }

    log (err, command, args = []) {
        if (this.logger) {
            const log = {
                error: err.message,
                command,
                args
            };
            this.logger.error(JSON.stringify(log));
        }
    }
};
<commit_msg>Better error handling and logging<commit_after>'use strict';

const RedisPool = require('redis-mpool');
const MapConfig = require('../models/mapconfig');

module.exports = class MapStore {
    constructor (options = {}) {
        Object.assign(this, {
            redis_host: '127.0.0.1',
            redis_port: 6379,
            redis_db: 0,
            redis_key_mapcfg_prefix: 'map_cfg|',
            expire_time: 300, // in seconds
            pool_max: 50,
            pool_idleTimeout: 10000, // in milliseconds
            pool_reapInterval: 1000, // in milliseconds
            pool_log: false
        }, options);

        if (!this.pool) {
            this.pool = new RedisPool({
                host: this._get('redis_host'),
                port: this._get('redis_port'),
                max: this._get('pool_max'),
                idleTimeoutMillis: this._get('pool_idleTimeout'),
                reapIntervalMillis: this._get('pool_reapInterval'),
                log: this._get('pool_log')
            });
        }

        this.logger = options.logger;
    }

    _get (key) {
        return this[key];
    }

    _redisCmd (func, args, callback) {
        const db = this._get('redis_db');

        this.pool.acquire(db, (err, client) => {
            if (err) {
                this.log(err, 'adquiring client');
                return callback(err);
            }

            client[func](...args, (err, data) => {
                this.pool.release(db, client);

                if (err) {
                    this.log(err, func, args);
                    return callback(err);
                }

                return callback(null, data);
            });
        });
    }

    // API: Load a saved MapStore object, renewing expiration time
    load (id, callback) {
        const key = this._get('redis_key_mapcfg_prefix') + id;
        const exp = this._get('expire_time');

        this._redisCmd('GET', [key], (err, json) => {
            if (err) {
                return callback(err);
            }

            if (!json) {
                const mapConfigError = new Error(`Invalid or nonexistent map configuration token '${id}'`);
                return callback(mapConfigError);
            }

            let mapConfig;
            try {
                const serializedMapConfig = JSON.parse(json);
                mapConfig = MapConfig.create(serializedMapConfig);
            } catch (error) {
                return callback(error);
            }

            this._redisCmd('EXPIRE', [key, exp], (err) => {
                if (err) {
                    this.log(err);
                }

                return callback(null, mapConfig);
            });
        });
    }

    // API: store map to redis
    save (map, callback) {
        const id = map.id();
        const key = this._get('redis_key_mapcfg_prefix') + id;
        const exp = this._get('expire_time');

        this._redisCmd('SETNX', [key, map.serialize()], (err, wasNew) => {
            if (err) {
                return callback(err);
            }

            this._redisCmd('EXPIRE', [key, exp], (err) => {
                if (err) {
                    this.log(err);
                }

                return callback(null, id, !wasNew);
            });
        });
    }

    // API: delete map from store
    del (id, callback) {
        const key = this._get('redis_key_mapcfg_prefix') + id;
        this._redisCmd('DEL', [key], callback);
    }

    log (err, command, args = []) {
        if (this.logger) {
            const log = {
                error: err.message,
                command,
                args
            };
            this.logger.error(JSON.stringify(log));
        }
    }
};

{'fraction_non_alphanumeric': 0.12612612612612611, 'fraction_numerical': 0.006723141051499261, 'mean_word_length': 1.405562742561449, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 2, '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': '6477678', 'n_tokens_mistral': 2186, 'n_tokens_neox': 2045, 'n_words': 459}