Skip to content

Commit bf3282c

Browse files
committed
performance: share a single secret store
Loading a Java Keystore can take anywhere from ~0.3s to upwards of 3s, so the pattern of loading one per variable we need to replace adds a significant amount of overhead on pipelines that use these variables, whether or not they are provided by the keystore. By providing a private, constant, lazy singleton, we ensure that we don't incur the cost of repeatedly building the keystore. Fixes #10794
1 parent 00f6f3b commit bf3282c

File tree

3 files changed

+65
-1
lines changed

3 files changed

+65
-1
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
require 'thread' # Mutex
2+
3+
# A [LazySingleton] wraps the result of the provided block,
4+
# which is guaranteed to be called at-most-once, even if the
5+
# block's return value is nil.
6+
class ::LogStash::Util::LazySingleton
7+
8+
def initialize(&block)
9+
@mutex = Mutex.new
10+
@block = block
11+
@instantiated = false
12+
end
13+
14+
def instance
15+
unless @instantiated
16+
@mutex.synchronize do
17+
unless @instantiated
18+
@instance = @block.call
19+
@instantiated = true
20+
end
21+
end
22+
end
23+
24+
return @instance
25+
end
26+
27+
def reset!
28+
@mutex.synchronize do
29+
@instantiated = false
30+
@instance = nil
31+
end
32+
end
33+
end

logstash-core/lib/logstash/util/substitution_variables.rb

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,17 @@
22

33
java_import "org.logstash.secret.store.SecretStoreExt"
44

5+
require_relative 'lazy_singleton'
6+
57
module ::LogStash::Util::SubstitutionVariables
68

79
include LogStash::Util::Loggable
810

911
SUBSTITUTION_PLACEHOLDER_REGEX = /\${(?<name>[a-zA-Z_.][a-zA-Z0-9_.]*)(:(?<default>[^}]*))?}/
1012

13+
SECRET_STORE = ::LogStash::Util::LazySingleton.new { load_secret_store }
14+
private_constant :SECRET_STORE
15+
1116
# Recursive method to replace substitution variable references in parameters
1217
def deep_replace(value)
1318
if value.is_a?(Hash)
@@ -42,7 +47,7 @@ def replace_placeholders(value)
4247
logger.debug("Replacing `#{placeholder}` with actual value")
4348

4449
#check the secret store if it exists
45-
secret_store = SecretStoreExt.getIfExists(LogStash::SETTINGS.get_setting("keystore.file").value, LogStash::SETTINGS.get_setting("keystore.classname").value)
50+
secret_store = SECRET_STORE.instance
4651
replacement = secret_store.nil? ? nil : secret_store.retrieveSecret(SecretStoreExt.getStoreId(name))
4752
#check the environment
4853
replacement = ENV.fetch(name, default) if replacement.nil?
@@ -54,4 +59,20 @@ def replace_placeholders(value)
5459
end
5560
end # def replace_placeholders
5661

62+
class << self
63+
private
64+
65+
# loads a secret_store from disk if available, or returns nil
66+
#
67+
# @api private
68+
# @return [SecretStoreExt,nil]
69+
def load_secret_store
70+
SecretStoreExt.getIfExists(LogStash::SETTINGS.get_setting("keystore.file").value, LogStash::SETTINGS.get_setting("keystore.classname").value)
71+
end
72+
73+
# @api test
74+
def reset_secret_store
75+
SECRET_STORE.reset!
76+
end
77+
end
5778
end

logstash-core/spec/logstash/settings_spec.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,11 @@
158158

159159
before :each do
160160
LogStash::SETTINGS.set("keystore.file", File.join(File.dirname(__FILE__), "../../src/test/resources/logstash.keystore.with.default.pass"))
161+
LogStash::Util::SubstitutionVariables.send(:reset_secret_store)
162+
end
163+
164+
after(:each) do
165+
LogStash::Util::SubstitutionVariables.send(:reset_secret_store)
161166
end
162167

163168
context "placeholders in flat logstash.yml" do
@@ -211,6 +216,7 @@
211216

212217
before :each do
213218
LogStash::SETTINGS.set("keystore.file", File.join(File.dirname(__FILE__), "../../src/test/resources/logstash.keystore.with.default.pass"))
219+
LogStash::Util::SubstitutionVariables.send(:reset_secret_store)
214220
end
215221

216222
before do
@@ -225,6 +231,10 @@
225231
ENV.delete('a')
226232
end
227233

234+
after(:each) do
235+
LogStash::Util::SubstitutionVariables.send(:reset_secret_store)
236+
end
237+
228238
subject do
229239
settings = described_class.new
230240
settings.register(LogStash::Setting::ArrayCoercible.new("host", String, []))

0 commit comments

Comments
 (0)