Repository navigation
out_opensearch: lock the client cache against flush threads - #195
Conversation
Before: client() cleared the shared @_os slot and then rebuilt it with `||=`, without a lock. With flush_thread_count of 2 or more and a host placeholder, a thread could pass the "is this the same host" check and then pick up the client another thread had just built for a different host, so its chunk went to that cluster instead. After: the whole check-and-build runs under a mutex, so a thread always gets a client for the host it asked for. The mutex is created in initialize because configure builds a client before it returns. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Kentaro Hayashi <hayashi@clear-code.com>
|
@kenhys This type of fix also ought to apply for https://github.com/uken/fluent-plugin-elasticsearch? |
I've overlooked it, maybe similar fix is needed. |
|
fluent-plugin-opensearch/lib/fluent/plugin/out_opensearch.rb Lines 355 to 365 in 907cb63 This code runs periodically using a timer. diff --git a/lib/fluent/plugin/out_opensearch.rb b/lib/fluent/plugin/out_opensearch.rb
index cd3ff78..90e6221 100644
--- a/lib/fluent/plugin/out_opensearch.rb
+++ b/lib/fluent/plugin/out_opensearch.rb
@@ -349,7 +349,6 @@ class << self
end
end
# Credential providers refresh themselves near expiration. This timer recreates them forcibly.
- @credential_mutex = Mutex.new
if @endpoint
@_aws_credentials = aws_credentials(@endpoint)
@@ -357,12 +356,14 @@ class << self
timer_execute(:out_opensearch_expire_credentials, @endpoint.refresh_credentials_interval) do
log.debug('Recreate the AWS credentials')
- @credential_mutex.synchronize do
- @_os = nil
- begin
- @_aws_credentials = aws_credentials(@endpoint)
- rescue => e
- log.error("Failed to get new AWS credentials: #{e}")
+ begin
+ credentials = aws_credentials(@endpoint)
+ rescue => e
+ log.error("Failed to get new AWS credentials: #{e}")
+ else
+ @client_mutex.synchronize do
+ @_aws_credentials = credentials
+ @_os = nil
end
end
end |
|
I think the above assignment also requires the mutex. |
Before: the credential refresh timer used its own mutex and the flush thread error path used none, so both could clear @_os while another thread was building a client under @client_mutex. A failed credential refresh also threw away the cached client for nothing. After: both paths take @client_mutex, and the timer keeps the old credentials when it cannot get new ones. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Kentaro Hayashi <hayashi@clear-code.com>
|
Now follow-up review feedback |
Before: client() cleared the shared @_os slot and then rebuilt it with
||=, without a lock. With flush_thread_count of 2 or more and a host placeholder, a thread could pass the "is this the same host" check and then pick up the client another thread had just built for a different host, so its chunk went to that cluster instead.After: the whole check-and-build runs under a mutex, so a thread always gets a client for the host it asked for. The mutex is created in initialize because configure builds a client before it returns.