|
Вложенные транзакции
|
|||
---|---|---|---|
#18+
Новая для меня тема частичных откатов, надеюсь я понимаю эти вещи правильно. Я написал процедуру Outer(), которую кто-то будет вызывать в контексте своей транзакции. О вызывающем предположений мало, поэтому мне надо себя вести вежливо для всех: вызов из джоба, вызов из автономной транзакции, вызов из триггера. Моя процедура помечает таблицу TBL10, затем вызывает библиотечную процедуру Inner(), которая обновляет другие какие-то таблицы. Библиотечную - значит я не могу ее редактировать, или даже читать. Что именно она обновляет, тоже не знаю. Есть вероятность, что кинет exception без обработки. Код: plsql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30.
Код в примере выше при exception в середине Inner() оставит изменения в таблице TBL21, нарушая целостность данных - таблицы 21-22-23 должны изменяться вместе. "Вместе" в житейском смысле, строгой атомарности не требуется. Ранее Inner() всегда вызывалась как одна целая транзакция, поэтому откат по умолчанию устраивал. Мне нужно чтобы процедура Inner() отработала как вложенная транзакция, т.е. если в середине Inner() происходит exception, чтобы произошел чистый откат только ее действий, но не других ранее происшедших. Процедура Inner не автономная, и не содержит commit, ее чистый откат возможен. Вызов commit перед вызовом Inner() не устраивает, т.к. это сломает транзакцию тому, кто меня вызывает. Вложенные транзакции Оракл не поддерживает, поэтому приходится их самому сочинять. Как я понимаю, у меня есть некоторый выбор: - поместить SAVEPOINT до вызова Inner(), подавить exception в блоке с откатом до SAVEPOINT, чтобы обеспечить вложенность. Это самый чистый вариант. Еще не проверил, что "rollback TO" будет позволен из триггера. Если нет, отмечу в документации что функция несовместима с вызовом из триггера. - поместить мой якорный код в автономную транзакцию и делать полный rollback по exception. Это ограничит откаты для вызывающего, но в моем случае одинокая строчка в TBL10 на целостность данных не влияет. Бывают еще другие варианты? ... |
|||
:
Нравится:
Не нравится:
|
|||
12.12.2020, 22:48 |
|
Вложенные транзакции
|
|||
---|---|---|---|
#18+
Обернул вызываемую чужую процедуру, теперь она "атомарная". Если вызывающий забудет отказаться от exceptions, получит их как есть. Код: plsql 1. 2. 3. 4. 5. 6. 7. 8. 9.
Теперь независимо от ошибок в Inner() или в вызывающем коде, недо-апдейты должны быть исключены. ... |
|||
:
Нравится:
Не нравится:
|
|||
12.12.2020, 23:30 |
|
Вложенные транзакции
|
|||
---|---|---|---|
#18+
НеофитSQL Код в примере выше при exception в середине Inner() оставит изменения в таблице TBL21, Заблуждаетесь. Код: plsql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51.
... |
|||
:
Нравится:
Не нравится:
|
|||
12.12.2020, 23:55 |
|
Вложенные транзакции
|
|||
---|---|---|---|
#18+
Если вы совсем ничего не знаете об Inner, значит у вас не может быть и идей о том, к какому результату приведет вызов Код: plsql 1.
(Никакого BeforeInner к этому моменту уже может просто не существовать). PS опять вы изобретаете собственную терминологию, или притягиваете "похожую" из соседних контекстов. Никаких "вложенных транзакций" у Oracle нет. Совсем нет. Ни точки останова сохранения, ни автономные транзакции не имитирует поведения того, что обычно принято называть "вложенными транзакциями". ... |
|||
:
Нравится:
Не нравится:
|
|||
13.12.2020, 00:10 |
|
Вложенные транзакции
|
|||
---|---|---|---|
#18+
andrey_anonymous, > Заблуждаетесь. Долго правил длинное сообщение, отослал с ошибкой в самом начале. Упс. Поначалу в коде примера не было savepoint/rollback to, и мое утверждение относилось к первоначальному коду, который выглядел примерно так: Код: plsql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11.
В этом виде, код оставил бы строку '3.1 Inner Success' в таблице, без соответствующих 3.2 и 3.3, а мне надо было чтобы 3.* ходили хором. Спасибо за написание примера - у меня написан почти такой же, пока я проверял свое понимание частичного отката с разных сторон. Других нареканий нет? Так и принято писать, если нужно откатить только вызываемую функцию, а не всю транзакцию? ... |
|||
:
Нравится:
Не нравится:
|
|||
13.12.2020, 00:25 |
|
Вложенные транзакции
|
|||
---|---|---|---|
#18+
НеофитSQL Так и принято писать, если нужно откатить только вызываемую функцию, а не всю транзакцию? Принято подтирать за собой в обработчике исключений, если предполагается подобный исход. Т.е. в приведенном Вами примере неправильно написана Inner. ... |
|||
:
Нравится:
Не нравится:
|
|||
13.12.2020, 00:30 |
|
Вложенные транзакции
|
|||
---|---|---|---|
#18+
booby Если вы совсем ничего не знаете об Inner, значит у вас не может быть и идей о том, к какому результату приведет вызов Код: plsql 1.
(Никакого BeforeInner к этому моменту уже может просто не существовать). Вы наверное готовы привести какой-то супер пример, где вызов Inner() подменяет базу данных и перекомпилирует код :) У меня есть достаточно сведений про процедуру Inner(), чтобы считать ее обыкновенным кодом. В нем есть селекты, апдейты, может даже вставки. Она может дать exception, специально или по ошибке. Там нет явных или скрытых commit/rollback, и там нет DDL или динамического кода. Я надеялся, что это будет понятно из первого сообщения. Самое главное, процедура Inner считается правильно работающей когда из нее состоит вся транзакция. Этого посыла должно быть достаточно. ... |
|||
:
Нравится:
Не нравится:
|
|||
13.12.2020, 00:34 |
|
Вложенные транзакции
|
|||
---|---|---|---|
#18+
andrey_anonymous НеофитSQL Так и принято писать, если нужно откатить только вызываемую функцию, а не всю транзакцию? Принято подтирать за собой в обработчике исключений, если предполагается подобный исход. Т.е. в приведенном Вами примере неправильно написана Inner. В моем случае, имеем что имеем. Не без причин: вызов Inner совпадал с транзакцией, и на обработку сообщений можно было забить - если никто не словит, все откатится полностью. Что и происходило, все работало прекрасно, юзер видел ошибку на экране. Теперь из-за автоматизации контекст вызова изменился, и мне нужно подавить/логнуть некритические ошибки, т.к. юзера больше нет. Мне было проще обернуть ее в "AtomicInner" как я сделал во втором сообщении этой темы, чем лезть в чужой код. Теперь AtomicInner должна подтереть за собой. ... |
|||
:
Нравится:
Не нравится:
|
|||
13.12.2020, 00:40 |
|
Вложенные транзакции
|
|||
---|---|---|---|
#18+
НеофитSQL booby Если вы совсем ничего не знаете об Inner, значит у вас не может быть и идей о том, к какому результату приведет вызов Код: plsql 1.
(Никакого BeforeInner к этому моменту уже может просто не существовать). Вы наверное готовы привести какой-то супер пример Достаточно изложить Inner чуть иначе: Код: plsql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12.
и увидеть зачем в примере строчка Код: plsql 1.
... |
|||
:
Нравится:
Не нравится:
|
|||
13.12.2020, 00:46 |
|
Вложенные транзакции
|
|||
---|---|---|---|
#18+
НеофитSQL, В общем, не надейтесь. Понять вас более чем затруднительно. Если "из неё состоит вся транзакция", то что в использующем коде делает установка savepoint вообще? Кроме того, ваш первый пост очевидно показывает, что вся транзакция из Inner точно не состоит. Более того, если она обязана быть чёрным ящиком, то все свойства ящика должны содержаться именно в ней, вместе с точкой сохранения и возврата к ней. Конечно, наличие в ней некоей Most_Inner потребует получения дополнительных уверенностей в способности возврата к объявленной точки. Вы зря раздражаетесь, кстати. Это вовсе не редкость и совсем не удивление, когда принципы "красивого программирования", нажитые в "обычных" языках, не ложатся и диссонируют с практиками, возникающими при работе с БД. По мере разрастания базы вашего собственного кода ваша уверенность в том, каким требованиям вызываемая процедура несомненно отвечает, а какие ее особенности окажутся неподходящими в конкретному случаю, достаточно быстро подвергнется испытаниям. ... |
|||
:
Нравится:
Не нравится:
|
|||
13.12.2020, 00:53 |
|
Вложенные транзакции
|
|||
---|---|---|---|
#18+
andrey_anonymous, Inner() не редактируется. Она "чужая". ... |
|||
:
Нравится:
Не нравится:
|
|||
13.12.2020, 02:46 |
|
Вложенные транзакции
|
|||
---|---|---|---|
#18+
booby, вам доводилось использовать savepoint конструкцию в своем коде? Вы звучите, как вроде она вас чем-то обидела. > Если "из неё состоит вся транзакция", то что в использующем коде делает установка savepoint вообще? Состояла. Ранее. Теперь inner() - это часть бОльшей транзакции, которую мне удалось поделить на секции с помощью savepoint. Очень полезная штука. Не дотягивает до вложенных транзакций, но при достаточной дисциплине программиста позволяет их имитировать. ... |
|||
:
Нравится:
Не нравится:
|
|||
13.12.2020, 02:53 |
|
Вложенные транзакции
|
|||
---|---|---|---|
#18+
Не важно, кому что доводилось. Но критически важно вот это: НеофитSQL ... Состояла. Ранее. Теперь ... Похоже, вы это продолжаете недооценивать. Ладно. Пусть сейчас ситуация отвечает вашему символу веры. Просто положите на ум, что вашему роллбеку когда-нибудь может оказаться не к чему откатываться. Такое понимание снабдит вас новым набором вопросов. ... |
|||
:
Нравится:
Не нравится:
|
|||
13.12.2020, 03:01 |
|
Вложенные транзакции
|
|||
---|---|---|---|
#18+
booby, Вопрос в начале темы был "так можно? а как лучше?". Соберитесь с мыслями и блесните экспертизой :) ... |
|||
:
Нравится:
Не нравится:
|
|||
13.12.2020, 03:28 |
|
Вложенные транзакции
|
|||
---|---|---|---|
#18+
Попробуем как это все работает. Первый этап: библиотечная "чужая" функция сама по себе. Код: plsql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69.
... |
|||
:
Нравится:
Не нравится:
|
|||
13.12.2020, 03:54 |
|
Вложенные транзакции
|
|||
---|---|---|---|
#18+
Это выглядит так, как если бы exec ставила точку отката перед вызовом функции, и к ней откатывала. Легко проверить: Код: plsql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21.
Получается, вызов процедуры (через exec или через анонимный блок) из SQL+ содержит встроенный savepoint, поэтому если процедура вылетит по exception, только действия этого вызова процедуры будут подвержены откату. При вызове двух процедур, точка отката одна, поэтому таблица пуста. Кстати, хороший вопрос для интервью: при каких условиях результат Код: plsql 1. 2. 3. 4. 5.
отличается от Код: plsql 1. 2. 3. 4. 5. 6. 7. 8. 9.
Теперь кто внимательно читал, знает. ... |
|||
:
Нравится:
Не нравится:
|
|||
13.12.2020, 04:13 |
|
Вложенные транзакции
|
|||
---|---|---|---|
#18+
НеофитSQL, вы унылы, но вот вам экспертиза: 1) вместо прямых SavePoint Имярек и т.п. используйте арсенал dbms_transaction 2) то, что следовало бы называть "якорной точкой" я бы предложил после объявления сейвпойнта, а не до, независимо от способа задания точки сохранения. 3) в описанном варианте вызов LogError может оказаться недостижимым кодом, а значит, стоит не на своем месте 4) Возможность потери точки сохранения, не предусмотренная вами, приведет к тому, что залогируете одну ошибку и при этом процедура завершится с кодом совершенно другой, хотя вы хотели, чтобы Outer ошибок не выбрасывал. Т.е. - либо - еще один обработчик ошибок, либо - 5) если вы на самом деле имеете дело с чужим кодом, при получении ошибки можно было бы сравнивать идентификатор транзакции после "якорной точки" и в месте обработки ошибки. В комбинации с динамическим именем точки сохранения, это позволит выдать дополнительную информацию в лог, как минимум, и не пытаться делать уже бессмысленный ролбек. ... |
|||
:
Нравится:
Не нравится:
|
|||
13.12.2020, 04:14 |
|
Вложенные транзакции
|
|||
---|---|---|---|
#18+
Осталось обернуть Inner. Цель: при exception в Inner() выполнить ограниченый откат, как в SQL+. Код: plsql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36.
Якорная строка на месте, Inner() чисто откатился в ноль. Еще разок: Код: plsql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20.
Ура! первый и третий вызовы сбойнули, чисто откатив неполные измения в TEST20. При этом якорная строчка осталась в таблице, как я и хотел. Успех? ... |
|||
:
Нравится:
Не нравится:
|
|||
13.12.2020, 04:56 |
|
Вложенные транзакции
|
|||
---|---|---|---|
#18+
booby НеофитSQL, вы унылы, но вот вам экспертиза: 1) вместо прямых SavePoint Имярек и т.п. используйте арсенал dbms_transaction 2) то, что следовало бы называть "якорной точкой" я бы предложил после объявления сейвпойнта, а не до, независимо от способа задания точки сохранения. 3) в описанном варианте вызов LogError может оказаться недостижимым кодом, а значит, стоит не на своем месте 4) Возможность потери точки сохранения, не предусмотренная вами, приведет к тому, что залогируете одну ошибку и при этом процедура завершится с кодом совершенно другой, хотя вы хотели, чтобы Outer ошибок не выбрасывал. Т.е. - либо - еще один обработчик ошибок, либо - 5) если вы на самом деле имеете дело с чужим кодом, при получении ошибки можно было бы сравнивать идентификатор транзакции после "якорной точки" и в месте обработки ошибки. В комбинации с динамическим именем точки сохранения, это позволит выдать дополнительную информацию в лог, как минимум, и не пытаться делать уже бессмысленный ролбек. Спасибо, по делу. Я почитаю про dbms_transaction, и узнаю что еще Оракл предлагает в этой области. 2) Якорная строчка, не точка. Это я так назвал строчку, которую я хочу сохранить даже если код после нее бросать exceptions. 3) Тут вы меня удивили. rollback to BeforeInner - это не goto BeforeInner; после него выполняется следующая строка. Вы считаете, что возможна ситуация, когда rollback исполнится, а LogError - нет? Я с нетерпением жду ответа. 4) отложим на минутку, я как раз собирался это протестировать. 5) дитто ... |
|||
:
Нравится:
Не нравится:
|
|||
13.12.2020, 05:05 |
|
Вложенные транзакции
|
|||
---|---|---|---|
#18+
"Если вы думаете, что все идет хорошо, вы что-то не замечаете" - Закон Мерфи. Все это обертывание чужого кода в savepoint/rollbackTO блок предполагало что чужой код будет продолжать сохранять свойства совместимые с моим подходом. Например, не будет вызывать глобальный rollback. Ведь такое действие разрушает все предшествующие сэйв пойнты, согласно документации. А вдруг? Booby сказал, что будет некуда возвращаться. Подложим моему коду свинью, изменив inner следующим образом: Код: plsql 1. 2. 3. 4. 5. 6. 7.
И повторим эксперимент: Код: plsql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25.
Вот она, коварная ошибка при попытке частичного rollback на несуществующий savepoint! Вывод: если не знаете кто вас вызывает, не используйте rollback или commit - вы необратимо сломаете транзакцию, о которой не имеете представления (и не должны иметь, т.к. ваш код ниже в иерархии и может вызываться из разных мест). Подозреваю, что от такого лома нет приема. Если вы вызвали чужую процедуру, которая сделала commit(или rollback) в неожиданный для вас момент, никакие бережные построения транзакций вас не уберегут. ... |
|||
:
Нравится:
Не нравится:
|
|||
13.12.2020, 05:42 |
|
Вложенные транзакции
|
|||
---|---|---|---|
#18+
НеофитSQL booby НеофитSQL, вы унылы, но вот вам экспертиза: 1) вместо прямых SavePoint Имярек и т.п. используйте арсенал dbms_transaction 2) то, что следовало бы называть "якорной точкой" я бы предложил после объявления сейвпойнта, а не до, независимо от способа задания точки сохранения. 3) в описанном варианте вызов LogError может оказаться недостижимым кодом, а значит, стоит не на своем месте 4) Возможность потери точки сохранения, не предусмотренная вами, приведет к тому, что залогируете одну ошибку и при этом процедура завершится с кодом совершенно другой, хотя вы хотели, чтобы Outer ошибок не выбрасывал. Т.е. - либо - еще один обработчик ошибок, либо - 5) если вы на самом деле имеете дело с чужим кодом, при получении ошибки можно было бы сравнивать идентификатор транзакции после "якорной точки" и в месте обработки ошибки. В комбинации с динамическим именем точки сохранения, это позволит выдать дополнительную информацию в лог, как минимум, и не пытаться делать уже бессмысленный ролбек. Спасибо, по делу. Я почитаю про dbms_transaction, и узнаю что еще Оракл предлагает в этой области. 2) Якорная строчка, не точка. Это я так назвал строчку, которую я хочу сохранить даже если код после нее бросать exceptions. 3) Тут вы меня удивили. rollback to BeforeInner - это не goto BeforeInner; после него выполняется следующая строка. Вы считаете, что возможна ситуация, когда rollback исполнится, а LogError - нет? Я с нетерпением жду ответа. 4) отложим на минутку, я как раз собирался это протестировать. 5) дитто Извиняюсь за двойную цитату, пишу дополнение. Пункт 3 прояснился, вместе с пунктом 4 когда я обозрел ошибку ORA-01086. rollback/TO может вызвать свой exception, теперь понятно. По поводу 5 я подумаю, спасибо за идею. Подавить все exception не самоцель, в этом конкретном случае это вызвано грубой ошибкой программирования. Если при этом остановится импорт данных, рухнет джоб или у кого-то запищит пейджер, в моих условиях это приемлемо. Мне нужно подавить exceptions, которыми процедура сигнализировала (ранее пользователям) о дефектах в данных. Теперь я такие exceptions ловлю и записываю, одновременно заботясь о частичном откате, т.к процедура этого не делала. ... |
|||
:
Нравится:
Не нравится:
|
|||
13.12.2020, 05:53 |
|
Вложенные транзакции
|
|||
---|---|---|---|
#18+
а так ошибки нет, но результат какой? Код: plsql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33.
ЗЫ: глобальные переменные - зло ... |
|||
:
Нравится:
Не нравится:
|
|||
13.12.2020, 09:44 |
|
Вложенные транзакции
|
|||
---|---|---|---|
#18+
andreymx а так ошибки нет, но результат какой? Ну может хоть сейчас поймет, если в 22247097 не догнал :) ... |
|||
:
Нравится:
Не нравится:
|
|||
13.12.2020, 13:28 |
|
Вложенные транзакции
|
|||
---|---|---|---|
#18+
andreymx, Думаю, что удалит строки, и переопределит savepoint. Вряд ли у оракла эти savepoints уважают scope, скорее всего это глобальный список name:SCN в сессии. Могло бы кинуть ошибку при переопределении savepoint, но тогда пришлось бы добавить "cancel savepoint", которого сейчас нет. ... |
|||
:
Нравится:
Не нравится:
|
|||
13.12.2020, 17:43 |
|
Вложенные транзакции
|
|||
---|---|---|---|
#18+
Ещё inner() может удалить все данные и сделать commit. Против этого у меня тоже лекарства нет. Функция не содержала transaction management потому что вызывалась "между двумя коммитами", тем самым являясь самостоятельной транзакцией. Теперь это потребовалось, потому что она стала участвовать в бОльшей транзакции неопределенного размера. SQL+ это хорошо понимает, поэтому там сделано как у меня - каждая DML команда изменения данных является "вложенной" транзакцией, чтоб результаты предыдущих команд не пропали. Да и сервер перед каждым DDL использует savepoint, согласно документации. ... |
|||
:
Нравится:
Не нравится:
|
|||
13.12.2020, 17:55 |
|
|
start [/forum/topic.php?fid=52&fpage=30&tid=1880609]: |
0ms |
get settings: |
9ms |
get forum list: |
12ms |
check forum access: |
3ms |
check topic access: |
3ms |
track hit: |
34ms |
get topic data: |
10ms |
get forum data: |
2ms |
get page messages: |
54ms |
get tp. blocked users: |
1ms |
others: | 12ms |
total: | 140ms |
0 / 0 |