Сокет jTDS зависает при проверке соединения C3P0 (SQL Server 2008 R2)

Вот окружение:

  • Ява 5
  • Веб-приложение, работающее в Tomcat 6.0.18 в Windows (версия неизвестна)
  • База данных: SQL Server 2008 R2
  • Драйвер JDBC: jTDS 1.2.5
  • Поставщик пула соединений: C3P0 0.9.1.2

Я пытаюсь отладить проблему, с которой сталкивается клиент. Практически каждые пару недель наше веб-приложение блокируется на их сервере, и они не могут получить к нему доступ. Перезапуск устраняет проблему. Дальнейшее расследование показывает, что причина того, что все заблокировано, заключается в том, что все ожидает возврата соединений с базой данных. Я думаю, что проблема скорее всего в SQL Server, а не в C3P0.

Я считаю, что происходит то, что «проверочный запрос на ожидание» C3P0 зависает. Запрос таков:

select * from c3p0_connection_test_table

Похоже, что этот запрос выполняется, и результат никогда не возвращается. Вот что я вижу в дампе потока. Обратите внимание на DefaultConnectionTester.activeCheckConnection(), это проверка бездействия:

"com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread-#2" daemon prio=6 tid=0x0000000007c32000 nid=0x1250 runnable [0x000000001072f000]
   java.lang.Thread.State: RUNNABLE
    at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.read(SocketInputStream.java:129)
    at java.io.DataInputStream.readFully(DataInputStream.java:178)
    at java.io.DataInputStream.readFully(DataInputStream.java:152)
    at net.sourceforge.jtds.jdbc.SharedSocket.readPacket(SharedSocket.java:841)
    at net.sourceforge.jtds.jdbc.SharedSocket.getNetPacket(SharedSocket.java:722)
    - locked <0x000000016ac03f48> (a java.util.ArrayList)
    at net.sourceforge.jtds.jdbc.ResponseStream.getPacket(ResponseStream.java:466)
    at net.sourceforge.jtds.jdbc.ResponseStream.read(ResponseStream.java:103)
    at net.sourceforge.jtds.jdbc.ResponseStream.peek(ResponseStream.java:88)
    at net.sourceforge.jtds.jdbc.TdsCore.wait(TdsCore.java:3928)
    at net.sourceforge.jtds.jdbc.TdsCore.executeSQL(TdsCore.java:1045)
    - locked <0x000000016d965268> (a net.sourceforge.jtds.jdbc.TdsCore)
    at net.sourceforge.jtds.jdbc.JtdsStatement.executeSQLQuery(JtdsStatement.java:465)
    at net.sourceforge.jtds.jdbc.JtdsStatement.executeQuery(JtdsStatement.java:1301)
    at com.mchange.v2.c3p0.impl.DefaultConnectionTester.activeCheckConnection(DefaultConnectionTester.java:73)
    at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager.testPooledConnection(C3P0PooledConnectionPool.java:374)
    at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager.refurbishIdleResource(C3P0PooledConnectionPool.java:310)
    at com.mchange.v2.resourcepool.BasicResourcePool$AsyncTestIdleResourceTask.run(BasicResourcePool.java:1999)
    at com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunner.java:547)

Но из-за чего такой простой запрос мог зависнуть при чтении из сокета? Я не верю, что в этой таблице будет какая-либо блокировка базы данных, поскольку она полностью управляется C3P0 и никогда не вставляется/не обновляется. Кроме того, любые неудачные попытки получить соединения из пула (если бы это было причиной зависания), я бы ожидал где-то трассировки стека. Вместо этого я вижу, что приложение просто зависает, потому что все будущие запросы на подключение ждут завершения этой «проверки бездействия».

