Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make AliasRegistry a singleton #14002

Merged
merged 4 commits into from
Apr 26, 2022
Merged

Make AliasRegistry a singleton #14002

merged 4 commits into from
Apr 26, 2022

Conversation

robbavey
Copy link
Member

The current implementation of AliasRegistry will create an instance of the Alias Registry for each
pipeline, which appears to potentially result in situations, such as in #13996, where multiple pipelines
are simultaneously loading an alias registry from a yaml file in the jar file using getResourceAsStream, with
the potential of the first thread closing the jar file underneath subsequent threads, leading to errors when
reading the yaml file as the JarFile has been closed after the initial thread completed accessing.

This commit changes the AliasRegistry to be a singleton, as is the PluginRegistry.

Relates: #13996, #13666

The current implementation of AliasRegistry will create an instance of the Alias Registry for each
pipeline, which appears to potentially result in situations, such as in elastic#13996, where multiple pipelines
are simultaneously loading an alias registry from a yaml file in the jar file using getResourceAsStream, with
the potential of the first thread closing the jar file underneath subsequent threads, leading to errors when
reading the yaml file as the JarFile has been closed after the initial thread completed accessing.

This commit changes the AliasRegistry to be a singleton, as is the PluginRegistry.

Relates: elastic#13996, elastic#13666
@andsel
Copy link
Contributor

andsel commented Apr 20, 2022

Hi @robbavey
I was also trying to figure out this, and more or less landed to same point #13666 (comment).

I put some head on how to verify this, without anything tangible, but happy to be your reviewer.

andsel added a commit to andsel/logstash that referenced this pull request Apr 21, 2022
Earlier versions of the reflections library used in the plugin registry would
use caches on JarUrlConnection, which when closed would also close the jar file
for other resources using it, such as the AliasRegistry. This, combined with the
fact that the AliasRegistry could be created simultaneously by many threads/pipelines
could cause issues during AliasRegistry creation leading to failure to create a
pipeline.
@robbavey
Copy link
Member Author

@andsel I reproduced the issue by introducing a random delay (500ms would trigger the issue regularly) into the load method in YamlWithChecksum, and running a multi-pipeline config. This would reliably trigger:

