Поддержка прозрачного для приложений восстановления после отказа (Transparent Application Failover или TAF) для OCI8

TAF - это механизм базы данных Oracle обеспечивающий высокую доступность. Он позволяет приложениям PHP использующим OCI8 автоматически переподключаться к резервной базе данных в случае сбоя на основной или при сетевых проблемах..

В сконфигурированной системе базы данных Oracle, TAF происходит когда приложение PHP определяет, что инстанс базы данных недоступен. В этом случае происходит соединение с другим узлом в Oracle » RAC. Это может быть горячий резерв или тот же самый инстанс базы данных. Более подробно о OCI TAF читайте в » Oracle Call Interface Programmer's Guide.

Функцию обратного вызова для приложения можно зарегистрировать с помощью oci_register_taf_callback(). В процессе восстановления после отказа исполнение приложения будет приостановлено и будет вызвана зарегистрированная функция обратного вызова. Эта функция будет оповещать приложение о событиях процесса восстановления. Если восстановление завершилось успешно, управление будет возвращено приложению. Если восстановление завершилось неудачно, то все последующие обращения к базе данных будут завершаться с ошибкой, так как отсутствует подключение.

Когда соединение переходит к другой базе данных, обратный вызов может сбросить любое необходимое состояние соединения, к примеру перевыполнить любую необходимую команду ALTER SESSION если для сервиса базы данных не включен -failover_restore.

Регистрацию функции обратного вызова можно удалить с помощью oci_unregister_taf_callback().

настройка TAF

TAF можно настроить на стороне PHP OCI8 или в конфигурации базы данных. Если настроено и там и там, то предпочтение отдается настройкам на стороне базы данных.

Настроить TAF в PHP OCI8 (на стороне клиента) можно добавив параметр FAILOVER_MODE в часть CONNECT_DATA дескриптора соединения. Более подробно о настройке TAF на стороне клиента читайте в »  Oracle Database Net Services Administrator's Guide.

Пример настройки TAF в tnsnames.ora для переподключения к той же самой БД:

    ORCL =
      (DESCRIPTION =
        (ADDRESS = (PROTOCOL = TCP)(HOST = 127.0.0.1)(PORT = 1521))
        (CONNECT_DATA =
          (SERVICE_NAME = orclpdb1)
          (FAILOVER_MODE =
            (TYPE = SELECT)
            (METHOD = BASIC)
            (RETRIES = 20)
            (DELAY = 15))))
 

Также можно настроить TAF на стороне базы данных путем модификации сервиса с помощью » srvctl (для RAC) или с помощью пакетной процедуры »  DBMS_SERVICE.MODIFY_SERVICE (для одиночных инстансов баз данных).

Использование функций обратного вызова TAF в OCI8

Функция обратного вызова TAF являются функцией зарегистрированной из приложения для запуска в процессе восстановления после сбоя. При восстановлении соединения она вызывается несколько раз.

Первый раз она запускается в момент обнаружения проблем с соединением. Это позволяет приложению корректно подготориться к задержке выполнения на время восстановления после сбоя. Если восстановление завершилось удачно, функция будет вызвана сразу после восстановления подключения. Этот запуск приложение может использовать для пересинхронизации настроек сессии и оповещения пользователя о том, что произошло восстановление после сбоя. Если восстановление завершилось неудачно, функция запускается еще раз для оповещения приложения о том, что восстановление завершилось с ошибкой и соединение с БД недоступно.

Интерфейс функции обратного вызова TAF:

userCallbackFn ( resource $connection , int $event , int $type ) : int

connection

Идентификатор соединения Oracle для которого эта функция была зарегистрирована с помощью oci_register_taf_callback(). Соединение недоступно во время аварийного восстановления.

event

Событие восстановления означает текущий статус восстановления.

  • OCI_FO_BEGIN означает, что произошла потеря соединения и процесс восстановления начат.

  • OCI_FO_END означает удачное восстановление соединения.

  • OCI_FO_ABORT означает, что восстановление завершилось неудачно и попыток восстановления больше не будет.

  • OCI_FO_ERROR также означает, что восстановление завершилось с ошибкой, но приложению дается возможность обарботать ошибку и вернуть OCI_FO_RETRY для еще одной попытки восстановления.

  • OCI_FO_REAUTH означает, что пользователь Oracle был повторно аутентифицирован..

type

