Skip to content

ORA-12516 TNS Listener No Available Handler - Connection Pool Management

ORA-12516: TNS Listener Could Not Find Available Handler

Section titled “ORA-12516: TNS Listener Could Not Find Available Handler”

Error Text: ORA-12516: TNS:listener could not find available handler with matching protocol stack

This critical connection error occurs when the TNS listener cannot find an available connection handler to process new client connection requests. Unlike basic connection failures, ORA-12516 specifically indicates connection pool exhaustion in high-traffic production systems.

Client Connection Flow
├── TNS Listener (Port 1521)
├── Dispatcher Process (Shared Server)
├── Dedicated Server Process
└── Connection Pool Management
  • Dedicated Servers - One process per connection
  • Shared Servers - Multiple connections per process
  • Connection Pooling - Reused connection handlers
  • Dispatcher Processes - Route shared server connections
  • Maximum connections reached (processes parameter)
  • All handlers busy with long-running operations
  • Connection leaks in application code
  • Insufficient shared server configuration
  • Operating system process limits exceeded
  • Memory exhaustion preventing new process creation
  • CPU saturation preventing connection processing
  • Network socket exhaustion
  • processes parameter too low for workload
  • Shared server configuration inadequate
  • Connection timeout values too high
  • Service registration problems
  • Applications not closing connections properly
  • Connection pool misconfiguration in middleware
  • Batch jobs holding connections too long
  • Runaway queries consuming handlers
