How to call mysql in virtual threads

303 Views Asked by At

env

  • mysql-connector-java:8.0.33
  • java version:jdk21
  • springboot:3.2.1
  • virtual thread mode: true
  • connector pool: hikari

problem

  • It seems that mysql-connector-java has many synchronized which pinned the carrier thread.
  • How to solve this problem?

The trace stack info

Thread[#93,ForkJoinPool-1-worker-4,5,CarrierThreads]
    java.base/java.lang.VirtualThread$VThreadContinuation.onPinned(VirtualThread.java:185)
    java.base/jdk.internal.vm.Continuation.onPinned0(Continuation.java:393)
    java.base/java.lang.VirtualThread.parkNanos(VirtualThread.java:631)
    java.base/java.lang.System$2.parkVirtualThread(System.java:2648)
    java.base/jdk.internal.misc.VirtualThreads.park(VirtualThreads.java:67)
    java.base/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:408)
    java.base/sun.nio.ch.Poller.pollIndirect(Poller.java:137)
    java.base/sun.nio.ch.Poller.poll(Poller.java:102)
    java.base/sun.nio.ch.Poller.poll(Poller.java:87)
    java.base/sun.nio.ch.NioSocketImpl.park(NioSocketImpl.java:175)
    java.base/sun.nio.ch.NioSocketImpl.timedRead(NioSocketImpl.java:280)
    java.base/sun.nio.ch.NioSocketImpl.implRead(NioSocketImpl.java:304)
    java.base/sun.nio.ch.NioSocketImpl.read(NioSocketImpl.java:346)
    java.base/sun.nio.ch.NioSocketImpl$1.read(NioSocketImpl.java:796)
    java.base/java.net.Socket$SocketInputStream.read(Socket.java:1099)
    com.mysql.cj.protocol.ReadAheadInputStream.fill(ReadAheadInputStream.java:107)
    com.mysql.cj.protocol.ReadAheadInputStream.readFromUnderlyingStreamIfNecessary(ReadAheadInputStream.java:150)
    com.mysql.cj.protocol.ReadAheadInputStream.read(ReadAheadInputStream.java:180) <== monitors:1
    java.base/java.io.FilterInputStream.read(FilterInputStream.java:119)
    com.mysql.cj.protocol.FullReadInputStream.readFully(FullReadInputStream.java:64)
    com.mysql.cj.protocol.a.SimplePacketReader.readHeaderLocal(SimplePacketReader.java:81)
    com.mysql.cj.protocol.a.SimplePacketReader.readHeader(SimplePacketReader.java:63)
    com.mysql.cj.protocol.a.SimplePacketReader.readHeader(SimplePacketReader.java:45)
    com.mysql.cj.protocol.a.TimeTrackingPacketReader.readHeader(TimeTrackingPacketReader.java:52)
    com.mysql.cj.protocol.a.TimeTrackingPacketReader.readHeader(TimeTrackingPacketReader.java:41)
    com.mysql.cj.protocol.a.MultiPacketReader.readHeader(MultiPacketReader.java:54)
    com.mysql.cj.protocol.a.MultiPacketReader.readHeader(MultiPacketReader.java:44)
    com.mysql.cj.protocol.a.NativeProtocol.readMessage(NativeProtocol.java:576)
    com.mysql.cj.protocol.a.NativeProtocol.checkErrorMessage(NativeProtocol.java:762)
    com.mysql.cj.protocol.a.NativeProtocol.sendCommand(NativeProtocol.java:701)
    com.mysql.cj.protocol.a.NativeProtocol.sendCommand(NativeProtocol.java:156)
    com.mysql.cj.NativeSession.ping(NativeSession.java:733)
    com.mysql.cj.jdbc.ConnectionImpl.pingInternal(ConnectionImpl.java:1478)
    com.mysql.cj.jdbc.ConnectionImpl.isValid(ConnectionImpl.java:2516) <== monitors:1
    com.zaxxer.hikari.pool.PoolBase.isConnectionDead(PoolBase.java:157
2

There are 2 best solutions below

0
igor.zh On

A very simple answer: don't call mySQL JDBC Connector on virtual thread to avoid pinning. Unless you are going to rewrite mySQL JDBC Connector, of course. Java is full of synchronized and even such a popular and excellent multithreading tool like java.util.concurrent.ConcurrentHashMap has it and it would be impossible to replace all synchronized and other pinning actions with non-pinning alternatives overnight. Project Loom and virtual threads are not yet stable enough and mature enough to warrant such immense refactoring. So, the best thing you can do is to avoid using virtual threads for frequently pinning code (while occasional pinning might be tolerable)

By other hand, if a virtual thread is pinned, it may not (or should not, if it is not a JDK bug) cause your system to hang indefinitely or die or anything like that. While this virtual thread remains pinned, the correspondent Carrier (platform) thread will be just excluded from Carriers thread pool. When the pinning action, e.g. Thread.sleep, completes, the pinned thread should become unpinned. Pinning, of course, decreases the performance of virtual threads machinery and itself is a misuse of it, but it should not be a reason for your system death. (Unless you stressed things to improbable extent, which may or may not be your personal case).

1
Hari On

There is a mysql bug ticket open for the same, with a PR attached to it. Virtual threads seem to be a case of 'so near yet so far'. In practice, if it is not the database driver that is pinning the thread, then it is cache library in front of it. So, we either wait for the synchronized pinning restriction to go away in a future jdk release (as mentioned in the spec) or for the ecosystem of I/O related libraries to become virtual thread friendly.