From a77e23bc71f178b8f195d53dc573db715cf43893 Mon Sep 17 00:00:00 2001 From: Kai Huang Date: Tue, 10 Mar 2026 09:25:59 -0700 Subject: [PATCH 1/2] Fix memory leak: ExecutionEngine recreated per query appending to global function registry The OpenSearchExecutionEngine was not scoped as @Singleton in the Guice module, causing a new instance to be created for every SQL/PPL request via injector.getInstance(SQLService.class). Each construction called registerOpenSearchFunctions(), which appended entries to the static PPLFuncImpTable.INSTANCE.externalFunctionRegistry via compute(). After N queries, the GEOIP function list contained N entries (each holding GeoIpFunction, SqlIdentifier, CalciteFuncSignature, PPLTypeChecker, etc.), leading to ~2GB heap retention after ~730K queries. Fix: 1. Add @Singleton to the ExecutionEngine provider in OpenSearchPluginModule so only one instance is created per Guice injector. 2. Change registerExternalOperator() from compute()/append to put()/replace so repeated registrations overwrite instead of accumulating. Signed-off-by: Kai Huang Signed-off-by: Kai Huang --- .../sql/expression/function/PPLFuncImpTable.java | 9 ++------- .../sql/plugin/config/OpenSearchPluginModule.java | 1 + 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java b/core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java index fe364c7b7c4..4ff8e1ce25f 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java @@ -423,14 +423,9 @@ public void registerExternalOperator(BuiltinFunctionName functionName, SqlOperat functionName.name(), operator instanceof SqlUserDefinedFunction); CalciteFuncSignature signature = new CalciteFuncSignature(functionName.getName(), typeChecker); - externalFunctionRegistry.compute( + externalFunctionRegistry.put( functionName, - (name, existingList) -> { - List> list = - existingList == null ? new ArrayList<>() : new ArrayList<>(existingList); - list.add(Pair.of(signature, (builder, args) -> builder.makeCall(operator, args))); - return list; - }); + List.of(Pair.of(signature, (FunctionImp) (builder, args) -> builder.makeCall(operator, args)))); } /** diff --git a/plugin/src/main/java/org/opensearch/sql/plugin/config/OpenSearchPluginModule.java b/plugin/src/main/java/org/opensearch/sql/plugin/config/OpenSearchPluginModule.java index 8027301073f..35504dd83c2 100644 --- a/plugin/src/main/java/org/opensearch/sql/plugin/config/OpenSearchPluginModule.java +++ b/plugin/src/main/java/org/opensearch/sql/plugin/config/OpenSearchPluginModule.java @@ -58,6 +58,7 @@ public StorageEngine storageEngine(OpenSearchClient client, Settings settings) { } @Provides + @Singleton public ExecutionEngine executionEngine( OpenSearchClient client, ExecutionProtector protector, PlanSerializer planSerializer) { return new OpenSearchExecutionEngine(client, protector, planSerializer); From 1d661acfce7f6e04384b421a2d5da495cca747b6 Mon Sep 17 00:00:00 2001 From: Kai Huang Date: Tue, 10 Mar 2026 09:35:49 -0700 Subject: [PATCH 2/2] Apply spotless formatting Signed-off-by: Kai Huang Signed-off-by: Kai Huang --- .../opensearch/sql/expression/function/PPLFuncImpTable.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java b/core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java index 4ff8e1ce25f..30d7c055470 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java @@ -425,7 +425,8 @@ public void registerExternalOperator(BuiltinFunctionName functionName, SqlOperat CalciteFuncSignature signature = new CalciteFuncSignature(functionName.getName(), typeChecker); externalFunctionRegistry.put( functionName, - List.of(Pair.of(signature, (FunctionImp) (builder, args) -> builder.makeCall(operator, args)))); + List.of( + Pair.of(signature, (FunctionImp) (builder, args) -> builder.makeCall(operator, args)))); } /**