-- Current session count vs limits
SELECT
resource_name,
current_utilization,
max_utilization,
limit_value,
ROUND((current_utilization/limit_value)*100, 2) as pct_used
FROM v$resource_limit
WHERE resource_name IN ('processes', 'sessions')
ORDER BY resource_name;
-- Active sessions by status
SELECT
status,
COUNT(*) as session_count,
ROUND(COUNT(*) * 100.0 / SUM(COUNT(*)) OVER(), 2) as percentage
FROM v$session
GROUP BY status
ORDER BY session_count DESC;
-- Sessions by program/machine
SELECT
program,
machine,
COUNT(*) as connection_count,
MAX(last_call_et) as max_idle_seconds
FROM v$session
WHERE username IS NOT NULL
GROUP BY program, machine
ORDER BY connection_count DESC;
Terminal window
# Check listener status and services
lsnrctl status LISTENER
# Show detailed service information
lsnrctl services LISTENER
# Check for connection queue and load
# Look for:
# - Current connections
# - Refused connections
# - Queue length
# - Load balancing
-- Connection history and patterns
SELECT
TO_CHAR(sample_time, 'YYYY-MM-DD HH24') as hour,
COUNT(DISTINCT session_id) as unique_sessions,
COUNT(*) as total_samples,
ROUND(AVG(CASE WHEN session_state = 'WAITING' THEN 1 ELSE 0 END) * 100, 2) as pct_waiting
FROM dba_hist_active_sess_history
WHERE sample_time > SYSDATE - 1
AND program IS NOT NULL
GROUP BY TO_CHAR(sample_time, 'YYYY-MM-DD HH24')
ORDER BY hour DESC;
-- Long-running sessions consuming handlers
SELECT
s.sid,
s.serial#,
s.username,
s.program,
s.machine,
s.status,
ROUND(s.last_call_et/3600, 2) as hours_idle,
sq.sql_text
FROM v$session s
LEFT JOIN v$sql sq ON s.sql_id = sq.sql_id
WHERE s.username IS NOT NULL
AND s.last_call_et > 3600 -- Idle > 1 hour
ORDER BY s.last_call_et DESC;
-- Operating system process count
SELECT
SUBSTR(name, 1, 30) as parameter,
value,
description
FROM v$parameter
WHERE name IN ('processes', 'sessions', 'shared_servers', 'dispatchers')
ORDER BY name;
-- Memory usage affecting connection creation
SELECT
pool,
name,
ROUND(bytes/1024/1024, 2) as mb
FROM v$sgastat
WHERE pool IS NOT NULL
AND name IN ('free memory', 'miscellaneous')
ORDER BY pool, mb DESC;
-- System load and resource usage
SELECT
stat_name,
value
FROM v$osstat
WHERE stat_name IN (
'NUM_CPUS', 'LOAD', 'PHYSICAL_MEMORY_BYTES',
'NUM_CPU_CORES', 'NUM_CPU_SOCKETS'
)
ORDER BY stat_name;
-- Check current settings
SHOW PARAMETER processes;
SHOW PARAMETER sessions;
-- Calculate new values (sessions = processes * 1.1 + 5)
-- For high-traffic systems, consider:
-- processes = 1000-5000 (depending on hardware)
-- sessions = processes * 1.5
-- Increase process limit (requires restart)
ALTER SYSTEM SET processes = 2000 SCOPE = SPFILE;
-- Increase session limit (can be done online if within process limit)
ALTER SYSTEM SET sessions = 3000 SCOPE = BOTH;
-- Restart database to apply process limit change
SHUTDOWN IMMEDIATE;
STARTUP;
-- Confirm new limits are active
SELECT
resource_name,
limit_value,
current_utilization,
max_utilization
FROM v$resource_limit
WHERE resource_name IN ('processes', 'sessions');
-- Configure shared servers for connection pooling
ALTER SYSTEM SET shared_servers = 20 SCOPE = BOTH;
ALTER SYSTEM SET max_shared_servers = 100 SCOPE = BOTH;
ALTER SYSTEM SET dispatchers = '(PROTOCOL=TCP)(DISPATCHERS=5)' SCOPE = BOTH;
-- Configure connection pooling
ALTER SYSTEM SET connection_brokers = '(TYPE=POOLED)(POOL_TYPE=DEDICATED)' SCOPE = BOTH;
-- Set appropriate timeouts
ALTER SYSTEM SET sqlnet.expire_time = 10 SCOPE = BOTH;
-- Check shared server utilization
SELECT
name,
value
FROM v$sysstat
WHERE name LIKE '%shared server%'
OR name LIKE '%dispatcher%';
-- Monitor dispatcher queue
SELECT
name,
network,
status,
accept_calls,
messages,
bytes,
breaks,
connections
FROM v$dispatcher;
-- Check shared server processes
SELECT
name,
status,
messages,
bytes,
breaks,
circuit
FROM v$shared_server;
-- Find long-idle sessions
SELECT
'ALTER SYSTEM KILL SESSION ''' || sid || ',' || serial# || ''' IMMEDIATE;' as kill_cmd,
sid,
serial#,
username,
program,
machine,
ROUND(last_call_et/3600, 2) as hours_idle,
status
FROM v$session
WHERE username IS NOT NULL
AND status = 'INACTIVE'
AND last_call_et > 7200 -- Idle > 2 hours
AND program NOT LIKE '%Oracle%' -- Exclude Oracle processes
ORDER BY last_call_et DESC;
-- Execute kill commands for problematic sessions
-- ALTER SYSTEM KILL SESSION 'sid,serial#' IMMEDIATE;
-- Create procedure to manage idle connections
CREATE OR REPLACE PROCEDURE kill_idle_sessions(
p_max_idle_hours NUMBER DEFAULT 2,
p_dry_run BOOLEAN DEFAULT TRUE
) AS
v_kill_cmd VARCHAR2(200);
v_count NUMBER := 0;
BEGIN
FOR rec IN (
SELECT sid, serial#, username, program, last_call_et
FROM v$session
WHERE username IS NOT NULL
AND status = 'INACTIVE'
AND last_call_et > (p_max_idle_hours * 3600)
AND program NOT LIKE '%Oracle%'
) LOOP
v_kill_cmd := 'ALTER SYSTEM KILL SESSION ''' || rec.sid || ',' || rec.serial# || ''' IMMEDIATE';
IF p_dry_run THEN
DBMS_OUTPUT.PUT_LINE('Would execute: ' || v_kill_cmd);
ELSE
EXECUTE IMMEDIATE v_kill_cmd;
DBMS_OUTPUT.PUT_LINE('Killed session: ' || rec.username || ' (' || rec.program || ')');
END IF;
v_count := v_count + 1;
END LOOP;
DBMS_OUTPUT.PUT_LINE('Total sessions processed: ' || v_count);
END;
/
-- Test run (dry run)
EXEC kill_idle_sessions(2, TRUE);
-- Actual execution (use with caution)
-- EXEC kill_idle_sessions(2, FALSE);

Connection Pool Configuration (Java Example)

