Март 2005


Профессионалу разработчику


Игорь Мельников,
Oracle 8i/9i Certified Professional DBA,
группа компаний TopS Business Integrator (www.topsbi.ru).

Разработка отказоустойчивых приложений для СУБД Oracle RAC

СОДЕРЖАНИЕ


Введение

Oracle Real Application Cluster [RAC] представляет собой программное обеспечение, которое позволяет работать Oracle Database на аппаратном кластере. Аппаратный кластер - это группа независимых серверов, объединенных в единое целое. Основными компонентами кластера являются:

  • аппаратные сервера (nodes)
  • межкластерное соединение (cluster interconnect)
  • общая дисковая подсистема в виде дискового массива (shared storage).

Отдельные аппаратные сервера разделяют дисковую подсистему и ресурсы, которыми они управляют, но они не разделяют собственную физическую память между другими серверами. Программное обеспечение кластерной базы данных объединяет память отдельных серверов, чтобы обеспечить единую распределенную кеш-память (технология Cache Fusion).

На рисунке ниже приведен пример простейшего, 2-х узлового кластера:

В рамках данного материала, предназначенного в первую очередь программистам, мы рассмотрим, каким образом можно доработать свои критичные к времени простоя приложения работающие с базой данных Oracle в кластерной конфигурации. В результате эти приложения приобретут возможность продолжать свое функционирование даже в случае сбоя узла кластера.

Сбой узла кластера, или что такое TAF

Сбой узла кластера - это отказ экземпляра Oracle на одном из узлов кластера. Все приложения, которые имели сессии с отказавшим узлом кластера, эти соединения, разумеется, теряют. Клиентское программное обеспечение Oracle Call Interface [OCI] в этом случае обеспечит переключение на один из оставшихся в "живых" узлов. Фактически, библиотеки времени выполнения OCI в этом осуществляют закрытие нерабочей сессии и открытие новой на одном из оставшихся работоспособных узлов кластера, причем это переключение происходит прозрачно для приложения и не требует дополнительного программирования. Данная функциональность носит название Transparent Application Failover [TAF] и поддерживается начиная с версий 9i. Для того чтобы включить эту возможность, необходимо предварительно в дескрипторе соединения с базой данных в файле tnsnames.ora определить несколько дополнительных параметров.

Одно из самых распространенных заблуждений относительно Oracle RAC состоит в том, что если в момент сбоя узла кластера приложение имело на нем незавершенную транзакцию, то после прохождения переключения на другой узел транзакция продолжит свое выполнение. К сожалению, это не так!

Рассмотрим ситуацию "падения" узла кластера и выполнения TAF более подробно:

  • Приложение имеет соединение с одним из узлов кластера Oracle RAC и начинает выполнение транзакции;
  • Несколько операторов транзакции успешно выполнены, но транзакция еще не завершена (НЕ зафиксирована);
  • Происходит "падение" узла кластера, с которым приложение имело соединение;
  • Клиентское программное обеспечение OCI произвело открытие новой сессии на одном из работоспособных узлов кластера;
  • Далее приложение получает новую сессию.

После последнего шага возникает проблема: поскольку все промежуточные операции транзакции потеряны, то выполнение оставшихся операторов транзакции не будет иметь смысла (может привести к непредсказуемым результатам). Помимо этого не следует забывать, что приложение могло иметь на сервере глобальные переменные пакетов pl/sql, значения которых в результате сбоя также было потеряно.
Вывод: поскольку контекст выполнения сессии не дублируется на всех узлах кластера (это было бы слишком расточительно), то нормальное выполнение приложения после отработки TAF невозможно.

Для решения этой проблемы разработчики СУБД Oracle предоставили два выхода:
 1) Приложению возвращается ошибка ORA-25402: transaction must roll back - в этом случае пользователь получит сообщение об ошибке и должен перезапустить приложение;
 2) Приложение регистрирует в OCI свою функцию обратного вызова, эта функция будет вызываться в процессе отработки TAF; в этой функции
