|
@@ -1,126 +0,0 @@
|
|
-const SQL = require('sql-template-strings');
|
|
|
|
-const _ = require('lodash');
|
|
|
|
-
|
|
|
|
-const ConnManager = require('../../db/ConnManager');//singleton
|
|
|
|
-
|
|
|
|
-let instance = null;
|
|
|
|
-
|
|
|
|
-//singleton
|
|
|
|
-class ReaderStorage {
|
|
|
|
- constructor() {
|
|
|
|
- if (!instance) {
|
|
|
|
- this.connManager = new ConnManager();
|
|
|
|
- this.storagePool = this.connManager.pool.readerStorage;
|
|
|
|
- this.periodicCleanCache(3*3600*1000);//1 раз в 3 часа
|
|
|
|
-
|
|
|
|
- instance = this;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return instance;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- async doAction(act) {
|
|
|
|
- if (!_.isObject(act.items))
|
|
|
|
- throw new Error('items is not an object');
|
|
|
|
-
|
|
|
|
- let result = {};
|
|
|
|
- switch (act.action) {
|
|
|
|
- case 'check':
|
|
|
|
- result = await this.checkItems(act.items);
|
|
|
|
- break;
|
|
|
|
- case 'get':
|
|
|
|
- result = await this.getItems(act.items);
|
|
|
|
- break;
|
|
|
|
- case 'set':
|
|
|
|
- result = await this.setItems(act.items, act.force);
|
|
|
|
- break;
|
|
|
|
- default:
|
|
|
|
- throw new Error('Unknown action');
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return result;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- async checkItems(items) {
|
|
|
|
- let result = {state: 'success', items: {}};
|
|
|
|
-
|
|
|
|
- const dbh = await this.storagePool.get();
|
|
|
|
- try {
|
|
|
|
- for (const id of Object.keys(items)) {
|
|
|
|
- if (this.cache[id]) {
|
|
|
|
- result.items[id] = this.cache[id];
|
|
|
|
- } else {
|
|
|
|
- const rows = await dbh.all(SQL`SELECT rev FROM storage WHERE id = ${id}`);
|
|
|
|
- const rev = (rows.length && rows[0].rev ? rows[0].rev : 0);
|
|
|
|
- result.items[id] = {rev};
|
|
|
|
- this.cache[id] = result.items[id];
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- } finally {
|
|
|
|
- dbh.ret();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return result;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- async getItems(items) {
|
|
|
|
- let result = {state: 'success', items: {}};
|
|
|
|
-
|
|
|
|
- const dbh = await this.storagePool.get();
|
|
|
|
- try {
|
|
|
|
- for (const id of Object.keys(items)) {
|
|
|
|
- const rows = await dbh.all(SQL`SELECT rev, data FROM storage WHERE id = ${id}`);
|
|
|
|
- const rev = (rows.length && rows[0].rev ? rows[0].rev : 0);
|
|
|
|
- const data = (rows.length && rows[0].data ? rows[0].data : '');
|
|
|
|
- result.items[id] = {rev, data};
|
|
|
|
- }
|
|
|
|
- } finally {
|
|
|
|
- dbh.ret();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return result;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- async setItems(items, force) {
|
|
|
|
- let check = await this.checkItems(items);
|
|
|
|
-
|
|
|
|
- //сначала проверим совпадение ревизий
|
|
|
|
- for (const id of Object.keys(items)) {
|
|
|
|
- if (!_.isString(items[id].data))
|
|
|
|
- throw new Error('items.data is not a string');
|
|
|
|
-
|
|
|
|
- if (!force && check.items[id].rev + 1 !== items[id].rev)
|
|
|
|
- return {state: 'reject', items: check.items};
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- const dbh = await this.storagePool.get();
|
|
|
|
- await dbh.run('BEGIN');
|
|
|
|
- try {
|
|
|
|
- const newRev = {};
|
|
|
|
- for (const id of Object.keys(items)) {
|
|
|
|
- await dbh.run(SQL`INSERT OR REPLACE INTO storage (id, rev, time, data) VALUES (${id}, ${items[id].rev}, strftime('%s','now'), ${items[id].data})`);
|
|
|
|
- newRev[id] = {rev: items[id].rev};
|
|
|
|
- }
|
|
|
|
- await dbh.run('COMMIT');
|
|
|
|
-
|
|
|
|
- Object.assign(this.cache, newRev);
|
|
|
|
- } catch (e) {
|
|
|
|
- await dbh.run('ROLLBACK');
|
|
|
|
- throw e;
|
|
|
|
- } finally {
|
|
|
|
- dbh.ret();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return {state: 'success'};
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- periodicCleanCache(timeout) {
|
|
|
|
- this.cache = {};
|
|
|
|
-
|
|
|
|
- setTimeout(() => {
|
|
|
|
- this.periodicCleanCache(timeout);
|
|
|
|
- }, timeout);
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-module.exports = ReaderStorage;
|
|
|