Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions server/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import org.elasticsearch.node.InternalSettingsPreparer;
import org.elasticsearch.node.Node;
import org.elasticsearch.node.NodeValidationException;
import org.elasticsearch.snapshots.SnapshotsService;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
Expand Down Expand Up @@ -168,6 +169,18 @@ private void setup(boolean addShutdownHook, Environment environment) throws Boot
BootstrapSettings.SYSTEM_CALL_FILTER_SETTING.get(settings),
BootstrapSettings.CTRLHANDLER_SETTING.get(settings));

final long cacheSize = SnapshotsService.SNAPSHOT_CACHE_SIZE_SETTING.get(settings).getBytes();
final long regionSize = SnapshotsService.SNAPSHOT_CACHE_REGION_SIZE_SETTING.get(settings).getBytes();
final int numRegions = Math.toIntExact(cacheSize / regionSize);
final long fileSize = numRegions * regionSize;
if (fileSize > 0) {
try {
Natives.tryCreateCacheFile(environment, fileSize);
} catch (Exception e) {
throw new BootstrapException(e);
}
}

// initialize probes before the security manager is installed
initializeProbes();

Expand Down
74 changes: 74 additions & 0 deletions server/src/main/java/org/elasticsearch/bootstrap/JNAFalloc.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

package org.elasticsearch.bootstrap;

import com.sun.jna.Native;
import com.sun.jna.Platform;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.lucene.util.Constants;
import org.elasticsearch.common.Nullable;

/**
* System specific wrappers of the fallocate system call via JNA for Linux and OSX.
*/
abstract class JNAFalloc {

private static final Logger logger = LogManager.getLogger(JNAFalloc.class);

public abstract int fallocate(int fd, long offset, long length);

@Nullable
public static JNAFalloc falloc() {
try {
if (Constants.MAC_OS_X) {
return OSX.INSTANCE;
} else if (Constants.LINUX) {
return Linux.INSTANCE;
}
} catch (Throwable t) {
logger.warn("unable to link C library. native (falloc) will be disabled.", t);
}
return null;
}

private static class Linux extends JNAFalloc {

static final Linux INSTANCE = new Linux();

static {
Native.register(Platform.C_LIBRARY_NAME);
}

@Override
public int fallocate(int fd, long offset, long length) {
final int res = fallocate(fd, 0, offset, length);
return res == 0 ? 0 : Native.getLastError();
}

private static native int fallocate(int fd, int mode, long offset, long length);
}

private static class OSX extends JNAFalloc {

static final OSX INSTANCE = new OSX();

static {
Native.register("c");
}

@Override
public int fallocate(int fd, long offset, long length) {
return posix_fallocate(fd, offset, length);
}

private static native int posix_fallocate(int fd, long offset, long length);
}

}
44 changes: 44 additions & 0 deletions server/src/main/java/org/elasticsearch/bootstrap/JNANatives.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,17 @@

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.message.ParameterizedMessage;
import org.apache.lucene.util.Constants;
import org.elasticsearch.common.SuppressForbidden;
import org.elasticsearch.env.Environment;
import org.elasticsearch.monitor.jvm.JvmInfo;
import org.elasticsearch.snapshots.SnapshotUtils;

import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;

import static org.elasticsearch.bootstrap.JNAKernel32Library.SizeT;
Expand Down Expand Up @@ -260,4 +268,40 @@ static void tryInstallSystemCallFilter(Path tmpFile) {
logger.warn("unable to install syscall filter: ", e);
}
}

@SuppressForbidden(reason = "need access to fd on FileOutputStream")
static void fallocateSnapshotCacheFile(Environment environment, long fileSize) throws IOException {
final JNAFalloc falloc = JNAFalloc.falloc();
if (falloc == null) {
logger.debug("not trying to create a shared cache file using fallocate because native fallocate library could not be loaded.");
return;
}

Path cacheFile = SnapshotUtils.findCacheSnapshotCacheFilePath(environment, fileSize);
if (cacheFile == null) {
throw new IOException("could not find a directory with adequate free space for cache file");
}
boolean success = false;
try (FileOutputStream fileChannel = new FileOutputStream(cacheFile.toFile())) {
long currentSize = fileChannel.getChannel().size();
if (currentSize < fileSize) {
final Field field = fileChannel.getFD().getClass().getDeclaredField("fd");
field.setAccessible(true);
final int result = falloc.fallocate((int) field.get(fileChannel.getFD()), currentSize, fileSize - currentSize);
if (result == 0) {
success = true;
logger.info("allocated cache file [{}] using fallocate", cacheFile);
} else {
logger.warn("failed to initialize cache file [{}] using fallocate errno [{}]", cacheFile, result);
}
}
} catch (Exception e) {
logger.warn(new ParameterizedMessage("failed to initialize cache file [{}] using fallocate", cacheFile), e);
} finally {
if (success == false) {
// if anything goes wrong, delete the potentially created file to not waste disk space
Files.deleteIfExists(cacheFile);
}
}
}
}
18 changes: 18 additions & 0 deletions server/src/main/java/org/elasticsearch/bootstrap/Natives.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.env.Environment;

