Как правльно сделать Free транзакции на "мертвом" соединении?

Вопросы программирования и использования среды Lazarus.

Модератор: Модераторы

Ответить
GreyCrazyWolf
новенький
Сообщения: 14
Зарегистрирован: 02.03.2023 14:23:57

Как правльно сделать Free транзакции на "мертвом" соединении?

Сообщение GreyCrazyWolf »

Добрейшего времени суток.
Возник следующий вопрос. Написал приложение использующее самописный пул запросов к БД PostgreSQL. Все в общем работает, есть одно небольшое но: если соединение "падает", например закрыто сервером по таймауту, и в этот момент была открыта транзакция при попытке "Destroy" начинается ругань на " Operation cannot be performed on an active transaction".
Попробовал сделать так

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

/// деструктор
destructor TPgConnection.Destroy;
begin
  // "прибиваем" транзакцию
  if Assigned(fSQLTransaction) then
     begin
       try
           if fSQLTransaction.Active then
              fSQLTransaction.Rollback;
       except
         on E: Exception do
            begin
                 LogToFile('CONN_DESTRUCTOR', Format('Ignoring exception during transaction rollback on destroy: %s', [E.Message]), Warning);
            end;
       end;
       fSQLTransaction.DataBase := nil;
     end;
  // "прибиваем"  соединение
  if Assigned(fPQConnection) then
     begin
       try
           if fPQConnection.Connected then
              fPQConnection.Connected := False;
       except
           on E: Exception do
            begin
                 LogToFile('CONN_DESTRUCTOR', Format('Ignoring exception during connection on destroy: %s', [E.Message]), Warning);
            end;
       end;
     end;

   try
     LogToFile('CONN_DESTRUCTOR', Format('Destroing connection %s', [GUIDToString(Guid)]), Info);
     fSQLTransaction.Free;
     fPQConnection.Free;
     LogToFile('CONN_DESTRUCTOR', Format('Destroy connection %s', [GUIDToString(Guid)]), Info);
   except
     on E: Exception do
        begin
           LogToFile('CONN_DESTRUCTOR', Format('Exception during free component on destroy connection %s: %s', [GUIDToString(Guid), E.Message]), Warning);
        end;
   end;
   inherited Destroy;
end;                   
все равно в лог падает 'Ignoring exception during transaction rollback on destroy"
Оно как бы и нечего, но как-то неаккуратненько :roll:
Аватара пользователя
WAYFARER
энтузиаст
Сообщения: 565
Зарегистрирован: 09.10.2009 00:00:04
Откуда: г. Курган

Сообщение WAYFARER »

Проверяйте не только активна ли транзакция, но и живо ли подключение.

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

if Assigned(fSQLTransaction) then
  begin
    // Rollback имеет смысл только если соединение живое
    if Assigned(fPQConnection) and fPQConnection.Connected and fSQLTransaction.Active then
    begin
      try
        fSQLTransaction.Rollback;
      except
         ...
      end;
    end;
    fSQLTransaction.DataBase := nil;
    FreeAndNil(fSQLTransaction);
  end;
GreyCrazyWolf
новенький
Сообщения: 14
Зарегистрирован: 02.03.2023 14:23:57

Сообщение GreyCrazyWolf »

WAYFARER писал(а): 15.01.2026 19:15:53 Проверяйте не только активна ли транзакция, но и живо ли подключение.
Спасибо, попробовал, ну оно все - Operation cannot be performed on an active transaction.
Собсвенно, в лог падает такое.

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

20:15:04;Connection {73B663AD-61D2-4C5D-9B83-B46AA590406B} ping failed with exception: TPQConnection : Preparation of query failed.  (PostgreSQL: сервер неожиданно закрыл соединение
	Скорее всего сервер прекратил работу из-за сбоя
	до или в процессе выполнения запроса.
);crazywolf;Error
20:15:04;Found and destroying a stale connection during validation.;crazywolf;Warning
20:15:16;Ignoring exception during transaction rollback on destroy: TPQConnection : нет соединения с сервером
  (PostgreSQL: );crazywolf;Warning
