IBX - корректная работа с транзакциями

IBX, FIBPlus, UIB, ADO, .Net и прочее-прочее-прочее, в общем все, что относится к созданию приложений, работающих с InterBase, Firebird и Yaffil - клиент-серверных, трехзвенных, консольных и т.п.

Модератор: kdv

Кузнецов Евгений
Сообщения: 144
Зарегистрирован: 16 фев 2006, 22:36

IBX - корректная работа с транзакциями

Сообщение Кузнецов Евгений » 08 мар 2007, 13:10

Доброго времени суток!

Хотя этот вопрос уже поднимался,
остались моменты, которые я не совсем понимаю.

Как правильно работать с транзакциями в IBX?

На сегодняшний момент я делаю так
1)

Код: Выделить всё

StartTransaction
try
  <изменения>
  Commit;
except
  RollBack;
  raise;
end;
что в общем, совпадает с кодом, который приводил
Dimitry Sibiryakov в http://forum.ibase.ru/phpBB2/viewtopic.php?t=1444

Однако в топике http://forum.ibase.ru/phpBB2/viewtopic.php?t=396
рекомендовался несколько другой подход
2)

Код: Выделить всё

StartTransaction
try
  <изменения>
finally
  Commit;
end;
если изменения затрагивают одну запись (хотя, в чем разница, не ясно - ведь
если изменение одно и потерпело неудачу, то все равно Rollback
переведет транзакцию в подтвержденное состояние)

или
3)

Код: Выделить всё

StartTransaction
try
  <изменения>
except
  Rollback;
  raise;
end;
Commit;
если несколько.

Если рассматривать общий случай (изменения могут затрагивать несколько
записей) и не учитывать вариант обрыва соединения (который
IBX не может обработать корректно), какой из способов предпочтительнее - 1 или 3?

Т.е. вопрос сводится к тому, в каких случаях Commit навернется,
но соединение с сервером останется? Merlin упоминал о DDL-statements,
но может существуют и другие случаи?

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 08 мар 2007, 19:31

.е. вопрос сводится к тому, в каких случаях Commit навернется,
но соединение с сервером останется? Merlin упоминал о DDL-statements,
но может существуют и другие случаи?
не надо морочить голову себе и людям. в
www.ibase.ru/devinfo/ibx.htm все описано.
Commit если "навернется", то остается сделать Rollback. Если соединение оборвано, то транзакция будет сервером переведена в rollback.

Кузнецов Евгений
Сообщения: 144
Зарегистрирован: 16 фев 2006, 22:36

Сообщение Кузнецов Евгений » 09 мар 2007, 10:28

Доброго времени суток!

To KDV

Спасибо за внесение ясности.

Статью www.ibase.ru/devinfo/ibx.htm я, естественно, смотрел,
но, хоть убейте, примера корректной работы с транзакциями
не нашел - все примеры сводятся к

Код: Выделить всё

StartTransaction;
....
Commit;
даже без обработки исключений.
Собственно, меня смутил Ваш пост в http://forum.ibase.ru/phpBB2/viewtopic.php?t=1444
от Вт Окт 25, 2005 4:15 pm, где Вы приводите пример кода.

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 09 мар 2007, 10:45

по-моему в том самом топике все разъяснено.
Commit - это применение тех изменений в транзакции, которые "прошли".
Соответственно, это решает разработчик, что делать - Commit или rollback, если какой то из нескольких операторов не прошел.
т.е. я вообще не вижу, где тут "смущаться" :)

за неясности спасибо, я подумаю, как расширить ibx.htm

Кузнецов Евгений
Сообщения: 144
Зарегистрирован: 16 фев 2006, 22:36

Сообщение Кузнецов Евгений » 09 мар 2007, 13:19

kdv писал(а):Commit - это применение тех изменений в транзакции, которые "прошли".
Соответственно, это решает разработчик, что делать - Commit или rollback, если какой то из нескольких операторов не прошел.
Насколько я представляю, в большинстве случаев делают rollback, иначе зачем тогда транзакция?
kdv писал(а):т.е. я вообще не вижу, где тут "смущаться" :)
Вот Ваш пример из упомянутого топика:

Код: Выделить всё

StartTransaction 
try 
  <изменения> 
except 
  Rollback; 
end; 
Commit;
Ну raise в Except здесь не упомянут, но не в этом дело.
Получается, нет особой разницы, где ставить Commit - вне
или внутри секции try-except - все ограничения вроде бы проверяются
при вставке/обновлении записей, а при потере соединения клиентскому
приложению будет уже безразлично, как завершилась транзакция.

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 09 мар 2007, 14:53

Насколько я представляю, в большинстве случаев делают rollback, иначе зачем тогда транзакция?
в большинстве - да. Но тут нет обязаловки или принудиловки - вроде того что если возникла ошибка, то ты ДОЛЖЕН сделать rollback. Не должен.
Получается, нет особой разницы, где ставить Commit - вне
или внутри секции try-except
это как? except обрабатывается при ошибке. commit тут обычно не пишут.
а при потере соединения клиентскому
приложению будет уже безразлично, как завершилась транзакция.
при потере соединения транзакция rollback-ом на сервере откатывается, безусловно.

CyberMax
Заслуженный разработчик
Сообщения: 638
Зарегистрирован: 31 янв 2006, 09:05

Сообщение CyberMax » 09 мар 2007, 14:56

А разве Commit после Rollback не вызовет исключения, что транзакция не активна?

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 09 мар 2007, 15:39

