<?php
include_once $_SERVER['DOCUMENT_ROOT'] . '/include/shared-manual.inc';
$TOC = array();
$TOC_DEPRECATED = array();
$PARENTS = array();
include_once dirname(__FILE__) ."/toc/book.oci8.inc";
$setup = array (
  'home' => 
  array (
    0 => 'index.php',
    1 => 'PHP Manual',
  ),
  'head' => 
  array (
    0 => 'UTF-8',
    1 => 'ru',
  ),
  'this' => 
  array (
    0 => 'oci8.taf.php',
    1 => 'Поддержка прозрачного для приложений восстановления после отказа (Transparent Application Failover или TAF) для OCI8',
    2 => 'Поддержка прозрачного для приложений восстановления после отказа (Transparent Application Failover или TAF) для OCI8',
  ),
  'up' => 
  array (
    0 => 'book.oci8.php',
    1 => 'OCI8',
  ),
  'prev' => 
  array (
    0 => 'oci8.fan.php',
    1 => 'Поддержка OCI8 Fast Application Notification (FAN)',
  ),
  'next' => 
  array (
    0 => 'oci8.dtrace.php',
    1 => 'OCI8 и динамическая трассировка DTrace',
  ),
  'alternatives' => 
  array (
  ),
  'source' => 
  array (
    'lang' => 'ru',
    'path' => 'reference/oci8/taf.xml',
  ),
  'history' => 
  array (
  ),
);
$setup["toc"] = $TOC;
$setup["toc_deprecated"] = $TOC_DEPRECATED;
$setup["parents"] = $PARENTS;
manual_setup($setup);

contributors($setup);

?>
<div id="oci8.taf" class="chapter">
 <h1 class="title">Поддержка прозрачного для приложений восстановления после отказа (Transparent Application Failover или TAF) для OCI8</h1>

 <p class="para">
  TAF — функция базы данных Oracle, которая обеспечивает высокую доступность.
  TAF помогает OCI8-приложениям PHP автоматически переподключаться
  к предварительно настроенной базе данных при сбое подключения к базе данных
  из-за сбоя экземпляра или сети.
 </p>
 <p class="para">
  В сконфигурированной системе базы данных Oracle TAF-обработка происходит, когда
  PHP-приложение обнаруживает, что экземпляр базы данных не работает или недоступен.
  Он устанавливает соединение с другим узлом в конфигурации
  Oracle <a href="https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-DEF850F6-27E9-428E-B8FC-530230D78AD2" class="link external">&raquo;&nbsp;RAC</a>, базой данных горячего
  резерва или самим экземпляром базы данных. Подробнее об OCI TAF
  рассказывает краткое руководство <a href="https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-F7817CD2-4A2C-4D37-BD36-56DBABD4725F" class="link external">&raquo;&nbsp;Oracle
  Call Interface Programmer&#039;s Guide</a>.
 </p>
 <p class="para">
  Callback-функцию приложения регистрируют функцией
  <span class="function"><a href="function.oci-register-taf-callback.php" class="function">oci_register_taf_callback()</a></span>. В процессе восстановления после
  отказа исполнение приложения приостанавливается и вызывается зарегистрированная
  callback-функция. Эта функция будет оповещать приложение о событиях
  процесса восстановления. Если восстановление завершилось успешно, управление
  возвращается приложению. Если восстановление завершилось неудачно, то очередные
  обращения к базе данных завершаются с ошибкой, поскольку отсутствует подключение.
 </p>
 <p class="para">
  Когда соединение переходит к другой базе данных, обратный вызов может сбросить
  любое необходимое состояние соединения, например, перевыполнить
  необходимую команду ALTER SESSION, если для сервиса базы данных не включили параметр
  -failover_restore.
 </p>
 <p class="para">
  Регистрацию callback-функции приложения удаляют вызовом функции <span class="function"><a href="function.oci-unregister-taf-callback.php" class="function">oci_unregister_taf_callback()</a></span>.
 </p>

 <div class="section">
  <h2 class="title">настройка TAF</h2>
  <p class="para">
   TAF настраивают на стороне PHP-модуля OCI8 или в конфигурации базы данных.
   Если настроено и там, и там, предпочтение отдаётся настройкам на стороне базы данных.
  </p>
  <p class="para">
   Настроить TAF в PHP-модуле OCI8 (на стороне клиента) можно через параметр
   FAILOVER_MODE в части CONNECT_DATA дескриптора соединения.
   Подробнее о настройке TAF на стороне клиента рассказывает секция
   «Настройка прозрачного переключения приложений при отказе» (Configuring Transparent Application Failover)
   краткого руководства <a href="https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-8F532535-C401-4B51-BE0B-04FD74BB0621" class="link external">&raquo;&nbsp;
    Oracle Database Net Services Administrator&#039;s Guide</a>.
  </p>
  <p class="para">
   Пример настройки TAF в файле tnsnames.ora для переподключения к той же самой БД:
  </p>
  <p class="para">
   <div class="informalexample">
    <div class="example-contents screen">
