On 29/05/2024 10:26, Mark Thomas wrote:
On 28/05/2024 16:26, Eric Robinson wrote:

<snip/>

Took a bunch of thread and heap dumps during today's painful debacle. Will send a link to those as soon as I can.

Thanks. I have them. I have taken a look and I am starting to form a theory. To help with that I have a couple of questions.

Scratch that. I've found some further information in the data Eric sent me off-list and I am now pretty sure what is going on.

There are multiple web applications deployed on the servers. I assume there are related but it actually doesn't matter.

At least one application is using the "new" MySQL JDBC driver:
com.mysql.cj.jdbc.Driver

At least one application is using the "old" MySQL JDBC driver:
com.mysql.jdbc.Driver


(I've told Eric off-list which application is using which).

There are, therefore, two drivers registered with the java.sql.DriverManager


The web applications are not using connection pooling. Or, if they are using it, they are using it very inefficiently. The result is that there is a high volume of calls to create new database connections.

This is problem number 1. Creating a database connection is expensive. That is why the concept of database connection pooling was created.


When a new connection is created, java.sql.DriverManager iterates over the list of registered drivers and
- tests to see if the current class loader can see the driver
- if yes, tests to see if that driver can service the connection url
- if yes, use it and exit
- go on to the next driver in the list and repeat

The test to see if the current class loader can use the driver is, essentially, to call Class.forName(driver.getClass(), true, classloader)

And that is problem number 2. That check is expensive if the current class loader can't load that driver.


It is also problem number 3. The reason it is expensive is that class loaders don't cache misses so if a web application has a large number of JARs, they all get scanned every time the DriverManager tries to create a new connection.


The slowness occurs in the web application that depends on the second JDBC driver in DriverManager's list. When a request that requires a database connection is received, there is a short delay while the web application tries, and fails, to load the first JDBC driver in the list. Class loading is synchronized on class name being loaded so if any other requests also need a database connection, they have to wait for this request to finish the search for the JDBC driver before they can continue. This creates a bottleneck. Requests are essentially rate limited to 1 request that requires a database connection per however long it takes to scan every JAR in the web application for a class that isn't there. If the average rate of requests exceeds this rate limit then a queue is going to build up and it won't subside until the average rate of requests falls below this rate limit.



Problem number 1 is an application issue. It should be using pooling. It seems unlikely that we'll see a solution from the application vendor and - even if the vendor does commit to a fix - I suspect it will take months.


Problem number 2 is a JRE issue. I think there are potentially more efficient ways to perform that check but that needs research as things like OSGI and JPMS make class loading more complicated.


Problem number 3 is a Tomcat issue. It should be relatively easy to start caching misses (i.e. this class loader cannot load this class) and save the time spent repeatedly scanning JARs for a class that isn't there.


I intend to wok on a patch for Tomcat that will add caching that should speed things up considerably. I hope to have something for Eric to test today but it might take me until tomorrow as I have a few other time critical things fighting to get tot he top of my TODO list at the moment.


Moving the JDBC driver JARs from WEB-INF/lib to $CATALINA_BASE/lib may also be a short-term fix but is likely to create problems if the same JAR ever exists in both locations at the same time.


Mark

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
For additional commands, e-mail: users-h...@tomcat.apache.org

Reply via email to