mirror of
https://github.com/dbgate/dbgate
synced 2024-11-07 20:26:23 +00:00
cache improvement solves #219
This commit is contained in:
parent
4e221ecd3a
commit
b3b7bd0f83
@ -48,7 +48,7 @@ module.exports = {
|
||||
async write({ data }) {
|
||||
const fileName = path.join(datadir(), 'query-history.jsonl');
|
||||
await fs.appendFile(fileName, JSON.stringify(data) + '\n');
|
||||
socket.emitChanged('query-history-changed');
|
||||
socket.emit('query-history-changed');
|
||||
return 'OK';
|
||||
},
|
||||
};
|
||||
|
@ -23,7 +23,7 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
emitChanged(key) {
|
||||
this.emit('clean-cache', key);
|
||||
this.emit(key);
|
||||
this.emit('changed-cache', key);
|
||||
// this.emit(key);
|
||||
},
|
||||
};
|
||||
|
@ -1,11 +1,11 @@
|
||||
import resolveApi, { resolveApiHeaders } from './resolveApi';
|
||||
import { writable } from 'svelte/store';
|
||||
import { cacheClean } from './cache';
|
||||
// import { cacheClean } from './cache';
|
||||
import getElectron from './getElectron';
|
||||
// import socket from './socket';
|
||||
|
||||
let eventSource;
|
||||
let cacheCleanerRegistered;
|
||||
// let cacheCleanerRegistered;
|
||||
|
||||
function wantEventSource() {
|
||||
if (!eventSource) {
|
||||
@ -60,10 +60,10 @@ export function apiOn(event: string, handler: Function) {
|
||||
eventSource.addEventListener(event, apiHandlers.get(handler));
|
||||
}
|
||||
|
||||
if (!cacheCleanerRegistered) {
|
||||
cacheCleanerRegistered = true;
|
||||
apiOn('clean-cache', reloadTrigger => cacheClean(reloadTrigger));
|
||||
}
|
||||
// if (!cacheCleanerRegistered) {
|
||||
// cacheCleanerRegistered = true;
|
||||
// apiOn('clean-cache', reloadTrigger => cacheClean(reloadTrigger));
|
||||
// }
|
||||
}
|
||||
|
||||
export function apiOff(event: string, handler: Function) {
|
||||
|
@ -1,40 +1,120 @@
|
||||
import { apiOn } from './api';
|
||||
import getAsArray from './getAsArray';
|
||||
|
||||
let cachedByKey = {};
|
||||
let cachedPromisesByKey = {};
|
||||
const cachedByKey = {};
|
||||
const cachedPromisesByKey = {};
|
||||
const cachedKeysByReloadTrigger = {};
|
||||
const subscriptionsByReloadTrigger = {};
|
||||
const cacheGenerationByKey = {};
|
||||
|
||||
export function cacheGet(key) {
|
||||
let cacheGeneration = 0;
|
||||
|
||||
function cacheGet(key) {
|
||||
return cachedByKey[key];
|
||||
}
|
||||
|
||||
export function cacheSet(key, value, reloadTrigger) {
|
||||
cachedByKey[key] = value;
|
||||
function addCacheKeyToReloadTrigger(cacheKey, reloadTrigger) {
|
||||
for (const item of getAsArray(reloadTrigger)) {
|
||||
if (!(item in cachedKeysByReloadTrigger)) {
|
||||
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)) {
|
||||
const keys = cachedKeysByReloadTrigger[item];
|
||||
if (keys) {
|
||||
for (const key of keys) {
|
||||
delete cachedByKey[key];
|
||||
delete cachedPromisesByKey[key];
|
||||
cacheGenerationByKey[key] = cacheGeneration;
|
||||
}
|
||||
}
|
||||
delete cachedKeysByReloadTrigger[item];
|
||||
}
|
||||
}
|
||||
|
||||
export function getCachedPromise(key, func) {
|
||||
if (key in cachedPromisesByKey) return cachedPromisesByKey[key];
|
||||
function getCachedPromise(reloadTrigger, cacheKey, func) {
|
||||
if (cacheKey in cachedPromisesByKey) return cachedPromisesByKey[cacheKey];
|
||||
const promise = func();
|
||||
cachedPromisesByKey[key] = promise;
|
||||
cachedPromisesByKey[cacheKey] = promise;
|
||||
addCacheKeyToReloadTrigger(cacheKey, reloadTrigger);
|
||||
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 { cacheGet, cacheSet, getCachedPromise } from './cache';
|
||||
import { loadCachedValue, subscribeCacheChange, unsubscribeCacheChange } from './cache';
|
||||
import stableStringify from 'json-stable-stringify';
|
||||
import { cacheClean } from './cache';
|
||||
import getAsArray from './getAsArray';
|
||||
import { DatabaseInfo } from 'dbgate-types';
|
||||
import { derived } from 'svelte/store';
|
||||
@ -123,7 +122,7 @@ const appFilesLoader = ({ folder }) => ({
|
||||
|
||||
const usedAppsLoader = ({ conid, database }) => ({
|
||||
url: 'apps/get-used-apps',
|
||||
params: { },
|
||||
params: {},
|
||||
reloadTrigger: `used-apps-changed`,
|
||||
});
|
||||
|
||||
@ -172,11 +171,7 @@ async function getCore(loader, args) {
|
||||
return res;
|
||||
}
|
||||
|
||||
const fromCache = cacheGet(key);
|
||||
if (fromCache) return fromCache;
|
||||
const res = await getCachedPromise(key, doLoad);
|
||||
|
||||
cacheSet(key, res, reloadTrigger);
|
||||
const res = await loadCachedValue(reloadTrigger, key, doLoad);
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -187,77 +182,20 @@ function useCore(loader, args) {
|
||||
return {
|
||||
subscribe: onChange => {
|
||||
async function handleReload() {
|
||||
async function doLoad() {
|
||||
const resp = await apiCall(url, params);
|
||||
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);
|
||||
}
|
||||
const res = await getCore(loader, args);
|
||||
onChange(res);
|
||||
}
|
||||
|
||||
// if (reloadTrigger && !socket) {
|
||||
// console.error('Socket not available, reloadTrigger not planned');
|
||||
// }
|
||||
handleReload();
|
||||
|
||||
if (reloadTrigger) {
|
||||
for (const item of getAsArray(reloadTrigger)) {
|
||||
apiOn(item, handleReload);
|
||||
}
|
||||
subscribeCacheChange(reloadTrigger, cacheKey, handleReload);
|
||||
return () => {
|
||||
for (const item of getAsArray(reloadTrigger)) {
|
||||
apiOff(item, handleReload);
|
||||
}
|
||||
unsubscribeCacheChange(reloadTrigger, cacheKey, 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>} */
|
||||
@ -439,8 +377,6 @@ export function useAppFolders(args = {}) {
|
||||
return useCore(appFoldersLoader, args);
|
||||
}
|
||||
|
||||
|
||||
|
||||
export function getUsedApps(args = {}) {
|
||||
return getCore(usedAppsLoader, args);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user