ээээ.... да.

CyberMax
Заслуженный разработчик
Сообщения: 638
Зарегистрирован: 31 янв 2006, 09:05

Сообщение CyberMax » 09 мар 2007, 17:04

kdv писал(а):ээээ.... да.
В смысле вызовет или не вызовет? К сожалению, под рукой нет Дельфи, чтобы проверить.
А по сути вопроса: правильней использовать первый вариант. Commit должен быть в try'e, так как именно его исключения нам надо отловить.

WildSery
Заслуженный разработчик
Сообщения: 1738
Зарегистрирован: 05 июн 2006, 16:19

Сообщение WildSery » 09 мар 2007, 17:11

Вызовет.

Код: Выделить всё

if MyTransaction.inTransaction then MyTransaction.Commit

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 09 мар 2007, 17:25

прошу ревизию
www.ibase.ru/devinfo/ibx.htm#try

CyberMax
Заслуженный разработчик
Сообщения: 638
Зарегистрирован: 31 янв 2006, 09:05

Сообщение CyberMax » 09 мар 2007, 17:45

Код: Выделить всё

TW.StartTransaction;
try
  Q1.ExecQuery;
  Q2.ExecQuery;
  ...
except
  TW.Rollback;
end
if TW.InTransaction then
  TW.Commit;
Проведем анализ вышеуказанного кода.
1. В случае возникновения исключения на Q2.ExecQuery, вызовется TW.Rollback, а после - ничего (так как TW.InTransaction = False).
2. В случае отсутствия исключений будет выполнен код TW.Commit.

Теперь перенесем Commit в конец try.
1. В случае возникновения исключения на Q2.ExecQuery, вызовется TW.Rollback, а после - ничего (нет кода).
2. В случае отсутствия исключений будет выполнен код TW.Commit.
Как видим, логика одна и та же, но второй вариант нагляднее и короче (нет проверки активности транзакции). Таким образом, предлагаю правильным вариантом принять:

Код: Выделить всё

TW.StartTransaction;
try
  Q1.ExecQuery;
  Q2.ExecQuery;
  ...
  TW.Commit;
except
  TW.Rollback;
end;

Кузнецов Евгений
Сообщения: 144
Зарегистрирован: 16 фев 2006, 22:36

Сообщение Кузнецов Евгений » 09 мар 2007, 18:25

kdv писал(а):прошу ревизию
www.ibase.ru/devinfo/ibx.htm#try
Спасибо, все ясно, единственное замечание - на код

Код: Выделить всё

TW.StartTransaction;
  try 
    try 
      Q1.ExecQuery;
    except 
      TW.Rollback;
      Result:=False; // говорит о том что произошла ошибка 
    end;
  finally 
    if TW.InTransaction then
      begin
        TW.Commit; 
        Result:=True     // ошибок нет
      end;
 end;
Delphi будет ругаться: Return value of function might be undefined,
так что Result:=False лучше перенести в самое начало.

Остается проблема потери коннекта - как я понимаю, IBX в этом
случае впадает в ступор и упомянутый код вывалится с исключением.
Правда, есть пара патчей у Вас на сайте и версия 6.084 от О. Пащенко - надо будет попробовать.

EvilsInterrupt
Сообщения: 66
Зарегистрирован: 29 авг 2006, 10:00

Сообщение EvilsInterrupt » 09 мар 2007, 19:00

так что Result:=False
зы:
По окончании реализации смотрят чего больше возвращает ф-ция, и следовательно именно это "больше" присваивают в самом начале! :)

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 09 мар 2007, 19:05

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

EvilsInterrupt
Сообщения: 66
Зарегистрирован: 29 авг 2006, 10:00

Сообщение EvilsInterrupt » 09 мар 2007, 19:15

kdv
В плане отладки твой метод помогает! Но! Когда надо написать ф-цию которая будет вызываться очень часть и которую надо оптимизировать по эффективности выполнения кода. Излишние присваивания никчему!

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 09 мар 2007, 19:23

надо оптимизировать по эффективности выполнения кода. Излишние присваивания никчему!
бог ты мой. result:=False в начале, и :=True когда все выполнилось. Где тут лишние присваивания?

Кстати, при тщательном разборе кода оказалось, что код, который привел Каратаев в этом топике:
http://forum.ibase.ru/phpBB2/viewtopic.php?t=1444
избыточен.

Код: Выделить всё

Start
try
 try
  Exec
 except
  rollback
 end
finally
 commit
end
? Зачем тут два блока try, если правильно группируются Exec и Commit, причем работать это будет в любой ситуации.

hvlad
Разработчик Firebird
Сообщения: 1244
Зарегистрирован: 21 мар 2005, 10:48

Сообщение hvlad » 09 мар 2007, 19:46

EvilsInterrupt писал(а):Когда надо написать ф-цию которая будет вызываться очень часть и которую надо оптимизировать по эффективности выполнения кода. Излишние присваивания никчему!
Это работа компилятора.

Кузнецов Евгений
Сообщения: 144
Зарегистрирован: 16 фев 2006, 22:36

Сообщение Кузнецов Евгений » 09 мар 2007, 19:54

kdv писал(а):Кстати, при тщательном разборе кода оказалось, что код, который привел Каратаев в этом топике:
http://forum.ibase.ru/phpBB2/viewtopic.php?t=1444
избыточен.
Это код автора топика, просто там цитирование не поставлено.

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 09 мар 2007, 20:16

да, действительно...

Ответить