Skip to content

Commit

Permalink
[Java] Add method to ensuring that a link file exists.
Browse files Browse the repository at this point in the history
  • Loading branch information
mikeb01 committed Jul 13, 2023
1 parent e439036 commit 29e946c
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 4 deletions.
75 changes: 74 additions & 1 deletion agrona/src/main/java/org/agrona/MarkFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,19 @@
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.function.IntConsumer;

import static java.nio.channels.FileChannel.MapMode.READ_WRITE;
import static java.nio.file.StandardOpenOption.*;
import static java.nio.charset.StandardCharsets.US_ASCII;
import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.READ;
import static java.nio.file.StandardOpenOption.SPARSE;
import static java.nio.file.StandardOpenOption.WRITE;
import static org.agrona.BitUtil.SIZE_OF_INT;
import static org.agrona.BitUtil.SIZE_OF_LONG;

Expand Down Expand Up @@ -703,6 +710,72 @@ public static boolean isActive(
return timestampAgeMs <= timeoutMs;
}

/**
* Ensure a link file exists if required for the actual mark file. A link file will contain the pathname of the
* actual mark file. This is useful if the mark file should be stored on a different storage medium to the directory
* of the service. This will create a file with name of {@code linkFilename} in the {@code serviceDir} that will
* contain the parent directory of {@code actualFile}. If {@code actualFile} is an immediate child of {@code
* serviceDir} then any file with the name of {@code linkFilename} will be deleted from the {@code serviceDir} (so
* that links won't be present if not required).
*
* @param serviceDir directory where the mark file would normally be stored (e.g. archiveDir, clusterDir).
* @param actualFile location of actual mark file, e.g. /dev/shm/service/node0/archive-mark.dat
* @param linkFilename short name that should be used for the link file, e.g. archive-mark.lnk
*/
public static void ensureMarkFileLink(final File serviceDir, final File actualFile, final String linkFilename)
{
final String archiveDirPath;
final String markFileParentPath;

try
{
archiveDirPath = serviceDir.getCanonicalPath();
}
catch (final IOException ex)
{
throw new IllegalArgumentException("failed to resolve canonical path for archiveDir=" + serviceDir);
}

try
{
markFileParentPath = actualFile.getParentFile().getCanonicalPath();
}
catch (final IOException ex)
{
throw new IllegalArgumentException(
"failed to resolve canonical path for markFile parent dir of " + actualFile);
}

final Path linkFile = new File(archiveDirPath, linkFilename).toPath();
if (archiveDirPath.equals(markFileParentPath))
{
try
{
Files.deleteIfExists(linkFile);
}
catch (final IOException ex)
{
throw new RuntimeException("failed to remove old link file", ex);
}
}
else
{
try
{
Files.write(
linkFile,
markFileParentPath.getBytes(US_ASCII),
StandardOpenOption.CREATE,
StandardOpenOption.WRITE,
StandardOpenOption.TRUNCATE_EXISTING);
}
catch (final IOException ex)
{
throw new RuntimeException("failed to create link for mark file directory", ex);
}
}
}

/**
* Put thread to sleep for the given duration and restore interrupted status if thread is interrupted while
* sleeping.
Expand Down
40 changes: 37 additions & 3 deletions agrona/src/test/java/org/agrona/MarkFileTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,25 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

class MarkFileTest
{
@TempDir
File directory;
File serviceDirectory;
@TempDir
File alternativeDirectory;

@Test
void shouldWaitForMarkFileToContainEnoughDataForVersionCheck() throws IOException
{
final String filename = "markfile.dat";
final Path markFilePath = directory.toPath().resolve(filename);
final Path markFilePath = serviceDirectory.toPath().resolve(filename);
Files.createFile(markFilePath);

try (FileChannel channel = FileChannel.open(markFilePath, StandardOpenOption.WRITE))
Expand All @@ -47,6 +53,34 @@ void shouldWaitForMarkFileToContainEnoughDataForVersionCheck() throws IOExceptio
}

assertThrows(IllegalStateException.class,
() -> new MarkFile(directory, filename, 0, 16, 10, new SystemEpochClock(), (v) -> {}, (msg) -> {}));
() -> new MarkFile(serviceDirectory, filename, 0, 16, 10, new SystemEpochClock(), (v) -> {}, (msg) -> {}));
}

@Test
void shouldCreateLinkFileIfFileInDifferentLocation() throws IOException
{
final String linkFilename = "markfile.lnk";
final File markFileLocation = new File(alternativeDirectory, "markfile.dat");

MarkFile.ensureMarkFileLink(serviceDirectory, markFileLocation, linkFilename);
final File linkFileLocation = new File(serviceDirectory, linkFilename);
assertTrue(linkFileLocation.exists());
final List<String> strings = Files.readAllLines(linkFileLocation.toPath());
assertEquals(1, strings.size());
assertEquals(markFileLocation.getCanonicalFile().getParent(), strings.get(0));
}

@Test
void shouldRemoveLinkFileIfMarkFileIsInServiceDirectory() throws IOException
{
final String linkFilename = "markfile.lnk";
final File markFileLocation = new File(serviceDirectory, "markfile.dat");
final File linkFileLocation = new File(serviceDirectory, linkFilename);

assertTrue(linkFileLocation.createNewFile());
assertTrue(linkFileLocation.exists());

MarkFile.ensureMarkFileLink(serviceDirectory, markFileLocation, linkFilename);
assertFalse(linkFileLocation.exists());
}
}

0 comments on commit 29e946c

Please sign in to comment.