mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-20 19:26:00 +00:00
cache improvement solves #219
This commit is contained in:
@@ -48,7 +48,7 @@ module.exports = {
|
|||||||
async write({ data }) {
|
async write({ data }) {
|
||||||
const fileName = path.join(datadir(), 'query-history.jsonl');
|
const fileName = path.join(datadir(), 'query-history.jsonl');
|
||||||
await fs.appendFile(fileName, JSON.stringify(data) + '\n');
|
await fs.appendFile(fileName, JSON.stringify(data) + '\n');
|
||||||
socket.emitChanged('query-history-changed');
|
socket.emit('query-history-changed');
|
||||||
return 'OK';
|
return 'OK';
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
emitChanged(key) {
|
emitChanged(key) {
|
||||||
this.emit('clean-cache', key);
|
this.emit('changed-cache', key);
|
||||||
this.emit(key);
|
// this.emit(key);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import resolveApi, { resolveApiHeaders } from './resolveApi';
|
import resolveApi, { resolveApiHeaders } from './resolveApi';
|
||||||
import { writable } from 'svelte/store';
|
import { writable } from 'svelte/store';
|
||||||
import { cacheClean } from './cache';
|
// import { cacheClean } from './cache';
|
||||||
import getElectron from './getElectron';
|
import getElectron from './getElectron';
|
||||||
// import socket from './socket';
|
// import socket from './socket';
|
||||||
|
|
||||||
let eventSource;
|
let eventSource;
|
||||||
let cacheCleanerRegistered;
|
// let cacheCleanerRegistered;
|
||||||
|
|
||||||
function wantEventSource() {
|
function wantEventSource() {
|
||||||
if (!eventSource) {
|
if (!eventSource) {
|
||||||
@@ -60,10 +60,10 @@ export function apiOn(event: string, handler: Function) {
|
|||||||
eventSource.addEventListener(event, apiHandlers.get(handler));
|
eventSource.addEventListener(event, apiHandlers.get(handler));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cacheCleanerRegistered) {
|
// if (!cacheCleanerRegistered) {
|
||||||
cacheCleanerRegistered = true;
|
// cacheCleanerRegistered = true;
|
||||||
apiOn('clean-cache', reloadTrigger => cacheClean(reloadTrigger));
|
// apiOn('clean-cache', reloadTrigger => cacheClean(reloadTrigger));
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
export function apiOff(event: string, handler: Function) {
|
export function apiOff(event: string, handler: Function) {
|
||||||
|
|||||||
@@ -1,40 +1,120 @@
|
|||||||
|
import { apiOn } from './api';
|
||||||
import getAsArray from './getAsArray';
|
import getAsArray from './getAsArray';
|
||||||
|
|
||||||
let cachedByKey = {};
|
const cachedByKey = {};
|
||||||
let cachedPromisesByKey = {};
|
const cachedPromisesByKey = {};
|
||||||
const cachedKeysByReloadTrigger = {};
|
const cachedKeysByReloadTrigger = {};
|
||||||
|
const subscriptionsByReloadTrigger = {};
|
||||||
|
const cacheGenerationByKey = {};
|
||||||
|
|
||||||
export function cacheGet(key) {
|
let cacheGeneration = 0;
|
||||||
|
|
||||||
|
function cacheGet(key) {
|
||||||
return cachedByKey[key];
|
return cachedByKey[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function cacheSet(key, value, reloadTrigger) {
|
function addCacheKeyToReloadTrigger(cacheKey, reloadTrigger) {
|
||||||
cachedByKey[key] = value;
|
|
||||||
for (const item of getAsArray(reloadTrigger)) {
|
for (const item of getAsArray(reloadTrigger)) {
|
||||||
if (!(item in cachedKeysByReloadTrigger)) {
|
if (!(item in cachedKeysByReloadTrigger)) {
|
||||||
cachedKeysByReloadTrigger[item] = [];
|
cachedKeysByReloadTrigger[item] = [];
|
||||||
}
|
}
|
||||||
cachedKeysByReloadTrigger[item].push(key);
|
cachedKeysByReloadTrigger[item].push(cacheKey);
|
||||||
}
|
}
|
||||||
delete cachedPromisesByKey[key];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function cacheClean(reloadTrigger) {
|
function cacheSet(cacheKey, value, reloadTrigger, generation) {
|
||||||
|
cachedByKey[cacheKey] = value;
|
||||||
|
addCacheKeyToReloadTrigger(cacheKey, reloadTrigger);
|
||||||
|
delete cachedPromisesByKey[cacheKey];
|
||||||
|
cacheGenerationByKey[cacheKey] = generation;
|
||||||
|
}
|
||||||
|
|
||||||
|
function cacheClean(reloadTrigger) {
|
||||||
|
cacheGeneration += 1;
|
||||||
for (const item of getAsArray(reloadTrigger)) {
|
for (const item of getAsArray(reloadTrigger)) {
|
||||||
const keys = cachedKeysByReloadTrigger[item];
|
const keys = cachedKeysByReloadTrigger[item];
|
||||||
if (keys) {
|
if (keys) {
|
||||||
for (const key of keys) {
|
for (const key of keys) {
|
||||||
delete cachedByKey[key];
|
delete cachedByKey[key];
|
||||||
delete cachedPromisesByKey[key];
|
delete cachedPromisesByKey[key];
|
||||||
|
cacheGenerationByKey[key] = cacheGeneration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delete cachedKeysByReloadTrigger[item];
|
delete cachedKeysByReloadTrigger[item];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCachedPromise(key, func) {
|
function getCachedPromise(reloadTrigger, cacheKey, func) {
|
||||||
if (key in cachedPromisesByKey) return cachedPromisesByKey[key];
|
if (cacheKey in cachedPromisesByKey) return cachedPromisesByKey[cacheKey];
|
||||||
const promise = func();
|
const promise = func();
|
||||||
cachedPromisesByKey[key] = promise;
|
cachedPromisesByKey[cacheKey] = promise;
|
||||||
|
addCacheKeyToReloadTrigger(cacheKey, reloadTrigger);
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function acquireCacheGeneration() {
|
||||||
|
cacheGeneration += 1;
|
||||||
|
return cacheGeneration;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCacheGenerationForKey(cacheKey) {
|
||||||
|
return cacheGenerationByKey[cacheKey] || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function loadCachedValue(reloadTrigger, cacheKey, func) {
|
||||||
|
const fromCache = cacheGet(cacheKey);
|
||||||
|
if (fromCache) {
|
||||||
|
return fromCache;
|
||||||
|
} else {
|
||||||
|
const generation = acquireCacheGeneration();
|
||||||
|
try {
|
||||||
|
const res = await getCachedPromise(reloadTrigger, cacheKey, func);
|
||||||
|
if (getCacheGenerationForKey(cacheKey) > generation) {
|
||||||
|
return cacheGet(cacheKey) || res;
|
||||||
|
} else {
|
||||||
|
cacheSet(cacheKey, res, reloadTrigger, generation);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error when using cached promise', err);
|
||||||
|
cacheClean(cacheKey);
|
||||||
|
const res = await func();
|
||||||
|
cacheSet(cacheKey, res, reloadTrigger, generation);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function subscribeCacheChange(reloadTrigger, cacheKey, reloadHandler) {
|
||||||
|
for (const item of getAsArray(reloadTrigger)) {
|
||||||
|
if (!subscriptionsByReloadTrigger[item]) {
|
||||||
|
subscriptionsByReloadTrigger[item] = [];
|
||||||
|
}
|
||||||
|
subscriptionsByReloadTrigger[item].push(reloadHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function unsubscribeCacheChange(reloadTrigger, cacheKey, reloadHandler) {
|
||||||
|
for (const item of getAsArray(reloadTrigger)) {
|
||||||
|
if (subscriptionsByReloadTrigger[item]) {
|
||||||
|
subscriptionsByReloadTrigger[item] = subscriptionsByReloadTrigger[item].filter(x => x != reloadHandler);
|
||||||
|
}
|
||||||
|
if (subscriptionsByReloadTrigger[item].length == 0) {
|
||||||
|
delete subscriptionsByReloadTrigger[item];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function dispatchCacheChange(reloadTrigger) {
|
||||||
|
cacheClean(reloadTrigger);
|
||||||
|
|
||||||
|
for (const item of getAsArray(reloadTrigger)) {
|
||||||
|
if (subscriptionsByReloadTrigger[item]) {
|
||||||
|
for (const handler of subscriptionsByReloadTrigger[item]) {
|
||||||
|
handler();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apiOn('changed-cache', reloadTrigger => dispatchCacheChange(reloadTrigger));
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { cacheGet, cacheSet, getCachedPromise } from './cache';
|
import { loadCachedValue, subscribeCacheChange, unsubscribeCacheChange } from './cache';
|
||||||
import stableStringify from 'json-stable-stringify';
|
import stableStringify from 'json-stable-stringify';
|
||||||
import { cacheClean } from './cache';
|
|
||||||
import getAsArray from './getAsArray';
|
import getAsArray from './getAsArray';
|
||||||
import { DatabaseInfo } from 'dbgate-types';
|
import { DatabaseInfo } from 'dbgate-types';
|
||||||
import { derived } from 'svelte/store';
|
import { derived } from 'svelte/store';
|
||||||
@@ -123,7 +122,7 @@ const appFilesLoader = ({ folder }) => ({
|
|||||||
|
|
||||||
const usedAppsLoader = ({ conid, database }) => ({
|
const usedAppsLoader = ({ conid, database }) => ({
|
||||||
url: 'apps/get-used-apps',
|
url: 'apps/get-used-apps',
|
||||||
params: { },
|
params: {},
|
||||||
reloadTrigger: `used-apps-changed`,
|
reloadTrigger: `used-apps-changed`,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -172,11 +171,7 @@ async function getCore(loader, args) {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fromCache = cacheGet(key);
|
const res = await loadCachedValue(reloadTrigger, key, doLoad);
|
||||||
if (fromCache) return fromCache;
|
|
||||||
const res = await getCachedPromise(key, doLoad);
|
|
||||||
|
|
||||||
cacheSet(key, res, reloadTrigger);
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,77 +182,20 @@ function useCore(loader, args) {
|
|||||||
return {
|
return {
|
||||||
subscribe: onChange => {
|
subscribe: onChange => {
|
||||||
async function handleReload() {
|
async function handleReload() {
|
||||||
async function doLoad() {
|
const res = await getCore(loader, args);
|
||||||
const resp = await apiCall(url, params);
|
onChange(res);
|
||||||
const res = (transform || (x => x))(resp);
|
|
||||||
if (onLoaded) onLoaded(res);
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cacheKey) {
|
|
||||||
const fromCache = cacheGet(cacheKey);
|
|
||||||
if (fromCache) {
|
|
||||||
onChange(fromCache);
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
const res = await getCachedPromise(cacheKey, doLoad);
|
|
||||||
cacheSet(cacheKey, res, reloadTrigger);
|
|
||||||
onChange(res);
|
|
||||||
} catch (err) {
|
|
||||||
console.error(`Error when using cached promise ${url}`, err);
|
|
||||||
cacheClean(cacheKey);
|
|
||||||
const res = await doLoad();
|
|
||||||
cacheSet(cacheKey, res, reloadTrigger);
|
|
||||||
onChange(res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const res = await doLoad();
|
|
||||||
onChange(res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if (reloadTrigger && !socket) {
|
|
||||||
// console.error('Socket not available, reloadTrigger not planned');
|
|
||||||
// }
|
|
||||||
handleReload();
|
handleReload();
|
||||||
|
|
||||||
if (reloadTrigger) {
|
if (reloadTrigger) {
|
||||||
for (const item of getAsArray(reloadTrigger)) {
|
subscribeCacheChange(reloadTrigger, cacheKey, handleReload);
|
||||||
apiOn(item, handleReload);
|
|
||||||
}
|
|
||||||
return () => {
|
return () => {
|
||||||
for (const item of getAsArray(reloadTrigger)) {
|
unsubscribeCacheChange(reloadTrigger, cacheKey, handleReload);
|
||||||
apiOff(item, handleReload);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// const useTrack = track => ({
|
|
||||||
// subscribe: onChange => {
|
|
||||||
// onChange('TRACK ' + track);
|
|
||||||
// if (track) {
|
|
||||||
// const handle = setInterval(() => onChange('TRACK ' + track + ';' + new Date()), 1000);
|
|
||||||
// // console.log("ON", track);
|
|
||||||
// const oldTrack = track;
|
|
||||||
// return () => {
|
|
||||||
// clearInterval(handle);
|
|
||||||
// // console.log("OFF", oldTrack);
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
|
|
||||||
// const res = useFetch({
|
|
||||||
// url,
|
|
||||||
// params,
|
|
||||||
// reloadTrigger,
|
|
||||||
// cacheKey,
|
|
||||||
// transform,
|
|
||||||
// });
|
|
||||||
|
|
||||||
// return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @returns {Promise<import('dbgate-types').DatabaseInfo>} */
|
/** @returns {Promise<import('dbgate-types').DatabaseInfo>} */
|
||||||
@@ -439,8 +377,6 @@ export function useAppFolders(args = {}) {
|
|||||||
return useCore(appFoldersLoader, args);
|
return useCore(appFoldersLoader, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export function getUsedApps(args = {}) {
|
export function getUsedApps(args = {}) {
|
||||||
return getCore(usedAppsLoader, args);
|
return getCore(usedAppsLoader, args);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user