6. Fail-Over#
The Fail-Over feature is provided so that a fault that occurs while a database is providing service can be overcome and service can continue to be provided as though no fault had occurred. This chapter explains the Fail-Over feature that is provided with Altibase, and how to use it.
Fail-Over Overview#
Concept#
"Fail-Over" refers to the ability to overcome a fault that occurs while a database is providing service, so that service can continue to be provided as though no fault had occurred.
The kinds of faults that can occur include the case in which the DBMS server hardware itself develops a fault, the case in which the server's network connection is interrupted, and the case in which a software error causes the DBMS to shut down abnormally. When any of the above kinds of fault occurs, Fail-Over makes it possible to connect to another server, so that service can be provided without interruption, and so that client applications are never aware that a fault has occurred.
There are two types of Fail-Over, distinguished from each other according to the time of failure cognition.
-
CTF (Connection Time Fail-Over)
-
STF (Service Time Fail-Over)
CTF refers to the case where the fault is noted at the time of connection to the DBMS, and connection is made to a DBMS on another available node rather than to the DBMS suffering from the fault, so that service can continue to be provided.
In the case of STF, in contrast, because a fault occurs while service is being provided after successful connection to the DBMS, reconnection is made to a DBMS on another available node, and session properties are restored, so that the business logic of the user's application can continue to be used. Therefore, tasks currently being executed on the DBMS in which the fault occurred may need to be reexecuted.
With this kind of Fail-Over, in order to have confidence in the results of a task, the databases on the DBMS in which the fault occurred and the DBMS that is available to provide service must be guaranteed to be in exactly the same state and to contain exactly the same data.
In order to guarantee that the databases match, Altibase copies the database using Off-Line Replication. In Off-Line Replication, the Standby Server reads the logs from the Active Server so that it can harmonize its database with that on the Active Server.
Because one of the characteristics of replication is that the databases might not be in exactly the same state, we recommend that the Fail-Over Callback function be used to confirm that the databases match.
Fail-Over settings of Altibase include a Fail-Over property, which is set to TRUE to specify that Fail-Over is to be executed. Additionally, the Fail-Over Callback function can be used to check whether the databases match before Fail-Over is executed.
The three kinds of Fail-Over-related tasks that must be executed by the client application are summarized as follows:
-
The Fail-Over properties must be registered
-
The Fail-Over Callback function must be registered.
-
Additional tasks may be necessary depending on the result of callback
Fail-Over Process#
The Fail-Over registration and handling process is as shown in the following figure.

Fail-Over Callback must be registered by the user, and, once registered, during the Fail-Over process the Altibase User Library (for example, the JDBC and CLI libraries) communicates with client applications, as shown in the picture above.
If Fail-Over Callback is not registered, Fail-Over takes place without communication with the client application, and a trace log of the steps shown above is kept. In a replicated Altibase database environment, the use of callback is strongly recommended, so that Fail-Over Validation can be conducted.

