Приложение 3. Пример работы с системой резервирования
В качестве примера клиентского приложения приводится листинг программы testrez.c, которая демонстрирует обработку ситуации отказа главного сервера и переход на работу с резервным сервером. Взаимодействие с СУБД ЛИНТЕР выполняется с помощью интерфейса нижнего уровня (call-интерфейса).
Программа создает таблицу TEST, состоящую из 20 записей типа INT, затем в режиме AUTOCOMMIT циклически выполняет следующие операции:
-
открывает канал;
-
выполняет выборку каждой записи с выводом на экран;
-
увеличивает значение каждой записи на 1;
-
закрывает канал.
При получении ошибки выводится соответствующее сообщение, и повторяется последняя итерация цикла.
В режиме резервирования (одновременно работают главный и резервный серверы) изменения таблицы на главном сервере передаются в БД на резервной машине.
Если принудительно остановить главный сервер, то программа получит соответствующий код завершения и повторит последнюю итерацию уже с резервной БД. Выведенные значения записей таблицы TEST должны совпадать с теми, что были перед остановом главного сервера, или быть больше.
#include <stdio.h>
#include <signal.h>
#include "intlib.h"
#include "errors.h"
#define RECORDCOUNT 20
struct ErrString
{
int ErrCode;
char *ErrString;
};
static int Cbrk = 0;
static struct ErrString ES[]=
{
{ERR_RECV, "receive error"},
{ERR_SEND, "send error"},
{ERR_CONNECT, "connect error"},
{ERR_RESLINK, "reserver link lost "},
{ERR_SEARCH, "reserved server search timeout"},
{ERR_LINKDOWN, "connection has been closed"},
{ERRNMRKAN, "wrong channel number"},
{ERROPENQUE, "linter not found"},
{KernelShutdown, "linter kernel is shutdowning"},
{FORCED_ROLLB, "forced rollback"},
{ChannelBusy, "channel is busy"},
{0," "}
};
static int TestError(int Error)
{
if ( Error >= 4000 && Error < 5000 )
return 0;
switch ( Error )
{
case ERRNMRKAN:
case ERROPENQUE:
case KernelShutdown:
case FORCED_ROLLB:
case ChannelBusy:
return 0;
}
return 1;
}
static char *ErrorString(int Err)
{
struct ErrString *ESP=ES;
while(ESP->ErrCode)
{
if(ESP->ErrCode==Err)
return(ESP->ErrString);
ESP++;
}
return(ESP->ErrString);
}
static int CloseChannel(TCBL *Cbl)
{
memcpy(Cbl->Command, "CLOS", 4);
inter(Cbl, NULL, NULL, NULL, NULL);
if ( Cbl->CodErr )
{
printf("CLOSE error=%ld,%s\n", Cbl->CodErr, ErrorString(Cbl->CodErr));
return -1;
}
return 0;
}
void SIGINTHandler(int Sig)
{
printf("cbrk received\n");
signal(SIGINT, SIGINTHandler);
Cbrk = 1;
}
int main(int argc, char **argv)
{
TCBL Cbl;
int i;
int AfterError = 0;
signal(SIGINT, SIGINTHandler);
/* Open channel */
memset(&Cbl, 0, sizeof(Cbl));
memcpy (Cbl.Command, "OPEN", 4);
if(argc == 2)
strncpy(Cbl.Node, argv[1], sizeof(Cbl.Node));
inter(&Cbl, "SYSTEM/MANAGER8", NULL, NULL, NULL);
if (Cbl.CodErr != 0)
{
printf("Channel Open error=%ld: %s\n", Cbl.CodErr, ErrorString(Cbl.CodErr));
exit(1);
}
/* Create test table */
memset(Cbl.Command, ' ', 4);
inter(&Cbl, NULL, "CREATE TABLE TEST(I INTEGER);", NULL, NULL);
if (Cbl.CodErr)
{
if(Cbl.CodErr == RELEXIST)
{
printf("table TEST already exist\n");
}
else
{
printf("Create Table error=%d:%s\n", Cbl.CodErr, ErrorString(Cbl.CodErr));
CloseChannel(&Cbl);
return -1;
}
}
else
{
/* Full test table */
for(i = 0; i < RECORDCOUNT; i++)
{
char ComString[128];
sprintf(ComString, "INSERT INTO Test VALUES (%d);", i);
inter(&Cbl, NULL, ComString, NULL, NULL);
if (Cbl.CodErr != 0)
{
printf("INSERT error=%d:%s\n", Cbl.CodErr, ErrorString(Cbl.CodErr));
CloseChannel(&Cbl);
return -1;
}
}
}
if ( CloseChannel(&Cbl) )
{
return -1;
}
while(!Cbrk)
{
int StrCnt = 0;
/* Open channel */
memcpy (Cbl.Command, "OPEN", 4);
inter(&Cbl, "SYSTEM/MANAGER8", NULL, NULL, NULL);
if (Cbl.CodErr != 0)
{
printf("Channel Open error=%ld:%s\n", Cbl.CodErr,ErrorString(Cbl.CodErr));
if( TestError(Cbl.CodErr) == 0)
{
AfterError = 1;
sleep(5);
printf("try again\n");
continue;
}
return -1;
}
/* Select all data */
memcpy(Cbl.Command, "SLCT", 4);
Cbl.PrzExe = M_BINARY;
Cbl.LnBufRow = sizeof(int);
inter(&Cbl,NULL,"SELECT I FROM TEST;", NULL,(HPCHAR)&i);
if (Cbl.CodErr != 0)
{
printf("SELECT error=%ld:%s\n",Cbl.CodErr,ErrorString(Cbl.CodErr));
if(TestError(Cbl.CodErr) == 0)
{
CloseChannel(&Cbl);
continue;
}
CloseChannel(&Cbl);
return -1;
}
/* Print data */
printf("\n");
while (Cbl.CodErr == 0)
{
StrCnt++;
if ( StrCnt == 7 )
{
printf("\n");
StrCnt=1;
}
printf(" i=%d", i);
memcpy(Cbl.Command, "GETN", 4); /* Get Next Answer */
inter(&Cbl, NULL, NULL, NULL, (HPCHAR)&i);
} /* while */
if (Cbl.CodErr == 2) /* End of Answer */
printf("\n no more records\n");
else
{
if ( TestError(Cbl.CodErr) == 0 )
{
CloseChannel(&Cbl);
continue;
}
else
{
CloseChannel(&Cbl);
return -1;
}
}
/* Wait for compare */
if(AfterError)
{
printf("press key to continue\n");
getchar();
}
AfterError = 0;
/* Update data */
memset(Cbl.Command, ' ', 4); /* Command is not Select ! */
inter(&Cbl, NULL,"update TEST set I = I+1;", NULL, NULL);
if (Cbl.CodErr != 0)
{
printf("update error=%ld:%s\n", Cbl.CodErr,ErrorString(Cbl.CodErr));
if ( TestError(Cbl.CodErr) == 0 )
{
CloseChannel(&Cbl);
continue;
}
else
{
CloseChannel(&Cbl);
return -1;
}
}
if (CloseChannel(&Cbl))
{
continue;
}
}
return 0;
}