:exception=>"Java::JavaUtil::NoSuchElementException", :message=>"No line found", :backtrace=>["java.base/java.util.Scanner.nextLine(Scanner.java:1651)", "org.logstash.plugins.AliasRegistry$YamlWithChecksum.load(AliasRegistry.java:110)"

Digging a little further, this would always happen just after the reflections library completed:

[2022-04-25T11:37:40,557][INFO ][org.reflections.Reflections] Reflections took 78 ms to scan 1 urls, producing 122 keys and 423 values 

Digging a little further, the root cause of this appears to a multi-threading issue in the reflections library used in the PluginRegistry (which is why your test program would not trigger the issue), where its use of caches, and in getResourceAsStream would clash, with any AliasRegistry creations occurring after the call to reflections failing.

I've made changes to the PR -

  • Updated the version of the reflections library to include the fix to avoid use of caches
  • Explicitly avoiding the use of URLConnection cache for the yaml resource.

These changes should fix the issue regardless of whether we use a singleton AliasRegistry, but I'm not sure why we would ever want one more than one AliasRegistry to be created anyway, so I'm inclined to keep that change anyway. WDYT?

@andsel
Copy link
Contributor

andsel commented Apr 26, 2022

I absolutely agree to keep the AliasRegistry as a singleton. There is no meaning in keep multiple instances of registry that's mainly a "read only entity".
About the other changes, they are fine to me.

  • we use URLConnection to disable caching
  • update a library that load jar resources and in the previous versions suffered of the same URLConnection caching problem we got.

I wonder if we have other cases of libraries that touches jars and get this problem with the cached resource. The only other part where Logstash loads jars that comes to my mind, are the JDBC plugins. But in that case the classes are loaded leveraging JRuby so I think it's well behaving respect to this problem.

@robbavey
Copy link
Member Author

@andsel Cool, this is ready for review.

Trying to unit test this is somewhat tricky - I could only trigger this reliably by introducing an artificial randomized delay in the load method of AliasRegistry and the use of multiple pipelines, which would mean the PluginRegistry action triggering the close of the JarFileInputStream would be performed during the AliasRegistry load. Given that we are already encountering this issue semi-regularly in integration tests (#13996), I'm inclined to treat that as the test for this issue. WDYT?

@andsel
Copy link
Contributor

andsel commented Apr 26, 2022

I'm inclined to treat that as the test for this issue.

I agree with you, it's useless to put test code in production code. If it used some other interfaces that we could mock and reimplement in the test code to make it trigger, would have been nice to test, but since we don't use such a think it's fine for me to do not test it.

@andsel andsel self-requested a review April 26, 2022 14:14
Copy link
Contributor

@andsel andsel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Left only a nitpick

logstash-core/lib/logstash/plugins/registry.rb Outdated Show resolved Hide resolved
Co-authored-by: Andrea Selva <selva.andre@gmail.com>
@robbavey robbavey merged commit 4e77f1b into elastic:main Apr 26, 2022
@robbavey
Copy link
Member Author

@logstashmachine backport 8.2

github-actions bot pushed a commit that referenced this pull request Apr 26, 2022
* Make AliasRegistry a singleton

The current implementation of AliasRegistry will create an instance of the Alias Registry for each
pipeline, which appears to potentially result in situations, such as in #13996, where multiple pipelines
are simultaneously loading an alias registry from a yaml file in the jar file using getResourceAsStream, with
the potential of the first thread closing the jar file underneath subsequent threads, leading to errors when
reading the yaml file as the JarFile has been closed after the initial thread completed accessing.

This commit changes the AliasRegistry to be a singleton, as is the PluginRegistry.

Relates: #13996, #13666

* Update reflections library to 0.9.12 to avoid multi threading bug

Earlier versions of the reflections library used in the plugin registry would
use caches on JarUrlConnection, which when closed would also close the jar file
for other resources using it, such as the AliasRegistry. This, combined with the
fact that the AliasRegistry could be created simultaneously by many threads/pipelines
could cause issues during AliasRegistry creation leading to failure to create a
pipeline.

* Avoid use of URLConnection caching when getting alias yaml resource
* Use idiomatic ruby when accessing Java getInstance method

Co-authored-by: Andrea Selva <selva.andre@gmail.com>
(cherry picked from commit 4e77f1b)
robbavey added a commit that referenced this pull request May 6, 2022
* Make AliasRegistry a singleton

The current implementation of AliasRegistry will create an instance of the Alias Registry for each
pipeline, which appears to potentially result in situations, such as in #13996, where multiple pipelines
are simultaneously loading an alias registry from a yaml file in the jar file using getResourceAsStream, with
the potential of the first thread closing the jar file underneath subsequent threads, leading to errors when
reading the yaml file as the JarFile has been closed after the initial thread completed accessing.

This commit changes the AliasRegistry to be a singleton, as is the PluginRegistry.

Relates: #13996, #13666

* Update reflections library to 0.9.12 to avoid multi threading bug

Earlier versions of the reflections library used in the plugin registry would
use caches on JarUrlConnection, which when closed would also close the jar file
for other resources using it, such as the AliasRegistry. This, combined with the
fact that the AliasRegistry could be created simultaneously by many threads/pipelines
could cause issues during AliasRegistry creation leading to failure to create a
pipeline.

* Avoid use of URLConnection caching when getting alias yaml resource
* Use idiomatic ruby when accessing Java getInstance method

Co-authored-by: Andrea Selva <selva.andre@gmail.com>
(cherry picked from commit 4e77f1b)

Co-authored-by: Rob Bavey <rob.bavey@elastic.co>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants