Infinite loop when mysql-connector-java or netty is on the classpath
An infinite loop occurs when trying to use Chronicle-Logger when mysql-connector-java is on the classpath and also netty.
[main] WARN net.openhft.chronicle.wire.WireMarshaller - Found this$0, in class com.mysql.cj.PerConnectionLRUFactory$PerConnectionLRU which will be ignored!
java.lang.StackOverflowError
at net.openhft.chronicle.bytes.NativeBytesStore.addressForWrite(NativeBytesStore.java:544)
at net.openhft.chronicle.bytes.AbstractBytes.addressForWritePosition(AbstractBytes.java:968)
at net.openhft.chronicle.bytes.MappedBytes.append8bit0(MappedBytes.java:647)
at net.openhft.chronicle.bytes.MappedBytes.append8bit(MappedBytes.java:620)
at net.openhft.chronicle.bytes.MappedBytes.append8bit(MappedBytes.java:46)
at net.openhft.chronicle.bytes.ByteStringAppender.append8bit(ByteStringAppender.java:249)
at net.openhft.chronicle.wire.BinaryWire.writeField0(BinaryWire.java:1170)
at net.openhft.chronicle.wire.BinaryWire.writeField(BinaryWire.java:1154)
at net.openhft.chronicle.wire.BinaryWire.write(BinaryWire.java:1113)
at net.openhft.chronicle.wire.WireMarshaller$FieldAccess.write(WireMarshaller.java:509)
at net.openhft.chronicle.wire.WireMarshaller.writeMarshallable(WireMarshaller.java:197)
at net.openhft.chronicle.wire.Wires.writeMarshallable(Wires.java:329)
at net.openhft.chronicle.wire.BinaryWire$FixedBinaryValueOut.marshallable(BinaryWire.java:1879)
at net.openhft.chronicle.wire.ValueOut.typedMarshallable(ValueOut.java:451)
at net.openhft.chronicle.wire.ValueOut.object(ValueOut.java:671)
at net.openhft.chronicle.wire.ValueOut.object(ValueOut.java:516)
at net.openhft.chronicle.wire.WireMarshaller$ObjectFieldAccess.getValue(WireMarshaller.java:663)
at net.openhft.chronicle.wire.WireMarshaller$FieldAccess.write(WireMarshaller.java:516)
at net.openhft.chronicle.wire.WireMarshaller.writeMarshallable(WireMarshaller.java:197)
at net.openhft.chronicle.wire.Wires.writeMarshallable(Wires.java:329)
at net.openhft.chronicle.wire.ValueOut.lambda$object$20(ValueOut.java:689)
at net.openhft.chronicle.wire.BinaryWire$FixedBinaryValueOut.marshallable(BinaryWire.java:1840)
at net.openhft.chronicle.wire.ValueOut.object(ValueOut.java:689)
at net.openhft.chronicle.wire.ValueOut.object(ValueOut.java:516)
at net.openhft.chronicle.wire.WireMarshaller$ObjectFieldAccess.getValue(WireMarshaller.java:663)
at net.openhft.chronicle.wire.WireMarshaller$FieldAccess.write(WireMarshaller.java:516)
at net.openhft.chronicle.wire.WireMarshaller.writeMarshallable(WireMarshaller.java:197)
at net.openhft.chronicle.wire.Wires.writeMarshallable(Wires.java:329)
at net.openhft.chronicle.wire.BinaryWire$FixedBinaryValueOut.marshallable(BinaryWire.java:1879)
at net.openhft.chronicle.wire.ValueOut.typedMarshallable(ValueOut.java:451)
at net.openhft.chronicle.wire.ValueOut.object(ValueOut.java:671)
at net.openhft.chronicle.wire.ValueOut.object(ValueOut.java:516)
at net.openhft.chronicle.wire.WireMarshaller$ObjectFieldAccess.getValue(WireMarshaller.java:663)
at net.openhft.chronicle.wire.WireMarshaller$FieldAccess.write(WireMarshaller.java:516)
at net.openhft.chronicle.wire.WireMarshaller.writeMarshallable(WireMarshaller.java:197)
at net.openhft.chronicle.wire.Wires.writeMarshallable(Wires.java:329)
at net.openhft.chronicle.wire.ValueOut.lambda$object$20(ValueOut.java:689)
at net.openhft.chronicle.wire.BinaryWire$FixedBinaryValueOut.marshallable(BinaryWire.java:1840)
at net.openhft.chronicle.wire.ValueOut.object(ValueOut.java:689)
at net.openhft.chronicle.wire.ValueOut.object(ValueOut.java:516)
These are my full Gradle dependencies:
dependencies {
implementation group: 'net.openhft', name: 'chronicle-logger', version: '4.21ea1', ext: 'pom'
implementation group: 'net.openhft', name: 'chronicle-logger-slf4j', version: '4.21ea1'
implementation group: 'net.openhft', name: 'chronicle-logger-core', version: '4.21ea1'
implementation 'net.openhft:chronicle-logger-tools:4.20ea2'
implementation group: 'commons-io', name: 'commons-io', version: '2.8.0'
implementation group: 'mysql', name: 'mysql-connector-java', version: '8.0.22'
implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.11'
implementation group: 'com.zaxxer', name: 'HikariCP', version: '3.4.5'
implementation group: 'org.slf4j', name: 'slf4j-api', version: '1.7.30'
implementation group: 'io.netty', name: 'netty-all', version: '4.1.53.Final'
implementation group: 'org.jctools', name: 'jctools-core', version: '3.1.0'
implementation group: 'it.unimi.dsi', name: 'fastutil', version: '8.4.3'
}
And my chronicle-logger.properties:
# shared properties
chronicle.base=data/chronicle-logs/
# logger : default
chronicle.logger.root.path=data/chronicle-logs/
chronicle.logger.root.level=debug
# optional tweaks
chronicle.logger.root.cfg.rollCycle=SMALL_DAILY
Temporary fix is to define Wires.java and add ignore cases to the top of the method:
public static void writeMarshallable(@NotNull Object marshallable, @NotNull WireOut wire) {
Like so:
public static void writeMarshallable(@NotNull Object marshallable, @NotNull WireOut wire) {
String packageName = marshallable.getClass().getPackageName();
if (packageName.contains("com.mysql.cj") || packageName.contains("io.netty")) return;
WireMarshaller wm = WireMarshaller.WIRE_MARSHALLER_CL.get(marshallable.getClass());
wm.writeMarshallable(marshallable, wire);
}
This is definitely not good for performance though, and it seems to corrupt the logs so that they can't be read with ChroniCat etc.
Wires has a limitation that it can't serialize objects which are not trees i.e. contain no circular references. Detecting and handling such circular references is expensive. A simpler approach is to make the fields transient
Closing - transient is the best way to handle this