Skip to content

Commit bcc0f76

Browse files
committed
chore: Use UUID with hashing
1 parent 10a15c1 commit bcc0f76

2 files changed

Lines changed: 34 additions & 52 deletions

File tree

java-datastore/google-cloud-datastore/src/main/java/com/google/cloud/datastore/telemetry/BuiltInDatastoreMetricsProvider.java

Lines changed: 33 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,15 @@
1919
import com.google.auth.Credentials;
2020
import com.google.cloud.NoCredentials;
2121
import com.google.common.base.Preconditions;
22+
import com.google.common.hash.HashFunction;
23+
import com.google.common.hash.Hashing;
2224
import io.opentelemetry.api.OpenTelemetry;
2325
import io.opentelemetry.api.common.Attributes;
2426
import io.opentelemetry.api.common.AttributesBuilder;
2527
import io.opentelemetry.sdk.OpenTelemetrySdk;
2628
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
2729
import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder;
2830
import io.opentelemetry.sdk.resources.Resource;
29-
import java.lang.management.ManagementFactory;
30-
import java.lang.reflect.Method;
31-
import java.net.InetAddress;
32-
import java.net.UnknownHostException;
3331
import java.util.HashMap;
3432
import java.util.Map;
3533
import java.util.UUID;
@@ -61,18 +59,44 @@ class BuiltInDatastoreMetricsProvider {
6159
private static volatile String location;
6260
private static final String DEFAULT_LOCATION = "global";
6361

64-
// Pre-computed once per JVM; hostname lookup can block, so we pay the cost at class-init time.
65-
private static final String PID_AND_HOSTNAME = getProcessId() + "@" + getHostnameSafely();
66-
6762
private BuiltInDatastoreMetricsProvider() {}
6863

6964
static Map<String, String> buildClientAttributes() {
7065
Map<String, String> attrs = new HashMap<>();
71-
attrs.put(TelemetryConstants.CLIENT_UID_KEY.getKey(), getDefaultTaskValue());
66+
attrs.put(
67+
TelemetryConstants.CLIENT_UID_KEY.getKey(), hashClientUId(UUID.randomUUID().toString()));
7268
attrs.put(TelemetryConstants.SERVICE_KEY.getKey(), TelemetryConstants.SERVICE_VALUE);
7369
return attrs;
7470
}
7571

72+
/**
73+
* Generates a 6-digit zero-padded all lower case hexadecimal representation of hash of the
74+
* accounting group. The hash utilizes the 10 most significant bits of the value returned by
75+
* `Hashing.goodFastHash(64).hashBytes()`, so effectively the returned values are uniformly
76+
* distributed in the range [000000, 0003ff].
77+
*
78+
* <p>The primary purpose of this function is to generate a hash value for the `client_uid` metric
79+
* field. The range of values is chosen to be small enough to keep the cardinality under control.
80+
*
81+
* <p>Note: If at later time the range needs to be increased, it can be done by increasing the
82+
* value of `kPrefixLength` to up to 24 bits without changing the format of the returned value.
83+
*
84+
* @return Returns a 6-digit zero-padded all lower case hexadecimal representation of hash of the
85+
* accounting group.
86+
*/
87+
private static String hashClientUId(String uuid) {
88+
if (uuid == null) {
89+
return "000000";
90+
}
91+
92+
HashFunction hashFunction = Hashing.goodFastHash(64);
93+
long hash = hashFunction.hashBytes(uuid.getBytes()).asLong();
94+
// Don't change this value without reading above comment
95+
int kPrefixLength = 10;
96+
long shiftedValue = hash >>> (64 - kPrefixLength);
97+
return String.format("%06x", shiftedValue);
98+
}
99+
76100
/**
77101
* Creates a new {@link OpenTelemetry} instance for a single Datastore client's built-in metrics.
78102
*
@@ -133,7 +157,7 @@ public OpenTelemetry createOpenTelemetry(
133157
*/
134158
String detectClientLocation() {
135159
if (location == null) {
136-
location = "global";
160+
location = DEFAULT_LOCATION;
137161
}
138162
return location;
139163
}
@@ -157,46 +181,4 @@ Attributes createResourceAttributes(String projectId, String databaseId) {
157181
.put(TelemetryConstants.LOCATION_ID_KEY, detectClientLocation());
158182
return attributesBuilder.build();
159183
}
160-
161-
/**
162-
* Generates a unique identifier for the {@code client_uid} metric field.
163-
*
164-
* <p>Combines a random UUID with the pre-computed {@code PID_AND_HOSTNAME} (typically {@code
165-
* pid@hostname}). The UUID prefix ensures uniqueness across process restarts that reuse the same
166-
* PID, preventing Cloud Monitoring from conflating time series from different process lifecycles.
167-
*
168-
* @return a unique identifier string.
169-
*/
170-
private static String getDefaultTaskValue() {
171-
return UUID.randomUUID().toString() + "@" + PID_AND_HOSTNAME;
172-
}
173-
174-
private static String getHostnameSafely() {
175-
try {
176-
return InetAddress.getLocalHost().getHostName();
177-
} catch (UnknownHostException e) {
178-
logger.log(Level.CONFIG, "Unable to get the hostname.", e);
179-
return "localhost";
180-
}
181-
}
182-
183-
private static String getProcessId() {
184-
try {
185-
// Check if Java 9+ and ProcessHandle class is available
186-
Class<?> processHandleClass = Class.forName("java.lang.ProcessHandle");
187-
Method currentMethod = processHandleClass.getMethod("current");
188-
Object processHandleInstance = currentMethod.invoke(null);
189-
Method pidMethod = processHandleClass.getMethod("pid");
190-
long pid = (long) pidMethod.invoke(processHandleInstance);
191-
return Long.toString(pid);
192-
} catch (Exception e) {
193-
// Fallback to Java 8 method
194-
final String jvmName = ManagementFactory.getRuntimeMXBean().getName();
195-
if (jvmName != null && jvmName.contains("@")) {
196-
return jvmName.split("@")[0];
197-
} else {
198-
return "unknown";
199-
}
200-
}
201-
}
202184
}

java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/telemetry/BuiltInDatastoreMetricsProviderTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public class BuiltInDatastoreMetricsProviderTest {
3737
public void testBuildClientAttributes() {
3838
Map<String, String> attributes = BuiltInDatastoreMetricsProvider.buildClientAttributes();
3939
assertThat(attributes).containsKey(TelemetryConstants.CLIENT_UID_KEY.getKey());
40-
assertThat(attributes.get(TelemetryConstants.CLIENT_UID_KEY.getKey())).contains("@");
40+
assertThat(attributes.get(TelemetryConstants.CLIENT_UID_KEY.getKey())).isNotEmpty();
4141
assertThat(attributes).containsKey(TelemetryConstants.SERVICE_KEY.getKey());
4242
assertThat(attributes.get(TelemetryConstants.SERVICE_KEY.getKey()))
4343
.isEqualTo(TelemetryConstants.SERVICE_VALUE);

0 commit comments

Comments
 (0)