20:15:17;Failed to DESTROY a ManagedConnection. Error: Operation cannot be performed on an active transaction;crazywolf;Error
Я так пнимаю, ситуация следующая: серевр закрыл соединение по таймауту, но стандартная компонента Lazarusa думает что

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

 fPQConnection.Connected = True (хотя на самом деле нет) 
и честно пытается сделать RollBack.
В принципе оно и не страшно (данные все равно уже куку, а сервер, по идее, уже сам rollback сделал). Интересно чисто с точки зрения сделать правильно и красиво :roll:

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

destructor TPgConnection.Destroy;
begin
  // "прибиваем" транзакцию
  if Assigned(fSQLTransaction) then
     begin
       // Rollback имеет смысл только если соединение живое
       if Assigned(fPQConnection) and fPQConnection.Connected and fSQLTransaction.Active then
       begin
         try
           fSQLTransaction.Rollback;
         except
           on E: Exception do
              begin
                 LogToFile('DESTRUCTOR_WARN', Format('Ignoring exception during transaction rollback on destroy: %s', [E.Message]), Warning);
              end;
         end;
       end;
       fSQLTransaction.DataBase := nil;
     end;
  // "прибиваем"  соединение
  if Assigned(fPQConnection) then
     begin
       try
           if fPQConnection.Connected then
              fPQConnection.Connected := False;
       except
           on E: Exception do
            begin
                 LogToFile('DESTRUCTOR_WARN', Format('Ignoring exception during connection on destroy: %s', [E.Message]), Warning);
            end;
       end;
     end;

   try
     LogToFile('DESTRUCTOR_INFO', Format('Destroing connection %s', [GUIDToString(Guid)]), Info);
     fSQLTransaction.Free;
     fPQConnection.Free;
     LogToFile('DESTRUCTOR_INFO', Format('Destroy connection %s', [GUIDToString(Guid)]), Info);
   except
     on E: Exception do
        begin
           LogToFile('DESTRUCTOR_WARN', Format('Exception during free component on destroy connection %s: %s', [GUIDToString(Guid), E.Message]), Warning);
        end;
   end;
   inherited Destroy;
end; 
Аватара пользователя
Снег Север
долгожитель
Сообщения: 3069
Зарегистрирован: 27.11.2007 15:14:47
Контактная информация:

Сообщение Снег Север »

Не знаю конкретно про PostgreSQL, но общее для всех серверных баз данных, что поручать следить за транзакциями клиенту - грубейшая ошибка. Всё транзакционное должно делаться только на сервере, а клиент присоединяется для справки: прошло/ не прошло и почему, если не прошло и для получения новой выборки. Поэтому большинство грамотно спроектированных клиентских приложений не пытаются долго удерживать соединения, а предпоситают их переустанавливать при каждом обращении к серверу.
sts
энтузиаст
Сообщения: 529
Зарегистрирован: 04.04.2008 12:15:44
Откуда: Тольятти

Сообщение sts »

Снег Север писал(а): 06.02.2026 12:22:53 Поэтому большинство грамотно спроектированных клиентских приложений не пытаются долго удерживать соединения, а предпоситают их переустанавливать при каждом обращении к серверу.
это от бедности, т.е. вынужденное решение, еслиб это было предпочтительным способом то в бд не было бы таких вещей как контекст и права доступа, слишком много времени на это уходит
Аватара пользователя
Снег Север
долгожитель
Сообщения: 3069
Зарегистрирован: 27.11.2007 15:14:47
Контактная информация:

Сообщение Снег Север »

Права доступа тут как-то "перпендикулярны", а от бедности как раз "много времени уходит". Любой удалённый коннект, дело ненадёжное по определению. Поэтому всё критичное по времени и по обрывам надо делать встроенными процедурами на серваке, у него на то голова большая и ноги быстрые. А клиент даёт короткие команды и получает выжимки результатов. Всё иное - ересь от времен нищебродов с локально развернотыми серверами.
sts
энтузиаст
Сообщения: 529
Зарегистрирован: 04.04.2008 12:15:44
Откуда: Тольятти

Сообщение sts »

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