import java.io.IOException;
import java.nio.file.Path;

/**
Expand Down Expand Up @@ -132,4 +134,20 @@ static boolean isSystemCallFilterInstalled() {
}
return JNANatives.LOCAL_SYSTEM_CALL_FILTER;
}

/**
* On Linux, this method tries to create the searchable snapshot frozen cache file using fallocate if JNA is available. This enables
* a much faster creation of the file than the fallback mechanism in the searchable snapshots plugin that will pre-allocate the cache
* file by writing zeros to the file.
*
* @throws IOException on failure to determine free disk space for a data path
*/
public static void tryCreateCacheFile(Environment environment, long fileSize) throws IOException {
if (JNA_AVAILABLE == false) {
logger.warn("cannot use fallocate to create cache file because JNA is not available");
return;
}
JNANatives.fallocateSnapshotCacheFile(environment, fileSize);
}

}
10 changes: 10 additions & 0 deletions server/src/main/java/org/elasticsearch/env/Environment.java
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,16 @@ public static FileStore getFileStore(final Path path) throws IOException {
return new ESFileStore(Files.getFileStore(path));
}

public static long getUsableSpace(Path path) throws IOException {
long freeSpaceInBytes = Environment.getFileStore(path).getUsableSpace();

/* See: https://bugs.openjdk.java.net/browse/JDK-8162520 */
if (freeSpaceInBytes < 0) {
freeSpaceInBytes = Long.MAX_VALUE;
}
return freeSpaceInBytes;
}

