Working through the numerous replies ... -----Original Message----- From: Robert Turner <rtur...@e-djuster.ca.INVALID> Sent: Saturday, August 16, 2025 1:03 AM To: Tomcat Users List <users@tomcat.apache.org> Subject: Re: [EXTERNAL EMAIL] How to access a REST service
Dan, What's your thread pool size? DGS: I don't know how to determine this. With the default of 5, I can never get the high watermark for the connection above 5 (as I would expect). If I bump up the thread pool max size, and decrease the maximum db connection pool size, I can then get connections to queue, etc. They behave exactly as I would expect and I can never trigger a message about being unable to obtain a connection. I added a sleep of 200ms in the previous code I shared while holding the connection to simulate being slow. Everything worked without issue. I can fire thousands of connections at it with no running out of connection problems. Requests queue up and wait for a connection to be released. There must be something wrong with how you are managing and using the connections. Could you provide a sample of the code that uses the connection? DGS: Here is the current version of the code fragment that retrieves the list of countries. The method call "GetCountryList.doIt(connection);" does create a prepared statement, but this is always closed immediately after use. In any case, the DB connection is always closed in the code below, as far as I can tell. I've never seen an exception thrown in the running code, so I don't see any possibility for a memory leak. @Path("holidaysandevents") public class HolidaysRESTJSONResource { @Context private UriInfo context; DataSource dataSource; public HolidaysRESTJSONResource() { dataSource = DataSourceSingleton.getInstance().dataSource; } @GET @Path("/countries") @Produces(MediaType.APPLICATION_JSON) public String getJsonCountries() { if (TempDataStorage.countryList == null) { Connection connection = null; try { connection = dataSource.getConnection(); TempDataStorage.countryList = GetCountryList.doIt(connection); } catch (SQLException e) { System.out.println(e.getStackTrace()); } finally { if (connection != null) { try { connection.close(); } catch (SQLException ex) { System.out.println(ex.getStackTrace()); } } } } Robert On Sat, Aug 16, 2025, 00:35 Robert Turner <rtur...@e-djuster.ca> wrote: > Dan, > > FWIW, I just set up test Glassfish server, and made a trivial app to > acquire and release a connection from the pool. > > I configured the connection with a minimum and starting size of 1. > > I tested with 1 connection, and it did exactly what I expected. > Acquired and released 1 connection. No more connections were allocated. > > The setting for the minimum size is also the initial size (no > surprise), and I could set that value to 1. > > My code to acquire the connection was this trivial code: > > final InitialContext ic = new InitialContext(); final DataSource ds = > (DataSource) ic.lookup("test1"); try (Connection conn = > ds.getConnection()) { } catch (SQLException e) { > throw new Runtime exception(e); > } > > So, I don't agree with your "conclusion" about 8 connections per. > > Robert > > > > On Sat, Aug 16, 2025, 00:02 Daniel Schwartz <d...@danielgschwartz.com> > wrote: > >> Chuck, >> >> Okay, here is the text of the attachment. >> >> Instance Name: >> server >> >> >> >> >> Resource : HolidaysConnectionPool Application : >> HolidaysRESTJSON-1.0-SNAPSHOT >> >> Monitor (14 Statistics) >> JDBC Connection Pool Statistics : HolidaysConnectionPool Name Value >> Start Time Last Sample Time Details Description NumConnCreated >> 8 count >> Aug 15, 2025 1:26:20 AM >> Aug 15, 2025 1:26:21 AM >> -- >> The number of physical connections that were created since the last reset. >> NumConnFree >> 8count >> Aug 15, 2025 1:26:20 AM >> Aug 15, 2025 1:26:21 AM >> High Water Mark: 8 count >> Low Water Mark: 0 count >> The total number of free connections in the pool as of the last sampling. >> NumConnReleased >> 1 count >> Aug 15, 2025 1:26:20 AM >> Aug 15, 2025 1:26:21 AM >> -- >> Number of logical connections released to the pool. >> NumPotentialConnLeak >> 0 count >> Aug 15, 2025 1:26:20 AM >> -- >> -- >> Number of potential connection leaks >> NumConnFailedValidation >> 0 count >> Aug 15, 2025 1:26:20 AM >> -- >> -- >> The total number of connections in the connection pool that failed >> validation from the start time until the last sample time. >> ConnRequestWaitTime >> 485millisecond >> Aug 15, 2025 1:26:20 AM >> Aug 15, 2025 1:26:21 AM >> High Water Mark: 485 millisecond >> Low Water Mark: 0 millisecond >> The longest and shortest wait times of connection requests. The >> current value indicates the wait time of the last request that was >> serviced by the pool. >> NumConnAcquired >> 1 count >> Aug 15, 2025 1:26:20 AM >> Aug 15, 2025 1:26:21 AM >> -- >> Number of logical connections acquired from the pool. >> AverageConnWaitTime >> 485 millisecond >> Aug 15, 2025 1:26:20 AM >> Aug 15, 2025 1:26:33 AM >> -- >> Average wait-time-duration per successful connection request >> NumConnDestroyed >> 0 count >> Aug 15, 2025 1:26:20 AM >> -- >> -- >> Number of physical connections that were destroyed since the last reset. >> NumConnSuccessfullyMatched >> 0 count >> Aug 15, 2025 1:26:20 AM >> -- >> -- >> Number of connections succesfully matched >> NumConnNotSuccessfullyMatched >> 0 count >> Aug 15, 2025 1:26:20 AM >> -- >> -- >> Number of connections rejected during matching NumConnUsed 0count Aug >> 15, 2025 1:26:20 AM Aug 15, 2025 1:26:21 AM High Water Mark: 1 count >> Low Water Mark: 0 count Provides connection usage statistics. The >> total number of connections that are currently being used, as well as >> information about the maximum number of connections that were used >> (the high water mark). >> WaitQueueLength >> 0 count >> Aug 15, 2025 1:26:20 AM >> -- >> -- >> Number of connection requests in the queue waiting to be serviced. >> NumConnTimedOut >> 0 count >> Aug 15, 2025 1:26:20 AM >> -- >> -- >> The total number of connections in the pool that timed out between >> the start time and the last sample time. >> >> Regarding your statement "What is more likely is that the GlassFish >> connection pool simply initializes the configured minimum number of >> connections on the first attempt to acquire a connection.", I have no >> idea hat this means. >> >> Dan >> >> From: Chuck Caldarale <n82...@gmail.com> >> Sent: Friday, August 15, 2025 11:39 PM >> To: Tomcat Users List <users@tomcat.apache.org> >> Subject: Re: [EXTERNAL EMAIL] How to access a REST service >> >> > On 2025 Aug 15, at 22:03, Daniel Schwartz <d...@danielgschwartz.com >> <mailto:d...@danielgschwartz.com>> wrote: > > I think I found the answer. >> > > A few days ago, someone suggested that I try setting the >> > > Glassfish >> maximum pool size to 1 and see what happens. >> NkdkJdXPPEBannerStart >> Be Careful With This Message >> From (Chuck Caldarale <n82...@gmail.com>)< >> https://godaddy1.cloud-protect.net/email-details/?k=k1&payload=53616c >> 7465645f5f4a8adbed4b5cf7a587f5f4df537a8eaec1a2b42d4d58936018b1ed681a2 >> 3d9919bf8332d2b6b61429feb03a72599510c1c3ab28d5aaddc506ec4d56aaa261fcb >> 98bd9aad462cad1cc8e2c0a7c87426fd5ac7bd5b6e2701e483c6c0dabfa7863325d5e >> 1cce43460a01fdb3123cef7a62998f33320b7ee6a504f58e005390e8ee17f0cc87547 >> 79156e61decfa830f679ecb7c1779d40b153b44dd3c8946f5c5040afad656b12d5482 >> 92c93d680a4c9c588dd499f91f4d3fe02cd457cde8bab1d939f1dcbfe104966e4bccd >> 617fa2cf6cf872584c675598f814ea3508244febcc34102818eff6415cdd >> > >> Learn More< >> https://godaddy1.cloud-protect.net/email-details/?k=k1&payload=53616c >> 7465645f5f4a8adbed4b5cf7a587f5f4df537a8eaec1a2b42d4d58936018b1ed681a2 >> 3d9919bf8332d2b6b61429feb03a72599510c1c3ab28d5aaddc506ec4d56aaa261fcb >> 98bd9aad462cad1cc8e2c0a7c87426fd5ac7bd5b6e2701e483c6c0dabfa7863325d5e >> 1cce43460a01fdb3123cef7a62998f33320b7ee6a504f58e005390e8ee17f0cc87547 >> 79156e61decfa830f679ecb7c1779d40b153b44dd3c8946f5c5040afad656b12d5482 >> 92c93d680a4c9c588dd499f91f4d3fe02cd457cde8bab1d939f1dcbfe104966e4bccd >> 617fa2cf6cf872584c675598f814ea3508244febcc34102818eff6415cdd >> > >> Potential Impersonation >> The sender's identity could not be verified and someone may be >> impersonating the sender. Take caution when interacting with this message. >> >> NkdkJdXPPEBannerEnd >> >> >> >> > On 2025 Aug 15, at 22:03, Daniel Schwartz <d...@danielgschwartz.com >> <mailto:d...@danielgschwartz.com>> wrote: >> >> > >> >> > I think I found the answer. >> >> > >> >> > A few days ago, someone suggested that I try setting the Glassfish >> maximum pool size to 1 and see what happens. I reported back that in >> Glassfish the minimum pool size is 8, to which someone responded that >> this seems strange. I really didn't know. But now I do. >> >> > >> >> > Today I decided to run a test where I cleared out Glassfish so I >> > could >> start from scratch. I executed one REST request using the URL I >> posted recently, which returns a list of countries. Then I looked at >> the Glassfish JDBC pool monitor. A PDF of this is attached. >> >> >> >> >> >> The list strips nearly all attachments, for safely reasons. You need >> to post the text. >> >> >> >> >> >> > You will see that it says the following: >> >> > >> >> > 1. NumConnAcquired, 1 count, Number of logical connection >> >> > >> >> > 2. NumConnReleased, 1 count, Number of logical connections released >> > to >> the pool. >> >> > >> >> > 3. NumConnCreated, 8 count, The number of physical connections that >> were created since the last reset. >> >> > >> >> > 4. NumConnFree, 8count, The total number of free connections in the >> pool as of the last sampling. >> >> > >> >> > I believe that this is why the minimum pool size is 8; each logical >> connection requires 8 physical connections. >> >> > >> >> > This leads me to believe that this is why the number of connections >> > in >> my connection pool is much larger than what one would expect. >> Assuming that Tomcat only requires one connection object per query, >> this implies that Glassfish requires 8 times that amount. >> >> >> >> >> >> I think that is extremely unlikely. What is more likely is that the >> GlassFish connection pool simply initializes the configured minimum >> number of connections on the first attempt to acquire a connection. >> >> >> >> You can test this by making concurrent requests and seeing what >> happens to the pool counters. Try inserting a delay in your code >> between opening a connection and closing it; something like >> Thread.currentThread().sleep(10000) would likely suffice. Then >> initiate independent requests from several browser tabs in parallel; >> the 10-second delay should allow time for each request to grab its >> own connection from the pool while the other requests are still active. >> >> >> >> >> >> > So, while a normal pool size for Tomcat might be 20 connections, in >> Glassfish this same activity would require 160 connections. >> >> > >> >> > In any case, I'm now 100% sure that my program doesn't, and never >> > did, >> have a memory leak. I have modified my code according to Chris's >> recommendations, as this surely is good advice, but it hasn't changed >> the performance, since no exceptions were ever being thrown. >> >> >> >> >> >> Did you apply the try-catch-finally pattern to all DB-related >> objects, such as statements, prepared statements, and result sets? >> >> >> >> - Chuck >> >> >> >> >> >> > It appears to me that Glassfish is performing normally according to >> > its >> internal design, and there really is no problem as long as I keep the >> maximum pool size large enough. >> >> > >> >> > What do you think? >> >> > >> >> > Dan >> >> > >> >> > -----Original Message----- >> >> > From: Christopher Schultz <ch...@christopherschultz.net<mailto: >> ch...@christopherschultz.net>> >> >> > Sent: Friday, August 15, 2025 1:07 PM >> >> > To: users@tomcat.apache.org<mailto:users@tomcat.apache.org> >> >> > Subject: Re: [EXTERNAL EMAIL] How to access a REST service >> >> > >> >> > Dan, >> >> > >> >> > The only reason we are all looking for resource leaks (technically >> > not >> memory leaks, but leaks nonetheless) is because it's the best >> explanation for why your connection pool seems to be running dry. >> >> > >> >> > I don't think that switching to Tomcat (or TomEE) is going to make >> > any >> difference. >> >> > >> >> > -chris >> >> > >> >> > On 8/15/25 12:33 PM, Daniel Schwartz wrote: >> >> >> >> >> >> >> >> >> -----Original Message----- >> >> >> From: Christopher Schultz <ch...@christopherschultz.net<mailto: >> ch...@christopherschultz.net>> >> >> >> Sent: Friday, August 15, 2025 12:18 PM >> >> >> To: users@tomcat.apache.org<mailto:users@tomcat.apache.org> >> >> >> Subject: Re: [EXTERNAL EMAIL] How to access a REST service >> >> >> >> >> >> Daniel, >> >> >> >> >> >> On 8/15/25 12:49 AM, Daniel Schwartz wrote: >> >> >>> Robert (and all), >> >> >>> >> >> >>> I will work on answers your various questions. However, I >> >>> decided to >> first explore the comment (by someone) that a servlet can "swallow" >> an exception, which I take to mean that it can throw an exception >> without reporting the exception or terminating the program. >> >> >>> >> >> >>> I have this system running on a PC identified as localhost. The >> >>> test >> URL is: >> >> >>> >> >> >>> https://urldefense.proofpoint.com/v2/url?u=http-3A__localhost-3A8 >> >>> 080_ >> >> >>> H >> >> >>> olidaysRESTJSON-2D1.0-2DSNAPSHOT_webresources_holidaysandevents_c >> >>> ount >> >> >>> r >> >> >>> ies&d=DwICaQ&c=euGZstcaTDllvimEN8b7jXrwqOf-v5A_CdpgnVfiiMM&r=AbCa >> >>> lLxz >> >> >>> o >> >> >>> pgQUG9LLcXdB80OM-GtDfItX76RMxNYqz4&m=NKm1FUayvDzFHyhbqCI0JR32OpW1 >> >>> rfTe >> >> >>> d >> >> >>> HFkAoC_xT6Cjqt4-wsVFtYKPtR38vFY&s=4BYNb88ZN6WvyMyyZcR1FyT6Jg-qa4J >> >>> SDa9 >> >> >>> P >> >> >>> xsSFgB4&e= >> >> >>> >> >> >>> I ran two tests. First, I wrote in code to throw an SQL >> >>> exception, >> which did get caught in my catch clause, which printed out some >> messages and a stack trace. The web browser showed the retrieved >> list of countries, but nothing else. >> >> >>> >> >> >>> Second, I replaced the line that throws the SQL exception by one >> >>> that >> tries to do a division by zero. This of course was not caught, but >> it did print out a stack trace and reported a 505 error in the >> browser, and the program did terminate. >> >> >>> >> >> >>> I take this to mean that exceptions are not being "swallowed" by >> >>> my >> program. When/if an exception occurs, there is definitely some >> indication of this, either in the server.log or the browser. Because >> I have never seen either of these actions, I'm fairly sure that my >> program is not throwing exceptions and all database connections are >> being closed immediately after they are used, i.e., no memory leaks in this >> respect. >> >> >>> >> >> >>> The actual code fragments and outputs are copied below. >> >> >>> >> >> >>> ---------------------------------------------------------------- >> >> >>> The code that throws the SQL exception >> >> >>> ---------------------------------------------------------------- >> >> >>> @GET >> >> >>> @Path("/countries") >> >> >>> @Produces(MediaType.APPLICATION_JSON) >> >> >>> public String getJsonCountries() { >> >> >>> if (TempDataStorage.countryList == null) { >> >> >>> Connection connection = null; >> >> >>> try { >> >> >>> connection = dataSource.getConnection(); >> >> >>> System.out.println("countries connection has >> >>> been >> opened"); >> >> >>> TempDataStorage.countryList = >> GetCountryList.doIt(connection); >> >> >>> throw new SQLException("testing sql exception"); >> >> >>> // connection.close(); >> >> >>> // System.out.println("countries connection has been >> closed"); >> >> >>> } catch (SQLException e) { >> >> >>> System.out.println(e); >> >> >>> System.out.println("catching sql exception"); >> >> >>> if (connection != null) { >> >> >>> try { >> >> >>> connection.close(); >> >> >>> } catch (SQLException ex) { >> >> >>> System.out.println(ex); >> >> >>> } >> >> >>> } >> >> >>> } >> >> >>> } >> >> >> >> >> >> 100% connection leak every time, guaranteed. >> >> >> >> >> >> You are never closing the connection, because it's been commented out. >> >> >> Perhaps this was just for testing, because I see you are throwing >> >> an >> exception. >> >> >> >> >> >> DGS: When I put in the throw statement, I had to comment out those >> >> two >> lines because the Java compiler complains that they are unreachable. >> This was just to test for what would happen if an SQL exception was >> thrown, in which case, yes, there will be a memory. The point is >> that my program never throws SQL exceptions, because if it did, I would know >> about it. >> >> >> >> >> >> The conn.close() *MUST* be in a finally {} block. Do not put it in >> >> the >> try block. Do not put it in the catch block. Always put it in the >> finally block. >> >> >> >> >> >> DGS: Sounds like good advice. I'll do this and see if it makes a >> difference. >> >> >> >> >> >>> ----------------------------------------------------------------- >> >>> - >> >> >>> The output in the server.log file >> >> >>> ----------------------------------------------------------------- >> >>> - >> >> >>> [2025-08-14T23:34:42.019-0400] [glassfish 4.1] [INFO] [] [] [tid: >> _ThreadID=40 _ThreadName=Thread-8] [timeMillis: 1755228882019] [levelValue: >> 800] [[ >> >> >>> countries connection has been opened]] >> >> >>> >> >> >>> [2025-08-14T23:34:42.022-0400] [glassfish 4.1] [INFO] [] [] [tid: >> _ThreadID=40 _ThreadName=Thread-8] [timeMillis: 1755228882022] [levelValue: >> 800] [[ >> >> >>> java.sql.SQLException: testing sql exception >> >> >>> at >> >> >>> com.worldholidaysandevents.restjsonwebservice.HolidaysRESTJSONRes >> >>> ourc >> >> >>> e >> >> >>> .getJsonCountries(HolidaysRESTJSONResource.java:54) >> >> >>> >> >> >>> ... stack trace ... >> >> >>> >> >> >>> [2025-08-14T23:34:42.022-0400] [glassfish 4.1] [INFO] [] [] [tid: >> _ThreadID=40 _ThreadName=Thread-8] [timeMillis: 1755228882022] [levelValue: >> 800] [[ >> >> >>> catching sql exception]] >> >> >> >> >> >> ... and the server does not crash. You can make any request >> >> without >> restarting Glassfish, right? >> >> >> >> >> >>> ---------------------------------------------------------------- >> >> >>> The code that divides by zero >> >> >>> ---------------------------------------------------------------- >> >> >>> @GET >> >> >>> @Path("/countries") >> >> >>> @Produces(MediaType.APPLICATION_JSON) >> >> >>> public String getJsonCountries() { >> >> >>> if (TempDataStorage.countryList == null) { >> >> >>> Connection connection = null; >> >> >>> try { >> >> >>> connection = dataSource.getConnection(); >> >> >>> System.out.println("countries connection has >> >>> been >> opened"); >> >> >>> TempDataStorage.countryList = >> GetCountryList.doIt(connection); >> >> >>> float something = 1/0; >> >> >>> connection.close(); >> >> >>> System.out.println("countries connection has >> >>> been >> closed"); >> >> >>> } catch (SQLException e) { >> >> >>> System.out.println(e); >> >> >>> System.out.println("catching sql exception"); >> >> >>> if (connection != null) { >> >> >>> try { >> >> >>> connection.close(); >> >> >>> } catch (SQLException ex) { >> >> >>> System.out.println(ex); >> >> >>> } >> >> >>> } >> >> >>> } >> >> >>> } >> >> >> >> >> >> >> >> >> 100% connection leak every time, guaranteed. >> >> >> >> >> >> You are never closing the connection, because your code is >> >> throwing an >> uncaught exception (divide by zero) Perhaps this was just for >> testing, because I see you are intentionally dividing by zero. >> >> >> >> >> >> DGS: Right. This was to test for what would happen if a non-SQL >> exception were thrown, i.e., one that is not caught in the catch clause. >> This created a 505 error and the program quit running. Here the >> point is that, if something like this were to happen, I would >> certainly know about it, and it has never happened, so no memory leak >> is being created in this way. >> >> >> >> >> >> The conn.close() *MUST* be in a finally {} block. Do not put it in >> >> the >> try block. Do not put it in the catch block. Always put it in the >> finally block. >> >> >> >> >> >> DGS: Thanks again. I'll do this and see if it makes any >> >> difference in >> the connection pooling. >> >> >> >> >> >> -chris >> >> >> >> >> >> >> >> >> ------------------------------------------------------------------ >> >> --- >> >> >> To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org<mailto: >> users-unsubscr...@tomcat.apache.org> >> >> >> For additional commands, e-mail: users-h...@tomcat.apache.org<mailto: >> users-h...@tomcat.apache.org> >> >> >> >> >> >> >> >> >> ------------------------------------------------------------------ >> >> --- >> >> >> To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org<mailto: >> users-unsubscr...@tomcat.apache.org> >> >> >> For additional commands, e-mail: users-h...@tomcat.apache.org<mailto: >> users-h...@tomcat.apache.org> >> >> >> >> >> > >> >> > >> >> > ------------------------------------------------------------------- >> > -- >> >> > To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org<mailto: >> users-unsubscr...@tomcat.apache.org> >> >> > For additional commands, e-mail: users-h...@tomcat.apache.org<mailto: >> users-h...@tomcat.apache.org> >> >> > >> >> > >> >> > ------------------------------------------------------------------- >> > -- >> >> > To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org<mailto: >> users-unsubscr...@tomcat.apache.org> >> >> > For additional commands, e-mail: users-h...@tomcat.apache.org<mailto: >> users-h...@tomcat.apache.org> >> >> >> >> >> >> --------------------------------------------------------------------- >> >> To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org<mailto: >> users-unsubscr...@tomcat.apache.org> >> >> For additional commands, e-mail: users-h...@tomcat.apache.org<mailto: >> users-h...@tomcat.apache.org> >> >> >> --------------------------------------------------------------------- To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org For additional commands, e-mail: users-h...@tomcat.apache.org