Вот один из потоков, ожидающих завершения «проверки простоя»:

    "http-80-3" daemon prio=6 tid=0x0000000007c33800 nid=0x122c in Object.wait() [0x000000001082d000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    at com.mchange.v2.resourcepool.BasicResourcePool.prelimCheckoutResource(BasicResourcePool.java:579)
    - locked <0x0000000167a88a60> (a com.mchange.v2.resourcepool.BasicResourcePool)
    at com.mchange.v2.resourcepool.BasicResourcePool.checkoutResource(BasicResourcePool.java:477)
    at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutPooledConnection(C3P0PooledConnectionPool.java:525)
    at com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource.getConnection(AbstractPoolBackedDataSource.java:128)
    at org.springframework.orm.hibernate3.LocalDataSourceConnectionProvider.getConnection(LocalDataSourceConnectionProvider.java:81)
    at org.hibernate.jdbc.ConnectionManager.openConnection(ConnectionManager.java:446)
    at org.hibernate.jdbc.ConnectionManager.getConnection(ConnectionManager.java:167)
    at org.hibernate.jdbc.JDBCContext.connection(JDBCContext.java:142)
    at org.hibernate.transaction.JDBCTransaction.begin(JDBCTransaction.java:85)
    at org.hibernate.impl.SessionImpl.beginTransaction(SessionImpl.java:1354)
    at org.springframework.orm.hibernate3.HibernateTransactionManager.doBegin(HibernateTransactionManager.java:555)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:371)
    at sun.reflect.GeneratedMethodAccessor105.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:309)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:89)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
    at $Proxy15.getTransaction(Unknown Source)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:317)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:105)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:89)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
    at $Proxy79.getCrowdProperties(Unknown Source)
    at com.jamasoftware.contour.gateway.crowd.CrowdSsoServices.autoLogin(Unknown Source)
    at com.jamasoftware.contour.security.AutoLoginServicesManager.autoLogin(Unknown Source)
    at org.springframework.security.ui.rememberme.RememberMeProcessingFilter.doFilterHttp(RememberMeProcessingFilter.java:74)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.ui.AbstractProcessingFilter.doFilterHttp(AbstractProcessingFilter.java:277)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at com.jamasoftware.contour.view.filter.CheckSetupFilter.doFilter(Unknown Source)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.context.HttpSessionContextIntegrationFilter.doFilterHttp(HttpSessionContextIntegrationFilter.java:235)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.concurrent.ConcurrentSessionFilter.doFilterHttp(ConcurrentSessionFilter.java:99)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at com.jamasoftware.contour.view.filter.ExpirationFilter.doFilter(Unknown Source)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at com.jamasoftware.contour.view.filter.GzipFilter.doFilter(Unknown Source)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.util.FilterChainProxy.doFilter(FilterChainProxy.java:175)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:237)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:167)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
    at org.apache.coyote.http11.Http11AprProcessor.process(Http11AprProcessor.java:864)
    at org.apache.coyote.http11.Http11AprProtocol$Http11ConnectionHandler.process(Http11AprProtocol.java:579)
    at org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:1665)
    at java.lang.Thread.run(Thread.java:619)

Глядя на исходный код C3P0, две строки вверху:

at java.lang.Object.wait(Native Method)
at com.mchange.v2.resourcepool.BasicResourcePool.prelimCheckoutResource(BasicResourcePool.java:579)

... происходит только тогда, когда соединение ожидает «проверки простоя».

К сожалению, это клиент, и я не могу сообщить здесь точные сведения об окружении. Но любые предложения я могу передать им во время нашего следующего звонка.

ОБНОВЛЕНИЕ:

Сегодня их приложение снова заблокировали. Вот что мы пробовали до сих пор:

  • Они обновили драйвер jTDS до последней версии (1.2.5).
  • Я установил для свойства socketTimeout в строке подключения значение 300, но драйвер по-прежнему зависает на socketRead0() через 5 минут.
  • Мы изменили активный контрольный запрос с «выбрать * из c3p0_connection_test_table» на «выбрать 1».
  • Мы добавили свойства unreturnedConnectionTimeout и debugUnreturnedConnectionStackTraces в C3P0 (однако они не перехватывают разорванные соединения)

Вот все свойства соединения C3P0, которые мы установили:

    <property name="minPoolSize" value="1"/>
    <property name="maxPoolSize" value="30"/>
    <property name="acquireIncrement" value="3"/>
    <property name="automaticTestTable" value="c3p0_connection_test_table"/>
    <property name="idleConnectionTestPeriod" value="30"/>
    <property name="testConnectionOnCheckin" value="true"/>
    <property name="testConnectionOnCheckout" value="true"/>

Одна странная вещь, которая произошла, заключалась в том, что, когда приложение было заблокировано, мы попытались войти в SQL Server Management Studio с пользователем приложения, и оно не пустило нас (я думаю, это была обычная ошибка тайм-аута). База данных была запущена и прослушивала порт 1433 (проверено с помощью telnet), поэтому я думаю, что это означает, что достигнуто максимальное количество подключений к базе данных. Однако я не уверен, поможет ли это определить причину этой проблемы.

