Skip to content

Commit 45d6231

Browse files
committed
Added logic to fail back based on a pid file.
1 parent a749fae commit 45d6231

File tree

4 files changed

+175
-1
lines changed

4 files changed

+175
-1
lines changed

pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@
6363
<artifactId>commons-pool2</artifactId>
6464
<version>2.11.1</version>
6565
</dependency>
66+
<dependency>
67+
<groupId>commons-io</groupId>
68+
<artifactId>commons-io</artifactId>
69+
<version>2.11.0</version>
70+
</dependency>
6671
<dependency>
6772
<groupId>org.slf4j</groupId>
6873
<artifactId>slf4j-simple</artifactId>
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.redis.benchmark.utils;
2+
3+
import java.io.File;
4+
import org.apache.commons.io.monitor.FileAlterationListener;
5+
import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
6+
import org.apache.commons.io.monitor.FileAlterationMonitor;
7+
import org.apache.commons.io.monitor.FileAlterationObserver;
8+
9+
public final class FileEventListener {
10+
11+
public static final FileEventListener FILE_EVENT_LISTENER = new FileEventListener();
12+
13+
public FileEventListener() {
14+
}
15+
16+
/*
17+
Apache commons monitoring uses a polling mechanism with a configurable polling interval.
18+
In every poll, it calls listFiles() method of File class and compares with the listFiles()
19+
output of the previous iteration to identify file creation, modification and deletion.
20+
The algorithm is robust enough. Works even on network drives.
21+
*/
22+
public void start(String dir, int pollInterval) throws Exception {
23+
FileAlterationObserver observer = new FileAlterationObserver(dir);
24+
FileAlterationMonitor monitor = new FileAlterationMonitor(pollInterval);
25+
FileAlterationListener listener = new FileAlterationListenerAdaptor() {
26+
@Override
27+
public void onFileCreate(File file) {
28+
System.out.println("\nDetected file create event. File: " + file);
29+
int failBackIndex = Integer.parseInt(file.getName().substring(0, 1));
30+
JedisConnectionManagement.firstActiveIndex = failBackIndex;
31+
System.out.println("User have requested a fail-back event. Setting the active multi cluster index to " + failBackIndex);
32+
JedisConnectionManagement.provider.setActiveMultiClusterIndex(JedisConnectionManagement.firstActiveIndex);
33+
}
34+
35+
@Override
36+
public void onFileDelete(File file) {
37+
System.out.println("\nDetected file delete event. File: " + file);
38+
}
39+
40+
@Override
41+
public void onFileChange(File file) {
42+
System.out.println("\nDetected file change event. File: " + file);
43+
}
44+
};
45+
observer.addListener(listener);
46+
monitor.addObserver(observer);
47+
monitor.start();
48+
System.out.println("Starting File Listener Service on " + observer.getDirectory());
49+
}
50+
51+
}

src/main/java/com/redis/benchmark/utils/JedisConnectionManagement.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,26 @@
88
import redis.clients.jedis.commands.JedisCommands;
99
import redis.clients.jedis.MultiClusterJedisClientConfig;
1010
import redis.clients.jedis.providers.MultiClusterPooledConnectionProvider;
11+
12+
import java.io.File;
13+
import java.nio.file.Files;
14+
import java.nio.file.Path;
1115
import java.util.Set;
1216