программист может корректно обработать операцию потерю контекста сессии и, возможно, восстановить его.

Итак, рассмотрим более подробно, какие события возникают в процессе обработки TAF (идентификаторы событий обозначены в соответствии с документацией OCI Programmer's Guide):

  • BEGIN - был определен факт потери соединения с экземпляром, - начинается обработка TAF;
  • END - TAF успешно выполнен - приложение имеет соединение с одним из работоспособных узлов кластера;
  • ABORT - обработка TAF была неуспешной и прервана, приложение вообще не имеет никакого соединения;
  • REAUTH - производится попытка аутентификации на одном из узлов кластера;
  • ERROR - в процессе переключения на другой узел возникла ошибка, выполнение TAF либо задержано, либо будет выполнена следующая попытка;
  • RETRY - будет выполнена повторная попытка выполнения TAF;
  • EVENT_UNKNOWN - событие не определено (произошла внутренняя неустранимая ошибка OCI)

В процессе отработки TAF программное обеспечение OCI может, помимо восстановления соединения, произвести также восстановление открытых курсоров операторов select. Таким образом, всего возможны два типа TAF:

  • SESSION - после выполнения TAF приложение получает новую сессию, весь контекст предыдущей сессии теряется;
  • SELECT - после выполнения TAF, помимо восстановления соединения, также восстанавливаются состояния открытых курсоров (объявленных с помощью оператора select) предыдущей сессии; выполнение оператора выборки (fetch) может быть продолжены начиная с первой невыбранной записи.

Поддержка обработки TAF в приложениях

Программный интерфейс клиентского программного обеспечения Oracle Call Interface [OCI] предоставляет в распоряжение разработчиков возможность обработки всех этапов TAF. Для того чтобы воспользоваться этим, приложение должно зарегистрировать в интерфейсе OCI пользовательскую функцию обратного вызова (callback function).
Далее, при возникновении сбоя узла кластера на котором работает приложение, клиентское программное обеспечение OCI автоматически будет вызывать эту функцию для обработки всех этапов TAF. Формат и параметры, которые должна иметь такая функция обратного вызова, подробно описан в руководстве разработчика Oracle Call Interface [Oracle Call Interface Programmer's Guide].
Заголовок этой функции следующий (язык программирования C):

sb4 appfocallback_fn (dvoid * svchp,
                      dvoid * envhp,
                      dvoid * fo_ctx,
                      ub4     fo_type,
                      ub4     fo_event);

Применение языка C и функций OCI требуют от программиста большого объема работы и наличия опыта разработки приложений работающих с СУБД Oracle посредством низкоуровневого интерфейса OCI, либо с использованием препроцессоров Pro*C.

Наиболее практично, на мой взгляд, рассмотреть и использовать возможности по реализации таких функций предоставляемые высокоуровневыми библиотеками, которые обеспечивают прямой доступ к базам данных Oracle посредством обращения к функциям OCI, например Oracle Data Access Component для среды программирования Borland Delphi, JDBC (для языка программирования Java) и Oracle ADO .Net Provider (для среды выполнения Microsoft .Net). В следующих разделах мы рассмотрим примеры использования этих библиотек для реализации поддержки TAF.

Среди параметров, передаваемых в функцию обработки TAF, для нас наибольший интерес представляют два последних параметра: fo_type и fo_event. Значение параметра fo_type, передаваемое в функцию, определяет тип TAF (SESSION или SELECT).
Через параметр fo_event функция обратного вызова определяет этап выполнения TAF.
Значения параметра fo_type могут быть следующими:

  OCI_FO_SESSION  = 1 
  OCI_FO_SELECT   = 2 

Значения параметра fo_event могут быть следующими:

  OCI_FO_BEGIN         = 1
  OCI_FO_ABORT         = 2 
  OCI_FO_ERROR         = 3 
  OCI_FO_REAUTH        = 4 
  OCI_FO_END           = 5
  OCI_FO_EVENT_UNKNOWN = 6

Где имена констант соответствуют типам и событиям TAF описанным в предыдущем разделе.

Далее мы рассмотрим, какие средства по обработке событий TAF предоставляет библиотека доступа к базам данных JDBC.

Пример обработки в приложениях Java

Прежде всего, необходимо отметить, что для того чтобы использовать поддержку TAF в программах JDBC, нужно использовать OCI-драйвер. Очевидно, это связано с тем, что функциональность TAF не входит в стандарт JDBC и требует для своей реализации использования функций OCI.

Итак, для обработки событий TAF программист должен реализовать Java-класс, который реализует интерфейс OracleOCIFailover. Данный интерфейс состоит всего из одного метода: callbackFn.

public interface OracleOCIFailover
{
  public int callbackFn (Connection conn, 
                         Object     ctxt, 
                         int        type, 
                         int        event)
}

Первый параметр функции callbackFn хорошо знаком программистам, работающим с JDBC: это объект представляющий соединение с БД. Второй параметр представляет собой контекст приложения: его значение передается в момент регистрации обработчика, и далее в нем может быть использовано. Последние два параметра характеризуют тип TAF и идентификатор события. Эти параметры полностью соответствуют параметрам C-функции appfocallback_fn определенной в интерфейсах прикладного программирования OCI: type - тип TAF (SESSION или SELECT), event - идентификатор события обработки TAF.

Для регистрации функции-обработчика используется метод registerTAFCallback класса OracleConnection
Метод имеет два параметра:
 1)  экземпляр класса реализующего интерфейс OracleOCIFailover.
 2) произвольный объект, который может хранить контекст состояния клиентского приложения: доступен при вызове обработчика TAF и его значение передается вторым параметром функции-обработчика TAF

