Skip to content

Commit 7875f15

Browse files
lifepuzzlefunhangc0276
authored andcommitted
Try to use jdk api to create hardlink when rename file when compaction. (#3876)
### Motivation Current HardLink will create a process to execute mv like command to rename file in compaction logic. maybe we can just use jdk api to do this with lower overhead. see javadoc: https://docs.oracle.com/javase/7/docs/api/java/nio/file/Files.html#createLink(java.nio.file.Path,%20java.nio.file.Path) ### Changes test if `Files.createLink` is available in hardlink static code block. if test fails means `Files.createLink` is not available. else will use `Files.createLink` when call createHardlink (cherry picked from commit af419cc)
1 parent e7187f6 commit 7875f15

File tree

3 files changed

+114
-2
lines changed

3 files changed

+114
-2
lines changed

Diff for: bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/DefaultEntryLogger.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -683,7 +683,7 @@ private void createNewCompactionLog() throws IOException {
683683
private void removeCurCompactionLog() {
684684
synchronized (compactionLogLock) {
685685
if (compactionLogChannel != null) {
686-
if (!compactionLogChannel.getLogFile().delete()) {
686+
if (compactionLogChannel.getLogFile().exists() && !compactionLogChannel.getLogFile().delete()) {
687687
LOG.warn("Could not delete compaction log file {}", compactionLogChannel.getLogFile());
688688
}
689689

Diff for: bookkeeper-server/src/main/java/org/apache/bookkeeper/util/HardLink.java

+31-1
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,18 @@
2222

2323
import static java.nio.charset.StandardCharsets.UTF_8;
2424

25+
import com.google.common.annotations.VisibleForTesting;
2526
import java.io.BufferedReader;
2627
import java.io.File;
2728
import java.io.FileNotFoundException;
2829
import java.io.IOException;
2930
import java.io.InputStreamReader;
31+
import java.nio.file.Files;
32+
import java.nio.file.Path;
3033
import java.util.Arrays;
34+
import java.util.concurrent.atomic.AtomicBoolean;
35+
import org.slf4j.Logger;
36+
import org.slf4j.LoggerFactory;
3137

3238
/**
3339
* Class for creating hardlinks.
@@ -42,7 +48,7 @@
4248
* efficient - and minimizes the impact of the extra buffer creations.
4349
*/
4450
public class HardLink {
45-
51+
private static final Logger LOG = LoggerFactory.getLogger(HardLink.class);
4652
/**
4753
* OS Types.
4854
*/
@@ -395,12 +401,19 @@ protected static int getMaxAllowedCmdArgLength() {
395401
return getHardLinkCommand.getMaxAllowedCmdArgLength();
396402
}
397403

404+
private static final AtomicBoolean CREATE_LINK_SUPPORTED = new AtomicBoolean(true);
405+
398406
/*
399407
* ****************************************************
400408
* Complexity is above. User-visible functionality is below
401409
* ****************************************************
402410
*/
403411

412+
@VisibleForTesting
413+
static void enableJdkLinkApi(boolean enable) {
414+
CREATE_LINK_SUPPORTED.set(enable);
415+
}
416+
404417
/**
405418
* Creates a hardlink.
406419
* @param file - existing source file
@@ -416,6 +429,23 @@ public static void createHardLink(File file, File linkName)
416429
throw new IOException(
417430
"invalid arguments to createHardLink: link name is null");
418431
}
432+
433+
// if createLink available try first, else fall back to shell command.
434+
if (CREATE_LINK_SUPPORTED.get()) {
435+
try {
436+
Path newFile = Files.createLink(linkName.toPath(), file.toPath());
437+
if (newFile.toFile().exists()) {
438+
return;
439+
}
440+
} catch (UnsupportedOperationException e) {
441+
LOG.error("createLink not supported", e);
442+
CREATE_LINK_SUPPORTED.set(false);
443+
} catch (IOException e) {
444+
LOG.error("error when create hard link use createLink", e);
445+
CREATE_LINK_SUPPORTED.set(false);
446+
}
447+
}
448+
419449
// construct and execute shell command
420450
String[] hardLinkCommand = getHardLinkCommand.linkOne(file, linkName);
421451
Process process = Runtime.getRuntime().exec(hardLinkCommand);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.apache.bookkeeper.util;
19+
20+
import java.io.File;
21+
import java.io.IOException;
22+
import java.util.UUID;
23+
import org.apache.commons.io.FileUtils;
24+
import org.junit.After;
25+
import org.junit.Assert;
26+
import org.junit.Before;
27+
import org.junit.Test;
28+
29+
30+
public class TestHardLink {
31+
32+
private File tempDir;
33+
34+
@Before
35+
public void setup() throws IOException {
36+
// Create at least one file so that target disk will never be empty
37+
tempDir = IOUtils.createTempDir("TestHardLink", "test-hardlink");
38+
}
39+
40+
@After
41+
public void tearDown() throws IOException {
42+
FileUtils.deleteDirectory(tempDir);
43+
}
44+
45+
private void verifyHardLink(File origin, File linkedOrigin) throws IOException {
46+
Assert.assertTrue(origin.exists());
47+
Assert.assertFalse(linkedOrigin.exists());
48+
49+
HardLink.createHardLink(origin, linkedOrigin);
50+
51+
Assert.assertTrue(origin.exists());
52+
Assert.assertTrue(linkedOrigin.exists());
53+
54+
// when delete origin file it should be success and not exist.
55+
origin.delete();
56+
Assert.assertFalse(origin.exists());
57+
Assert.assertTrue(linkedOrigin.exists());
58+
}
59+
60+
@Test
61+
public void testHardLink() throws IOException {
62+
String uuidSuffix = UUID.randomUUID().toString();
63+
64+
// prepare file
65+
File origin = new File(tempDir, "originFile." + uuidSuffix);
66+
File linkedOrigin = new File(tempDir, "linkedOrigin." + uuidSuffix);
67+
origin.createNewFile();
68+
69+
// disable jdk api link first
70+
HardLink.enableJdkLinkApi(false);
71+
verifyHardLink(origin, linkedOrigin);
72+
73+
// prepare file
74+
File jdkorigin = new File(tempDir, "jdkoriginFile." + uuidSuffix);
75+
File jdklinkedOrigin = new File(tempDir, "jdklinkedOrigin." + uuidSuffix);
76+
jdkorigin.createNewFile();
77+
78+
// enable jdk api link
79+
HardLink.enableJdkLinkApi(true);
80+
verifyHardLink(jdkorigin, jdklinkedOrigin);
81+
}
82+
}

0 commit comments

Comments
 (0)