Skip to content

Commit b290373

Browse files
Alexandre Dutraolim7t
authored andcommitted
JAVA-444: Add Java process information to UUIDs.makeNode() hash.
1 parent fc8ef94 commit b290373

6 files changed

Lines changed: 278 additions & 66 deletions

File tree

changelog/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
- [new feature] JAVA-982: Introduce new method ConsistencyLevel.isSerial().
3636
- [bug] JAVA-764: Retry with the normal consistency level (not the serial one) when a write times out on the Paxos phase.
3737
- [bug] JAVA-727: Allow monotonic timestamp generators to drift in the future + use microsecond precision when possible.
38+
- [improvement] JAVA-444: Add Java process information to UUIDs.makeNode() hash.
3839

3940
Merged from 2.0 branch:
4041

driver-core/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@
5757
<version>${jnr-ffi.version}</version>
5858
</dependency>
5959

60+
<dependency>
61+
<groupId>com.github.jnr</groupId>
62+
<artifactId>jnr-posix</artifactId>
63+
<version>${jnr-posix.version}</version>
64+
</dependency>
65+
6066
<!-- Compression libraries for the protocol. -->
6167
<!-- Each of them is only a mandatory runtime dependency if you want to use the compression it offers -->
6268

driver-core/src/main/java/com/datastax/driver/core/Clock.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class ClockFactory {
4848
private static final String USE_NATIVE_CLOCK_SYSTEM_PROPERTY = "com.datastax.driver.USE_NATIVE_CLOCK";
4949

5050
static Clock newInstance() {
51-
if (Native.isLibCLoaded() && SystemProperties.getBoolean(USE_NATIVE_CLOCK_SYSTEM_PROPERTY, true)) {
51+
if (Native.isGettimeofdayAvailable() && SystemProperties.getBoolean(USE_NATIVE_CLOCK_SYSTEM_PROPERTY, true)) {
5252
LOGGER.info("Using native clock to generate timestamps.");
5353
return new NativeClock();
5454
} else {

driver-core/src/main/java/com/datastax/driver/core/Native.java

Lines changed: 173 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -24,25 +24,49 @@
2424
import org.slf4j.Logger;
2525
import org.slf4j.LoggerFactory;
2626

27+
import java.lang.reflect.Method;
28+
2729
/**
28-
* Helper class to deal with native system call through JNR.
30+
* Helper class to deal with native system calls through the
31+
* <a href="https://github.com/jnr/jnr-ffi">JNR library</a>.
32+
* <p/>
33+
* The driver can benefit from native system calls to improve its performance and accuracy
34+
* in some situations.
35+
* <p/>
36+
* Currently, the following features may be used by the driver when available:
37+
* <ol>
38+
* <li>{@link #currentTimeMicros()}: thanks to a system call to {@code gettimeofday()},
39+
* the driver is able to generate timestamps with true microsecond precision
40+
* (see {@link AtomicMonotonicTimestampGenerator} or {@link ThreadLocalMonotonicTimestampGenerator} for
41+
* more information);</li>
42+
* <li>{@link #processId()}: thanks to a system call to {@code getpid()},
43+
* the driver has access to the JVM's process ID it is running under – which
44+
* makes time-based UUID generation easier and more reliable (see {@link com.datastax.driver.core.utils.UUIDs UUIDs}
45+
* for more information).</li>
46+
* </ol>
47+
* <p/>
48+
* The availability of the aforementioned system calls depends on the underlying operation system's
49+
* capabilities. For instance, {@code gettimeofday()} is not available under Windows systems.
50+
* You can check if any of the system calls exposed through this class is available
51+
* by calling {@link #isGettimeofdayAvailable()} or {@link #isGetpidAvailable()}.
52+
* <p/>
53+
* Note: This class is public because it needs to be accessible from other packages of the Java driver,
54+
* but it is not meant to be used directly by client code.
55+
*
56+
* @see <a href="https://github.com/jnr/jnr-ffi">JNR library on Github</a>
2957
*/
30-
class Native {
58+
public final class Native {
3159

3260
private static final Logger LOGGER = LoggerFactory.getLogger(Native.class);
3361

34-
/**
35-
* Interface for LIBC calls through JNR.
36-
* Note that this interface must be declared public.
37-
*/
38-
public interface LibC {
62+
private static class LibCLoader {
3963

4064
/**
4165
* Timeval struct.
4266
*
4367
* @see <a href="http://man7.org/linux/man-pages/man2/settimeofday.2.html">GETTIMEOFDAY(2)</a>
4468
*/
45-
class Timeval extends Struct {
69+
static class Timeval extends Struct {
4670

4771
public final time_t tv_sec = new time_t();
4872

@@ -54,56 +78,167 @@ public Timeval(Runtime runtime) {
5478
}
5579

5680
/**
57-
* JNR call to {@code gettimeofday}.
58-
*
59-
* @param tv Timeval struct
60-
* @param unused Timezone struct (unused)
61-
* @return 0 for success, or -1 for failure
62-
* @see <a href="http://man7.org/linux/man-pages/man2/settimeofday.2.html">GETTIMEOFDAY(2)</a>
81+
* Interface for LIBC calls through JNR.
82+
* Note that this interface must be declared public.
6383
*/
64-
int gettimeofday(@Out @Transient Timeval tv, Pointer unused);
84+
public interface LibC {
85+
86+
/**
87+
* JNR call to {@code gettimeofday}.
88+
*
89+
* @param tv Timeval struct
90+
* @param unused Timezone struct (unused)
91+
* @return 0 for success, or -1 for failure
92+
* @see <a href="http://man7.org/linux/man-pages/man2/settimeofday.2.html">GETTIMEOFDAY(2)</a>
93+
*/
94+
int gettimeofday(@Out @Transient Timeval tv, Pointer unused);
95+
96+
}
97+
98+
private static final LibC LIB_C;
99+
100+
private static final Runtime LIB_C_RUNTIME;
101+
102+
private static final boolean GETTIMEOFDAY_AVAILABLE;
103+
104+
static {
105+
LibC libc;
106+
Runtime runtime = null;
107+
try {
108+
libc = LibraryLoader.create(LibC.class).load("c");
109+
runtime = Runtime.getRuntime(libc);
110+
} catch (Throwable t) {
111+
libc = null; // dereference proxy to library if runtime could not be loaded
112+
if (LOGGER.isDebugEnabled())
113+
LOGGER.debug("Could not load JNR C Library, native system calls through this library will not be available", t);
114+
else
115+
LOGGER.info("Could not load JNR C Library, native system calls through this library will not be available " +
116+
"(set this logger level to DEBUG to see the full stack trace).");
117+
118+
}
119+
LIB_C = libc;
120+
LIB_C_RUNTIME = runtime;
121+
boolean gettimeofday = false;
122+
if (LIB_C_RUNTIME != null) {
123+
try {
124+
gettimeofday = LIB_C.gettimeofday(new Timeval(LIB_C_RUNTIME), null) == 0;
125+
} catch (Throwable t) {
126+
if (LOGGER.isDebugEnabled())
127+
LOGGER.debug("Native calls to gettimeofday() not available on this system.", t);
128+
else
129+
LOGGER.info("Native calls to gettimeofday() not available on this system " +
130+
"(set this logger level to DEBUG to see the full stack trace).");
131+
}
132+
}
133+
GETTIMEOFDAY_AVAILABLE = gettimeofday;
134+
}
135+
65136
}
66137

67-
private static final LibC LIB_C;
138+
private static class PosixLoader {
68139

69-
private static final Runtime LIB_C_RUNTIME;
140+
public static final jnr.posix.POSIX POSIX;
70141

71-
static {
72-
LibC libc = null;
73-
Runtime runtime = null;
142+
private static final boolean GETPID_AVAILABLE;
143+
144+
static {
145+
jnr.posix.POSIX posix;
146+
try {
147+
// use reflection below to get the classloader a chance to load this class
148+
Class<?> posixHandler = Class.forName("jnr.posix.POSIXHandler");
149+
Class<?> defaultPosixHandler = Class.forName("jnr.posix.util.DefaultPOSIXHandler");
150+
Class<?> posixFactory = Class.forName("jnr.posix.POSIXFactory");
151+
Method getPOSIX = posixFactory.getMethod("getPOSIX", posixHandler, Boolean.TYPE);
152+
posix = (jnr.posix.POSIX) getPOSIX.invoke(null, defaultPosixHandler.newInstance(), true);
153+
} catch (Throwable t) {
154+
posix = null;
155+
if (LOGGER.isDebugEnabled())
156+
LOGGER.debug("Could not load JNR POSIX Library, native system calls through this library will not be available.", t);
157+
else
158+
LOGGER.info("Could not load JNR POSIX Library, native system calls through this library will not be available " +
159+
"(set this logger level to DEBUG to see the full stack trace).");
160+
}
161+
POSIX = posix;
162+
boolean getpid = false;
163+
if (POSIX != null) {
164+
try {
165+
POSIX.getpid();
166+
getpid = true;
167+
} catch (Throwable t) {
168+
if (LOGGER.isDebugEnabled())
169+
LOGGER.debug("Native calls to getpid() not available on this system.", t);
170+
else
171+
LOGGER.info("Native calls to getpid() not available on this system " +
172+
"(set this logger level to DEBUG to see the full stack trace).");
173+
}
174+
}
175+
GETPID_AVAILABLE = getpid;
176+
}
177+
178+
}
179+
180+
/**
181+
* Returns {@code true} if JNR C library is loaded and
182+
* a call to {@code gettimeofday} is possible through this library
183+
* on this system, and {@code false} otherwise.
184+
*
185+
* @return {@code true} if JNR C library is loaded and
186+
* a call to {@code gettimeofday} is possible.
187+
*/
188+
public static boolean isGettimeofdayAvailable() {
74189
try {
75-
libc = LibraryLoader.create(LibC.class).load("c");
76-
runtime = Runtime.getRuntime(libc);
77-
} catch (Throwable t) {
78-
if (LOGGER.isDebugEnabled())
79-
LOGGER.debug("Could not load JNR LibC Library, native calls will not be available", t);
80-
else
81-
LOGGER.info("Could not load JNR LibC Library, native calls will not be available (set this logger level to DEBUG to see the full stack trace)");
190+
return LibCLoader.GETTIMEOFDAY_AVAILABLE;
191+
} catch (NoClassDefFoundError e) {
192+
return false;
82193
}
83-
LIB_C = libc;
84-
LIB_C_RUNTIME = runtime;
85194
}
86195

87196
/**
88-
* Returns true if LibC could be loaded with JNR.
197+
* Returns {@code true} if JNR POSIX library is loaded and
198+
* a call to {@code getpid} is possible through this library
199+
* on this system, and {@code false} otherwise.
89200
*
90-
* @return true if LibC could be loaded with JNR, false otherwise.
201+
* @return {@code true} if JNR POSIX library is loaded and
202+
* a call to {@code getpid} is possible.
91203
*/
92-
static boolean isLibCLoaded() {
93-
return LIB_C_RUNTIME != null;
204+
public static boolean isGetpidAvailable() {
205+
try {
206+
return PosixLoader.GETPID_AVAILABLE;
207+
} catch (NoClassDefFoundError e) {
208+
return false;
209+
}
210+
94211
}
95212

96213
/**
97214
* Returns the current timestamp with microsecond precision
98-
* via a system call to {@code gettimeofday}.
215+
* via a system call to {@code gettimeofday}, through JNR C library.
99216
*
100217
* @return the current timestamp with microsecond precision.
218+
* @throws UnsupportedOperationException if JNR C library is not loaded or {@code gettimeofday} is not available.
219+
* @throws IllegalStateException if the call to {@code gettimeofday} did not complete with return code 0.
101220
*/
102-
static long currentTimeMicros() {
103-
LibC.Timeval tv = new LibC.Timeval(LIB_C_RUNTIME);
104-
if (LIB_C.gettimeofday(tv, null) != 0)
105-
LOGGER.error("gettimeofday failed");
221+
public static long currentTimeMicros() {
222+
if (!isGettimeofdayAvailable())
223+
throw new UnsupportedOperationException("JNR C library not loaded or gettimeofday not available");
224+
LibCLoader.Timeval tv = new LibCLoader.Timeval(LibCLoader.LIB_C_RUNTIME);
225+
int res = LibCLoader.LIB_C.gettimeofday(tv, null);
226+
if (res != 0)
227+
throw new IllegalStateException("Call to gettimeofday failed with result " + res);
106228
return tv.tv_sec.get() * 1000000 + tv.tv_usec.get();
107229
}
108230

231+
/**
232+
* Returns the JVM's process identifier (PID)
233+
* via a system call to {@code getpid}.
234+
*
235+
* @return the JVM's process identifier (PID).
236+
* @throws UnsupportedOperationException if JNR POSIX library is not loaded or {@code getpid} is not available.
237+
*/
238+
public static int processId() {
239+
if (!isGetpidAvailable())
240+
throw new UnsupportedOperationException("JNR POSIX library not loaded or getpid not available");
241+
return PosixLoader.POSIX.getpid();
242+
}
243+
109244
}

0 commit comments

Comments
 (0)