Приложение 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; }