import setupLogger from '@/config/logger' ; import { IExplorer } from '@/services/datastores/base/datastore' ; import ClusterNotFoundError from '@/services/datastores/base/errors/ClusterNotFoundError' ; import { DatastoreType } from '@/typings/enums' ; import { IClusterDefinition } from '@/typings/typings' ; import LRU from 'lru-cache' ; const logger = setupLogger ( module ) ; const TTL = 1000 * 60 * 10 ; singleton export default class ExplorerCache { private discoveryCache = new Map < string , IClusterDefinition > ( ) ; private explorerCache : LRU < string , IExplorer > ; public constructor ( ) { this . explorerCache = new LRU < string , IExplorer > ( { maxAge : TTL , noDisposeOnSet : true , dispose : async ( key , explorer ) => { logger . info ( `evicting ${ key }` ) ; if ( explorer ) { logger . info ( `  shutdown connections to: ${ key }` ) ; try { await explorer . shutdown ( ) ; } catch ( err ) { logger . error ( `    shutdown of ${ key } connections failed with: ${ err . message }` ) ; } } } , } ) ; setInterval ( ( ) => { logger . info ( 'pruning explorer cache...' ) ; this . explorerCache . prune ( ) ; } , TTL ) ; } param datastoreType param clusters public updateClusters ( datastoreType , clusters ) { clusters . forEach ( ( cluster ) => { const key = this . getKey ( datastoreType , cluster . name , cluster . region , cluster . env ) ; this . discoveryCache . set ( key , cluster ) ; } ) ; } param datastoreType param clusterName param region param env public peekExplorer ( datastoreType , clusterName , region , env ) { const key = this . getKey ( datastoreType , clusterName , region , env ) ; const cluster = this . discoveryCache . get ( key ) ; if ( ! cluster ) { logger . error ( 'cluster not present in the cache. server may still be starting up...' ) ; throw new ClusterNotFoundError ( clusterName , region , env ) ; } return this . explorerCache . peek ( key ) ; } param datastoreType param clusterName param region param env param explorerCb public async getExplorer ( datastoreType , clusterName , region , env , createExplorerCb ) { const key = this . getKey ( datastoreType , clusterName , region , env ) ; const cluster = this . discoveryCache . get ( key ) ; if ( ! cluster ) { logger . error ( 'cluster not present in the cache. server may still be starting up...' ) ; throw new ClusterNotFoundError ( clusterName , region , env ) ; } let explorer = this . explorerCache . get ( key ) ; if ( ! explorer ) { const clusterDescription = `${ clusterName }.${ region }.${ env }` ; logger . info ( `no existing client connection for cluster: ${ clusterDescription }` ) ; explorer = await createExplorerCb ( cluster ) ; } else { logger . info ( 'using existing client connection...' ) ; } this . explorerCache . set ( key , explorer ) ; return explorer ; } private getKey ( type , clusterName , region , env ) { return [ type , env , region , clusterName ] . map ( ( item ) => item . toLowerCase ( ) ) . join ( ':::' ) ; } }