-
Notifications
You must be signed in to change notification settings - Fork 25k
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
Breaking FilePermission changes in JDK 9 b140+ and performance impact #21534
Comments
JDK9 removed pathname canonicalization when constructing FilePermission objects, which breaks some of the FilePermissions added by Elasticsearch. This commit adds the system property jdk.io.permissionsUseCanonicalPath which makes JDK9 behave like JDK8 w.r.t. FilePermissions (see elastic#21534).
I wonder if it is worthwhile to try doing a simple comparison first, and only if that fails normalize the requested path? |
JDK9 removed pathname canonicalization when constructing FilePermission objects, which breaks some of the FilePermissions added by Elasticsearch. This commit adds the system property jdk.io.permissionsUseCanonicalPath which makes JDK9 behave like JDK8 w.r.t. FilePermission objects (see #21534).
JDK9 removed pathname canonicalization when constructing FilePermission objects, which breaks some of the FilePermissions added by Elasticsearch. This commit adds the system property jdk.io.permissionsUseCanonicalPath which makes JDK9 behave like JDK8 w.r.t. FilePermission objects (see #21534).
JDK9 removed pathname canonicalization when constructing FilePermission objects, which breaks some of the FilePermissions added by Elasticsearch. This commit adds the system property jdk.io.permissionsUseCanonicalPath which makes JDK9 behave like JDK8 w.r.t. FilePermission objects (see #21534).
We have agreed on Fixit Friday to revisit this issue once JDK 9 goes GA / when we release ES v6.0.0. Leaving it open and marking it as blocker for v6.0.0. |
Hi, Here is the actual state, sent yesterday:
|
I'm the author of the new FilePermission behavior. In our benchmarking, the new behavior is much faster than the old one if every FilePermission on the right hand side of implies() is created from a brand new path, and much slower if it is not always new (Precisely, if the number of all different paths is <= 200). This is because although the File.getCanonicalPath() call (which is used by the old behavior) is quite slow, it has an internal cache that can hold 200 results. If you keep calling the method on these 200 paths, it will be super fast. Is your test also using a limited number of paths? |
@wangweij Each test uses it's own randomly generated path(s) (unique to that test), and we run many tests within the same JVM (close to 1000 unit tests classes). However, the paths which we add permissions for (when the test jvm starts up) is indeed limited to a few paths in the security manager policy. All of the relevant paths to the performance difference we see would be using this one root file permission: https://github.com/elastic/elasticsearch/blob/master/test/framework/src/main/java/org/elasticsearch/bootstrap/BootstrapForTesting.java#L116. But the paths compared against that would be mostly unique (subpaths). |
@rjernst So you granted FilePermission on /tmp/- and call checkRead on /tmp/random-sub-path? Are the 1000 tests running in parallel? It will be nice if you can create a standalone test or please tell me how I can run your test. Sorry I am not familiar with ElasticSearch. |
@wangweij You can run a single test as follows (just using the same one that @ywelsch used to benchmark this in the original issue description). You will need gradle 3.3.
Note that we have the canonical path option turned on. To run without that option, and with the originally suggested change here (adding explicit permissions for canonical paths), I created a branch. You can check it out from my fork, and you can see the diff. On my laptop and jdk9 b159, that test runs in about 15 seconds on master (which has |
@rjernst I can reproduce the problem here. It's 50s vs 92s on my laptop (jdk9+161). I will try to look into it. I see your branch added 2 extra permissions. When a file is accessed, are you usually using the original path or the real path? |
I believe we would be using the original path. For example, with |
Dumped all FilePermission checks of a test run into a log. The total number is 882642 with 29275 uniq ones. I'll write a test that simulate these 882642 checks tomorrow and see how it performs. |
The simulation on these 882642 checks shows the same behavior, i.e. jdk9 spends about 100% more time than jdk8. Of all the checks, 881303 are in the same directory ("core/build/testrun/integTest/J0/temp"). For jdk8 with a canonicalized path cache of size 200, 830696 checks (94.26%) are hits and 50607 (5.74%) are misses. The sub-directory names inside "tempDir-002/d?/nodes/?/indices" look like randomly generated strings (For example, "cdJ1TZYqRFCMpEn44235hg"), but there are only 25 of them. So the performance degradation can be explained now, which is because in jdk8 most canonicalized paths are cached so the most time consuming part is not a problem. I will think about if it is possible to make some similar optimization in jdk9. Thanks for reporting this issue about FilePermission checks in the real world. |
@wangweij thank you for investigating the issue and looking into possible solutions. This is much appreciated. |
https://bugs.openjdk.java.net/browse/JDK-8177969 filed including a link to the suggested fix. After code review, it will be pushed into jdk9. |
This is great news @wangweij. Thank you for your time investigating and fixing! |
Fixed now in jdk9+166. Please take a try. |
Initial testing shows very promising results (performance on-par with "Java 8" mode). Thank you for this fix @wangweij! |
@ywelsch Do you want to create a PR with your original change suggested in this issue and remove the canonicalpath override flag from jvm.options? I still see this as a blocker for 6.0, so that we can work with jdk9 on release. |
This commit makes the security code aware of the Java 9 FilePermission changes (see #21534) and allows us to remove the `jdk.io.permissionsUseCanonicalPath` system property.
This commit makes the security code aware of the Java 9 FilePermission changes (see #21534) and allows us to remove the `jdk.io.permissionsUseCanonicalPath` system property.
This commit makes the security code aware of the Java 9 FilePermission changes (see #21534) and allows us to remove the `jdk.io.permissionsUseCanonicalPath` system property.
Closed by #26302 |
JDK9 removed pathname canonicalization when constructing FilePermission objects, which breaks some of the FilePermissions added by Elasticsearch. This commit adds the system property jdk.io.permissionsUseCanonicalPath which makes JDK9 behave like JDK8 w.r.t. FilePermissions (see elastic/elasticsearch#21534).
A new change introduced in JDK 9 b140 removes pathname canonicalization when FilePermission objects are constructed. From the official announcement:
This possibly breaks file permissions for Elasticsearch. A concrete example is the default temp directory
java.io.tmpdir
on my Mac under/var/...
which is symlinked to/private/var/...
, which breaks running the tests from the IDE. Running them with Gradle also breaks asjava.io.tmpdir
is specified as./temp
by the test task.The JDK 9 change implements a compatibility layer which covers permissions defined in policy files:
Elasticsearch does not only rely on policy files though (e.g. adding permissions for temp directory is done programmatically).
Alternatively the JDK 9 also introduces a new system property that brings back the old behavior:
I've tried that one and everything runs again without any modifications. I wonder if we should treat this as a temporary fix for now. As a more permanent fix I've tried patching the ES security code so that it resolves symlinks / resolves relative to absolute paths before add file permissions by replacing the last two lines in
org.elasticsearch.bootstrap.security.Security#addPath(...)
:with
I observed however a big performance hit with this compared to JDK 8 (which goes as far as to make some tests fail due to timeouts that are too low):
Test that I've run (with fixed seed):
org.elasticsearch.cluster.allocation.ClusterRerouteIT#testDelayWithALargeAmountOfShards
-Djdk.io.permissionsUseCanonicalPath=true
(simulating JDK8 behavior): ~7 seconds-Djdk.io.permissionsUseCanonicalPath=false
which corresponds to not setting the system property at all: ~32 secondsI've looked at the runs using a profiler but couldn't notice anything beside the obvious that extra time is spent in
java.security.Permissions#implies
when using the new JDK9 behavior. Thoughts?The text was updated successfully, but these errors were encountered: