Skip to content

Commit 405b88b

Browse files
authored
Add zstd to native access (#105715)
This commit makes zstd compression available to Elasticsearch. The library is pulled in through maven in jar files for each platform, then bundled in a new platform directory under lib. Access to the zstd compression/decompression is through NativeAccess.
1 parent 8ff083b commit 405b88b

File tree

37 files changed

+870
-34
lines changed

37 files changed

+870
-34
lines changed

build-tools-internal/build.gradle

+4
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ gradlePlugin {
119119
id = 'elasticsearch.java-doc'
120120
implementationClass = 'org.elasticsearch.gradle.internal.ElasticsearchJavadocPlugin'
121121
}
122+
javaBase {
123+
id = 'elasticsearch.java-base'
124+
implementationClass = 'org.elasticsearch.gradle.internal.ElasticsearchJavaBasePlugin'
125+
}
122126
java {
123127
id = 'elasticsearch.java'
124128
implementationClass = 'org.elasticsearch.gradle.internal.ElasticsearchJavaPlugin'

build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/JdkDownloadPluginFuncTest.groovy

+1-5
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
package org.elasticsearch.gradle.internal
1010

11-
import spock.lang.TempDir
11+
1212
import spock.lang.Unroll
1313
import com.github.tomakehurst.wiremock.WireMockServer
1414

@@ -103,10 +103,6 @@ class JdkDownloadPluginFuncTest extends AbstractGradleFuncTest {
103103
plugins {
104104
id 'elasticsearch.jdk-download' apply false
105105
}
106-
107-
subprojects {
108-
109-
}
110106
"""
111107
3.times {
112108
subProject(':sub-' + it) << """

build-tools-internal/src/main/groovy/elasticsearch.ide.gradle

+8-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import org.elasticsearch.gradle.util.Pair
1010
import org.elasticsearch.gradle.util.GradleUtils
1111
import org.elasticsearch.gradle.internal.info.BuildParams
12+
import org.elasticsearch.gradle.internal.test.TestUtil
1213
import org.jetbrains.gradle.ext.JUnit
1314

1415
import java.nio.file.Files
@@ -128,9 +129,13 @@ if (providers.systemProperty('idea.active').getOrNull() == 'true') {
128129
':x-pack:plugin:esql:compute:gen:jar',
129130
':server:generateModulesList',
130131
':server:generatePluginsList',
131-
':generateProviderImpls'].collect { elasticsearchProject.right()?.task(it) ?: it })
132+
':generateProviderImpls',
133+
':libs:elasticsearch-native:elasticsearch-native-libraries:extractLibs'].collect { elasticsearchProject.right()?.task(it) ?: it })
132134
}
133135

136+
// this path is produced by the extractLibs task above
137+
String testLibraryPath = TestUtil.getTestLibraryPath("${elasticsearchProject.left()}/libs/native/libraries/build/platform")
138+
134139
idea {
135140
project {
136141
vcs = 'Git'
@@ -162,6 +167,8 @@ if (providers.systemProperty('idea.active').getOrNull() == 'true') {
162167
'-ea',
163168
'-Djava.security.manager=allow',
164169
'-Djava.locale.providers=SPI,COMPAT',
170+
'-Djava.library.path=' + testLibraryPath,
171+
'-Djna.library.path=' + testLibraryPath,
165172
// TODO: only open these for mockito when it is modularized
166173
'--add-opens=java.base/java.security.cert=ALL-UNNAMED',
167174
'--add-opens=java.base/java.nio.channels=ALL-UNNAMED',

build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/ElasticsearchJavaBasePlugin.java

+28
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,15 @@
1212
import org.elasticsearch.gradle.internal.conventions.precommit.PrecommitTaskPlugin;
1313
import org.elasticsearch.gradle.internal.info.BuildParams;
1414
import org.elasticsearch.gradle.internal.info.GlobalBuildInfoPlugin;
15+
import org.elasticsearch.gradle.internal.test.TestUtil;
16+
import org.elasticsearch.gradle.test.SystemPropertyCommandLineArgumentProvider;
1517
import org.elasticsearch.gradle.util.GradleUtils;
1618
import org.gradle.api.JavaVersion;
1719
import org.gradle.api.Plugin;
1820
import org.gradle.api.Project;
21+
import org.gradle.api.artifacts.Configuration;
1922
import org.gradle.api.artifacts.ResolutionStrategy;
23+
import org.gradle.api.file.FileCollection;
2024
import org.gradle.api.plugins.JavaBasePlugin;
2125
import org.gradle.api.plugins.JavaPluginExtension;
2226
import org.gradle.api.provider.Provider;
@@ -26,10 +30,13 @@
2630
import org.gradle.api.tasks.compile.CompileOptions;
2731
import org.gradle.api.tasks.compile.GroovyCompile;
2832
import org.gradle.api.tasks.compile.JavaCompile;
33+
import org.gradle.api.tasks.testing.Test;
2934
import org.gradle.jvm.toolchain.JavaLanguageVersion;
3035
import org.gradle.jvm.toolchain.JavaToolchainService;
3136

3237
import java.util.List;
38+
import java.util.Map;
39+
import java.util.function.Supplier;
3340

3441
import javax.inject.Inject;
3542

@@ -59,6 +66,7 @@ public void apply(Project project) {
5966
configureConfigurations(project);
6067
configureCompile(project);
6168
configureInputNormalization(project);
69+
configureNativeLibraryPath(project);
6270

6371
// convenience access to common versions used in dependencies
6472
project.getExtensions().getExtraProperties().set("versions", VersionProperties.getVersions());
@@ -165,6 +173,26 @@ public static void configureInputNormalization(Project project) {
165173
project.getNormalization().getRuntimeClasspath().ignore("IMPL-JARS/**/META-INF/MANIFEST.MF");
166174
}
167175

176+
private static void configureNativeLibraryPath(Project project) {
177+
String nativeProject = ":libs:elasticsearch-native:elasticsearch-native-libraries";
178+
Configuration nativeConfig = project.getConfigurations().create("nativeLibs");
179+
nativeConfig.defaultDependencies(deps -> {
180+
deps.add(project.getDependencies().project(Map.of("path", nativeProject, "configuration", "default")));
181+
});
182+
// This input to the following lambda needs to be serializable. Configuration is not serializable, but FileCollection is.
183+
FileCollection nativeConfigFiles = nativeConfig;
184+
185+
project.getTasks().withType(Test.class).configureEach(test -> {
186+
var systemProperties = test.getExtensions().getByType(SystemPropertyCommandLineArgumentProvider.class);
187+
var libraryPath = (Supplier<String>) () -> TestUtil.getTestLibraryPath(nativeConfigFiles.getAsPath());
188+
189+
test.dependsOn(nativeConfigFiles);
190+
// we may use JNA or the JDK's foreign function api to load libraries, so we set both sysprops
191+
systemProperties.systemProperty("java.library.path", libraryPath);
192+
systemProperties.systemProperty("jna.library.path", libraryPath);
193+
});
194+
}
195+
168196
private static Provider<Integer> releaseVersionProviderFromCompileTask(Project project, AbstractCompile compileTask) {
169197
return project.provider(() -> {
170198
JavaVersion javaVersion = JavaVersion.toVersion(compileTask.getTargetCompatibility());

build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/MrjarPlugin.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ private void addMrjarSourceset(
105105
testTask.dependsOn(jarTask);
106106

107107
SourceSetContainer sourceSets = GradleUtils.getJavaSourceSets(project);
108-
FileCollection mainRuntime = sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME).getRuntimeClasspath();
108+
FileCollection mainRuntime = sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME).getOutput();
109109
FileCollection testRuntime = sourceSets.getByName(SourceSet.TEST_SOURCE_SET_NAME).getRuntimeClasspath();
110110
testTask.setClasspath(testRuntime.minus(mainRuntime).plus(project.files(jarTask)));
111111
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
5+
* in compliance with, at your election, the Elastic License 2.0 or the Server
6+
* Side Public License, v 1.
7+
*/
8+
9+
package org.elasticsearch.gradle.internal.test;
10+
11+
import org.elasticsearch.gradle.Architecture;
12+
import org.elasticsearch.gradle.ElasticsearchDistribution;
13+
14+
import java.util.Locale;
15+
16+
public class TestUtil {
17+
18+
public static String getTestLibraryPath(String nativeLibsDir) {
19+
String arch = Architecture.current().toString().toLowerCase(Locale.ROOT);
20+
String platform = String.format(Locale.ROOT, "%s-%s", ElasticsearchDistribution.CURRENT_PLATFORM, arch);
21+
String existingLibraryPath = System.getProperty("java.library.path");
22+
23+
return String.format(Locale.ROOT, "%s/%s:%s", nativeLibsDir, platform, existingLibraryPath);
24+
}
25+
}

build-tools/src/testFixtures/groovy/org/elasticsearch/gradle/fixtures/AbstractGradleFuncTest.groovy

+7
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@ abstract class AbstractGradleFuncTest extends Specification {
5050
propertiesFile = testProjectDir.newFile('gradle.properties')
5151
propertiesFile <<
5252
"org.gradle.java.installations.fromEnv=JAVA_HOME,RUNTIME_JAVA_HOME,JAVA15_HOME,JAVA14_HOME,JAVA13_HOME,JAVA12_HOME,JAVA11_HOME,JAVA8_HOME"
53+
54+
def nativeLibsProject = subProject(":libs:elasticsearch-native:elasticsearch-native-libraries")
55+
nativeLibsProject << """
56+
plugins {
57+
id 'base'
58+
}
59+
"""
5360
}
5461

5562
def cleanup() {

distribution/archives/build.gradle

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ CopySpec archiveFiles(String distributionType, String os, String architecture, b
1515
return copySpec {
1616
into("elasticsearch-${version}") {
1717
into('lib') {
18-
with libFiles
18+
with libFiles(os, architecture)
1919
}
2020
into('config') {
2121
dirMode 0750

distribution/build.gradle

+10-2
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) {
261261
* Properties to expand when copying packaging files *
262262
*****************************************************************************/
263263
configurations {
264-
['libs', 'libsVersionChecker', 'libsCliLauncher', 'libsServerCli', 'libsWindowsServiceCli', 'libsPluginCli', 'libsKeystoreCli', 'libsSecurityCli', 'libsGeoIpCli', 'libsAnsiConsole'].each {
264+
['libs', 'libsVersionChecker', 'libsCliLauncher', 'libsServerCli', 'libsWindowsServiceCli', 'libsPluginCli', 'libsKeystoreCli', 'libsSecurityCli', 'libsGeoIpCli', 'libsAnsiConsole', 'libsNative'].each {
265265
create(it) {
266266
canBeConsumed = false
267267
canBeResolved = true
@@ -292,14 +292,15 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) {
292292
libsKeystoreCli project(path: ':distribution:tools:keystore-cli')
293293
libsSecurityCli project(':x-pack:plugin:security:cli')
294294
libsGeoIpCli project(':distribution:tools:geoip-cli')
295+
libsNative project(':libs:elasticsearch-native:elasticsearch-native-libraries')
295296
}
296297

297298
project.ext {
298299

299300
/*****************************************************************************
300301
* Common files in all distributions *
301302
*****************************************************************************/
302-
libFiles =
303+
libFiles = { os, architecture ->
303304
copySpec {
304305
// Delay by using closures, since they have not yet been configured, so no jar task exists yet.
305306
from(configurations.libs)
@@ -330,7 +331,14 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) {
330331
into('tools/ansi-console') {
331332
from(configurations.libsAnsiConsole)
332333
}
334+
into('platform') {
335+
from(configurations.libsNative)
336+
if (os != null) {
337+
include (os + '-' + architecture + '/*')
338+
}
339+
}
333340
}
341+
}
334342

335343
modulesFiles = { os, architecture ->
336344
copySpec {

distribution/packages/build.gradle

+2-1
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ def commonPackageConfig(String type, String architecture) {
131131

132132
// top level "into" directive is not inherited from ospackage for some reason, so we must
133133
// specify it again explicitly for copying common files
134+
String platform = 'linux-' + ((architecture == 'x64') ? 'x86_64' : architecture)
134135
into('/usr/share/elasticsearch') {
135136
into('bin') {
136137
with binFiles(type, false)
@@ -140,7 +141,7 @@ def commonPackageConfig(String type, String architecture) {
140141
fileMode 0644
141142
}
142143
into('lib') {
143-
with libFiles
144+
with libFiles('linux', architecture)
144145
}
145146
into('modules') {
146147
with modulesFiles('linux', architecture)

distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/SystemJvmOptions.java

+42
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@
1010

1111
import org.elasticsearch.common.settings.Settings;
1212
import org.elasticsearch.common.util.concurrent.EsExecutors;
13+
import org.elasticsearch.core.SuppressForbidden;
1314

15+
import java.io.File;
16+
import java.nio.file.Path;
17+
import java.nio.file.Paths;
1418
import java.util.List;
1519
import java.util.Map;
1620
import java.util.stream.Collectors;
@@ -21,6 +25,8 @@ final class SystemJvmOptions {
2125
static List<String> systemJvmOptions(Settings nodeSettings, final Map<String, String> sysprops) {
2226
String distroType = sysprops.get("es.distribution.type");
2327
boolean isHotspot = sysprops.getOrDefault("sun.management.compiler", "").contains("HotSpot");
28+
String libraryPath = findLibraryPath(sysprops);
29+
2430
return Stream.of(
2531
/*
2632
* Cache ttl in seconds for positive DNS lookups noting that this overrides the JDK security property networkaddress.cache.ttl;
@@ -71,6 +77,8 @@ static List<String> systemJvmOptions(Settings nodeSettings, final Map<String, St
7177
maybeOverrideDockerCgroup(distroType),
7278
maybeSetActiveProcessorCount(nodeSettings),
7379
setReplayFile(distroType, isHotspot),
80+
"-Djava.library.path=" + libraryPath,
81+
"-Djna.library.path=" + libraryPath,
7482
// Pass through distribution type
7583
"-Des.distribution.type=" + distroType
7684
).filter(e -> e.isEmpty() == false).collect(Collectors.toList());
@@ -127,4 +135,38 @@ private static String maybeEnableNativeAccess() {
127135
}
128136
return "";
129137
}
138+
139+
private static String findLibraryPath(Map<String, String> sysprops) {
140+
// working dir is ES installation, so we use relative path here
141+
Path platformDir = Paths.get("lib", "platform");
142+
String existingPath = sysprops.get("java.library.path");
143+
assert existingPath != null;
144+
145+
String osname = sysprops.get("os.name");
146+
String os;
147+
if (osname.startsWith("Windows")) {
148+
os = "windows";
149+
} else if (osname.startsWith("Linux")) {
150+
os = "linux";
151+
} else if (osname.startsWith("Mac OS")) {
152+
os = "darwin";
153+
} else {
154+
os = "unsupported_os[" + osname + "]";
155+
}
156+
String archname = sysprops.get("os.arch");
157+
String arch;
158+
if (archname.equals("amd64")) {
159+
arch = "x64";
160+
} else if (archname.equals("aarch64")) {
161+
arch = archname;
162+
} else {
163+
arch = "unsupported_arch[" + archname + "]";
164+
}
165+
return platformDir.resolve(os + "-" + arch).toAbsolutePath() + getPathSeparator() + existingPath;
166+
}
167+
168+
@SuppressForbidden(reason = "no way to get path separator with nio")
169+
private static String getPathSeparator() {
170+
return File.pathSeparator;
171+
}
130172
}

0 commit comments

Comments
 (0)