Skip to content

ORA-12518 TNS Listener Could Not Hand Off Client Connection - Connection Pool Guide

ORA-12518: TNS Listener Could Not Hand Off Client Connection

Section titled “ORA-12518: TNS Listener Could Not Hand Off Client Connection”

Error Text: ORA-12518: TNS:listener could not hand off client connection

This error occurs when the Oracle listener cannot transfer a client connection request to an available server process or dispatcher. It’s commonly seen in high-connection environments where the database server has reached its process limits, connection pools are exhausted, or there are configuration issues preventing proper connection handoff.

  1. Client connects to listener on port 1521 (default)
  2. Listener authenticates connection request
  3. Listener hands off connection to:
    • Dedicated server process (dedicated mode)
    • Shared dispatcher (shared server mode)
  4. Direct communication established between client and server process
Client → Listener → [HANDOFF FAILS] → ORA-12518
Reasons:
- No available processes
- Process limit exceeded
- Shared server pool exhausted
- Network configuration issues
  • PROCESSES parameter limit reached
  • SESSIONS parameter limit reached
  • Operating system process limits exceeded
  • PGA memory limit preventing new processes
  • Insufficient dispatchers configured
  • Shared server processes limit reached
  • Dispatcher queue overflow
  • Improper shared server sizing
  • Application connection pool exhausted
  • Connection leaks preventing handoff
  • Improper connection pool configuration
  • Database connection limits reached
  • High network latency affecting handoff
  • Firewall timeouts during handoff process
  • Insufficient system memory for new processes
  • Load balancer configuration problems
