import cache from "memory-cache" ; import promClient , { Registry } from "prom-client" ; import Debug from "debug" ; import Bluebird from "bluebird" ; import { Yildiz } from "./Yildiz" ; import { Metrics } from "./metrics/Metrics" ; import { ServiceConfig } from "../interfaces/ServiceConfig" ; const CACHE_TIME_MS = 15 * 60 * 1000 ; const REFRESH_DIFF_MS = CACHE_TIME_MS * 0.9 ; const FACTORY_METRICS_PREFIX = "__FACTORY__" ; const promRegistry = promClient . Registry ; const debug = Debug ( "yildiz:factory" ) ; export interface BlockedInstance { [ key ] : Bluebird < Yildiz > ; } export interface UpdatedTimestamp { [ key ] : number ; } export interface MetricsRegisters { [ key ] : Registry ; } export interface StatsObjects { [ key ] : { up : boolean ; } ; } export class YildizFactory { private baseConfig : ServiceConfig ; private cache : cache . CacheClass < string , Yildiz > ; private blocked : BlockedInstance ; private updated : UpdatedTimestamp ; public metrics : Metrics ; private metricsRegisters : MetricsRegisters ; constructor ( config ) { this . baseConfig = config ; this . cache = new cache . Cache ( ) ; this . blocked = { } ; this . updated = { } ; this . metricsRegisters = { } ; this . metrics = new Metrics ( FACTORY_METRICS_PREFIX ) ; this . metrics . registerDefault ( ) ; this . metricsRegisters [ FACTORY_METRICS_PREFIX ] = this . metrics . getRegister ( ) ; } public async getStats ( ) { return ( await Bluebird . all ( this . cache . keys ( ) . map ( async ( prefix ) => ( { prefix , stats : await this . cache . get ( prefix ) . getStats ( ) , } ) ) ) ) . reduce ( ( val , current ) => { const { prefix , stats } = current ; val [ prefix ] = stats ; return val ; } , { } ) ; } private storeInstance ( prefix , instance ) { const cacheTime = this . baseConfig . instanceTimeoutInSec ? this . baseConfig . instanceTimeoutInSec * 1000 : CACHE_TIME_MS ; this . cache . put ( prefix , instance , cacheTime , ( ) => { try { debug ( `factory instance timeout reached for ${ prefix }, closing and removing from cache.` ) ; delete this . updated [ prefix ] ; instance . close ( ) ; } catch ( error ) { debug ( "cache ran out, closing error" , error ) ; } } ) ; this . updated [ prefix ] = Date . now ( ) ; } private prolongCachetime ( prefix , instance ) { if ( ! this . updated [ prefix ] ) { return ; } const diff = Date . now ( ) - this . updated [ prefix ] ; if ( diff < REFRESH_DIFF_MS ) { return ; } this . storeInstance ( prefix , instance ) ; } public async get ( prefix = "kc0" ) { if ( this . blocked [ prefix ] ) { return await this . blocked [ prefix ] ; } let instance = this . cache . get ( prefix ) ; if ( instance ) { this . prolongCachetime ( prefix , instance ) ; return instance ; } this . blocked [ prefix ] = new Bluebird ( ( resolve , reject ) => { const newInstance = new Yildiz ( prefix , this . baseConfig ) ; newInstance . init ( this . updated [ prefix ] ) . then ( ( ) => { this . storeInstance ( prefix , newInstance ) ; resolve ( newInstance ) ; } ) . catch ( reject ) ; } ) ; instance = await this . blocked [ prefix ] ; delete this . blocked [ prefix ] ; this . metricsRegisters [ prefix ] = instance . metrics . getRegister ( ) ; return instance ; } public getAllMetricsRegisters ( ) { if ( ! Object . keys ( this . metricsRegisters ) . length ) { return [ ] ; } const array = [ ] ; Object . keys ( this . metricsRegisters ) . forEach ( ( prefix ) => { array . push ( this . metricsRegisters [ prefix ] ) ; } ) ; return promRegistry . merge ( array ) ; } public async closeAll ( ) { await Bluebird . all ( this . cache . keys ( ) . map ( ( key ) => this . cache . get ( key ) ) . map ( ( instance ) => instance . close ( ) ) ) ; this . metrics . close ( ) ; this . metricsRegisters = { } ; this . cache . clear ( ) ; } }