provide a more condensed session identifier for metrics

This commit is contained in:
Jonathan Shook
2023-08-28 16:15:34 -05:00
parent 0812f15ea2
commit aec7301e99
5 changed files with 193 additions and 33 deletions

View File

@@ -69,7 +69,7 @@ public class RawOpDef extends RawOpFields {
Object op = map.remove(keyName); Object op = map.remove(keyName);
if (op instanceof CharSequence s) { if (op instanceof CharSequence s) {
if (!keyName.equals("stmt")) { if (!keyName.equals("stmt")) {
logger.warn("Used implied stmt field under name '" + keyName + "'. You can just use 'stmt: ... "+ s +"' or the equivalent to avoid this warning."); logger.info("Used implied stmt field under name '" + keyName + "'. You can just use 'stmt: ... "+ s +"' or the equivalent to avoid this warning.");
} }
map.put("stmt",s.toString()); map.put("stmt",s.toString());
// setOp(new LinkedHashMap<String,Object>(Map.of("stmt",s.toString()))); // setOp(new LinkedHashMap<String,Object>(Map.of("stmt",s.toString())));
@@ -79,7 +79,7 @@ public class RawOpDef extends RawOpFields {
} }
if (found.size() > 1) { if (found.size() > 1) {
throw new BasicError("You used " + found + " as an op name, but only one of these is allowed at a time."); throw new BasicError("You used " + found + " as an op name, but only one of these is allowed at a time.");
} else if ((getName() == null || getName().isEmpty()) && op == null && map.size() > 0) { } else if ((getName() == null || getName().isEmpty()) && op == null && !map.isEmpty()) {
Map.Entry<String, Object> first = map.entrySet().iterator().next(); Map.Entry<String, Object> first = map.entrySet().iterator().next();
setName(first.getKey()); setName(first.getKey());
setOp(first.getValue()); setOp(first.getValue());
@@ -90,7 +90,7 @@ public class RawOpDef extends RawOpFields {
if (_op) { if (_op) {
if (_params) { if (_params) {
if (map.size() > 0) { if (!map.isEmpty()) {
throw new OpConfigError("If you have scoped op and params, you may not have dangling fields. Op template named '" + this.getName() + "' is invalid. Move dangling params ("+ map.keySet() +") under another field."); throw new OpConfigError("If you have scoped op and params, you may not have dangling fields. Op template named '" + this.getName() + "' is invalid. Move dangling params ("+ map.keySet() +") under another field.");
} }
} else { // no params. Op was a scoped field and there are dangling fields, so assume they belong to params } else { // no params. Op was a scoped field and there are dangling fields, so assume they belong to params

View File

@@ -86,6 +86,8 @@ public class NBCLI implements Function<String[], Integer>, NBLabeledElement {
private NBLabels labels; private NBLabels labels;
private String sessionName; private String sessionName;
private String sessionCode;
private long sessionTime;
public NBCLI(final String commandName) { public NBCLI(final String commandName) {
this.commandName = commandName; this.commandName = commandName;
@@ -153,10 +155,17 @@ public class NBCLI implements Function<String[], Integer>, NBLabeledElement {
// logger = LogManager.getLogger("NBCLI"); // logger = LogManager.getLogger("NBCLI");
NBCLI.loggerConfig.setConsoleLevel(NBLogLevel.ERROR); NBCLI.loggerConfig.setConsoleLevel(NBLogLevel.ERROR);
this.sessionTime = System.currentTimeMillis();
final NBCLIOptions globalOptions = new NBCLIOptions(args, Mode.ParseGlobalsOnly); final NBCLIOptions globalOptions = new NBCLIOptions(args, Mode.ParseGlobalsOnly);
this.labels=NBLabels.forKV("command",commandName).and(globalOptions.getLabelMap());
this.sessionName = SessionNamer.format(globalOptions.getSessionName()); this.sessionCode = SystemId.genSessionCode(sessionTime);
this.sessionName = SessionNamer.format(globalOptions.getSessionName(),sessionTime).replaceAll("SESSIONCODE",sessionCode);
this.labels = NBLabels.forKV("command", commandName, "appname", "nosqlbench")
.andInstances("node",SystemId.getNodeId())
.andInstances("nodeid",SystemId.getPackedNodeId())
// .andInstances("sesscode",sessionCode)
.andInstances("session",sessionName)
.and(globalOptions.getLabelMap());
NBCLI.loggerConfig NBCLI.loggerConfig
.setSessionName(sessionName) .setSessionName(sessionName)

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2022 nosqlbench * Copyright (c) 2022-2023 nosqlbench
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@ import oshi.hardware.CentralProcessor;
import oshi.hardware.HardwareAbstractionLayer; import oshi.hardware.HardwareAbstractionLayer;
import oshi.hardware.NetworkIF; import oshi.hardware.NetworkIF;
import java.net.*;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
@@ -36,32 +37,37 @@ public class SystemId {
* when you are managing configuration or results for a set of systems which * when you are managing configuration or results for a set of systems which
* share a common IP addressing scheme. This identifier should be stable as long * share a common IP addressing scheme. This identifier should be stable as long
* as the node's addresses do not change. * as the node's addresses do not change.
* * <p>
* If you are needing an identifier for a node but wish to expose any address data, * If you are needing an identifier for a node but do not with wish to expose any address data,
* you can use the {@link #getNodeFingerprint()} which takes this value and hashes * you can use the {@link #getNodeFingerprint()} which takes this value and hashes
* it with SHA-1 to produce a hex string. * it with SHA-1 to produce a hex string.
*
* @return A address for the node, likely to be unique and stable for its lifetime * @return A address for the node, likely to be unique and stable for its lifetime
*/ */
public static String getNodeId() { public static String getNodeId() {
return getMainInetAddrDirect().map(InetAddress::getHostAddress).orElse("UNKNOWN_HOST_ID");
}
private static String getNodeIdOSHI() {
SystemInfo sysinfo = new SystemInfo(); SystemInfo sysinfo = new SystemInfo();
HardwareAbstractionLayer hal = sysinfo.getHardware(); HardwareAbstractionLayer hal = sysinfo.getHardware();
List<NetworkIF> interfaces = hal.getNetworkIFs(); List<NetworkIF> interfaces = hal.getNetworkIFs();
Optional<String> first = interfaces.stream() Optional<String> first = interfaces.stream()
.filter(i -> !i.getName().startsWith("docker" )) .filter(i -> !i.getName().startsWith("docker"))
.filter(i -> !i.getName().equals("lo" )) .filter(i -> !i.getName().equals("lo"))
.sorted((o1, o2) -> { .sorted((o1, o2) -> {
if (o1.getName().startsWith("e" ) && o2.getName().startsWith("e" )) { if (o1.getName().startsWith("e") && o2.getName().startsWith("e")) {
return 0;
}
if (o1.getName().startsWith("e" )) {
return -1;
}
if (o2.getName().startsWith("e" )) {
return 1;
}
return 0; return 0;
}) }
if (o1.getName().startsWith("e")) {
return -1;
}
if (o2.getName().startsWith("e")) {
return 1;
}
return 0;
})
.flatMap(iface -> Arrays.stream(iface.getIPv4addr().clone())) .flatMap(iface -> Arrays.stream(iface.getIPv4addr().clone()))
.filter(addr -> !(addr.startsWith("127."))) .filter(addr -> !(addr.startsWith("127.")))
.findFirst(); .findFirst();
@@ -69,10 +75,51 @@ public class SystemId {
return systemID; return systemID;
} }
private static Optional<InetAddress> getMainInetAddrDirect() {
List<NetworkInterface> ifaces = getInterfacesDirect();
Optional<NetworkInterface> first = ifaces.stream()
.filter(i -> !i.getName().startsWith("docker"))
.filter(i -> !i.getName().equals("lo"))
.sorted((o1, o2) -> {
if (o1.getName().startsWith("e") && o2.getName().startsWith("e")) return 0;
if (o1.getName().startsWith("e")) return -1;
if (o2.getName().startsWith("e")) return 1;
return 0;
}).findFirst();
if (first.isEmpty()) return Optional.empty();
Optional<InetAddress> firstInetAddrForInterface = first.get().getInterfaceAddresses().stream()
.map(ia -> ia.getAddress())
.sorted((i1, i2) -> {
if (i1 instanceof Inet4Address && i2 instanceof Inet4Address) return 0;
if (i1 instanceof Inet4Address) return -1;
if (i2 instanceof Inet4Address) return 1;
return 0;
}).findFirst();
return firstInetAddrForInterface;
}
/**
* Using this to bypass OSHI because it calls logger init before we want it.
* TODO: Maybe remove OSHI altogether if there is a reasonable Java HAL view in current Java editions.
*
* @return a list of network interfaces
*/
private static List<NetworkInterface> getInterfacesDirect() {
try {
Enumeration<NetworkInterface> ni = NetworkInterface.getNetworkInterfaces();
return new ArrayList<>(Collections.list(ni));
} catch (SocketException e) {
throw new RuntimeException(e);
}
}
/** /**
* Produce a stable string identifier consisting of hexadecimal characters. * Produce a stable string identifier consisting of hexadecimal characters.
* The internal data used for this value is based on a stable ordering of non-local * The internal data used for this value is based on a stable ordering of non-local
* ip addresses available on the system. * ip addresses available on the system.
*
* @return A stable node identifier * @return A stable node identifier
*/ */
public static String getNodeFingerprint() { public static String getNodeFingerprint() {
@@ -81,9 +128,9 @@ public class SystemId {
MessageDigest sha1_digest = MessageDigest.getInstance("SHA-1"); MessageDigest sha1_digest = MessageDigest.getInstance("SHA-1");
byte[] addrBytes = sha1_digest.digest(addrId.getBytes(StandardCharsets.UTF_8)); byte[] addrBytes = sha1_digest.digest(addrId.getBytes(StandardCharsets.UTF_8));
String fingerprint = ""; String fingerprint = "";
for (int i=0; i < addrBytes.length; i++) { for (int i = 0; i < addrBytes.length; i++) {
fingerprint += fingerprint +=
Integer.toString( ( addrBytes[i] & 0xff ) + 0x100, 16).substring( 1 ); Integer.toString((addrBytes[i] & 0xff) + 0x100, 16).substring(1);
} }
return fingerprint.toUpperCase(Locale.ROOT); return fingerprint.toUpperCase(Locale.ROOT);
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
@@ -125,4 +172,75 @@ public class SystemId {
return gson.toJson(details); return gson.toJson(details);
} }
private final static String radixSymbols = "01234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ~-"; // 64 symbols, for 2^6!
private final int brailleStart = '';
private final int brailleEnd = '⣿';
private final int brailleRadix = brailleEnd - brailleStart;
public static String getBrailleNodeId() {
String nodeId = getNodeId();
String[] fields = nodeId.split("\\.");
byte[] addr;
try {
InetAddress inetAddr = Inet4Address.getByName(nodeId);
addr = inetAddr.getAddress();
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
return braille((addr[0] << 24) + (addr[1] << 16) + (addr[2] << 8) + addr[3]);
}
private static String braille(int value) {
StringBuilder buf = new StringBuilder(4);
for (int i = 0; i < 4; i++) {
int mask = value & 0xF;
value >>= 8;
int charat = '' + mask;
buf.append((char) charat);
}
return buf.toString();
}
private static String braille(long value) {
StringBuilder buf = new StringBuilder(8);
for (int i = 0; i < 8; i++) {
int mask = (int) value & 0xF;
value >>= 8;
int charat = '' + mask;
buf.append((char) charat);
}
return buf.toString();
}
public static String genSessionCode(long epochMillis) {
return pack(epochMillis) + "_" + getPackedNodeId();
}
public static String getPackedNodeId() {
String nodeId = getNodeId();
String[] fields = nodeId.split("\\.");
byte[] addr;
try {
InetAddress inetAddr = Inet4Address.getByName(nodeId);
addr = inetAddr.getAddress();
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
return pack((addr[0] << 24) + (addr[1] << 16) + (addr[2] << 8) + addr[3]);
}
public static String pack(long bitfield) {
StringBuilder sb = new StringBuilder(11);
while (bitfield > 0) {
long tail = bitfield & 0b00111111;
bitfield >>= 6;
sb.append(radixSymbols.charAt((int) tail));
}
return sb.toString();
}
public static String genSessionBits() {
return getBrailleNodeId() + ":" + braille(System.currentTimeMillis());
}
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2022 nosqlbench * Copyright (c) 2022-2023 nosqlbench
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -17,28 +17,61 @@
package io.nosqlbench.nb.api; package io.nosqlbench.nb.api;
import io.nosqlbench.api.metadata.SystemId; import io.nosqlbench.api.metadata.SystemId;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
public class SystemIdTest { public class SystemIdTest {
private static final Logger logger = LogManager.getLogger(SystemIdTest.class);
@Test @Test
public void testHostInfo() { public void testHostInfo() {
String info = SystemId.getHostSummary(); String hostSummary = SystemId.getHostSummary();
System.out.println(info); logger.info("host summary: " + hostSummary);
} }
@Test @Test
public void testNostId() { public void testNostId() {
String info = SystemId.getNodeId(); String nodeId = SystemId.getNodeId();
assertThat(info).matches("\\d+\\.\\d+\\.\\d+\\.\\d+"); assertThat(nodeId).matches("\\d+\\.\\d+\\.\\d+\\.\\d+");
logger.info("node id: " + nodeId);
} }
@Test @Test
public void testNodeFingerprint() { public void testNodeFingerprint() {
String hash = SystemId.getNodeFingerprint(); String nodeFingerprint = SystemId.getNodeFingerprint();
assertThat(hash).matches("[A-Z0-9]+"); assertThat(nodeFingerprint).matches("[A-Z0-9]+");
logger.info("node fingerprint: " + nodeFingerprint);
}
@Test
public void testBrailleNodeId() {
String brailleNodeId = SystemId.getBrailleNodeId();
assertThat(brailleNodeId).matches("[-⣿]{4}"); // note, that is not a space. It is the starting braille value of empty
logger.info("braille node id: " + brailleNodeId);
}
@Test
public void testPackedNodeId() {
String packedNodeId = SystemId.getPackedNodeId();
assertThat(packedNodeId).matches("[0-9A-Za-z_-]+");
logger.info("packed node id: " + packedNodeId);
}
@Test
public void testGenSessionCode() {
String sessionCode=SystemId.genSessionCode(234L);
assertThat(sessionCode).matches("[0-9a-zA-Z~-]+_[0-9a-zA-Z~-]+");
logger.info("session code: " + sessionCode);
}
@Test
public void testGenSessionBits() {
String sessionBits = SystemId.genSessionBits();
assertThat(sessionBits).matches("[-⣿]+:[-⣿]+");
logger.info("session bits: " + sessionBits);
} }
} }

View File

@@ -61,7 +61,7 @@
<module.adapter-tcp>adapter-tcp</module.adapter-tcp> <module.adapter-tcp>adapter-tcp</module.adapter-tcp>
<module.adapter-dynamodb>adapter-dynamodb</module.adapter-dynamodb> <module.adapter-dynamodb>adapter-dynamodb</module.adapter-dynamodb>
<module.adapter-mongodb>adapter-mongodb</module.adapter-mongodb> <module.adapter-mongodb>adapter-mongodb</module.adapter-mongodb>
<module.adapter-venice>adapter-venice</module.adapter-venice> <!-- <module.adapter-venice>adapter-venice</module.adapter-venice>-->
<module.adapter-pulsar>adapter-pulsar</module.adapter-pulsar> <module.adapter-pulsar>adapter-pulsar</module.adapter-pulsar>
<module.adapter-s4j>adapter-s4j</module.adapter-s4j> <module.adapter-s4j>adapter-s4j</module.adapter-s4j>
<module.adapter-kafka>adapter-kafka</module.adapter-kafka> <module.adapter-kafka>adapter-kafka</module.adapter-kafka>
@@ -102,7 +102,7 @@
<module>adapters-api</module> <module>adapters-api</module>
<!-- driver modules --> <!-- driver modules -->
<module>adapter-venice</module> <!-- <module>adapter-venice</module>-->
<module>adapter-diag</module> <module>adapter-diag</module>
<module>adapter-stdout</module> <module>adapter-stdout</module>
<module>adapter-cqld4</module> <module>adapter-cqld4</module>