/**
* asserts that the two environments are equivalent for all things the environment cares about (i.e., all but the setting
* object which may contain different setting)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,14 @@

import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.regex.Regex;
import org.elasticsearch.env.Environment;
import org.elasticsearch.index.IndexNotFoundException;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -107,4 +112,29 @@ public static List<String> filterIndices(List<String> availableIndices, String[]
}
return List.copyOf(result);
}

/**
* Tries to find a suitable path to a searchable snapshots shared cache file in the data paths founds in the environment.
*
* @return path for the cache file or {@code null} if none could be found
*/
@Nullable
public static Path findCacheSnapshotCacheFilePath(Environment environment, long fileSize) throws IOException {
Path cacheFile = null;
for (Path path : environment.dataFiles()) {
Files.createDirectories(path);
// TODO: be resilient to this check failing and try next path?
long usableSpace = Environment.getUsableSpace(path);
Path p = path.resolve(SnapshotsService.CACHE_FILE_NAME);
if (Files.exists(p)) {
usableSpace += Files.size(p);
}
// TODO: leave some margin for error here
if (usableSpace > fileSize) {
cacheFile = p;
break;
}
}
return cacheFile;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.elasticsearch.cluster.RestoreInProgress;
import org.elasticsearch.cluster.SnapshotDeletionsInProgress;
import org.elasticsearch.cluster.SnapshotsInProgress;
import org.elasticsearch.common.unit.ByteSizeValue;
import org.elasticsearch.common.util.CollectionUtils;
import org.elasticsearch.repositories.RepositoryShardId;
import org.elasticsearch.cluster.SnapshotsInProgress.ShardSnapshotStatus;
Expand Down Expand Up @@ -132,6 +133,26 @@ public class SnapshotsService extends AbstractLifecycleComponent implements Clus

public static final String UPDATE_SNAPSHOT_STATUS_ACTION_NAME = "internal:cluster/snapshot/update_snapshot_status";

public static final String SHARED_CACHE_SETTINGS_PREFIX = "xpack.searchable.snapshot.shared_cache.";

public static final Setting<ByteSizeValue> SHARED_CACHE_RANGE_SIZE_SETTING = Setting.byteSizeSetting(
SHARED_CACHE_SETTINGS_PREFIX + "range_size",
ByteSizeValue.ofMb(16), // default
Setting.Property.NodeScope
);
public static final Setting<ByteSizeValue> SNAPSHOT_CACHE_REGION_SIZE_SETTING = Setting.byteSizeSetting(
SHARED_CACHE_SETTINGS_PREFIX + "region_size",
SHARED_CACHE_RANGE_SIZE_SETTING,
Setting.Property.NodeScope
);
public static final Setting<ByteSizeValue> SNAPSHOT_CACHE_SIZE_SETTING = Setting.byteSizeSetting(
SHARED_CACHE_SETTINGS_PREFIX + "size",
ByteSizeValue.ZERO,
Setting.Property.NodeScope
);

public static final String CACHE_FILE_NAME = "shared_snapshot_cache";

private final ClusterService clusterService;

private final IndexNameExpressionResolver indexNameExpressionResolver;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,13 +134,8 @@ public ByteSizeValue getMinLocalStorageAvailable() {
return minLocalStorageAvailable;
}

// non-static indirection to enable mocking in tests
long getUsableSpace(Path path) throws IOException {
long freeSpaceInBytes = Environment.getFileStore(path).getUsableSpace();

/* See: https://bugs.openjdk.java.net/browse/JDK-8162520 */
if (freeSpaceInBytes < 0) {
freeSpaceInBytes = Long.MAX_VALUE;
}
return freeSpaceInBytes;
return Environment.getUsableSpace(path);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.elasticsearch.index.IndexSettings;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.snapshots.AbstractSnapshotIntegTestCase;
import org.elasticsearch.snapshots.SnapshotsService;
import org.elasticsearch.xpack.core.searchablesnapshots.MountSearchableSnapshotAction;
import org.elasticsearch.xpack.core.searchablesnapshots.MountSearchableSnapshotRequest;
import org.elasticsearch.xpack.searchablesnapshots.cache.CacheService;
Expand Down Expand Up @@ -72,22 +73,22 @@ protected Settings nodeSettings(int nodeOrdinal) {
);
}
builder.put(
FrozenCacheService.SNAPSHOT_CACHE_SIZE_SETTING.getKey(),
SnapshotsService.SNAPSHOT_CACHE_SIZE_SETTING.getKey(),
rarely()
? randomBoolean()
? new ByteSizeValue(randomIntBetween(0, 10), ByteSizeUnit.KB)
: new ByteSizeValue(randomIntBetween(0, 1000), ByteSizeUnit.BYTES)
: new ByteSizeValue(randomIntBetween(1, 10), ByteSizeUnit.MB)
);
builder.put(
FrozenCacheService.SNAPSHOT_CACHE_REGION_SIZE_SETTING.getKey(),
SnapshotsService.SNAPSHOT_CACHE_REGION_SIZE_SETTING.getKey(),
rarely()
? new ByteSizeValue(randomIntBetween(4, 1024), ByteSizeUnit.KB)
: new ByteSizeValue(randomIntBetween(1, 10), ByteSizeUnit.MB)
);
if (randomBoolean()) {
builder.put(
FrozenCacheService.FROZEN_CACHE_RANGE_SIZE_SETTING.getKey(),
SnapshotsService.SHARED_CACHE_RANGE_SIZE_SETTING.getKey(),
rarely()
? new ByteSizeValue(randomIntBetween(4, 1024), ByteSizeUnit.KB)
: new ByteSizeValue(randomIntBetween(1, 10), ByteSizeUnit.MB)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestHandler;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.snapshots.SnapshotsService;
import org.elasticsearch.snapshots.SourceOnlySnapshotRepository;
import org.elasticsearch.threadpool.ExecutorBuilder;
import org.elasticsearch.threadpool.ScalingExecutorBuilder;
Expand Down Expand Up @@ -250,9 +251,9 @@ public List<Setting<?>> getSettings() {
CacheService.SNAPSHOT_CACHE_MAX_FILES_TO_SYNC_AT_ONCE_SETTING,
CacheService.SNAPSHOT_CACHE_SYNC_SHUTDOWN_TIMEOUT,
SearchableSnapshotEnableAllocationDecider.SEARCHABLE_SNAPSHOTS_ALLOCATE_ON_ROLLING_RESTART,
FrozenCacheService.SNAPSHOT_CACHE_SIZE_SETTING,
FrozenCacheService.SNAPSHOT_CACHE_REGION_SIZE_SETTING,
FrozenCacheService.FROZEN_CACHE_RANGE_SIZE_SETTING,
SnapshotsService.SNAPSHOT_CACHE_SIZE_SETTING,
SnapshotsService.SNAPSHOT_CACHE_REGION_SIZE_SETTING,
SnapshotsService.SHARED_CACHE_RANGE_SIZE_SETTING,
FrozenCacheService.FROZEN_CACHE_RECOVERY_RANGE_SIZE_SETTING,
FrozenCacheService.SNAPSHOT_CACHE_MAX_FREQ_SETTING,
FrozenCacheService.SNAPSHOT_CACHE_DECAY_INTERVAL_SETTING,
Expand Down
Loading