Using failover in a high availability system

A high availability cluster includes two or more machines that are configured so that, if one machine (or application) is interrupted, the second machine assumes the workload of both machines. Each of these machines is called one node of the high availability cluster. A high availability cluster is typically used in an environment that must always be available, for example, a banking system to which clients must connect continuously, 365 days a year.

The machines in Figure 3-1 are configured so that each machine can read the other machine's disks, although not at the same time (all the disks that are failed-over should be shared disks).

For example, if Adaptive Server 1 is the primary companion server, and it crashes, Adaptive Server 2, as the secondary companion server, reads its disks (disks 1 - 4) and manages any databases on them until Adaptive Server 1 can be rebooted. Any clients connected to Adaptive Server 1 are automatically connected to Adaptive Server 2.

Figure 3-1: High availability cluster using failover

Failover enables Adaptive Server to work in a high availability cluster in active-active or active-passive configuration.

During failover, clients connected to the primary companion using the failover property automatically reestablish their network connections to the secondary companion. Failover can be enabled by setting the connection property HASession to “1” (default value is “0”). If this property is not set, the session failover does not occur, even if the server if configured for failover. You also have to set SecondaryServer (the IP address or the machine name of the secondary ASE server) and SecondaryPort (the port number of the secondary ASE server) properties. See the ASE book, Using Sybase Failover in a High Availability System, for information about configuring your system for high availability.

When the ASE ODBC driver detects a connection failure with the primary ASE server, it first tries to reconnect to the primary. If it cannot reconnect, it assumes a failover has occurred. Then, it automatically tries to connect to the secondary ASE server using the connection properties set in SecondaryServer, SecondaryPort.

If a connection to the secondary server is established, the ASE ODBC Driver returns SQL_ERROR for the function return code. You should further examine the SQLState and NativeError for values of “08S01” and “30130” respectively to confirm a successful failover. The error message returned on such failover is:

“Connection to Sybase server has been lost, you have been successfully connected to the next available HA server. All active transactions have been rolled back.”

You can access these values by calling SQLGetDiagRec on the StatementHandle. Then, the client must reapply the failed transaction with the new connection. If failover happens while a transaction is open, only changes that were committed to the database before failover are retained.

If the connection to the secondary server is not established, the ASE ODBC Driver returns SQL_ERROR for the function return code. You should further examine the SQLState and NativeError for values of “08S01” and “30131” to confirm that failover did not occur. The error message returned on an unsuccessful failover is:

“Connection to Sybase server has been lost, connection to the next available HA server also failed. All active transactions have been rolled back”.

You can access these values by calling SQLGetDiagRec on the StatementHandle.

The following code snippet shows how to code for a failover:

/* Declare required variables */
....
/* Open Database connection */
....
/* Perform a transaction */
...
/* Check return code and handle failover */
if( retcode == SQL_ERROR )
{
   retcode = SQLGetDiagRec(stmt, 1,       sqlstate,&NativeError, errmsg,100, NULL );
   if(retcode == SQL_SUCCESS || 
      retcode == SQL_SUCCESS_WITH_INFO)
   {
        if(NativeError == 30130 )
        {
        /* Successful failover retry transaction*/
        ...
     }
     else if (NativeError == 30131)
     {
        /* Failover failed. Return error */
        ...
     }
   }	
}