From 667e834c0f8b2e6dd90edb1fd3cea67f60edf5cb Mon Sep 17 00:00:00 2001 From: Tianle Huang Date: Mon, 8 Apr 2024 05:30:33 +0000 Subject: [PATCH] Improve dynamic config Signed-off-by: Tianle Huang --- config/opensearch_dashboards.yml | 4 +-- .../server/opensearch_config_client.ts | 26 ++++++++++++++++--- .../application_config/server/plugin.ts | 21 ++++++++++++--- .../csp_handler/server/csp_handlers.ts | 2 +- 4 files changed, 44 insertions(+), 9 deletions(-) diff --git a/config/opensearch_dashboards.yml b/config/opensearch_dashboards.yml index 40d643b014fd..67ff5a3d45a1 100644 --- a/config/opensearch_dashboards.yml +++ b/config/opensearch_dashboards.yml @@ -34,11 +34,11 @@ # opensearchDashboards.configIndex: ".opensearch_dashboards_config" # Set the value of this setting to true to enable plugin application config. By default it is disabled. -# application_config.enabled: false +application_config.enabled: true # Set the value of this setting to true to enable plugin CSP handler. By default it is disabled. # It requires the application config plugin as its dependency. -# csp_handler.enabled: false +csp_handler.enabled: true # The default application to load. #opensearchDashboards.defaultAppId: "home" diff --git a/src/plugins/application_config/server/opensearch_config_client.ts b/src/plugins/application_config/server/opensearch_config_client.ts index 9103919c396f..a2199cf8e535 100644 --- a/src/plugins/application_config/server/opensearch_config_client.ts +++ b/src/plugins/application_config/server/opensearch_config_client.ts @@ -3,8 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +import LRUCache from 'lru-cache'; import { IScopedClusterClient, Logger } from '../../../../src/core/server'; - import { ConfigurationClient } from './types'; import { validate } from './string_utils'; @@ -12,32 +12,48 @@ export class OpenSearchConfigurationClient implements ConfigurationClient { private client: IScopedClusterClient; private configurationIndexName: string; private readonly logger: Logger; + private cache: LRUCache; constructor( scopedClusterClient: IScopedClusterClient, configurationIndexName: string, - logger: Logger + logger: Logger, + cache: LRUCache ) { this.client = scopedClusterClient; this.configurationIndexName = configurationIndexName; this.logger = logger; + this.cache = cache; } async getEntityConfig(entity: string) { const entityValidated = validate(entity, this.logger); + const cachedValue = this.cache.get(entityValidated); + + if (this.cache.has(entityValidated)) { + this.logger.info(`found value ${cachedValue} from cache`); + return cachedValue; + } + + this.logger.info('No value found in cache'); + try { const data = await this.client.asInternalUser.get({ index: this.configurationIndexName, id: entityValidated, }); + const value = data?.body?._source?.value || ''; + + this.cache.set(entityValidated, value); - return data?.body?._source?.value || ''; + return value; } catch (e) { const errorMessage = `Failed to get entity ${entityValidated} due to error ${e}`; this.logger.error(errorMessage); + this.cache.set(entityValidated, ''); throw e; } } @@ -55,6 +71,8 @@ export class OpenSearchConfigurationClient implements ConfigurationClient { }, }); + this.cache.set(entityValidated, newValueValidated); + return newValueValidated; } catch (e) { const errorMessage = `Failed to update entity ${entityValidated} with newValue ${newValueValidated} due to error ${e}`; @@ -74,6 +92,8 @@ export class OpenSearchConfigurationClient implements ConfigurationClient { id: entityValidated, }); + this.cache.del(entityValidated); + return entityValidated; } catch (e) { if (e?.body?.error?.type === 'index_not_found_exception') { diff --git a/src/plugins/application_config/server/plugin.ts b/src/plugins/application_config/server/plugin.ts index d0bd2ab42270..a4193c4e5f2d 100644 --- a/src/plugins/application_config/server/plugin.ts +++ b/src/plugins/application_config/server/plugin.ts @@ -6,6 +6,7 @@ import { Observable } from 'rxjs'; import { first } from 'rxjs/operators'; +import LRUCache from 'lru-cache'; import { PluginInitializerContext, CoreSetup, @@ -14,6 +15,8 @@ import { Logger, IScopedClusterClient, SharedGlobalConfig, + OpenSearchDashboardsRequest, + IClusterClient, } from '../../../core/server'; import { @@ -31,11 +34,20 @@ export class ApplicationConfigPlugin private configurationClient: ConfigurationClient; private configurationIndexName: string; + private clusterClient: IClusterClient; + + private cache: LRUCache; constructor(initializerContext: PluginInitializerContext) { this.logger = initializerContext.logger.get(); this.config$ = initializerContext.config.legacy.globalConfig$; this.configurationIndexName = ''; + this.clusterClient = null; + + this.cache = new LRUCache({ + max: 30, + maxAge: 100 * 1000, + }); } private registerConfigurationClient(configurationClient: ConfigurationClient) { @@ -50,15 +62,16 @@ export class ApplicationConfigPlugin this.configurationClient = configurationClient; } - private getConfigurationClient(scopedClusterClient: IScopedClusterClient): ConfigurationClient { + private getConfigurationClient(request: OpenSearchDashboardsRequest): ConfigurationClient { if (this.configurationClient) { return this.configurationClient; } const openSearchConfigurationClient = new OpenSearchConfigurationClient( - scopedClusterClient, + this.clusterClient.asScoped(request), this.configurationIndexName, - this.logger + this.logger, + this.cache ); return openSearchConfigurationClient; @@ -81,6 +94,8 @@ export class ApplicationConfigPlugin } public start(core: CoreStart) { + this.clusterClient = core.opensearch.client; + return {}; } diff --git a/src/plugins/csp_handler/server/csp_handlers.ts b/src/plugins/csp_handler/server/csp_handlers.ts index 3bfa90115518..77ecf2af4377 100644 --- a/src/plugins/csp_handler/server/csp_handlers.ts +++ b/src/plugins/csp_handler/server/csp_handlers.ts @@ -49,7 +49,7 @@ export function createCspRulesPreResponseHandler( const [coreStart] = await core.getStartServices(); - const client = getConfigurationClient(coreStart.opensearch.client.asScoped(request)); + const client = getConfigurationClient(request); const cspRules = await client.getEntityConfig(CSP_RULES_CONFIG_KEY, { headers: request.headers,