Тип восстановления после отказа. Это позволяет функции понять, какой тип восстановления запрошен приложением. Допустимы такие значения:

  • OCI_FO_SESSION означает, что пользователь запросил только восстановление сессии. К примеру, если соединение пропало, то будет создана новая сессия на резервном сервере. Этот тип восстановления не будет пытаться восстановить запросы типа SELECT.

  • OCI_FO_SELECT означает, что запрошено восстановление запросов SELECT. Это позволит использовать открытый курсор для извлечения значений после восстановления.

return value

  • 0 означает, что шаги восстановления после отказа должны продолжаться нормально.

  • OCI_FO_RETRY означает, что необходимо попробвать восстановиться еще раз. В случае ошибки при переходе на новое соединение TAF может повторить переход на другой ресурс. Обычно перед возвращением кода OCI_FO_RETRY рекомендуется некоторое время подождать.

Пример регистрации функции обартного вызова TAF

<?php

// Определяем функцию обратного вызова в пространстве пользователя
class MyClass {
    public static 
$retry_count;
    public static function 
TAFCallback($conn$event$type)
    {
        switch (
$event) {
            case 
OCI_FO_BEGIN:
                
printf(" Failing Over ... Please stand by\n");
                
printf(" Failover type was found to be %s \n",
                       ((
$type==OCI_FO_SESSION) ? "SESSION"
                        
:(($type==OCI_FO_SELECT) ? "SELECT" "UNKNOWN!")));
                
self::$retry_count 0;
                break;
            case 
OCI_FO_ABORT:
                
// Приложение больше не может использовать базу данных
                
printf(" Восстановление невозможно.\n");
                break;
            case 
OCI_FO_END:
                
// Восстановление завершилось успешно. Оповестим пользователей, что была проблема.
                
printf(" Восстановление завершено ... восстанавливаю работу\n");
                break;
            case 
OCI_FO_REAUTH:
                
printf(" Пользователь переавторизован ... восстанавливаю работу\n");
                
// Заново выполняем все необходимые ALTER SESSION
                // т.е. oci_parse($conn, ‘ALTER SESSION …’) ;
                
break;
            case 
OCI_FO_ERROR:
                
// Прекращаем попытки соединения, если их было более 20.
                
if (self::$retry_count >= 20)
                    return 
0;
                
printf(" Ошибка восстановления. Повторная попытка через 10 секунд...\n");
                
sleep(10);
                
self::$retry_count++;
                return 
OCI_FO_RETRY// retry failover
                
break;
            default:
                
printf("Неизвестное событие восстановления: %d.\n"$event);
                break;
        }
        return 
0;
    }
}

$fn_name 'MyClass::TAFCallback';

$conn oci_connect('hr''welcome''orcl');
$sysconn oci_connect('system''oracle''orcl');

// Используйте привилегированное соединение для создания оператора SQL, который будет инициировать отработку отказа
$sql = <<< 'END'
select unique 'alter system disconnect session '''||sid||','||serial#||''''
from v$session_connect_info
where sid = sys_context('USERENV', 'SID')
END;

$s oci_parse($conn$sql);
oci_execute($s);
$r oci_fetch_array($s);
$disconnectssql $r[0];

oci_register_taf_callback($conn$fn_name); // Зарегистрируйте TAFCallback для Oracle TAF

print("Разбор пользовательского запроса\n");
$sql "select systimestamp from dual";
$stmt oci_parse($conn$sql);

// К примеру, если соединение было потеряно на этом шаге, oci_execute()
// определит это и запустит процедуру восстановления. В процессе восстановления
// oci_execute() будет вызовать зарегистрированную функцию обратного вызова
// несколько раз. Если восстановление пройдет успешно, то будет создано новое соединение 
// и выполнение oci_execute() будет продолжено в нормальном режиме.
// Настройки сессии могут быть сброшены в функции обратного вызова.
// Если восстановление завершится неудачно, oci_execute() вернет ошибку, так как
// будет отсутствовать соединение.

// Отключите пользователя, который инициирует аварийное переключение
print("Отключение пользователя\n");
$discsql oci_parse($sysconn$disconnectssql);
oci_execute($discsql);

print(
"Выполнение пользовательского запроса\n");
$e oci_execute($stmt);
if (!
$e) {
    
$m oci_error($stmt);
    
trigger_error('Не удалось выполнить условие:'$m['message'], E_USER_ERROR);
}
$row oci_fetch_array($stmt);
print(
$row[0] . "\n");

// выполняем другие SQL-запросы на новом подключении
// $stmt = oci_parse($conn,  . . .); 

?>