-
After connecting to the database, the user registers Fail-Over Callback in the connection attributes.
-
The business logic is conducted in the client application. While the client application is running, if it receives an error message about a fault occurring in the DBMS hardware (including a network error), it calls the Altibase User Library so that Fail-Over can be conducted.
-
This client library sends a Fail-Over Start Event (FO_BEGIN) to the registered Fail-Over Callback. Fail-Over Callback returns information about whether Fail-Over will continue to progress.
- If Fail-Over Callback determines that the Fail-Over process should continue (FO_GO), executed SQL statements are closed, an available server is located, and the Altibase User Library connects and logs in to that database. Additionally, the properties of the previous session (autocommit mode, optimization settings, XA connection settings, etc.) are restored on the new server.
- When step number 4 is complete, Fail-Over Callback sends an event indicating that the Fail-Over process has been completed successfully (FO_END).
- Fail-Over Callback executes a query to ensure that the databases match (Fail-Over Validation). In a replicated database environment, it is essential to ensure that the databases match.
Using Fail-Over#
Registering Connection Properties#
Once the Fail-Over connection properties have been registered, when a fault occurs, Altibase detects this and internally conducts the Fail-Over tasks according to the expressly specified connection properties.
The properties can be specified in the following two ways:
-
By specifying the Connection String when calling the API's connection function
-
By specifying connection properties in the appropriate Altibase settings file (altibase_cli.ini)
Specifying the Connection String in a Client Application#
When the connection function is executed in the client application, the following connection strings can be specified:
JDBC#
Jdbc:Altibase://192.168.3.51:20300/mydb?AlternateServers=(192.168.3.54:20300,192.168.3.53:20300) &ConnectionRetryCount=3&ConnectionRetryDelay=3&SessionFailOver=on;
ODBC, Embedded SQL#
DSN=192.168.3.51;UID=altibase;PWD=altibase;PORT_NO=20300;AlternateServers=(192.168.3.54:20300,192.168.3.53:20300);ConnectionRetryCount=3;ConnectionRetryDelay=5;SessionFailOver=on;
PDO#
Server=192.168.3.51;Port=20300;AlternateServers=(192.168.3.54:20300,192.168.3.53:20300);ConnectionRetryCount=3;ConnectionRetryDelay=5;SessionFailOver=on
AlternateServer indicates servers to which connection can be made in the event of a fault, and is expressed in the form (IP Address1:Port1, IP Address2:Port2,...).
ConnectionRetryCount is the number of repeated connection attempts when a connection to an available server fails. It can be omitted, and the default value is 1.
ConnectionRetryDelay is the number of seconds to wait before attempting to reconnect if an available server connection fails. It can be omitted, and the default value is 1.
SessionFailOver indicates whether to perform a service time fail-over (STF). It can be omitted, and the default value is off.
Specifying the Connection Properties in the Settings File#
The Fail-Over connection settings can be specified in the Data Source portion of the altibase_cli.ini file, which is located in the $ALTIBASE_HOME/conf directory, the $HOME directory, or the current directory of the relevant client application, and the DataSource name is specified in the Connection String of the connection function.
[MyDataSource1]
Server = 192.168.3.51
Port = 20300
User = altibase
Password = altibase
DataBase = mydb
AlternateServers = (192.168.3.54:20300,192.168.3.53:20300)
ConnectionRetryCount = 3
ConnectionRetryDelay = 5
SessionFailOver = off
The Connection String of the client application's connection function appears as shown below, depending on the connection interface used by the client application.
JDBC#
The data source name is specified as part of the Connection URL as follows:
Jdbc:Altibase://MyDataSource1//
ODBC, Embedded SQL#
The data source name is specified in the DSN properties as follows. Settings are made in the odic.ini file in the same way that they are made in the altibase.cli.ini file.
DSN=MyDataSource1
PDO#
The data source name is specified in the DSN properties as follows:
altibase:MyDataSource1
Checking Whether Fail-Over Succeeded#
Whether CTF (Connection Time Fail-Over) was successful can be quickly and easily determined merely by checking whether it is possible to connect to the database. In contrast, determining whether STF (Service Time Fail-Over) was successful involves checking for exceptions and errors.
For example, when using JDBC, a SQLException is caught, and the SQLException's getSQLState() method is used to check the value of SQLStates.status. If this value is ES_08FO01, Fail-Over is determined to have been successful.
When using a CLI or ODBC, if the result of SQLPrepare, SQLExecute, SQLFetch or the like is an error, rather than SQL_SUCCESS, a statement handle is handed over to the SQLGetDiagRec function, and if the native error code that is returned in the 5th argument of this function has a diagnostic record equal to ALTIBASE_FAILOVER_SUCCESS, STF (Service Time Fail-Over) can be determined to have succeeded.
When using Embedded SQL, after executing the EXEC SQL command, if sqlca.sqlcode is not SQL_SUCCESS but ALTIBASE_FAILOVER_SUCCESS, this means that STF (Service Time Fail-Over) was successful.
The actual method of determining whether Fail-Over has succeeded varies according to the type of client application, as will be explained below.
In case of PDO, if the driver-specific error code is same as PDO::ALTIBASE_FAIL_SUCCESS, this means that STF (Service Time Fail-Over) was successful.
Writing Fail-Over Callback Functions#
It is necessary to write a callback function to determine whether databases match when Fail-Over is executed. The method of writing Fail-Over Callback functions varies depending on the type of client application, but the basic structure is the same, and is as follows:
- Define data structures related to Fail-Over
- Write Fail-Over Callback function bodies for handling Fail-Over-related events
- Write code to determine whether Fail-Over was successful
Either Fail-Over events are defined in the data structure definition, or else a defined interface (header file) is included in the data structure definition.
Various tasks must be conducted in response to Fail-Over-related events such as the start or completion of Fail-Over. Code for performing these tasks, including for example the task of checking whether the contents of databases match, is located in the callback function body.
Determining that Fail-Over has succeeded consists of the successful completion of Fail-Over and the successful execution of a Fail-Over callback function, and means that service that was suspended due to a fault can continue to be provided.
The actual method of writing callback functions is described below for various client application environments.
Writing Callback in JDBC#
Fail-Over related Callback Interface#
To write a Fail-Over callback function in an application using JDBC, the user must write a class that implements the ABFailOverCallback interface provided by the Altibase JDBC driver. The ABFailOverCallback interface is defined as follows.
public interface ABFailOverCallback
{
int FO_BEGIN = 0;
int FO_END = 1;
int FO_ABORT = 2;
int FO_GO = 3;
int FO_QUIT = 4;
int failOverCallback(Connection aConnection,
Object aAppContext,
int aFailOverEvent);
};
The meaning of the values is as follows:
FO_BEGIN#
This event notifies Fail-Over callback that STF (Service Time FailOver) starts.
FO_END#
This event notifies Fail-Over callback that the STF was successful.
FO_ABORT#
This event notifies Fail-Over callback that the STF has failed.
FO_GO#
This is the value that the user returns from the callback funciton to the library in order to proceed with the next step of STF.
FO_QUIT#
This is the value that the user returns from the callback function to the library, meaning that the STF should not proceed.
aAppContext#
This is an arbitrary object that the user wants to save. If there is no object to save, set it to null.
Writing Fail-Over Callback Functions#
The MyFailOverCallback class, which implements the ABFailOverCallback Interface, must be written.
The tasks to be conducted in response to the FO_BEGIN and FO_END events, which are defined in the callback interface, must be handled by this class. That is to say, the required tasks for each of the Fail-Over events are described here.
For example, when the FO_BEGIN event occurs, code for handling tasks that are required before Fail-Over starts is provided, and when the FO_END event occurs, code for handling tasks that are required after Fail-Over ends and before service resumes is provided. One concrete example is the code that is used to check whether the data are consistent between available databases when the FO_END event occurs.
public class MyFailOverCallback implements ABFailOverCallback
{
public int failOverCallback(Connection aConnection,
Object aAppContext,
int aFailOverEvent)
{
Statement sStmt = null;
ResultSet sRes = null;
switch (aFailOverEvent)
{
case ABFailOverCallback.FO_BEGIN:
System.out.println("FailOver Started .... ");
break;
case ABFailOverCallback.FO_END:
try
{
sStmt = aConnection.createStatement();
}
catch( SQLException ex1 )
{
try
{
sStmt.close();
}
catch( SQLException ex3 )
{
}
return ABFailOverCallback.FO_QUIT;
} //catch SQLException ex1
try
{
sRes = sStmt.executeQuery("select 1 from dual");
while(sRes.next())
{
if(sRes.getInt(1) == 1 )
{
break;
}
}//while;
}
catch ( SQLException ex2 )
{
try
{
sStmt.close();
}
catch( SQLException ex3 )
{
}
return ABFailOverCallback.FO_QUIT;
}//catch
break;
}//switch
return ABFailOverCallback.FO_GO;
}
}
Furthermore, the MyFailOverCallback class defined above is used to create a callback object.
MyFailOverCallback sMyFailOverCallback = new MyFailOverCallback();
Properties sProp = new Properties();
String sURL = "jdbc:Altibase://192.168.3.51:20300+"/mydb?AlternateServers=(192.168.3.54:20300,192.168.3.53:20300)&connectionRetryCount=3&
connectionRetryDelay=10&sessionFailOver=on";
The created callback object is registered with the connection object.
((ABConnection)sCon).registerFailOverCallback(sMyFailOverCallback,null);
Checking Whether Fail-Over Succeeded#
Checking whether Fail-Over, particularly STF (Service Time Fail-Over), was successful is conducted using SQLException. An SQLException is caught, and the SQLException's getSQLState() method is used to check the value of SQLStates.status. If this value is ES_08FO01, Fail-Over is determined to have been successful.
The following example demonstrates how to check whether Fail-Over was successful.
while(true)
{
try
{
sRes = sStmt.executeQuery("SELECT C1 FROM T1");
while( sRes.next() )
{
System.out.println( "VALUE : " + sRes.getString(1) );
}//while
break;
}
catch ( SQLException e )
{
if(e.getSQLState().equals(SQLStates.status[SQLStates.ES_08FO01]) == true)
{
continue;
}
System.out.println( "EXCEPTION : " + e.getMessage() );
break;
}
}
Sending Fail-Over Connection Settings to WAS#
The Fail-Over property settings are added to the URL portion as follows:
"jdbc:Altibase://192.168.3.51:20300+"/mydb?AlternateServers=(128.1.3.53:20300,128.1.3.52:20300)&connectionRetryCount=3&connectionRetryDelay=10&sessionFailOver=on";
Example#
When the callback functions defined above are used, client applications are authored as seen below.
Please refer to the following example, which is included with the Altibase package and should have been installed in $ALTIBASE_HOME/sample/JDBC/Fail-Over/FailOverCallbackSample.java
When Fail-Over is completed, whether Fail-Over was successful is checked using SQLStates. The value of the element at index SQLStates.ES_08FO01 in the SQLStates.status array indicates that Fail-Over was successful, and that the client application can resume its tasks and service can be provided again
class FailOverCallbackSample
{
public static void main(String args[]) throws Exception
{
//---------------------------------------------------
// Initialization
//---------------------------------------------------
// AlternateServers is the available node property
String sURL = "jdbc:Altibase://127.0.0.1:" +
args[0]+"/mydb?AlternateServers=(128.1.3.53:20300,128.1.3.52:20301)&
ConnectionRetryCount=100&ConnectionRetryDelay=100&SessionFailOver=on";
try
{
Class.forName("Altibase.jdbc.driver.AltibaseDriver");
}
catch ( Exception e )
{
System.err.println("Can't register Altibase Driver\n");
return;
}
//---------------------------------------------------
// Test Body
//---------------------------------------------------
//-----------------------
// Preparation
//-----------------------
Properties sProp = new Properties();
Connection sCon;
PreparedStatement sStmt = null;
ResultSet sRes = null ;
sProp.put("user", "SYS");
sProp.put("password", "MANAGER");
MyFailOverCallback sMyFailOverCallback = new MyFailOverCallback();
sCon = DriverManager.getConnection(sURL, sProp);
//FailOverCallback is registered.
((ABConnection)sCon).registerFailOverCallback(sMyFailOverCallback, null);
// Programs must be written in the following form in order to support Session Fail-Over.
/*
while (true)
{
try
{
}
catch( SQLException e)
{
//Fail-Over occurs
if(e.getSQLState().equals(SQLStates.status[SQLStates.ES_08FO01]) == true)
{
continue;
}
System.out.println( "EXCEPTION : " + e.getMessage() );
break;
}
break;
} // while
*/
while(true)
{
try
{
sStmt = sCon.prepareStatement("SELECT C1 FROM T2 ORDER BY C1");
sRes = sStmt.executeQuery();
while( sRes.next() )
{
System.out.println( "VALUE : " + sRes.getString(1) );
}//while
}
catch ( SQLException e )
{
//FailOver Occurs
if(e.getSQLState().equals(SQLStates.status[SQLStates.ES_08FO01]) == true)
{
continue;
}
System.out.println( "EXCEPTION : " + e.getMessage() );
break;
}
break;
}
sRes.close();
//---------------------------------------------------
// Finalize
//---------------------------------------------------
sStmt.close();
sCon.close();
}
}
SQLCLI#
In this section, the structure of sqlcli.h and the Fail-Over related constants that are declared therein will be examined, and how to register Fail-Over Callback will be explained with reference to an example.
Related Data Structures#
The prototype of the Fail-Over callback function, used for communication between the client application and the CLI library during STF (Service Time Fail-Over), is shown below.
typedef SQLUINTEGER SQL_API (*SQLFailOverCallbackFunc)
(SQLHDBC aDBC,
void *aAppContext,
SQLUINTEGER aFailOverEvent);
aDBC is the SQLHDBC created by the client application using SQLAllocHandle.
aAppContext is a pointer, sent to the CLI library at the time of registration of FailOverCallbackContext, pointing to an object that the user wishes to save. When Fail-Over callback is called at the time of STF (Service Time FailOver), it is sent again to Fail-Over callback.
aFailOverEvent can be set to the following values, which have the meanings described below.
-
ALTIBASE_FO_BEGIN: 0
Fail-Over callback is notified of the start of STF (Service Time FailOver).
-
ALTIBASE_FO_END: 1
Fail-Over callback is notified of the success of STF (Service Time FailOver).
-
ALTIBASE_FO_ABORT: 2
Fail-Over callback is notified of the failure of STF (Service Time FailOver).
-
ALTIBASE_FO_GO: 3
Fail-Over callback sends aFailOverEvent to the CLI library so that STF can advance to the next step.
-
ALTIBASE_FO_QUIT: 4
Fail-Over callback sends aFailOverEvent to the CLI library to prevent STF from advancing to the next step.
The structure of SQLFailOverCallbackContext used when registering a fail-over callback in a CLI application is as follows:
typedef struct SQLFailOverCallbackContext
{
SQLHDBC mDBC;
void *mAppContext;
SQLFailOverCallbackFunc mFailOverCallbackFunc;
}SQLFailOverCallbackContext;
In the case of CLI, mDBC can be set to NULL.
mAppContext includes information about any objects that the user intends to save. If there are no objects to be saved, this is set to NULL
mFailOverCallbackFunc is the name of the user-defined FailOverCallback function.
Registering Fail-Over#
As can be seen below, the process of Fail-Over registration involves the creation of a SQLFailOverCallbackContext object, and after connection to the database is successful, FailOverCallbackContext is filled in with values.
The following is an example of Fail-Over registration.
SQLFailOverCallbackContext sFailOverCallbackContext;
...... <<some code omitted here>>
/* connect to server */
sRetCode = SQLDriverConnect(sDbc, NULL,
SQLCHAR*)"DSN=127.0.0.1;UID=unclee;PWD=unclee;PORT_NO=20300;
AlternateServers=(192.168.3.54:20300,192.168.3.53:20300);ConnectionRetryCount=3;
ConnectionRetryDelay=5;SessionFailOver=on;"),
SQL_NTS, NULL, 0, NULL, SQL_DRIVER_NOPROMPT);
sFailOverCallbackContext.mDBC = NULL;
sFailOverCallbackContext.mAppContext = NULL;
sFailOverCallbackContext.mFailOverCallbackFunc = myFailOverCallback;
sRetCode = SQLSetConnectAttr(sDbc,ALTIBASE_FAILOVER_CALLBACK,
(SQLPOINTER)&sFailOverCallbackContext, 0);
The contents of myFailOverCallback are as follows.
SQLUINTEGER myFailOverCallback(SQLHDBC aDBC,
void *aAppContext,
SQLUINTEGER aFailOverEvent)
{
SQLHSTMT sStmt = SQL_NULL_HSTMT;
SQLRETURN sRetCode;
SQLINTEGER sVal;
SQLLEN sLen;
SQLUINTEGER sFailOverIntension = ALTIBASE_FO_GO;
switch(aFailOverEvent)
{
case ALTIBASE_FO_BEGIN: // Start Fail-Over
break;
case ALTIBASE_FO_END:
sRetCode = SQLAllocStmt( aDBC,&sStmt);
if(sRetCode != SQ_SUCCESS)
{
printf("FailOver-Callback SQLAllocStmt Error ");
return ALTIBASE_FO_QUIT;
}
sRetCode = SQLBindCol(sStmt, 1, SQL_C_SLONG , &sVal,0,&sLen);
if(sRetCode != SQ_SUCCESS)
{
printf("FailOver-Callback SQLBindCol");
return ALTIBASE_FO_QUIT;
}
sRetCode = SQLExecDirect(sStmt, (SQLCHAR *) "SELECT 1 FROM DUAL",
SQL_NTS);
if(sRetCode != SQ_SUCCESS)
{
printf("FailOVer-Callback SQLExecDirect");
return ALTIBASE_FO_QUIT;
}
while ( (sRetCode = SQLFetch(sStmt)) != SQL_NO_DATA )
{
if(sRetCode != SQL_SUCCESS)
{
printf("FailOver-Callback SQLBindCol");
sFailOverIntension = ALTIBASE_FO_QUIT;
break;
}
printf("FailOverCallback->Fetch Value = %d \n",sVal );
fflush(stdout);
}
sRetCode = SQLFreeStmt( sStmt, SQL_DROP );
ATC_TEST(sRetCode,"SQLFreeStmt");
break;
default:
break;
}//switch
return sFailOverIntension;
}//myFailOverCallback
Checking Whether Fail-Over Succeeded#
If the result of SQLPrepare, SQLExecute, SQLFetch or the like is an error, rather than SQL_SUCCESS, a statement handle is handed over to SQLGetDiagRec, and if aNativeError has a diagnostic record equal to ALTIBASE_FAILOVER_SUCCESS, STF (Service Time Fail-Over) can be determined to have succeeded.
The following example demonstrates how to check whether STF (Service Time Fail-Over) was successful.
UInt isFailOverErrorEvent(SQLHSTMT aStmt)
{
SQLRETURN rc;
SQLSMALLINT sRecordNo;
SQLCHAR sSQLSTATE[6];
SQLCHAR sMessage[2048];
SQLSMALLINT sMessageLength;
SQLINTEGER sNativeError;
UInt sRet = 0;
sRecordNo = 1;
while ((rc = SQLGetDiagRec(SQL_HANDLE_STMT,
aStmt,
sRecordNo,
sSQLSTATE,
&sNativeError,
sMessage,
sizeof(sMessage),
&sMessageLength)) != SQL_NO_DATA)
{
sRecordNo++;
if(sNativeError == ALTIBASE_FAILOVER_SUCCESS)
{
sRet = 1;
break;
}
}
return sRet;
}
The following example shows that when a network error occurs while SQLExecDirect is being executed, whether STF (Service Time FailOver) was successful is checked, and it is re-executed if necessary (in a prepare/execute environment, re-execution would have to start at the prepare stage).
retry:
sRetCode = SQLExecDirect(sStmt,
(SQLCHAR *) "SELECT C1 FROM T2 WHERE C2 > ? ORDER BY C1",
SQL_NTS);
if(sRetCode != SQL_SUCCESS)
{
if(isFailOverErrorEvent(sStmt) == 1)
{
goto retry;
}
else
{
printf("Error While DirectExeute....");
exit(-1).
}
}
Example#
Environment Settings#
To implement the example, a data source called Test1 is described in altibase_cli.ini as follows:
[ Test1 ]
Server = 192.168.3.53
Port = 20300
User = altibase
Password = altibase
DataBase = mydb
AlternateServers = (192.168.3.54:20300,192.168.3.53:20300)
ConnectionRetryCount = 3
ConnectionRetryDelay = 5
SessionFailOver = on
Additionally, the FailOverCallback function uses myFailOverCallback, which was described above.
When STF (Service Time Fail-Over) takes place, if it is successful, execution must be repeated starting with SQLPrepare (in the case of SQLDirectExecute, the prepare process is not necessary, and only SQLDirectExecute need be re-executed).
If STF (Service Time Fail-Over) occurs while data are being fetched, it will be necessary to call SQLCloseCursor and start over again from the prepare process (in the case of SQLDirectExecute, the prepare process is not necessary, and only SQLDirectExecute will need to be re-executed).
Sample Code#
To view the complete contents of this example, please refer to $ALTIBASE_HOME/sample/SQLCLI/Fail-Over/FailOverCallbackSample.cpp.
#define ATC_TEST(rc, msg) if( ((rc)&(~1))!=0) { printf(msg); exit(1); }
//STF(Service Time FailOver) was successful.
UInt isFailOverErrorEvent(SQLHDBC aDBC,SQLHSTMT aStmt)
{
SQLRETURN rc;
SQLSMALLINT sRecordNo;
SQLCHAR sSQLSTATE[6];
SQLCHAR sMessage[2048];
SQLSMALLINT sMessageLength;
SQLINTEGER sNativeError;
UInt sRet = 0;
sRecordNo = 1;
while ((rc = SQLGetDiagRec(SQL_HANDLE_STMT, aStmt,
sRecordNo, sSQLSTATE,
&sNativeError, sMessage,
sizeof(sMessage),
&sMessageLength)) != SQL_NO_DATA)
{
sRecordNo++;
if(sNativeError == ALTIBASE_FAILOVER_SUCCESS)
{
sRet = 1;
break;
}
}
return sRet;
}
int main( SInt argc, SChar *argv[])
{
SChar sConnStr[BUFF_SIZE] = {0};
SQLHANDLE sEnv = SQL_NULL_HENV;
SQLHANDLE sDbc = SQL_NULL_HDBC;
SQLHSTMT sStmt = SQL_NULL_HSTMT;
SQLINTEGER sC2;
SQLRETURN sRetCode;
SQLINTEGER sInd;
SQLINTEGER sValue;
SQLLEN sLen;
UInt sDidCreate = 0;
SChar sBuff[BUFF_SIZE2];
SChar sQuery[BUFF_SIZE];
SQLFailOverCallbackContext sFailOverCallbackContext;
snprintf(sConnStr, sizeof(sConnStr), "DSN=Test1");
sprintf(sQuery,"SELECT C1 FROM T2 WHERE C2 > ? ORDER BY C1");
sRetCode = SQLAllocHandle(SQL_HANDLE_ENV, NULL, &sEnv);
ATC_TEST(sRetCode,"ENV");
sRetCode = SQLAllocHandle(SQL_HANDLE_DBC, sEnv, &sDbc);
ATC_TEST(sRetCode,"DBC");
/* connect to server */
sRetCode = SQLDriverConnect(sDbc, NULL, (SQLCHAR *)sConnStr,
SQL_NTS, NULL, 0, NULL,
SQL_DRIVER_NOPROMPT);
ATC_TEST(sRetCode,"SQLDriverConnect");
sRetCode = SQLAllocStmt( sDbc,&sStmt);
ATC_TEST(sRetCode,"SQLAllocStmt");
sRetCode = SQLBindCol(sStmt, 1, SQL_C_CHAR , sBuff,BUFF_SIZE2,&sLen);
ATC_TEST(sRetCode,"SQLBindCol");
sRetCode = SQLBindParameter(sStmt, 1, SQL_PARAM_INPUT,
SQL_C_SLONG, SQL_INTEGER,
0, 0, &sC2, 0, NULL);
ATC_TEST(sRetCode,"SQLBindParameter");
sFailOverCallbackContext.mDBC = NULL;
sFailOverCallbackContext.mAppContext = &sFailOverDirection;
sFailOverCallbackContext.mFailOverCallbackFunc = myFailOverCallback;
sRetCode = SQLSetConnectAttr(sDbc,ALTIBASE_FAILOVER_CALLBACK,
(SQLPOINTER)&sFailOverCallbackContext,0);
ATC_TEST(sRetCode,"SQLSetConnectAttr");
retry:
sRetCode = SQLPrepare(sStmt, (SQLCHAR *)sQuery, SQL_NTS);
if(sRetCode != SQL_SUCCESS)
{
// If STF was successful, start over again from the prepare stage.
if(isFailOverErrorEvent(sDbc,sStmt) == 1)
{
goto retry;
}
else
{
ATC_TEST(sRetCode,"SQLPrepare");
}
}
sC2 = 0;
sRetCode = SQLExecute(sStmt);
if(sRetCode != SQL_SUCCESS)
{
// If STF was successful, start over again from the prepare stage.
if(isFailOverErrorEvent(sDbc,sStmt) == 1)
{
goto retry;
}
else
{
ATC_TEST(sRetCode,"SQLExecDirect");
}
}
while ( (sRetCode = SQLFetch(sStmt)) != SQL_NO_DATA )
{
if(sRetCode != SQL_SUCCESS)
{
if(isFailOverErrorEvent(sDbc,sStmt) == 1)
{
//If STF occurs during a fetch operation, it is absolutely essential to call SQLCloseCursor.
SQLCloseCursor(sStmt);
goto retry;
}
else
{
ATC_TEST(sRetCode,"SQLExecDirect");
}
}
printf("Fetch Value = %s \n", sBuff);
fflush(stdout);
}
sRetCode = SQLFreeStmt( sStmt, SQL_DROP );
ATC_TEST(sRetCode,"SQLFreeStmt");
sRetCode = SQLDisconnect(sDbc);
ATC_TEST(sRetCode,"Disconnect()");
sRetCode = SQLFreeHandle(SQL_HANDLE_DBC, sDbc);
ATC_TEST(sRetCode,"Free HDBC");
sRetCode = SQLFreeHandle(SQL_HANDLE_ENV, sEnv);
ATC_TEST(sRetCode,"Free HENV");
}
Embedded SQL#
Because the Fail-Over data structures used here are the same as those used in CLI, and because the structure of an ESQLC (Embedded SQL in C) application is similar to that of a CLI application, only the features unique to ESQLC will be described here
Registering Fail-Over Callback Functions#
Because SQLHDBC of CLI cannot be directly checked in an Embedded SQL program, the process of registering a Fail-Over callback function is as shown below.
FailOverCallbackContext is declared in the declaration section.
EXEC SQL BEGIN DECLARE SECTION;
SQLFailOverCallbackContext sFailOverCallbackContext;
EXEC SQL END DECLARE SECTION;
FailOverCallbackContext is filled in with values.
sFailOverCallbackContext.mDBC = NULL;
sFailOverCallbackContext.mAppContext = NULL;
sFailOverCallbackContext.mFailOverCallbackFunc = myFailOverCallback;
myFailOverCallback is the function that was seen in the CLI Fail-Over example above, only the CLI function and Os function need to be written, and Embedded SQL commands cannot be used.
The following shows how a Fail-Over Callback function is registered in an Embedded SQL statement.
EXEC SQL [AT CONNECTUON-NAME] REGISTER FAIL_OVER_CALLBACK :sFailOverCallbackContext;
Checking Whether Fail-Over Succeeded#
After the EXEC SQL command is executed, if the result of sqlca.sqlcode is ALTIBASE_FAILOVER_SUCCESS, rather than SQL_SUCCESS, then STF (Service Time Fail-Over) can be determined to have succeeded.
The following example demonstrates how to check whether STF (Service Time Fail-Over) was successful.
re-execute:
EXEC SQL INSERT INTO T1 VALUES( 1 );
if (sqlca.sqlcode != SQL_SUCCESS)
{
if (sqlca.sqlcode == ALTIBASE_FAILOVER_SUCCESS)
{
goto re-execute;
}//if
else
{
printf("SQLCODE : %d\n", SQLCODE);
printf("sqlca.sqlerrm.sqlerrmc : %s\n", sqlca.sqlerrm.sqlerrmc);
printf("%d rows inserted\n", sqlca.sqlerrd[2]);
printf("%d times insert success\n\n", sqlca.sqlerrd[3]);
}//else
}
Example 1#
main()
{
EXEC SQL BEGIN DECLARE SECTION;
SQLFailOverCallbackContext sFailOverCallbackContext;
char sUser[10];
char sPwd[10];
char sConnOpt[1024];
EXEC SQL END DECLARE SECTION;
strcpy(sUser, "SYS");
strcpy(sPwd, "MANAGER");
sprintf(sConnOpt,"DSN=127.0.0.1;UID=altibase;PWD= altibase;PORT_NO=20300;
AlternateServers=(192.168.3.54:20300,192.168.3.53:20300);ConnectionRetryCount=3;
ConnectionRetryDelay=5;SessionFailOver=on;"" );
EXEC SQL CONNECT :sUser IDENTIFIED BY :sPwd USING : sConnOpt;
if (sqlca.sqlcode != SQL_SUCCESS)
{
printf("SQLCODE : %d\n", SQLCODE);
printf("sqlca.sqlerrm.sqlerrmc : %s\n", sqlca.sqlerrm.sqlerrmc);
return 0;
}
else
{
printf("CONNECTION SUCCESS\n");
}
//FailOverCallbackContext is populated with values
sFailOverCallbackContext.mDBC = NULL;
sFailOverCallbackContext.mAppContext = NULL;
sFailOverCallbackContext.mFailOverCallbackFunc = myFailOverCallback;
// FailOverCallbackContext is registered.
EXEC SQL REGISTER FAIL_OVER_CALLBACK :sFailOverCallbackContext;
re-execute:
EXEC SQL INSERT INTO T1 VALUES( 1 );
if (sqlca.sqlcode != SQL_SUCCESS)
{
if (SQLCODE == EMBEDED_ALTIBASE_FAILOVER_SUCCESS)
{
goto re-execute;
}//if
else
{
printf("SQLCODE : %d\n", SQLCODE);
printf("sqlca.sqlerrm.sqlerrmc : %s\n", sqlca.sqlerrm.sqlerrmc);
printf("%d rows inserted\n", sqlca.sqlerrd[2]);
printf("%d times insert success\n\n", sqlca.sqlerrd[3]);
return 0;
}//else
}
EXEC SQL DISCONNECT;
}
Example 2#
This example demonstrates the use of a cursor. If Fail-Over occurs while a cursor is being used, EXEC SQL CLOSE RELEASE Cursor is executed, and the EXEC SQL DECLARE CURSOR statement is executed again to Prepare on the available server again.
retry:
EXEC SQL DECLARE CUR1 CURSOR FOR SELECT C1 FROM T2 ORDER BY C1;
if (sqlca.sqlcode == SQL_SUCCESS)
{
printf("DECLARE CURSOR SUCCESS.!!! \n");
}
else
{
if( SQLCODE == EMBEDED_ALTIBASE_FAILOVER_SUCCESS)
{
printf("Fail-Over SUCCESS !!! \n");
goto retry;
}
else
{
printf("Error : [%d] %s\n\n", SQLCODE, sqlca.sqlerrm.sqlerrmc);
return(-1);
}
}
EXEC SQL OPEN CUR1;
if (sqlca.sqlcode == SQL_SUCCESS)
{
printf("DECLARE CURSOR SUCCESS !!!\n");
}
else
{
if( SQLCODE == EMBEDED_ALTIBASE_FAILOVER_SUCCESS)
{
printf("Fail-Over SUCCESS !!! \n");
/* If a cursor is OPEN when Fail-Over occurs, the cursor must be closed and released */
EXEC SQL CLOSE RELEASE CUR1;
goto retry;
}
else
{
printf("Error : [%d] %s\n\n", SQLCODE, sqlca.sqlerrm.sqlerrmc);
return(-1);
}
} //else
while(1)
{
EXEC SQL FETCH CUR1 INTO :sC1;
if (sqlca.sqlcode == SQL_SUCCESS)
{
printf("Fetch Value = %s \n",sC1);
}
else if (sqlca.sqlcode == SQL_NO_DATA)
{
break;
}
else
{
if(SQLCODE == EMBEDED_ALTIBASE_FAILOVER_SUCCESS)
{
printf("DECLARE CURSOR SUCCESS !!!");
/* If a fetch operation is underway when Fail-Over occurs, the cursor must be closed and released */
EXEC SQL CLOSE RELEASE CUR1;
goto retry;
}
else
{
printf("Error : [%d] %s\n\n", SQLCODE, sqlca.sqlerrm.sqlerrmc);
return(-1);
}//else
}//else
}//while
EXEC SQL CLOSE CUR1;
PDO - Callback#
Registering Fail-Over Callback#
To register a Fail-over callback in an application using PDO, use the following method.
...
public bool PDO::setFailoverCallback(string $callback, mixed $app_context)
...
-
$callback
This is the name of the function to register as a callback
-
$app_context
This is an arbitrary variable to be used as a callback. The variable transferred to $app_context is transferred as the $context argument in the callback function. This value is for reference when performing user callback, so if it is not needed it, do not use it. This returns TRUE if the callback registration was successful and FALSE if it failed.
Fail-Over Callback Related Interfaces#
In the application using PDO, FailOver Callback function is defined as below:
function focbfunnc($db, $context, $event)
{
// ...
return PDO::ALTIBASE_FO_GO;
}
-
$db
PDO object when Failover occurs
-
$context
Arbitrary value referred in callback function, and it can be specified when registering callback.
-
$event
Possible values for $event are as follows:
event Description PDO::ALTIBASE_FO_BEGIN STF Start PDO::ALTIBASE_FO_END STF Success PDO::ALTIBASE_FO_ABORT STF Fail -
Callback returns of one the following values:
Return Value Description PDO::ALTIBASE_FO_GO STF Progressing PDO::ALTIBASE_FO_QUIT STF Stopped
Registering Fail-Over Callback#
After a successful connection, the failover callback is specified. Examples are shown below:
function focbfunc($db, $context, $event) {
switch ($event) {
case PDO::ALTIBASE_FO_BEGIN:
if ($context == 1) {
return PDO::ALTIBASE_FO_QUIT;
}
// ...
break;
case PDO::ALTIBASE_FO_END:
// ...
break;
// ...
}
return PDO::ALTIBASE_FO_GO;
}
// ...
$db->setFailoverCallback('focbfunc', $appctx);
Checking Whether Fail-Over Succeeded#
In case of Failover, if the driver-specific error code is PDO::ALTIBASE_FAILOVER_SUCCESS, STF is successful.
Failover only guarantees connection, so please be sure to check whether STF is succeeded and the execution is retried.
The following is an example of checking whether STF is succeeded and the execution is retried.
FailoverRetry:
try {
$stmt = $db->prepare("SELECT c1 FROM T2 WHERE c2 ? ORDER BY c1");
$stmt->bindParam(1, $sC2);
$sC2 = 0;
$stmt->execute();
while (($row = $stmt->fetch()) !== FALSE) {
// ...
}
}
catch (PDOException $ex) {
$errInfo = $ex->errorInfo;
if ($errInfo[1] == PDO::ALTIBASE_FAILOVER_SUCCESS) {
goto FailoverRetry;
}
else {
// ...
}
}