오라클에서 제공하는 Fast connection Failover는 jdbc 3.0 스펙인 connection pooling과 연관이 있어 보인다. Fast connection failover에 대한 간략한 내용은 아래와 같다.

FCF를 사용하면, connection을 캐시에 저장에 저장합니다. 어떤 db RAC이벤트를 생성하고 JDBC에 연결된 JVM에 전달이 됩니다.

JVM의 데몬 쓰레드가 RAC 이벤트를 받고, Connection Cache Manager에 전달한다고 합니다. JVM에서는 connection을 정리하고 다른 node로 접속 시도를 하게 된다고 한다.


How It Works

Under Fast Connection Failover, each connection in the cache maintains a mapping to a service, instance, database, and hostname.

When a database generates a RAC event, that event is forwarded to the JVM in which JDBC is running. A daemon thread inside the JVM receives the RAC event and passes it on to the Connection Cache Manager. The Connection Cache Manager then throws SQL exceptions to the applications affected by the RAC event.

A typical failover scenario may work like this:

  1. A database instance fails, leaving several stale connections in the cache.

  2. The RAC mechanism in the database generates a RAC event which is sent to the JVM containing JDBC.

  3. The daemon thread inside the JVM finds all the connections affected by the RAC event, notifies them of the closed connection through SQL exceptions, and rolls back any open transactions.

  4. Each individual connection receives a SQL exception and must retry.


인용
http://download.oracle.com/docs/cd/B19306_01/java.102/b14355/fstconfo.htm#CIHECJIJ


실제 테스트 환경은 다음의 동영상에서 확인가능하다.



이 기능을 사용하려면 오라클 서버에서 작업이 필요하며..(DB Server는 connection cache, ONS(Oracle Notification Service) 사용하도록 해야함)
자바 에서 쓰려면 다음과 같이 해야 한다.

DB Client는 이렇게..

ods.setConnectionCachingEnabled(true);

ods.setFastConnectionFailoverEnabled(true):

java실행시 -Doracle.jdbc.FastConnectionFailover  프로퍼티를 줄 것




소스를 보면 좀 명쾌한데.

OracleDataSource.java 안에 쓰레드 생성에 대한 설정 부분이 들어가 있다. 
    public synchronized void setFastConnectionFailoverEnabled(boolean flag)
        throws SQLException
    {
        if(connCachingEnabled)
        {
            if(!fastConnFailover)
            {
                fastConnFailover = flag;
                setSpawnNewThreadToCancel(flag);
            } else
            if(!flag)
                DatabaseError.throwSqlException(255);
        } else
        {
            DatabaseError.throwSqlException(137);
        }
    }



 private void setSpawnNewThreadToCancel(boolean flag)
    {
        if(flag)
        {
            if(connectionProperties == null)
                connectionProperties = new Properties();
            connectionProperties.setProperty("oracle.jdbc.spawnNewThreadToCancel", "true");
        } else
        if(connectionProperties != null)
            connectionProperties.remove("oracle.jdbc.spawnNewThreadToCancel");
    }






private static final boolean fastConnectionFailoverSysProperty = "true".equalsIgnoreCase(OracleDriver.getSystemPropertyFastConnectionFailover("false"));

    void processFastConnectionFailoverSysProperty()
    {
        if(isOracleDataSource && fastConnectionFailoverSysProperty)
        {
            connCachingEnabled = true;
            if(cacheManager == null)
                try
                {
                    cacheManager = OracleConnectionCacheManager.getConnectionCacheManagerInstance();
                }
                catch(SQLException sqlexception) { }
            fastConnFailover = true;
            setSpawnNewThreadToCancel(true);
        }
    }




OracleDriver.java안에는 이런 코드가 있다. 즉 시스템  프로퍼티 (oracle.jdbc.FastConnectionFailove)을 보고 실행할지를 결정한다.
    public static String getSystemPropertyFastConnectionFailover(String s)
    {
        return getSystemProperty("oracle.jdbc.FastConnectionFailover", s);
    }


내부적으로 보면,  OracleConnectionCacheManager 를 생성하는데, 이 클래스에는 한 쓰레드(OracleFailoverEventHandlerThread)가 돌아서 이상시 이벤트를 던지도록 하는 녀석이 존재한다.
이 쓰레드가 connection에 대한 부분에 대해서 실패시 event를 날리고, event를 받은 녀석은 connection을 실패할 때, RAC로 표현된 다른 URL 로 connection을 하게 된다.

    public void createCache(String s, OracleDataSource oracledatasource, Properties properties)
        throws SQLException
    {
        if(oracledatasource == null || !oracledatasource.getConnectionCachingEnabled())
            DatabaseError.throwSqlException(137);
        if(s == null)
            DatabaseError.throwSqlException(138);
        if(m_connCache.containsKey(s))
            DatabaseError.throwSqlException(140);
        boolean flag = oracledatasource.getFastConnectionFailoverEnabled();
        if(flag && failoverEventHandlerThread == null)
        {
            final String onsConfigStr = oracledatasource.getONSConfiguration();
            if(onsConfigStr != null && !onsConfigStr.equals(""))
                synchronized(this)
                {
                    if(!isONSInitializedForRemoteSubscription)
                    {
                        try
                        {
                            AccessController.doPrivileged(new PrivilegedExceptionAction() {

                                public Object run()
                                    throws ONSException
                                {
                                    ONS ons = new ONS(onsConfigStr);
                                    return null;
                                }

                            }
);
                        }
                        catch(PrivilegedActionException privilegedactionexception)
                        {
                            DatabaseError.throwSqlException(175, privilegedactionexception);
                        }
                        isONSInitializedForRemoteSubscription = true;
                    }
                }
            failoverEventHandlerThread = new OracleFailoverEventHandlerThread();
        }
        OracleImplicitConnectionCache oracleimplicitconnectioncache = new OracleImplicitConnectionCache(oracledatasource, properties);
        oracleimplicitconnectioncache.cacheName = s;
        oracledatasource.odsCache = oracleimplicitconnectioncache;
        m_connCache.put(s, oracleimplicitconnectioncache);
        if(flag)
            checkAndStartThread(failoverEventHandlerThread);
    }
Posted by '김용환'
,