Отслеживание лидов безуспешно:

  • У этого человека возникла проблема с операционными системами и, возможно, количеством сетевых карт на сервере.
  • Ответивший ниже указал мне на ошибку в старой версии jTDS здесь

Полезные ответы могут либо попросить меня проверить что-то на компьютере клиента, либо указать мне на возможную основную причину. Какие бы ответы ни помогли мне отследить это, я получу награду.


person Sean Adkinson    schedule 08.08.2011    source источник


Ответы (4)


Первые идеи: проверьте, что они используют последнюю версию jTDS, а SQL Server исправлен и обновлен до последней версии SP!

Есть несколько сообщений о таком поведении при отслеживании ошибок jTDS, в первую очередь этот. По-видимому, SQL Server закрывает соединение, но jTDS этого не замечает.

В более новых версиях jTDS есть socketTimeout свойство (по умолчанию = 0), возможно, это поможет.

Вы также можете протестировать другой драйвер JDBC.

person Vlad    schedule 08.08.2011
comment
Отличные предложения, спасибо. Я рассмотрю их, когда встречусь с ними здесь в ближайшее время, и обновлю этот вопрос любыми новыми подробностями. - person Sean Adkinson; 09.08.2011
comment
Они используют последнюю версию jTDS (1.2.5), а SQL Server выглядит довольно простой установкой последней версии SP (SQL Server 2008 RC2). Я попросил их добавить свойство socketTimeout (мы установили для него чрезвычайно высокое значение 300, или 5 минут), но они все равно снова столкнулись с этой проблемой. - person Sean Adkinson; 22.08.2011
comment
@SeanA: 1. Чем выше значение socketTimeout, тем больше времени требуется jTDS, чтобы понять, что соединение разорвано (то же самое для loginTimeout). 2. Поскольку вы упомянули о невозможности подключения с помощью Management Studio, мне интересно, каково настроенное максимальное количество подключений на пользователя. 3. RC2 как в R2? 4. Вы не рассматриваете возможность обратиться за поддержкой к M$? 5. Вы подключаетесь локально, используете ли вы адрес сервера localhost? - person Vlad; 23.08.2011
comment
1. Я установил 5-минутный socketTimeout, но соединения оставались заблокированными в течение нескольких часов. 2. Это также мое подозрение, и в настоящее время мы изучаем, сколько подключений SQL Server настроен на разрешение, и сколько, по мнению нашего пула подключений, он может иметь. 3. Отредактировал на R2 (моя ошибка). 4. Клиент может эскалироваться в Microsoft, но я сделаю все, что смогу до этого. 5. Приложение общается с базой данных, используя имя хоста, установленное в их DNS. - person Sean Adkinson; 23.08.2011
comment
Пробовали ли вы использовать драйвер без c3p0, чтобы узнать, связана ли проблема с c3p0 или какой-либо настройкой sqlserver? То есть, пусть приложение просто использует драйвер напрямую и смотрит, возникают ли у вас те же проблемы с тайм-аутом сокета. - person mooreds; 05.09.2011

На сервере sql проверьте, отключено ли - свойства - соединения - «Использовать регулятор запросов для предотвращения длительных запросов».

person Helge    schedule 21.09.2011

Я столкнулся с аналогичной проблемой с зависанием c3p0 (0.9.1) при подключении к серверу sql с помощью jtds.

Я решил это, не используя c3p0 и просто открывая соединение всякий раз, когда оно мне нужно, с помощью DriverManager. Я уверен, что это не так эффективно, но это работает.

person mooreds    schedule 06.09.2011

Был ли еще один поток, закрывающий соединение во время тестирования соединения? Похоже, C3P0 исправил это в c3p0-0.9.2-pre3:

-- Требуются тесты PooledConnections для владения блокировками, связанными с этими PooledConnections, чтобы предотвратить случайные взаимоблокировки, когда PooledConnection.close() совпадает с тестом Connection. Большое спасибо анонимному пользователю SourceForge за привлечение внимания к этой проблеме.

из: https://github.com/swaldman/c3p0/blob/master/src/dist-static/CHANGELOG

person Niraj Bhawnani    schedule 09.02.2015