Oracle 12. Максимальная продолжительность выбора для обновления для occi С++

Мы используем occi для доступа к Oracle 12 через процесс C++. Одна из операций должна гарантировать, что клиент должен выбрать самые последние данные в базе данных и работать в соответствии с последним значением. Заявление

std::string sqlStmt = "SELECT REF(a) FROM O_RECORD a WHERE G_ID= :1 AND P_STATUS IN (:2, :3) FOR UPDATE OF PL_STATUS"

(мы используем ТИПЫ). По какой-то причине эта команда не прошла, и таблица базы данных ЗАБЛОКИРОВАНА. Все остальные операции ожидают завершения первого потока, однако поток убит, и мы зашли в тупик.

Каково оптимальное решение, чтобы избежать этого катастрофического сценария? Могу ли я установить тайм-аут в операторе, чтобы на 100% поток мог работать с «выбором для обновления», скажем, максимум 10 секунд? Другими словами, поток выполнения может заблокировать таблицу/строку базы данных, но не более чем на заданное время.

Это возможно?


person cateof    schedule 02.12.2016    source источник


Ответы (2)


Есть параметр сеанса ddl_lock_timeout, но нет dml_lock_timeout. Таким образом, вы не можете пойти по этому пути. Так что либо вы должны использовать

SELECT REF(a) 
FROM O_RECORD a 
WHERE G_ID= :1 AND P_STATUS IN (:2, :3) 
FOR UPDATE OF PL_STATUS SKIP LOCKED

И изменить логику приложения. Или вы можете реализовать свой собственный механизм прерывания. Просто запустите параллельный поток и через некоторое время выполните OCIBreak. Это документированное и поддерживаемое решение. Вызов OCIBreak является потокобезопасным. Заблокированный оператор SELECT .. FOR UPDATE будет выпущен, и вы получите сообщение об ошибке ORA-01013: user requested cancel of current operation.

Так что на уровне OCCI вам придется обрабатывать эту ошибку.

person ibre5041    schedule 02.12.2016
comment
Было бы здорово, если бы вы могли сослаться или привести пример. Я предполагаю, что если мы идем с OCIBreak, то SKIP LOCKED не нужен. - person cateof; 07.12.2016
comment
да. эти варианты являются эксклюзивными. ИМХО, вам следует искать чистые примеры OCI ">twiki.cern.ch/twiki/bin/viewfile/PSSGroup/ вместо OCCI. - person ibre5041; 07.12.2016
comment
PS: избегайте использования неблокирующего OCI. ИХМО было довольно глючно. - person ibre5041; 07.12.2016

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

с помощью диспетчера ресурсов:

Диспетчер ресурсов позволяет определять более сложные политики, чем те, которые доступны для профилей, и в вашем случае больше подходит, чем последние.

Вы должны определить план и группы пользователей, связанные с планом, должны указать политики, связанные с планом/группами, и, наконец, должны присоединить пользователей к группам. Чтобы понять, как это сделать, вы можете повторно использовать этот example @support.oracle.com (кажется, что он слишком длинный, чтобы размещать его здесь), но заменив MAX_IDLE_TIME на MAX_IDLE_BLOCKER_TIME.

Основная линия будет

dbms_resource_manager.create_plan_directive(
  plan => 'TEST_PLAN',
  group_or_subplan => 'my_limited_throttled_group',
  comment => 'Limit blocking idle time to 300 seconds',
  MAX_IDLE_BLOCKER_TIME => 300)
;

с помощью профилей:

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

СОЗДАТЬ ПРОФИЛЬ:

Если пользователь превышает лимит ресурсов сеанса CONNECT_TIME или IDLE_TIME, база данных откатывает текущую транзакцию и завершает сеанс. Когда пользовательский процесс выполняет следующий вызов, база данных возвращает ошибку

Для этого укажите профиль с максимальным временем простоя и примените его только к соответствующим пользователям (чтобы вы не затронули всех пользователей или приложения).

CREATE PROFILE o_record_consumer
  LIMIT IDLE_TIME 2; --2 minutes timeout

alter user the_record_consumer profile o_record_consumer;

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

представляет интерес...

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

  • FOR UPDATE WAIT x; Если вы добавите предложение WAIT x к оператору select for update, сеанс ожидания прекратит ожидание по истечении "x" секунд. (целое число «x» должно быть жестко закодировано там, например, значение «3»; переменная не годится, по крайней мере, в Oracle 11gR2).
  • SKIP LOCKED; Если вы добавите предложение SKIP LOCKED к своему оператору select for update, выбор не вернет заблокированные записи (как ibre5041 уже указал вверх).
  • Вы можете сообщить дополнительному сеансу (что-то вроде сторожевого таймера), что ваш сеанс готов к запуску запроса, и после успешного выполнения предупредить его о завершении. Сеанс сторожевого таймера может реализовать свою логику «убить сеанс после истечения времени ожидания». Вы должны заплатить за дополнительную сложность, но получите преимущество в том, что тайм-аут применяется к этому конкретному оператору, а не к сеансу. Для этого см. раздел ORACLE-BASE — DBMS_PIPE или 3.2 DBMS_ALERT: рассылка предупреждений пользователям, Стивен Фейерштейн, 1998.

Наконец, может случиться так, что вы пытаетесь реализовать самодельную инфраструктуру очередей. В этом случае имейте в виду, что Oracle уже имеет свою собственную механику очередей, называемую Advanced Queue, и вы можете получить много с очень небольшими затратами, просто используя их; см. ORACLE-BASE — Oracle Advanced Queuing.

person Antonio    schedule 09.12.2016