Section titled “Connection Pool Configuration (Java Example)”
// Proper connection pool configuration
@Configuration
public class DatabaseConfig {
@Bean
@Primary
public DataSource dataSource() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:oracle:thin:@//hostname:1521/service");
config.setUsername("username");
config.setPassword("password");
// Critical pool settings for ORA-12516 prevention
config.setMaximumPoolSize(50); // Limit concurrent connections
config.setMinimumIdle(10); // Keep minimum connections ready
config.setConnectionTimeout(30000); // 30 seconds
config.setIdleTimeout(300000); // 5 minutes
config.setMaxLifetime(1800000); // 30 minutes
config.setLeakDetectionThreshold(60000); // 1 minute leak detection
// Connection validation
config.setConnectionTestQuery("SELECT 1 FROM DUAL");
config.setValidationTimeout(5000);
return new HikariDataSource(config);
}
}
// Proper connection usage pattern
@Service
public class DatabaseService {
@Autowired
private JdbcTemplate jdbcTemplate;
// Good: Uses connection pool properly
public List<User> getUsers() {
try {
return jdbcTemplate.query(
"SELECT * FROM users",
new UserRowMapper()
);
} catch (DataAccessException e) {
if (e.getCause() instanceof SQLException) {
SQLException sqlEx = (SQLException) e.getCause();
if (sqlEx.getErrorCode() == 12516) {
// Handle ORA-12516 specifically
throw new ServiceUnavailableException(
"Database connection pool exhausted. Please try again later."
);
}
}
throw e;
}
}
// Bad: Manual connection management (avoid)
public void badExample() {
Connection conn = null;
try {
conn = DriverManager.getConnection(url, user, pass);
// ... database operations
// Missing conn.close() in finally block
} catch (SQLException e) {
// Connection leak!
}
}
}
@Component
public class ConnectionMonitor {
@Autowired
private HikariDataSource dataSource;
@Scheduled(fixedRate = 60000) // Every minute
public void monitorConnections() {
HikariPoolMXBean poolBean = dataSource.getHikariPoolMXBean();
int activeConnections = poolBean.getActiveConnections();
int totalConnections = poolBean.getTotalConnections();
int maxPoolSize = dataSource.getMaximumPoolSize();
double utilizationPct = (double) activeConnections / maxPoolSize * 100;
if (utilizationPct > 80) {
// Send alert - approaching connection exhaustion
alertService.sendAlert(
"Connection pool at " + utilizationPct + "% capacity"
);
}
// Log metrics
logger.info("Connection Pool Stats - Active: {}, Total: {}, Max: {}, Utilization: {}%",
activeConnections, totalConnections, maxPoolSize, utilizationPct);
}
}
-- Configure resource manager to limit concurrent connections per service
BEGIN
DBMS_RESOURCE_MANAGER.CLEAR_PENDING_AREA();
DBMS_RESOURCE_MANAGER.CREATE_PENDING_AREA();
-- Create consumer group for application connections
DBMS_RESOURCE_MANAGER.CREATE_CONSUMER_GROUP(
consumer_group => 'APP_CONNECTIONS',
comment => 'Application connection group'
);
-- Create resource plan
DBMS_RESOURCE_MANAGER.CREATE_PLAN(
plan => 'CONNECTION_THROTTLING',
comment => 'Limit concurrent connections'
);
-- Create plan directive with session limits
DBMS_RESOURCE_MANAGER.CREATE_PLAN_DIRECTIVE(
plan => 'CONNECTION_THROTTLING',
group_or_subplan => 'APP_CONNECTIONS',
comment => 'Application connection limits',
max_idle_time => 1800, -- 30 minutes max idle
max_idle_blocker_time => 600, -- 10 minutes for blocking sessions
parallel_degree_limit_p1 => 8
);
DBMS_RESOURCE_MANAGER.VALIDATE_PENDING_AREA();
DBMS_RESOURCE_MANAGER.SUBMIT_PENDING_AREA();
END;
/
-- Activate the resource plan
ALTER SYSTEM SET resource_manager_plan = 'CONNECTION_THROTTLING';
-- Enable connection broker for pooled connections
ALTER SYSTEM SET connection_brokers =
'(TYPE=POOLED)(POOL_TYPE=DEDICATED)(CONNECTIONS=200)(SESSIONS=1000)' SCOPE = BOTH;
-- Configure DRCP (Database Resident Connection Pooling)
EXEC DBMS_CONNECTION_POOL.CONFIGURE_POOL(
pool_name => 'SYS_DEFAULT_CONNECTION_POOL',
minsize => 10,
maxsize => 200,
incrsize => 5,
session_cached_cursors => 50,
inactivity_timeout => 300,
max_think_time => 600
);
EXEC DBMS_CONNECTION_POOL.START_POOL('SYS_DEFAULT_CONNECTION_POOL');
-- Create monitoring procedure
CREATE OR REPLACE PROCEDURE monitor_connection_usage AS
v_current_sessions NUMBER;
v_max_sessions NUMBER;
v_utilization NUMBER;
v_processes_current NUMBER;
v_processes_limit NUMBER;
BEGIN
-- Check session utilization
SELECT current_utilization, limit_value
INTO v_current_sessions, v_max_sessions
FROM v$resource_limit
WHERE resource_name = 'sessions';
-- Check process utilization
SELECT current_utilization, limit_value
INTO v_processes_current, v_processes_limit
FROM v$resource_limit
WHERE resource_name = 'processes';
v_utilization := ROUND((v_current_sessions / v_max_sessions) * 100, 2);
-- Alert thresholds
IF v_utilization > 85 THEN
DBMS_OUTPUT.PUT_LINE('CRITICAL: Session utilization at ' || v_utilization || '%');
-- Send alert to monitoring system
ELSIF v_utilization > 75 THEN
DBMS_OUTPUT.PUT_LINE('WARNING: Session utilization at ' || v_utilization || '%');
END IF;
-- Log current stats
INSERT INTO connection_usage_log (
log_time, sessions_used, sessions_limit, processes_used, processes_limit, utilization_pct
) VALUES (
SYSTIMESTAMP, v_current_sessions, v_max_sessions,
v_processes_current, v_processes_limit, v_utilization
);
COMMIT;
END;
/
-- Schedule monitoring job
BEGIN
DBMS_SCHEDULER.CREATE_JOB(
job_name => 'MONITOR_CONNECTIONS',
job_type => 'STORED_PROCEDURE',
job_action => 'monitor_connection_usage',
repeat_interval => 'FREQ=MINUTELY; INTERVAL=5',
enabled => TRUE
);
END;
/
-- Calculate recommended settings based on workload
WITH workload_analysis AS (
SELECT
MAX(current_utilization) as peak_sessions,
AVG(current_utilization) as avg_sessions,
PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY current_utilization) as p95_sessions
FROM (
SELECT sample_time, COUNT(DISTINCT session_id) as current_utilization
FROM dba_hist_active_sess_history
WHERE sample_time > SYSDATE - 30 -- Last 30 days
GROUP BY sample_time
)
)
SELECT
peak_sessions,
avg_sessions,
p95_sessions,
CEIL(p95_sessions * 1.5) as recommended_processes,
CEIL(p95_sessions * 1.8) as recommended_sessions
FROM workload_analysis;
  • ORA-12519 - TNS No appropriate service handler found
  • ORA-12518 - TNS Listener could not hand off client connection
  • ORA-00020 - Maximum number of processes exceeded
  • ORA-00018 - Maximum number of sessions exceeded
  1. Immediate Relief

    -- Kill idle sessions
    ALTER SYSTEM KILL SESSION 'sid,serial#' IMMEDIATE;
    -- Increase sessions if possible
    ALTER SYSTEM SET sessions = higher_value SCOPE = MEMORY;
  2. Temporary Workaround

    Terminal window
    # Restart listener to clear stuck connections
    lsnrctl stop LISTENER
    lsnrctl start LISTENER
  3. Monitor Recovery

    -- Watch connection recovery
    SELECT resource_name, current_utilization, limit_value
    FROM v$resource_limit
    WHERE resource_name IN ('processes', 'sessions');
-- Check current connection status
SELECT resource_name, current_utilization, limit_value
FROM v$resource_limit WHERE resource_name IN ('processes', 'sessions');
-- Find connection-hungry programs
SELECT program, COUNT(*) FROM v$session GROUP BY program ORDER BY 2 DESC;
-- Kill idle sessions
SELECT 'ALTER SYSTEM KILL SESSION '''||sid||','||serial#||''' IMMEDIATE;'
FROM v$session WHERE status='INACTIVE' AND last_call_et > 3600;
-- Increase connection limits
ALTER SYSTEM SET processes = 2000 SCOPE = SPFILE;
ALTER SYSTEM SET sessions = 3000 SCOPE = BOTH;