В приведенном ниже примере приведен фрагмент кода для реализации событий TAF в программах JDBC.

import oracle.jdbc.OracleConnection;
import oracle.jdbc.OracleOCIFailover;

/*
 * Опраделяем обработчик события:
 * Класс реализует интерфейс OracleOCIFailover
 */
class CallBackTest implements OracleOCIFailover 
   
  //Функция обратного вызова TAF  
  public int callbackFn 
                 (Connection conn, Object ctxt, int type, int event) 
  {
 /* Здесь выполняем действия  
  *  по предотвращению потери данных пользователя... 
  */
    return 0;
  }
}

/*
 * Пример регистрации функции обратного вызова TAF:
 *
 */
public class OCIFailOverTest {

  public static void main (String[] args) throws Exception {
  {
    Connection conn = null;
    String     msg  = null;

    CallBackTest   fcbk = new CallBackTest();  
    //Создаем экземпляр класс реализающего call-back функцию

    // регистрируем call-back функцию TAF !
    ((OracleConnection) conn).registerTAFCallback(fcbk, msg);
    
  }

Пример обработки в приложениях Borland Delphi (с помощью Oracle Data Access Component)

Система программирования Delphi компании Borland пользуется большой популярностью в России и странах СНГ. Для данной среды разработано несколько библиотек классов прямого доступа к базам данных Oracle. Под словом "прямой" имеется в виду то, что данные библиотеки для доступа к СУБД Oracle не требуют промежуточных интерфейсов, таких как ODBC, BDE, ADO.DB, а используют прямой вызов функций интерфейса прикладного программирования OCI.

В качестве примера обработки TAF рассмотрим библиотеку Oracle Data Access Component [ODAC] компании Core Lab.
ODAC представляет собой библиотеку классов обеспечивающих объектно-ориентированный интерфейс доступа к базам данных Oracle в приложениях разработанных на языках программирования Object Pascal (среда разработки Borland Delphi) м C++ (среда разработки Borland C++ Builder).

Стоит отметить, что среди всех имеющихся на рынке библиотек прямого доступа к Oracle из Delphi-приложений, ODAC единственная, которая имеет так называемый NET-режим. В этом режиме приложение вообще не использует клиентское API OCI, а напрямую устанавливает соединение с СУБД. В этом случае не требуется установка клиентского программного обеспечения Oracle Client на компьютер пользователя. Очевидно, что в NET-режиме функциональность TAF становится недоступной приложению.

Итак, рассмотрим, каким образом можно встроить обработку TAF в приложения разработанные на языке Object Pascal и использующие для доступа к базам Oracle библиотеку ODAC.

Для обработки TAF, в приложении достаточно определить обработчик события OnFailover компонента TOraSession.
Функция обработки события OnFailover должна иметь следующий тип (здесь и далее используется язык программирования Object Pascal):

  Type
    TFailoverEvent = Procedure (Sender        : TObject; 
                                FailoverState : TFailoverState;
                                FailoverType  : TFailoverType; 
                                Var Retry     : Boolean) of Object;

Параметр FailoverState имеет значение соответствующее состоянию TAF, а параметр FailoverType - типу TAF. В переменной Retry обработчик возвращает логическое значение TRUE, если необходимо повторить процесс создания новой сессии, или FALSE если обработка события прошла успешно.

  Type
    TFailoverState = (fsEnd, fsAbort, fsReauth, fsBegin, fsError);

    TFailoverType = (ftNone, ftSession, ftSelect, ftTransaction);

Ниже приведен небольшой пример использования обработчика события OnFailover компонента TOraSession:

Procedure TMainForm.OraSessionFailover(Sender        : TObject;
                                       FailoverState : TFailoverState; 
                                       FailoverType  : TFailoverType;
                                       var Retry     : Boolean);
Begin
  Retry := False;

  Сase FailoverState Of
    fsBegin: begin
      MainStatusBar.SimpleText := 
      'Произошел сбой: попытка присоединения, Пожалуйста подождите...';
    end;
    fsAbort: begin
      MainStatusBar.SimpleText :=
      'Обработка сбоя прервана ...';
    end;
    fsEnd:
      MainStatusBar.SimpleText :=
      'Сбой успешно обработан';
    fsError: 
      Begin
        MainStatusBar.SimpleText :=
        'Ошибка обработки сбоя: попытка присоединения.
                                               Пожалуйста подождите...'; 
        Retry := True;
      End;
    fsReauth: 
      MainStatusBar.SimpleText := 
      'Идет аутентификация на базе данных. 
                                               Пожалуйста подождите...';
  Else
    MainStatusBar.SimpleText :=
    'Ошибка обработки сбоя';
  End; //Case
End;

Пример обработки в приложениях MS .NET (C# и ODP.NET)

И в заключение, рассмотрим пример использования обработки TAF в приложениях разработанных для среды выполнения Microsoft .Net.

Для работы с базами данных в среде .Net в нее входит дополнительный набор классов под названием ADO .Net.
ADO .Net представляет собой набор высокоуровневых классов которые инкапсулируют все необходимые методы и свойства для работы с базами данных. Для выполнения низкоуровневых функций клиентского API конкретной СУБД используется так называемый провайдер доступа - ADO.Net Provider. Для доступа к каждой СУБД имеется свой собственный провайдер доступа. Корпорация Oracle для доступа к своим серверам баз данных предоставляет соответствующий провайдер: - Oracle ADO.Net Provider [ODP.Net]. Данный провайдер доступа поставляется в составе клиентского программного обеспечения Oracle10g Database Client.

Фактически, Oracle ADO.Net Provider служит высокоуровневой "оберткой" над функциями OCI, то есть обеспечивает трансляцию вызовов методов классов ADO.Net в низкоуровневые вызовы соответствующих функций интерфейса OCI.
Помимо реализации стандартных методов определенных в ADO.Net, Oracle ADO.Net Provider обеспечивает поддержку специфических особенностей используемых при работе с СУБД Oracle, например: работу с ссылками на курсор (ref-cursor), работу с переменными привязки (bind-variables), манипуляции с данными типа xmlType и т.д. В число поддерживаемых возможностей входит и создание классов-обработчиков событий TAF.
Рассмотрим эту дополнительную возможность Oracle ADO.Net Provider более подробно.

Для демонстрации возможности определения обработчиков событий TAF в ADO.Net мы будем использовать "модный" в настоящее время язык программирования C#, хотя, стоит отметить, что для работы с ADO.Net можно использовать любой другой язык, который поддерживает среду .Net, например: Delphi.Net, C++, Visual Basic, JavaScript, Visual J# и т.д.(набор языков поддерживающих .Net постоянно расширяется).

Итак, в классе, в котором необходимо определить обработчик TAF, должен быть реализован метод с следующей сигнатурой:

  public static FailoverReturnCode OnFailover(object sender,
                                    OracleFailoverEventArgs eventArgs)

Вся информация необходимая этому обработчику содержится в параметре eventArgs:
  FailoverEvent - идентификатор события TAF;
  FailoverType - тип TAF

Для регистрации данного метода необходимо присвоить его свойству Failover класса OracleConnection передав этот метод в качестве параметра конструктора специализированного класса OracleFailoverEventHandler:

  OracleConnection v_xCoon = new OracleConnection();

  v_xConn.Failover += new OracleFailoverEventHandler(OnFailover);

Вот как будет выглядеть полный пример использования этих классов в ADO.Net :

using System;
using System.Data;
using Oracle.DataAccess.Client;

class TAFTest
{
  public static OracleConnection v_xConn;

  public static FailoverReturnCode OnFailover(object sender,
                                     OracleFailoverEventArgs eventArgs)
  {
    switch (eventArgs.FailoverEvent)
    {
      case FailoverEvent.Begin :
        Console.WriteLine("Начало обработки сбоя: "
                                         + eventArgs.FailoverType);
        break;
      case FailoverEvent.Abort :
        Console.WriteLine("Обработка сбоя прервана .\n");
        break;
      case FailoverEvent.End :
        Console.WriteLine(" Обработка сбоя успешно 
                           завершена. Работа продолжается... \n");
        break;
      case FailoverEvent.Reauth :
        Console.WriteLine(" Ошибка подключения\n");
        break;
      case FailoverEvent.Error :
        Console.WriteLine(" Ошибка обработки сбоя. 
                                             Повтор. Sleeping...\n");
        return FailoverReturnCode.Retry;
      default :
        Console.WriteLine(" Ошибочное событие : 
                                       %d.\n", eventArgs.FailoverEvent);
        break;
    }
    return FailoverReturnCode.Success;
  }


  static void Main(String[] args)
  {
    String v_xUsername,v_xPassword,v_xAlias;
    
    v_xUsername = args[0];
    v_xPassword = args[1];
    v_xAlias    = args[2];

    v_xConn = new OracleConnection();
    v_xConn.ConnectionString = 
                 "User Id=" + v_xUsername + ";
          Password=" + v_xPassword + ";
          Data Source=" + v_xAlias + ";";
    v_xConn.Open();

    v_xConn.Failover += new OracleFailoverEventHandler(OnFailover); 
     //регистрируем обработчик
                                     
    v_xConn.Close();
    v_xConn.Dispose();
  }
}

Заключение

Разумеется, данная статья не претендует на полное описание всех тонкостей работы с событиями обработки сбоя узла кластера Oracle RAC. Полное описание приведено в документации Oracle и в соответствующей документации производителей прикладных библиотек доступа.

Ссылки

Oracle Data Provider for .NET Developer's Guide 10g Release 1 (10.1.0.3)
Oracle Database JDBC Developer's Guide and Reference 10g Release 1 (10.1)
Oracle Call Interface Programmer's Guide 10g Release 1 (10.1)

E-mail this page