SimpleFAN: Extending Oracle RAC High Availability to Custom Connection Management

Leverage Oracle’s SimpleFAN API to build RAC-aware connection managers with full FAN event support

Oracle Real Application Clusters (RAC) provides enterprise applications with robust high availability through Fast Application Notification (FAN) events. While Oracle Universal Connection Pool (UCP) and Oracle JDBC drivers offer comprehensive FAN support out of the box, there are scenarios where developers need to build custom connection management solutions or integrate FAN capabilities into existing application architectures.

SimpleFAN is Oracle’s standalone Java API that enables any Java application to harness the full power of Oracle RAC FAN events, providing the building blocks for sophisticated, RAC-aware connection management systems.

When to Use SimpleFAN

SimpleFAN is ideal when you’re building:

  • Custom connection pooling solutions tailored to specific application requirements
  • Application-level connection managers with specialized routing logic
  • Integration layers that need RAC awareness for existing systems
  • Third-party JDBC drivers for the Oracle Database
  • Middleware components that require direct FAN event handling

If you’re starting a new project, Oracle Universal Connection Pool remains the recommended solution for comprehensive Oracle RAC integration.

Understanding FAN Events

Oracle RAC FAN events provide real-time notifications about cluster state changes. SimpleFAN exposes four essential event types:

  1. Service Up Events – Database services becoming available
  2. Service Down Events – Services becoming unavailable (planned or unplanned)
  3. Node Down Events – Complete node failures
  4. Load Advisory Events – Real-time load balancing metrics

Prerequisites and Setup

Dependencies

SimpleFAN is available through Maven Central. Use the latest versions:

<dependency>
        <groupId>com.oracle.ojdbc</groupId>
        <artifactId>simplefan</artifactId>
        <version>23.8.0.25.04</version>
    </dependency>
    <dependency>
        <groupId>com.oracle.ojdbc</groupId>
        <artifactId>ons</artifactId>
        <version>23.8.0.25.04</version>
    </dependency>
    

Service Configuration

Critical: Your Oracle RAC service must be configured with FAN enabled for SimpleFAN to receive events:

# Create or modify a service with FAN enabled
    srvctl add service -db myrac -service myapp_service -notification TRUE
    
    # Or modify existing service
    srvctl modify service -db myrac -service myapp_service -notification TRUE
    
    # Verify FAN configuration
    srvctl config service -db myrac -service myapp_service
    

The Hybrid Approach: Database-Driven ONS Configuration

One of SimpleFAN’s most powerful capabilities is retrieving ONS (Oracle Notification Service) configuration directly from the database connection – the same method Oracle’s JDBC driver uses internally. This eliminates manual ONS configuration and ensures consistency with Oracle’s native approach.

Core Implementation

import oracle.simplefan.*;
    import oracle.jdbc.pool.OracleDataSource;
    import java.sql.Connection;
    import java.util.Properties;
    import java.util.logging.Logger;
    
    public class SimpleFanConnectionManager implements FanEventListener {
        private static final Logger logger = Logger.getLogger(SimpleFanConnectionManager.class.getName());
        
        private FanManager fanManager;
        private FanSubscription fanSubscription;
        private String serviceName;
        
        public void initialize(String serviceName) {
            this.serviceName = serviceName;
            
            try {
                // Step 1: Get ONS config from database like Oracle JDBC driver
                String onsConfig = getONSConfigFromDatabase();
                
                // Step 2: Initialize SimpleFAN with retrieved config
                initializeSimpleFan(onsConfig);
                
            } catch (Exception e) {
                logger.severe("Failed to initialize SimpleFAN: " + e.getMessage());
                throw new RuntimeException("SimpleFAN initialization failed", e);
            }
        }
    }
    

Database ONS Configuration Retrieval

This method mirrors Oracle’s internal approach for ONS discovery:

private String getONSConfigFromDatabase() throws SQLException {
        OracleDataSource ods = new OracleDataSource();
        // Disable OJDBC's built-in FAN support since we're using standalone SimpleFAN
        ods.setURL("jdbc:oracle:thin:@//hostname:1521/servicename?oracle.jdbc.fanEnabled=false");
        ods.setUser("username");
        ods.setPassword("password");
        
        Connection dbConnection = ods.getConnection();
        
        try {
            if (dbConnection instanceof oracle.jdbc.OracleConnection) {
                oracle.jdbc.OracleConnection oracleConn = 
                    (oracle.jdbc.OracleConnection) dbConnection;
                
                Properties serverInfo = oracleConn.getServerSessionInfo();
                
                // Get AUTO ONS config - Oracle's standard method
                String autoONSConfig = serverInfo.getProperty("AUTH_ONS_CONFIG");
                
                if (autoONSConfig != null && !autoONSConfig.isEmpty()) {
                    logger.info("Found AUTO ONS config: " + autoONSConfig);
                    return autoONSConfig;
                } else {
                    throw new SQLException("AUTO ONS config not available from database connection");
                }
            } else {
                throw new SQLException("Connection is not an Oracle connection");
            }
        } finally {
            dbConnection.close();
        }
    }
    