-- Current process and session utilization
SELECT resource_name, current_utilization, max_utilization,
initial_allocation, limit_value,
ROUND(current_utilization/limit_value * 100, 2) as pct_used
FROM v$resource_limit
WHERE resource_name IN ('processes', 'sessions');
-- Process details by type
SELECT program, COUNT(*) as process_count,
ROUND(AVG(pga_used_mem)/1024/1024, 2) as avg_pga_mb,
ROUND(SUM(pga_used_mem)/1024/1024, 2) as total_pga_mb
FROM v$process
WHERE background IS NULL -- Exclude background processes
GROUP BY program
ORDER BY process_count DESC;
-- Session distribution by status
SELECT status, COUNT(*) as session_count,
ROUND(COUNT(*) * 100 / SUM(COUNT(*)) OVER(), 2) as pct_total
FROM v$session
GROUP BY status
ORDER BY session_count DESC;
-- Check if shared server is enabled
SHOW PARAMETER shared_server;
SHOW PARAMETER dispatchers;
SHOW PARAMETER shared_servers;
SHOW PARAMETER max_shared_servers;
-- Dispatcher status and utilization
SELECT name, network as protocol, status, accept,
connections, busy/(busy+idle) * 100 as busy_pct
FROM v$dispatcher
ORDER BY name;
-- Shared server processes
SELECT name, status, requests, busy_time, idle_time,
busy_time/(busy_time + idle_time) * 100 as busy_pct
FROM v$shared_server
ORDER BY name;
-- Queue information for shared servers
SELECT type, queued, wait, totalq
FROM v$queue
WHERE type IN ('COMMON', 'DISPATCHER');
-- Current connections by program
SELECT program, COUNT(*) as connections,
MIN(logon_time) as earliest_logon,
MAX(logon_time) as latest_logon
FROM v$session
WHERE username IS NOT NULL
AND program IS NOT NULL
GROUP BY program
ORDER BY connections DESC;
-- Long-running connections
SELECT username, sid, serial#, program, machine,
ROUND((SYSDATE - logon_time) * 24, 2) as hours_connected,
status, sql_id
FROM v$session
WHERE username IS NOT NULL
AND (SYSDATE - logon_time) * 24 > 8 -- Connected > 8 hours
ORDER BY logon_time;
-- Connection failures from listener log analysis
-- (This would typically be done through external log analysis)
SELECT TO_CHAR(sample_time, 'HH24') as hour,
COUNT(*) as connection_attempts
FROM v$active_session_history
WHERE event LIKE '%TNS%'
AND sample_time > SYSDATE - 1
GROUP BY TO_CHAR(sample_time, 'HH24')
ORDER BY hour;
-- Check current limits
SHOW PARAMETER processes;
SHOW PARAMETER sessions;
-- Calculate recommended values
-- SESSIONS = PROCESSES * 1.1 + 5 (minimum)
-- Example: If PROCESSES = 1500, then SESSIONS = 1655
-- Increase process limits (requires restart)
ALTER SYSTEM SET processes = 2000 SCOPE=SPFILE;
ALTER SYSTEM SET sessions = 2205 SCOPE=SPFILE; -- processes * 1.1 + 5
-- Restart required for these changes
SHUTDOWN IMMEDIATE;
STARTUP;
-- Verify changes
SHOW PARAMETER processes;
SHOW PARAMETER sessions;
Terminal window
# Check current OS limits
ulimit -a
# Check specific limits for Oracle user
su - oracle
ulimit -u # Max user processes
ulimit -n # Max file descriptors
# Set higher limits in /etc/security/limits.conf
echo "oracle soft nproc 16384" >> /etc/security/limits.conf
echo "oracle hard nproc 16384" >> /etc/security/limits.conf
echo "oracle soft nofile 65536" >> /etc/security/limits.conf
echo "oracle hard nofile 65536" >> /etc/security/limits.conf
# Or in systemd service file for Oracle
[Service]
LimitNOFILE=65536
LimitNPROC=16384
-- Calculate shared server requirements
-- Formula: SHARED_SERVERS = (concurrent_users / 10) to (concurrent_users / 5)
-- Example: For 1000 concurrent users, start with 100-200 shared servers
-- Configure shared servers
ALTER SYSTEM SET shared_servers = 100 SCOPE=BOTH;
ALTER SYSTEM SET max_shared_servers = 200 SCOPE=BOTH;
-- Configure dispatchers (usually 1 per 100-200 connections)
ALTER SYSTEM SET dispatchers = '(PROTOCOL=TCP)(DISPATCHERS=10)' SCOPE=BOTH;
-- Monitor and adjust based on utilization
SELECT AVG(busy_time/(busy_time + idle_time) * 100) as avg_busy_pct
FROM v$shared_server;
-- If avg_busy_pct > 80%, increase shared_servers
-- If avg_busy_pct < 20%, decrease shared_servers (gradually)
-- Use dedicated server when:
-- 1. Long-running transactions/queries
-- 2. High CPU per session
-- 3. Small number of concurrent users (< 100)
-- Use shared server when:
-- 1. Many concurrent connections (> 200)
-- 2. Short transactions
-- 3. OLTP workload with brief connections
-- Mixed configuration example:
-- Default to shared server, but allow dedicated for specific services
ALTER SYSTEM SET dispatchers =
'(PROTOCOL=TCP)(SERVICE=mydb_shared)(DISPATCHERS=5)(POOL=ON)' SCOPE=BOTH;
-- Create dedicated service for long-running operations
BEGIN
DBMS_SERVICE.CREATE_SERVICE(
service_name => 'mydb_dedicated',
network_name => 'mydb_dedicated'
);
DBMS_SERVICE.START_SERVICE(service_name => 'mydb_dedicated');
END;
/
// Example connection pool settings (Java)
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:oracle:thin:@server:1521:mydb");
config.setUsername("appuser");
config.setPassword("password");
// Pool sizing
config.setMaximumPoolSize(50); // Max connections
config.setMinimumIdle(10); // Always keep these ready
config.setConnectionTimeout(30000); // 30 seconds to get connection
config.setIdleTimeout(300000); // 5 minutes idle timeout
config.setMaxLifetime(1800000); // 30 minutes max connection life
// Validation
config.setConnectionTestQuery("SELECT 1 FROM DUAL");
config.setValidationTimeout(5000); // 5 seconds validation timeout
HikariDataSource dataSource = new HikariDataSource(config);
-- Create monitoring for connection leaks
CREATE TABLE connection_audit (
audit_time TIMESTAMP DEFAULT SYSTIMESTAMP,
username VARCHAR2(30),
program VARCHAR2(64),
machine VARCHAR2(64),
session_id NUMBER,
action VARCHAR2(10) -- CONNECT, DISCONNECT
);
-- Trigger to track connections (example)
CREATE OR REPLACE TRIGGER audit_connections
AFTER LOGON ON DATABASE
BEGIN
INSERT INTO connection_audit (username, program, machine, session_id, action)
VALUES (USER, SYS_CONTEXT('USERENV', 'MODULE'),
SYS_CONTEXT('USERENV', 'HOST'),
SYS_CONTEXT('USERENV', 'SID'), 'CONNECT');
COMMIT;
END;
/
-- Create services for different connection types
BEGIN
-- OLTP service with connection load balancing
DBMS_SERVICE.CREATE_SERVICE(
service_name => 'mydb_oltp',
network_name => 'mydb_oltp',
failover_method => 'BASIC',
failover_type => 'SELECT',
failover_retries => 180,
failover_delay => 5,
clb_goal => 'SHORT', -- Connection load balancing
rlb_goal => 'SERVICE_TIME' -- Runtime load balancing
);
-- Reporting service with session load balancing
DBMS_SERVICE.CREATE_SERVICE(
service_name => 'mydb_reports',
network_name => 'mydb_reports',
clb_goal => 'LONG', -- Better for long sessions
rlb_goal => 'THROUGHPUT'
);
DBMS_SERVICE.START_SERVICE('mydb_oltp');
DBMS_SERVICE.START_SERVICE('mydb_reports');
END;
/
-- Configure listener for services
-- In listener.ora:
-- SID_LIST_LISTENER =
-- (SID_LIST =
-- (SID_DESC =
-- (GLOBAL_DBNAME = mydb_oltp.domain.com)
-- (SERVICE_NAME = mydb_oltp)
-- (SID_NAME = mydb1)
-- )
-- (SID_DESC =
-- (GLOBAL_DBNAME = mydb_reports.domain.com)
-- (SERVICE_NAME = mydb_reports)
-- (SID_NAME = mydb1)
-- )
-- )
-- Create capacity monitoring procedure
CREATE OR REPLACE PROCEDURE capacity_check AS
v_processes_pct NUMBER;
v_sessions_pct NUMBER;
v_dispatchers_busy NUMBER;
BEGIN
-- Check process utilization
SELECT current_utilization/limit_value * 100
INTO v_processes_pct
FROM v$resource_limit
WHERE resource_name = 'processes';
-- Check session utilization
SELECT current_utilization/limit_value * 100
INTO v_sessions_pct
FROM v$resource_limit
WHERE resource_name = 'sessions';
-- Check dispatcher utilization
SELECT AVG(busy/(busy+idle) * 100)
INTO v_dispatchers_busy
FROM v$dispatcher;
-- Alert conditions
IF v_processes_pct > 85 THEN
DBMS_OUTPUT.PUT_LINE('WARNING: Process utilization at ' ||
ROUND(v_processes_pct, 2) || '%');
END IF;
IF v_sessions_pct > 85 THEN
DBMS_OUTPUT.PUT_LINE('WARNING: Session utilization at ' ||
ROUND(v_sessions_pct, 2) || '%');
END IF;
IF v_dispatchers_busy > 80 THEN
DBMS_OUTPUT.PUT_LINE('WARNING: Dispatcher utilization at ' ||
ROUND(v_dispatchers_busy, 2) || '%');
END IF;
END;
/
-- Schedule capacity monitoring
BEGIN
DBMS_SCHEDULER.CREATE_JOB(
job_name => 'CAPACITY_CHECK_JOB',
job_type => 'STORED_PROCEDURE',
job_action => 'capacity_check',
repeat_interval => 'FREQ=MINUTELY; INTERVAL=10', -- Every 10 minutes
enabled => TRUE
);
END;
/
-- Monitor connection patterns
CREATE VIEW v_connection_summary AS
SELECT program,
COUNT(*) as current_connections,
AVG(SYSDATE - logon_time) * 24 as avg_hours_connected,
MAX(SYSDATE - logon_time) * 24 as max_hours_connected,
MIN(logon_time) as oldest_connection
FROM v$session
WHERE username IS NOT NULL
AND program IS NOT NULL
GROUP BY program;
-- Daily connection summary
SELECT * FROM v_connection_summary
ORDER BY current_connections DESC;
-- Create automatic connection management
CREATE OR REPLACE PROCEDURE manage_connections AS
CURSOR long_connections IS
SELECT sid, serial#, username, program, machine,
ROUND((SYSDATE - logon_time) * 24, 2) as hours_connected
FROM v$session
WHERE username IS NOT NULL
AND status = 'INACTIVE'
AND (SYSDATE - logon_time) * 24 > 12 -- > 12 hours inactive
AND program NOT LIKE '%Background%';
v_process_pct NUMBER;
BEGIN
-- Check if we're approaching limits
SELECT current_utilization/limit_value * 100
INTO v_process_pct
FROM v$resource_limit
WHERE resource_name = 'processes';
-- If > 90% utilized, kill long inactive connections
IF v_process_pct > 90 THEN
FOR rec IN long_connections LOOP
BEGIN
EXECUTE IMMEDIATE 'ALTER SYSTEM KILL SESSION ''' ||
rec.sid || ',' || rec.serial# || ''' IMMEDIATE';
DBMS_OUTPUT.PUT_LINE('Killed session: ' || rec.username ||
' (' || rec.program || ') - inactive ' ||
rec.hours_connected || ' hours');
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Failed to kill session: ' || rec.sid);
END;
END LOOP;
END IF;
END;
/
  • ORA-12519 - TNS no appropriate service handler found
  • ORA-12520 - TNS listener could not find available handler
  • ORA-00020 - Maximum number of processes exceeded
  • ORA-00018 - Maximum number of sessions exceeded
  • ORA-12154 - TNS could not resolve connect identifier
  1. Check current utilization

    SELECT resource_name, current_utilization, limit_value,
    ROUND(current_utilization/limit_value * 100, 2) as pct_used
    FROM v$resource_limit
    WHERE resource_name IN ('processes', 'sessions');
  2. Kill inactive sessions

    -- Find and kill long inactive sessions
    SELECT 'ALTER SYSTEM KILL SESSION ''' || sid || ',' || serial# || ''' IMMEDIATE;'
    FROM v$session
    WHERE status = 'INACTIVE'
    AND username IS NOT NULL
    AND (SYSDATE - logon_time) * 24 > 8 -- > 8 hours
    AND program NOT LIKE '%Background%';
  3. Temporary process limit increase

    -- If possible, increase limits temporarily
    ALTER SYSTEM SET processes = 3000 SCOPE=MEMORY;
    ALTER SYSTEM SET sessions = 3305 SCOPE=MEMORY;
  4. Check shared server status

    -- Increase shared servers if needed
    ALTER SYSTEM SET shared_servers = 200 SCOPE=MEMORY;
    ALTER SYSTEM SET max_shared_servers = 400 SCOPE=MEMORY;
  • Implement proper connection pooling
  • Monitor and tune process limits
  • Configure shared server appropriately
  • Set up service-based load balancing
  • Establish connection monitoring and alerting

The key to preventing ORA-12518 is proactive capacity management and proper connection architecture design.