<div class="cdata"><pre>
    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))))
</pre></div>
    </div>
   </div>
  </p>
  <p class="para">
   TAF также настраивается на стороне базы данных путём изменения целевой службы
   утилитой <a href="https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-8DC4D5E0-CA9D-47BC-BAD0-8769405AFEC5" class="link external">&raquo;&nbsp;srvctl</a>
   (для RAC) или через пакетную процедуру
   <a href="https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-C11449DC-EEDE-4BB8-9D2C-0A45198C1928" class="link external">&raquo;&nbsp;
    DBMS_SERVICE.MODIFY_SERVICE</a>
   (для одиночных экземпляров баз данных).
  </p>
 </div>
 <div class="section">
  <h2 class="title">Callback-функции TAF в модуле OCI8</h2>
  <p class="para">
   Callback-функция TAF — функция приложения, которую можно зарегистрировать для вызова
   во время аварийного переключения. При восстановлении соединения функция
   вызывается несколько раз.
  </p>
  <p class="para">
   Первый раз функция запускается в момент обнаружения проблем с соединением.
   Это позволяет приложению корректно подготовиться к задержке выполнения
   на время восстановления после сбоя. Если восстановление завершилось успешно,
   функция вызывается сразу после восстановления подключения. Этот запуск
   приложение может использовать для пересинхронизации настроек сессии и оповещения
   пользователя о восстановлении после сбоя. Если восстановление не удалось,
   callback-функция запускается ещё раз, чтобы сообщить приложению,
   что восстановление не произошло и соединение невозможно использовать.
  </p>
  <p class="para">
   Интерфейс callback-функции модуля TAF:
  </p>
  <div class="methodsynopsis dc-description">
   <span class="methodname"><strong>userCallbackFn</strong></span>(<span class="methodparam"><span class="type"><a href="language.types.resource.php" class="type resource">resource</a></span> <code class="parameter">$connection</code></span>, <span class="methodparam"><span class="type"><a href="language.types.integer.php" class="type int">int</a></span> <code class="parameter">$event</code></span>, <span class="methodparam"><span class="type"><a href="language.types.integer.php" class="type int">int</a></span> <code class="parameter">$type</code></span>): <span class="type"><a href="language.types.integer.php" class="type int">int</a></span></div>

  <p class="para">
   <dl>
    
     <dt><code class="parameter">connection</code></dt>
     <dd>
      <p class="para">
       Идентификатор соединения Oracle, на котором функцией <span class="function"><a href="function.oci-register-taf-callback.php" class="function">oci_register_taf_callback()</a></span>
       зарегистрировали callback-функцию TAF.
       Соединение недействительно, пока восстановление не завершится успешно.
      </p>
     </dd>
    
    
     <dt><code class="parameter">event</code></dt>
     <dd>
      <p class="para">
       Событие восстановления означает текущий статус восстановления.
      </p>
      <p class="para">
       <ul class="itemizedlist">
        <li class="listitem">
         <p class="para">
          <strong><code><a href="oci8.constants.php#constant.oci-fo-begin">OCI_FO_BEGIN</a></code></strong> означает, что произошла потеря
          соединения и процесс восстановления начат.
         </p>
        </li>
        <li class="listitem">
         <p class="para">
          <strong><code><a href="oci8.constants.php#constant.oci-fo-end">OCI_FO_END</a></code></strong> означает удачное восстановление соединения.
         </p>
        </li>
        <li class="listitem">
         <p class="para">
          <strong><code><a href="oci8.constants.php#constant.oci-fo-abort">OCI_FO_ABORT</a></code></strong> означает, что восстановление завершилось
          неудачно и попыток восстановления больше не будет.
         </p>
        </li>
        <li class="listitem">
         <p class="para">
          <strong><code><a href="oci8.constants.php#constant.oci-fo-error">OCI_FO_ERROR</a></code></strong> также означает, что восстановление
          завершилось с ошибкой, но приложению даётся возможность обработать
          ошибку и вернуть OCI_FO_RETRY для ещё одной попытки восстановления.
         </p>
        </li>
        <li class="listitem">
         <p class="para">
          <strong><code><a href="oci8.constants.php#constant.oci-fo-reauth">OCI_FO_REAUTH</a></code></strong> означает, что пользователь
          Oracle был повторно аутентифицирован.
         </p>
        </li>
       </ul>
      </p>
     </dd>
    
    
     <dt><code class="parameter">type</code></dt>
     <dd>
      <p class="para">
       Тип восстановления после отказа. Это позволяет функции понять,
       какой тип восстановления запрошен приложением. Допустимы такие значения:
      </p>
      <p class="para">
       <ul class="itemizedlist">
        <li class="listitem">
         <p class="para">
          <strong><code><a href="oci8.constants.php#constant.oci-fo-session">OCI_FO_SESSION</a></code></strong> означает, что пользователь
          запросил только восстановление сессии. К примеру, если соединение пропало,
          то будет создана новая сессия на резервном сервере. Этот тип восстановления
          не будет пытаться восстановить запросы типа SELECT.
         </p>
        </li>
        <li class="listitem">
         <p class="para">
          <strong><code><a href="oci8.constants.php#constant.oci-fo-select">OCI_FO_SELECT</a></code></strong> означает, что запрошено восстановление
          запросов SELECT. Это позволит использовать открытый курсор для
          извлечения значений после восстановления.
         </p>
        </li>
       </ul>
      </p>
     </dd>
    
    
     <dt><code class="parameter">return value</code></dt>
     <dd>
      <p class="para">
       <ul class="itemizedlist">
        <li class="listitem">
         <p class="para">
          <code class="literal">0</code> означает, что шаги восстановления после отказа должны
          продолжаться нормально.
         </p>
        </li>
        <li class="listitem">
         <p class="para">
          <strong><code><a href="oci8.constants.php#constant.oci-fo-retry">OCI_FO_RETRY</a></code></strong> означает, что необходимо попробовать
          восстановиться ещё раз. В случае возникновения ошибки при переходе на новое соединение TAF
          может повторить переход на другой ресурс. Обычно перед возвращением кода
          OCI_FO_RETRY рекомендуется некоторое время подождать.
         </p>
        </li>
       </ul>
      </p>
     </dd>
    
   </dl>
  </p>
  <div class="example" id="example-1">
   <p><strong>Пример #1 Регистрация callback-функции TAF</strong></p>
   <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br /></span><span style="color: #FF8000">// Определяем callback-функцию в пространстве пользователя<br /></span><span style="color: #007700">class </span><span style="color: #0000BB">MyClass<br /></span><span style="color: #007700">{<br />    public static </span><span style="color: #0000BB">$retry_count</span><span style="color: #007700">;<br /><br />    public static function </span><span style="color: #0000BB">TAFCallback</span><span style="color: #007700">(</span><span style="color: #0000BB">$conn</span><span style="color: #007700">, </span><span style="color: #0000BB">$event</span><span style="color: #007700">, </span><span style="color: #0000BB">$type</span><span style="color: #007700">)<br />    {<br />        switch (</span><span style="color: #0000BB">$event</span><span style="color: #007700">) {<br />            case </span><span style="color: #0000BB">OCI_FO_BEGIN</span><span style="color: #007700">:<br />                </span><span style="color: #0000BB">printf</span><span style="color: #007700">(</span><span style="color: #DD0000">" Failing Over ... Please stand by\n"</span><span style="color: #007700">);<br />                </span><span style="color: #0000BB">printf</span><span style="color: #007700">(</span><span style="color: #DD0000">" Failover type was found to be %s \n"</span><span style="color: #007700">,<br />                       ((</span><span style="color: #0000BB">$type</span><span style="color: #007700">==</span><span style="color: #0000BB">OCI_FO_SESSION</span><span style="color: #007700">) ? </span><span style="color: #DD0000">"SESSION"<br />                        </span><span style="color: #007700">:((</span><span style="color: #0000BB">$type</span><span style="color: #007700">==</span><span style="color: #0000BB">OCI_FO_SELECT</span><span style="color: #007700">) ? </span><span style="color: #DD0000">"SELECT" </span><span style="color: #007700">: </span><span style="color: #DD0000">"UNKNOWN!"</span><span style="color: #007700">)));<br />                </span><span style="color: #0000BB">self</span><span style="color: #007700">::</span><span style="color: #0000BB">$retry_count </span><span style="color: #007700">= </span><span style="color: #0000BB">0</span><span style="color: #007700">;<br />                break;<br />            case </span><span style="color: #0000BB">OCI_FO_ABORT</span><span style="color: #007700">:<br />                </span><span style="color: #FF8000">// Приложение больше не может использовать базу данных<br />                </span><span style="color: #0000BB">printf</span><span style="color: #007700">(</span><span style="color: #DD0000">" Восстановление невозможно.\n"</span><span style="color: #007700">);<br />                break;<br />            case </span><span style="color: #0000BB">OCI_FO_END</span><span style="color: #007700">:<br />                </span><span style="color: #FF8000">// Восстановление завершилось успешно. Оповестим пользователей, что была проблема.<br />                </span><span style="color: #0000BB">printf</span><span style="color: #007700">(</span><span style="color: #DD0000">" Восстановление завершено ... восстанавливаю работу\n"</span><span style="color: #007700">);<br />                break;<br />            case </span><span style="color: #0000BB">OCI_FO_REAUTH</span><span style="color: #007700">:<br />                </span><span style="color: #0000BB">printf</span><span style="color: #007700">(</span><span style="color: #DD0000">" Пользователь переавторизован ... восстанавливаю работу\n"</span><span style="color: #007700">);<br />                </span><span style="color: #FF8000">// Заново выполняем все необходимые ALTER SESSION<br />                // т.е. oci_parse($conn, ‘ALTER SESSION …’) ;<br />                </span><span style="color: #007700">break;<br />            case </span><span style="color: #0000BB">OCI_FO_ERROR</span><span style="color: #007700">:<br />                </span><span style="color: #FF8000">// Прекращаем попытки соединения, если их было более 20.<br />                </span><span style="color: #007700">if (</span><span style="color: #0000BB">self</span><span style="color: #007700">::</span><span style="color: #0000BB">$retry_count </span><span style="color: #007700">&gt;= </span><span style="color: #0000BB">20</span><span style="color: #007700">)<br />                    return </span><span style="color: #0000BB">0</span><span style="color: #007700">;<br />                </span><span style="color: #0000BB">printf</span><span style="color: #007700">(</span><span style="color: #DD0000">" Ошибка восстановления. Повторная попытка через 10 секунд...\n"</span><span style="color: #007700">);<br />                </span><span style="color: #0000BB">sleep</span><span style="color: #007700">(</span><span style="color: #0000BB">10</span><span style="color: #007700">);<br />                </span><span style="color: #0000BB">self</span><span style="color: #007700">::</span><span style="color: #0000BB">$retry_count</span><span style="color: #007700">++;<br />                return </span><span style="color: #0000BB">OCI_FO_RETRY</span><span style="color: #007700">; </span><span style="color: #FF8000">// retry failover<br />                </span><span style="color: #007700">break;<br />            default:<br />                </span><span style="color: #0000BB">printf</span><span style="color: #007700">(</span><span style="color: #DD0000">"Неизвестное событие восстановления: %d.\n"</span><span style="color: #007700">, </span><span style="color: #0000BB">$event</span><span style="color: #007700">);<br />                break;<br />        }<br />        return </span><span style="color: #0000BB">0</span><span style="color: #007700">;<br />    }<br />}<br /><br /></span><span style="color: #0000BB">$fn_name </span><span style="color: #007700">= </span><span style="color: #DD0000">'MyClass::TAFCallback'</span><span style="color: #007700">;<br /><br /></span><span style="color: #0000BB">$conn </span><span style="color: #007700">= </span><span style="color: #0000BB">oci_connect</span><span style="color: #007700">(</span><span style="color: #DD0000">'hr'</span><span style="color: #007700">, </span><span style="color: #DD0000">'welcome'</span><span style="color: #007700">, </span><span style="color: #DD0000">'orcl'</span><span style="color: #007700">);<br /></span><span style="color: #0000BB">$sysconn </span><span style="color: #007700">= </span><span style="color: #0000BB">oci_connect</span><span style="color: #007700">(</span><span style="color: #DD0000">'system'</span><span style="color: #007700">, </span><span style="color: #DD0000">'oracle'</span><span style="color: #007700">, </span><span style="color: #DD0000">'orcl'</span><span style="color: #007700">);<br /><br /></span><span style="color: #FF8000">// Используйте привилегированное соединение для создания SQL-запроса, который инициирует отработку отказа<br /></span><span style="color: #0000BB">$sql </span><span style="color: #007700">= &lt;&lt;&lt; 'END'<br /></span><span style="color: #DD0000">select unique 'alter system disconnect session '''||sid||','||serial#||''''<br />from v$session_connect_info<br />where sid = sys_context('USERENV', 'SID')<br /></span><span style="color: #007700">END;<br /><br /></span><span style="color: #0000BB">$s </span><span style="color: #007700">= </span><span style="color: #0000BB">oci_parse</span><span style="color: #007700">(</span><span style="color: #0000BB">$conn</span><span style="color: #007700">, </span><span style="color: #0000BB">$sql</span><span style="color: #007700">);<br /></span><span style="color: #0000BB">oci_execute</span><span style="color: #007700">(</span><span style="color: #0000BB">$s</span><span style="color: #007700">);<br /></span><span style="color: #0000BB">$r </span><span style="color: #007700">= </span><span style="color: #0000BB">oci_fetch_array</span><span style="color: #007700">(</span><span style="color: #0000BB">$s</span><span style="color: #007700">);<br /></span><span style="color: #0000BB">$disconnectssql </span><span style="color: #007700">= </span><span style="color: #0000BB">$r</span><span style="color: #007700">[</span><span style="color: #0000BB">0</span><span style="color: #007700">];<br /><br /></span><span style="color: #0000BB">oci_register_taf_callback</span><span style="color: #007700">(</span><span style="color: #0000BB">$conn</span><span style="color: #007700">, </span><span style="color: #0000BB">$fn_name</span><span style="color: #007700">); </span><span style="color: #FF8000">// Зарегистрируйте TAFCallback для Oracle TAF<br /><br /></span><span style="color: #007700">print </span><span style="color: #DD0000">"Разбор пользовательского запроса\n"</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">$sql </span><span style="color: #007700">= </span><span style="color: #DD0000">"select systimestamp from dual"</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">$stmt </span><span style="color: #007700">= </span><span style="color: #0000BB">oci_parse</span><span style="color: #007700">(</span><span style="color: #0000BB">$conn</span><span style="color: #007700">, </span><span style="color: #0000BB">$sql</span><span style="color: #007700">);<br /><br /></span><span style="color: #FF8000">// Например, если соединение было потеряно на этом шаге, функция oci_execute()<br />// определит это и запустит процедуру восстановления. В процессе восстановления<br />// функция oci_execute() будет вызывать зарегистрированную callback-функцию<br />// несколько раз. Если восстановление пройдёт успешно, callback-функция создаст новое соединение<br />// и выполнение функции oci_execute() продолжится в нормальном режиме.<br />// Настройки сессии могут быть сброшены в callback-функции.<br />// Если восстановление завершится неудачно, функция oci_execute() вернёт ошибку,<br />// поскольку будет отсутствовать соединение.<br /><br />// Отключите пользователя, который инициирует аварийное переключение<br /></span><span style="color: #007700">print </span><span style="color: #DD0000">"Отключение пользователя\n"</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">$discsql </span><span style="color: #007700">= </span><span style="color: #0000BB">oci_parse</span><span style="color: #007700">(</span><span style="color: #0000BB">$sysconn</span><span style="color: #007700">, </span><span style="color: #0000BB">$disconnectssql</span><span style="color: #007700">);<br /></span><span style="color: #0000BB">oci_execute</span><span style="color: #007700">(</span><span style="color: #0000BB">$discsql</span><span style="color: #007700">);<br /><br />print </span><span style="color: #DD0000">"Выполнение пользовательского запроса\n"</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">$e </span><span style="color: #007700">= </span><span style="color: #0000BB">oci_execute</span><span style="color: #007700">(</span><span style="color: #0000BB">$stmt</span><span style="color: #007700">);<br />if (!</span><span style="color: #0000BB">$e</span><span style="color: #007700">) {<br />    </span><span style="color: #0000BB">$m </span><span style="color: #007700">= </span><span style="color: #0000BB">oci_error</span><span style="color: #007700">(</span><span style="color: #0000BB">$stmt</span><span style="color: #007700">);<br />    </span><span style="color: #0000BB">trigger_error</span><span style="color: #007700">(</span><span style="color: #DD0000">'Не удалось выполнить условие:'</span><span style="color: #007700">. </span><span style="color: #0000BB">$m</span><span style="color: #007700">[</span><span style="color: #DD0000">'message'</span><span style="color: #007700">], </span><span style="color: #0000BB">E_USER_ERROR</span><span style="color: #007700">);<br />}<br /></span><span style="color: #0000BB">$row </span><span style="color: #007700">= </span><span style="color: #0000BB">oci_fetch_array</span><span style="color: #007700">(</span><span style="color: #0000BB">$stmt</span><span style="color: #007700">);<br />print </span><span style="color: #0000BB">$row</span><span style="color: #007700">[</span><span style="color: #0000BB">0</span><span style="color: #007700">] . </span><span style="color: #DD0000">"\n"</span><span style="color: #007700">;<br /><br /></span><span style="color: #FF8000">// Выполняем другие SQL-запросы на новом подключении<br />// $stmt = oci_parse($conn,  . . .);<br /><br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
   </div>

  </div>
 </div>

 <div class="section">
  <h2 class="title">Смотрите также</h2>
  <ul class="simplelist">
   <li><span class="function"><a href="function.oci-register-taf-callback.php" class="function">oci_register_taf_callback()</a></span></li>
   <li><span class="function"><a href="function.oci-unregister-taf-callback.php" class="function">oci_unregister_taf_callback()</a></span></li>
  </ul>
 </div>


</div>
<?php manual_footer($setup); ?>