SimpleFAN Initialization

Initialize SimpleFAN using Oracle’s native configuration approach:

private void initializeSimpleFan(String onsConfigStr) {
        try {
            logger.info("Initializing SimpleFAN with ONS config: " + onsConfigStr);
            
            // Get FanManager singleton instance
            fanManager = FanManager.getInstance();
            
            // Configure ONS using Oracle's method
            Properties onsProps = new Properties();
            onsProps.setProperty("onsRemoteConfig", onsConfigStr);
            fanManager.configure(onsProps);
            
            // Create service subscription
            Properties subscriptionProps = new Properties();
            subscriptionProps.setProperty("serviceName", serviceName);
            
            fanSubscription = fanManager.subscribe(subscriptionProps);
            fanSubscription.addListener(this);
            
            logger.info("SimpleFAN subscription created for service: " + serviceName);
            
        } catch (Exception e) {
            logger.severe("SimpleFAN initialization failed: " + e.getMessage());
            throw new RuntimeException("Failed to initialize SimpleFAN", e);
        }
    }
    

Event Handler Implementation

Implement the FanEventListener interface to respond to Oracle RAC events:

Service Down Event Handling

@Override
    public void handleEvent(ServiceDownEvent event) {
        logger.warning("SERVICE DOWN event received for: " + event.getServiceName());
        
        System.out.println("\n=== SERVICE DOWN EVENT ===");
        System.out.println("Service: " + event.getServiceName());
        System.out.println("Time: " + event.getTimestamp());
        System.out.println("Reason: " + event.getReason());
        System.out.println("Kind: " + event.getKind());
        System.out.println("Database: " + event.getDatabaseUniqueName());
        
        if (event.getKind().toString().equals("MEMBER")) {
            // Specific instance down
            String instanceName = event.getServiceMemberEvent().getInstanceName();
            String nodeName = event.getServiceMemberEvent().getNodeName();
            
            System.out.println("Instance: " + instanceName);
            System.out.println("Node: " + nodeName);
            
            // Handle instance-specific outage
            handleInstanceDown(instanceName, nodeName);
        } else {
            // Entire service down
            handleServiceDown(event.getServiceName());
        }
        System.out.println("========================\n");
    }
    

Load Advisory Event Handling

@Override
    public void handleEvent(LoadAdvisoryEvent event) {
        logger.info("LOAD ADVISORY event received");
        
        System.out.println("\n=== LOAD ADVISORY EVENT ===");
        System.out.println("Service: " + event.getServiceName());
        System.out.println("Database: " + event.getDatabaseUniqueName());
        System.out.println("Instance: " + event.getInstanceName());
        System.out.println("Service Quality: " + event.getServiceQuality());
        System.out.println("Percent: " + event.getPercent());
        System.out.println("Time: " + event.getTimestamp());
        System.out.println("==========================\n");
        
        // Implement load balancing logic
        updateLoadBalancing(event.getInstanceName(), 
                           event.getServiceQuality(), 
                           event.getPercent());
    }
    

Node Down Event Handling

@Override
    public void handleEvent(NodeDownEvent event) {
        logger.warning("NODE DOWN event received for: " + event.getNodeName());
        
        System.out.println("\n=== NODE DOWN EVENT ===");
        System.out.println("Node: " + event.getNodeName());
        System.out.println("Time: " + event.getTimestamp());
        System.out.println("======================\n");
        
        // Handle complete node failure
        handleNodeDown(event.getNodeName());
    }
    

Example Log Output

When SimpleFAN is running and Oracle RAC events occur, you’ll see console output like this:

Application Startup

INFO: Found AUTO ONS config: nodes=racnode1:6200,racnode2:6200
    INFO: Initializing SimpleFAN with ONS config: nodes=racnode1:6200,racnode2:6200
    INFO: SimpleFAN subscription created for service: myapp_service
    

Normal Load Advisory Event

INFO: LOAD ADVISORY event received
    
    === LOAD ADVISORY EVENT ===
    Service: myapp_service
    Database: MYRAC
    Instance: myrac_1
    Service Quality: 25
    Percent: 35
    Time: 2025-06-23T14:25:30.123Z
    ==========================
    

Planned Instance Maintenance

