Debugging AJP Connection Timeouts and Thread Blocking in Apache-Tomcat-Oracle Stack


1 views

When dealing with Apache-Tomcat integration via AJP connector, we often encounter intermittent connection issues that manifest as:

  • Gradual thread exhaustion (MaxThreads limit reached)
  • Database query latency spikes (2000-5000ms vs normal 5-50ms)
  • Various AJP-related errors in Apache logs

The transition from dual-NIC to single-NIC setup appears to be a significant change point. While network simplification is desirable, it may introduce:

# Potential network bottleneck scenarios:
1. Bandwidth saturation between web/database layers
2. TCP connection reuse issues
3. Increased latency in localhost communication

The thread dump reveals a critical pattern - 199 threads blocked waiting on Oracle connection pool locks, while one thread remains stuck in socket read:

"TP-Processor2" daemon prio=1 tid=0x08f135a8 nid=0x6529 runnable [0x75cfe000..0x75cfef30]
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:129)
at oracle.net.ns.Packet.receive(Unknown Source)

The default Oracle connection pool settings may not handle network hiccups gracefully. Consider these adjustments:

# In context.xml or connection pool configuration
<Resource name="jdbc/myDB" 
          auth="Container"
          type="javax.sql.DataSource"
          maxActive="50"
          maxIdle="10"
          maxWait="10000"
          validationQuery="SELECT 1 FROM DUAL"
          testOnBorrow="true"
          removeAbandoned="true"
          removeAbandonedTimeout="60"
          logAbandoned="true"
          factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"/>

Modify your Tomcat's server.xml to handle connection timeouts better:

<Connector port="8009" protocol="AJP/1.3" 
           maxThreads="300"
           minSpareThreads="25"
           connectionTimeout="20000"
           keepAliveTimeout="30000"
           socketBuffer="8192"
           packetSize="8192"
           redirectPort="8443" />

For single-NIC setups, these sysctl adjustments may help:

# /etc/sysctl.conf additions
net.ipv4.tcp_keepalive_time = 300
net.ipv4.tcp_keepalive_probes = 5
net.ipv4.tcp_keepalive_intvl = 15
net.core.somaxconn = 4096
net.ipv4.tcp_max_syn_backlog = 4096

Implement a simple watchdog script to detect and recover from hangs:

#!/bin/bash
# Monitor Oracle connection responsiveness
RESPONSE=$(echo "SELECT 1 FROM DUAL;" | sqlplus -s user/pass@DB)

if [[ $RESPONSE != *"1"* ]]; then
    echo "$(date) - Database unresponsive" >> /var/log/tomcat_watchdog.log
    /etc/init.d/tomcat restart
fi

If the issue persists, consider:

  • Upgrading to Tomcat 7+ with improved AJP handling
  • Using mod_jk instead of mod_proxy_ajp
  • Implementing separate connection pools for read/write operations

When dealing with AJP connectivity between Apache and Tomcat, several telltale signs emerge in this scenario:

// Common error patterns in Apache logs
[error] (70007)The timeout specified has expired: ajp_ilink_receive() can't receive header
[error] (104)Connection reset by peer: ajp_ilink_receive() can't receive header
[error] proxy: AJP: disabled connection for (localhost)

The thread dump reveals a critical contention point in the Oracle connection pool:

// Sample thread stack trace showing lock contention
"TP-Processor200" daemon prio=1 tid=0x73a4dbf0 nid=0x70dd waiting for monitor entry
at oracle.jdbc.pool.OracleConnectionCacheImpl.getActiveSize(OracleConnectionCacheImpl.java:988)
- waiting to lock <0x7e3455a0> (a oracle.jdbc.pool.OracleConnectionCacheImpl)

First, adjust your AJP connector in server.xml:

<Connector port="8009" protocol="AJP/1.3" 
           connectionTimeout="20000" 
           keepAliveTimeout="30000" 
           maxThreads="500"
           acceptorThreadCount="2"
           socketBuffer="32768"
           packetSize="32768" 
           redirectPort="8443" />

Modify your Oracle connection pool configuration:

// Recommended Oracle pool settings
OracleDataSource ds = new OracleDataSource();
ds.setConnectionCachingEnabled(true);
ds.setConnectionCacheProperties(
   new Properties() {{
      put("MinLimit", "10");
      put("MaxLimit", "100");
      put("InitialLimit", "10");
      put("ConnectionWaitTimeout", "30");
      put("InactivityTimeout", "180");
      put("AbandonedConnectionTimeout", "300");
   }});

For single-NIC environments, consider these sysctl tweaks:

# Add to /etc/sysctl.conf
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30
net.core.somaxconn = 32768
net.ipv4.tcp_max_syn_backlog = 65536

Implement this JMX monitoring snippet for real-time insights:

// Java code to monitor thread states
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
long[] threadIds = threadBean.getAllThreadIds();

for (long threadId : threadIds) {
    ThreadInfo threadInfo = threadBean.getThreadInfo(threadId, Integer.MAX_VALUE);
    if (threadInfo != null && threadInfo.getLockName() != null 
        && threadInfo.getLockName().contains("OracleConnectionCacheImpl")) {
        System.out.println("Blocked thread detected: " + threadInfo);
    }
}

Consider replacing AJP with HTTP connectors when possible:

<Connector port="8080" protocol="HTTP/1.1"
           connectionTimeout="20000"
           maxThreads="500"
           redirectPort="8443" />