1317
public final class JedisConnectionManagement {
1418
private static final JedisConnectionManagement connectionManagement = new JedisConnectionManagement();
1519
private static Boolean connectionCreated = false;
1620
private UnifiedJedis unifiedJedis;
21+
public static MultiClusterPooledConnectionProvider provider;
22+
public static int firstActiveIndex;
23+
public static String pidPath;
24+
public static String pidFile;
25+
1726

1827
private JedisConnectionManagement() {
1928
}
2029

2130
private void createJedisConnection() {
22-
MultiClusterPooledConnectionProvider provider;
2331
MultiClusterJedisClientConfig.Builder multiClusterJedisClientConfig;
2432
Set<HostAndPort> hostAndPorts = BenchmarkConfiguration.get().getRedisHostAndPorts();
2533
int index = 0;
@@ -52,6 +60,13 @@ private void createJedisConnection() {
5260
provider = new MultiClusterPooledConnectionProvider(multiClusterJedisClientConfig.build());
5361

5462
connectionManagement.unifiedJedis = new UnifiedJedis(provider);
63+
64+
firstActiveIndex = Integer.parseInt(provider.getClusterCircuitBreaker().getName().split(":")[1]);
65+
pidPath = System.getProperty("user.dir") + File.separator + "jedisPid" + File.separator;
66+
pidFile = pidPath + firstActiveIndex + ".pid";
67+
PidFile.create(Path.of(pidFile), true, firstActiveIndex);
68+
69+
FileEventListener.FILE_EVENT_LISTENER.start(pidPath, 1000);
5570
}
5671
} catch (Exception e) {
5772
System.err.println("------------------- Failed UnifiedJedis " + e.getMessage());
@@ -63,6 +78,9 @@ public static JedisCommands getCommands() {
6378
connectionManagement.createJedisConnection();
6479
connectionCreated = Boolean.TRUE;
6580
}
81+
if (!Files.exists(Path.of(pidFile)) && !Files.isRegularFile(Path.of(pidFile))) {
82+
provider.setActiveMultiClusterIndex(firstActiveIndex);
83+
}
6684
return connectionManagement.unifiedJedis;
6785
}
6886
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package com.redis.benchmark.utils;
2+
3+
import java.io.IOException;
4+
import java.io.OutputStream;
5+
import java.lang.management.ManagementFactory;
6+
import java.nio.charset.Charset;
7+
import java.nio.file.Files;
8+
import java.nio.file.Path;
9+
import java.nio.file.StandardOpenOption;
10+
11+
/**
12+
* Process ID file abstraction that writes the current pid into a file and optionally
13+
* removes it on system exit.
14+
*/
15+
public final class PidFile {
16+
17+
private final long pid;
18+
private final Path path;
19+
private final boolean deleteOnExit;
20+
21+
private PidFile(Path path, boolean deleteOnExit, long pid) {
22+
this.path = path;
23+
this.deleteOnExit = deleteOnExit;
24+
this.pid = pid;
25+
}
26+
27+
/**
28+
* Creates a new PidFile and writes the current process ID into the provided path
29+
*
30+
* @param path the path to the pid file. The file is newly created or truncated if it already exists
31+
* @param deleteOnExit if <code>true</code> the pid file is deleted with the best effort on system exit
32+
* @throws IOException if an IOException occurs
33+
*/
34+
public static PidFile create(Path path, boolean deleteOnExit) throws IOException {
35+
return create(path, deleteOnExit, ManagementFactory.getRuntimeMXBean().getPid());
36+
}
37+
38+
static PidFile create(Path path, boolean deleteOnExit, long pid) throws IOException {
39+
Path parent = path.getParent();
40+
if (parent != null) {
41+
if (Files.exists(parent) && !Files.isDirectory(parent)) {
42+
throw new IllegalArgumentException(parent + " exists but is not a directory");
43+
}
44+
if (!Files.exists(parent)) {
45+
// only do this if it doesn't exist we get a better exception further down
46+
// if there are security issues etc. this also doesn't work if the parent exists
47+
// and is a soft-link like on many linux systems /var/run can be a link and that should
48+
// not prevent us from writing the PID
49+
Files.createDirectories(parent);
50+
}
51+
}
52+
if (Files.exists(path) && !Files.isRegularFile(path))
53+
throw new IllegalArgumentException(path + " exists but is not a regular file");
54+
55+
try (OutputStream stream = Files.newOutputStream(path, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {
56+
stream.write(Long.toString(pid).getBytes(Charset.defaultCharset()));
57+
}
58+
59+
if (deleteOnExit)
60+
addShutdownHook(path);
61+
62+
return new PidFile(path, deleteOnExit, pid);
63+
}
64+
65+
66+
/**
67+
* Returns the current process id
68+
*/
69+
public long getPid() {
70+
return pid;
71+
}
72+
73+
/**
74+
* Returns the process id file path
75+
*/
76+
public Path getPath() {
77+
return path;
78+
}
79+
80+
/**
81+
* Returns <code>true</code> if the process id file is deleted on system exit. Otherwise <code>false</code>.
82+
*/
83+
public boolean isDeleteOnExit() {
84+
return deleteOnExit;
85+
}
86+
87+
private static void addShutdownHook(final Path path) {
88+
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
89+
try {
90+
Files.deleteIfExists(path);
91+
} catch (IOException e) {
92+
try {
93+
throw new Exception("Failed to delete pid file " + path, e);
94+
} catch (Exception ex) {
95+
throw new RuntimeException(ex);
96+
}
97+
}
98+
}));
99+
}
100+
}

0 commit comments

Comments
 (0)