WARNING: SERVICE DOWN event received for: myapp_service
    
    === SERVICE DOWN EVENT ===
    Service: myapp_service
    Time: 2025-06-23T14:30:45.456Z
    Reason: USER
    Kind: MEMBER
    Database: MYRAC
    Instance: myrac_1
    Node: racnode1
    ========================
    
    INFO: Removed connections to instance: myrac_1
    

Load Shifts to Remaining Instance

INFO: LOAD ADVISORY event received
    
    === LOAD ADVISORY EVENT ===
    Service: myapp_service
    Database: MYRAC
    Instance: myrac_2
    Service Quality: 75
    Percent: 85
    Time: 2025-06-23T14:31:00.789Z
    ==========================
    

Instance Returns Online

INFO: SERVICE UP event received for: myapp_service
    
    === SERVICE UP EVENT ===
    Service: myapp_service
    Time: 2025-06-23T14:35:12.321Z
    Kind: MEMBER
    Database: MYRAC
    Instance: myrac_1
    Node: racnode1
    ========================
    

Complete Node Failure

WARNING: NODE DOWN event received for: racnode2
    
    === NODE DOWN EVENT ===
    Node: racnode2
    Time: 2025-06-23T14:40:15.987Z
    ======================
    
    WARNING: SERVICE DOWN event received for: myapp_service
    
    === SERVICE DOWN EVENT ===
    Service: myapp_service
    Time: 2025-06-23T14:40:16.123Z
    Reason: FAILURE
    Kind: MEMBER
    Database: MYRAC
    Instance: myrac_2
    Node: racnode2
    ========================
    
    INFO: Removed connections to instance: myrac_2
    

Connection Manager Integration

Custom Connection Manager Example

Here’s how to integrate SimpleFAN into a custom connection manager:

public class RACConnectionManager {
        private SimpleFanConnectionManager fanManager;
        private ConnectionPool connectionPool;
        
        public void initialize(String serviceName) {
            // Initialize your connection pool
            connectionPool = new CustomConnectionPool();
            
            // Initialize SimpleFAN
            fanManager = new SimpleFanConnectionManager();
            fanManager.initialize(serviceName);
        }
        
        // Implement event response methods
        private void handleInstanceDown(String instanceName, String nodeName) {
            // Remove connections to specific instance
            connectionPool.removeConnectionsToInstance(instanceName);
            logger.info("Removed connections to instance: " + instanceName);
        }
        
        private void handleServiceDown(String serviceName) {
            // Handle complete service unavailability
            connectionPool.markServiceUnavailable(serviceName);
            logger.warning("Service marked as unavailable: " + serviceName);
        }
        
        private void updateLoadBalancing(String instanceName, String serviceQuality, int percent) {
            // Adjust connection routing based on load metrics
            connectionPool.updateInstanceWeight(instanceName, percent);
        }
    }
    

Production Considerations

Clean Shutdown

Always implement proper cleanup:

public void shutdown() {
        logger.info("Shutting down SimpleFAN connection manager...");
        
        if (fanSubscription != null) {
            try {
                fanSubscription.close();
                logger.info("SimpleFAN subscription closed successfully");
            } catch (Exception e) {
                logger.warning("Error closing SimpleFAN subscription: " + e.getMessage());
            }
        }
    }
    
    // Register shutdown hook
    Runtime.getRuntime().addShutdownHook(new Thread(this::shutdown));
    
    
     

Testing Your Implementation

Use Oracle’s srvctl commands to test FAN event handling:

# Planned service down with drain timeout and immediate stop option
    srvctl stop service -db myrac -service myapp_service -instance myrac_1 -drain_timeout 30 -stopoption IMMEDIATE
    
    # Service up
    srvctl start service -db myrac -service myapp_service -instance myrac_1
    

Monitor your application logs to verify that SimpleFAN receives and processes these events correctly.

Key Benefits

Implementing SimpleFAN in custom connection managers provides:

  1. Oracle RAC Awareness: Real-time notification of cluster state changes
  2. Zero-Downtime Maintenance: Graceful handling of planned Oracle operations
  3. Intelligent Connection Routing: Load balancing based on actual cluster metrics
  4. Faster Failure Detection: Immediate notification of outages
  5. Enterprise-Grade Reliability: Leverage Oracle’s proven high availability infrastructure

Conclusion

SimpleFAN empowers developers to build sophisticated, Oracle RAC-aware connection management solutions while leveraging Oracle’s proven high availability infrastructure. The hybrid approach demonstrated here ensures your custom connection manager integrates seamlessly with Oracle’s native FAN event system.

By implementing SimpleFAN, you can create connection management solutions that respond intelligently to Oracle RAC cluster changes, providing your applications with enterprise-grade database high availability.