问题
在ES 2.x版本中发生的一起动失败报错:
java.nio.file.FileAlreadyExistsException: /elasticsearch-20/cluster_name/nodes/0/__es__.tmp
问题分析
在ES的源码中通过关键字__es__.tmp找到了相关源码,如下所示:
/**
* This method tries to write an empty file and moves it using an atomic move operation.
* This method throws an {@link IllegalStateException} if this operation is
* not supported by the filesystem. This test is executed on each of the data directories.
* This method cleans up all files even in the case of an error.
*/
public void ensureAtomicMoveSupported() throws IOException {
final NodePath[] nodePaths = nodePaths();
for (NodePath nodePath : nodePaths) {
assert Files.isDirectory(nodePath.path) : nodePath.path + " is not a directory";
final Path src = nodePath.path.resolve("__es__.tmp");
Files.createFile(src);
final Path target = nodePath.path.resolve("__es__.final");
try {
Files.move(src, target, StandardCopyOption.ATOMIC_MOVE);
} catch (AtomicMoveNotSupportedException ex) {
throw new IllegalStateException("atomic_move is not supported by the filesystem on path ["
+ nodePath.path
+ "] atomic_move is required for elasticsearch to work correctly.", ex);
} finally {
Files.deleteIfExists(src);
Files.deleteIfExists(target);
}
}
}
ensureAtomicMoveSupported方法是在ES启动时初始化GatewayMetaState对象时调用的,从方法注释可以看出这个方法是为了测试ES运行环境中是否支原子性地移动一个文件。测试的方法就是,先创建一个名为__es__.tmp的文件,在将其移动到名为__es__.final的文件,如果这个操作成功,说明支持,如果报异常说明不支持。测试结束后会通过调用Files.deleteIfExists(src);Files.deleteIfExists(target);将__es__.tmp 与__es__.final这两个文件都删除。
我们再回到ES的报错:
java.nio.file.FileAlreadyExistsException: /elasticsearch-20/cluster_name/nodes/0/__es__.tmp
显然报这个错是因为ensureAtomicMoveSupported方法方法试图创建__es__.tmp的时候他已经存在了。至于为什么存在这个文件,我的推测是,上次ES启动过程中在__es__.tmp创建到删除这段时间,ES进程被强制杀死导致__es__.tmp没有被删除。
所以ES代码没有考虑到这种情况,我认为确实是ES的锅。
解决方法
解决这个异常的方法很容易想到,那就是手动把__es__.tmp删除,再重启ES即可。
另外,需要说明的是ES的开发者已经意识到这个缺陷,并且将其在5.x版本中修复。
他们的修复也非常简单,就是把上述源码中的Files.createFile(src); 移动到try 代码块中即可,这样异常就能被捕获,不会影响ES的启动。
public void ensureAtomicMoveSupported() throws IOException {
final NodePath[] nodePaths = nodePaths();
for (NodePath nodePath : nodePaths) {
assert Files.isDirectory(nodePath.path) : nodePath.path + " is not a directory";
final Path src = nodePath.path.resolve("__es__.tmp");
// Files.createFile(src);
final Path target = nodePath.path.resolve("__es__.final");
try {
Files.createFile(src);
Files.move(src, target, StandardCopyOption.ATOMIC_MOVE);
} catch (AtomicMoveNotSupportedException ex) {
throw new IllegalStateException("atomic_move is not supported by the filesystem on path ["
+ nodePath.path
+ "] atomic_move is required for elasticsearch to work correctly.", ex);
} finally {
Files.deleteIfExists(src);
Files.deleteIfExists(target);
}
}
}