Страница 1 из 2

Синхронизация DataSet/DBGrid на клиентах по сети

СообщениеДобавлено: 09.07.2020 22:27:58
wwswowsogon
Всем доброго времени суток!

Внезапно столкнулся с проблемой: при внесении/удалении данных в/из БД, внесенные изменения не отображаются на других сетевых клиентах, вплоть до перезагрузки программы на клиентской машине. Переоткрытие DataSet методами Refresh или Open/Close не помогает, увы.

Подскажите, плиз, в какую сторону копать.

Используется клиент-серверный Firebird 2.5, если что.

Re: Синхронизация DataSet/DBGrid на клиентах по сети

СообщениеДобавлено: 09.07.2020 22:44:38
Снег Север
Делать реконнект на клиенте? А вообще похоже на неверную настройку параметров транзакций.

ЗЫ. Я совершенно не знаток Firebird, но вот что нашел на хабре:
Для обычной работы (показ данных в гриде и т.п.) обычно используются режим изолированности READ COMMITED (Options.Isolation = xiReadCommited), т.к. он позволяет транзакции видеть чужие, committed изменения базы данных просто путём повторного выполнения запросов (перечитывания данных). Поскольку эта транзакция используется только для чтения, установим свойство Options.ReadOnly в значение True. Таким образом, наша транзакция будет иметь параметры read read_commited rec_version. Транзакция с такими параметрами в Firebird может быть открытой сколь угодно долгое время (дни, недели, месяцы), без блокирования других транзакций или влияния на накопление мусора в базе данных (потому что на самом деле, на сервере такая транзакция стартует как committed).
https://habr.com/ru/post/273549/

Re: Синхронизация DataSet/DBGrid на клиентах по сети

СообщениеДобавлено: 10.07.2020 01:40:33
zoltanleo
+1 к уровню изоляции. Еще есть механизм эвентов/событий, который позволяет делать рассылку уведомлений всем заинтересованным слушателям сети.

Вообще, про уровни изоляции и с чем их едят лучше читать здесь.

Re: Синхронизация DataSet/DBGrid на клиентах по сети

СообщениеДобавлено: 10.07.2020 10:31:42
haword
если уровень изоляции snapshot то так и будет до переконекта с обоих сторон. надо ставить readcommited и начинать усиленно использовать транзакции везде где нужно.

Re: Синхронизация DataSet/DBGrid на клиентах по сети

СообщениеДобавлено: 10.07.2020 22:33:55
olegy123
1) клиенты должны знать когда наступили изменения.
Они могут получить сообщение о том что данные изменились путем посылки POST_EVENT

Как правило POST_EVENT может сидеть в Тригерах на INSERT/UPDATE/DELETE или в PROCEDURE Interbase/Firebird

2) Клиент должен быть подписан на событие, тогда к нему прийдет данное сообщение.

3) Клиент получив событие волен обновить таблицу или данные.

Любое действие с БД обязано идти через Транзакцию, компоненты типа Zeos/Ado и другие - не требуют у клиента точного подхода, сами решают когда им открыть или закрыть транзакцию.
В IBX компонентах вы даже SELECT не сделаете без точного указания в каком режиме будет работать Транзакция.

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

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

Поэтому если вы пишите многосессионную систему, то пожалуйста изучите эту главу , и нужно очно знать как работают компоненты на стороне клиента.

Re: Синхронизация DataSet/DBGrid на клиентах по сети

СообщениеДобавлено: 10.07.2020 23:22:34
wwswowsogon
Спасибо всем за развёрнутые ответы!

Действительно, конструкция типа

Код: Выделить всё
SQLTransaction1.Active := false;
SQLTransaction1.Active := true;


частично спасает положение. Но, подозреваю, что этот приём может привести к проблемам, особенно если открыто несколько DataSet'ов.

про режимы изоляции и события ознакомился, по диагонали пока, спасибо. Я правильно понимаю, что эти механизмы реализуются больше через SQL-запросы, чем через методы и свойства объектов стандартных компонентов? :)

Re: Синхронизация DataSet/DBGrid на клиентах по сети

СообщениеДобавлено: 11.07.2020 01:22:42
zoltanleo
На самом деле компоненты доступа (кроме TIBTable, по-моему) используют все те же SQL-запросы, поэтому без них никуда.
Чтобы комфортно работать с данными и видеть все закоммиченные записи, достаточно задать параметры читающей транзакции (уровень изоляции readcommited):
read
nowait
record version

Для обновления содержимого датасета необязательно перезапускать транзакцию. Достаточно сделать Dataset.Refresh. Это делается либо через определенные интервалы времени программно, либо по кнопке пользователем, либо при получении post_event, о чем писалось выше.

Вообще, работа с транзакциями в компонентах IBX - это краеугольный камень работы с БД. Без их уверенного знания писать нормально работающие приложения вы не сможете. Поэтому настоятельно советую их разобрать подробно

Re: Синхронизация DataSet/DBGrid на клиентах по сети

СообщениеДобавлено: 11.07.2020 04:02:40
olegy123
wwswowsogon писал(а): SQLTransaction1.Active := false;
SQLTransaction1.Active := true;


SQLTransaction - можно автоматически настроить путем галок. Когда ему начинать работать и когда завершать, в каком виде Commit/Rollback. И работает до SQLTransaction.Active:=false или уничтожении самого компонента SQLTransaction, завершается согласно установленному состоянию.


SQLTransaction.Active:=true => это есть SQLTransaction.StartTransaction; - при AutoOpen
SQLTransaction.Active:=false => SQLTransaction.Commit или SQLTransaction.Rollback - при AutoClose

а можно в ручную, советую
SQLTransaction.StartTransaction;
SQLQuery.Open
SQLQuery.Post;
SQLQuery.Close;
SQLQuery.ExecSQL;
...
...
...
SQLQuery.Open
SQLQuery.Post;
SQLQuery.Close;
SQLQuery.ExecSQL;

SQLTransaction.Commit; - все изменения подтверждаем; данные становятся видимые для всех.
SQLTransaction.Rollback; - все изменения отменяем; данные которая изменила состояние базы данных в этой транзакции откатываются до состояния SQLTransaction.StartTransaction;

попробуйте разместить две SQLTransaction1; SQLTransaction2; и соответственно SQLQuery1, SQLQuery2 подключите SQLTransaction1; SQLTransaction2.

и попробуйте посмотреть что будет происходить с базой данных для каждой SQLQuery1, SQLQuery2

Добавлено спустя 42 минуты 7 секунд:
wwswowsogon писал(а): правильно понимаю, что эти механизмы реализуются больше через SQL-запросы, чем через методы и свойства объектов стандартных компонентов?

А стандартные компоненты ничего не меняют при работе с базой данных. Они ведут себя как на автопилоте, если заложено в них открыть и не беспокоить программиста глубиным изучением правил работы с базой данных, то компоненты сами открывают транзакцию при первом обращении, и закрывают только после уничтожение компонента, или даже закрытия подключения к базы данных(в SQLConnection.transaction как бы намекает на это поведение), удобно при монопольной работе с базой данных.
Или же любое обращение порождает новую транзакцию. Этот режим позволяет увидить сразу все внесенные изменения. Но с ним невозможно работать в рамках комплексных работ:

необходимо внести 1000000 изменений за раз, нужно открыть и зафиксировать транзакцией 1000000 раз, что для базы данных это требует дополнительной нагрузки. Например в 1Ске, без явного указания запуска транзакций изменений и коммита - скорость работы в сотни раз уменьшается при массовых изменений, то что вы сможете за 1 минуту сделать - будете делать 2 часа.

синхронное работу связанных таблиц - необходимо комплексное изменение в разных таблицах разом. Без четкого применения транзакции, другие клиенты смогут увидеть одни данные раньше чем другие обновятся.

ну как пример - клиент меняет данные документа (контрагента, вносит шапку документа, заполняет реквизиты, добавляет позиции товар..) и клиент решает что не станет вносить изменения, жмет кнопку отменить. Транзакция делает Rollback - это достаточно отменить все изменения в рамках этой транзакции. Либо надо откатывается путем Delete.. этим очень сложно провести базу данных в то состояние до начало вбивая отмененного документа.

Если клиент отвалился - то база данных сама закроет все вносимые им изменения(они не подтверждены им).

При тонкой работе с StartTransaction/Commit/Rollback можно сделать комфортной работу для клиента, самим не придумывать "костыли" как вернуть первичное состояние, разрулить одновременную работу между многими клиентами.
С автопилотом, которые встроили разработчики компонента - он подходить скорее для монопольного режима.

Re: Синхронизация DataSet/DBGrid на клиентах по сети

СообщениеДобавлено: 11.07.2020 07:12:27
Снег Север
wwswowsogon, транзакции делаются сервером, ваши открытые или закрытые датасеты ни при чём. Ваша задача - правильно указать режим транзакции, которая привязана к вашему датасету. А режим указывается в свойствах компонента транзакций.

Re: Синхронизация DataSet/DBGrid на клиентах по сети

СообщениеДобавлено: 19.07.2020 21:15:20
wwswowsogon
zoltanleo писал(а):На самом деле компоненты доступа (кроме TIBTable, по-моему) используют все те же SQL-запросы, поэтому без них никуда.


Да это понятно. ;)

zoltanleo писал(а):На самом деле компоненты доступа (кроме TIBTable, по-моему) используют все те же SQL-запросы, поэтому без них никуда.
Чтобы комфортно работать с данными и видеть все закоммиченные записи, достаточно задать параметры читающей транзакции (уровень изоляции readcommited):


А вот как это сделать средствами Lazarus, это вопрос. Там нет TDatabase, как в Delphi, видимо, в силу неактуальности BDE и иже с ним.

Снег Север писал(а):режим указывается в свойствах компонента транзакций.


Подозреваю, что в Lazarus это SQLTransaction.Action = (caNone, caCommit, caCommitRetaining, caRollback,
caRollbackRetaining);

Пробую менять эти значения, но пока особого эффекта не заметил. Скорее всего, я чего-то не понимаю.

Re: Синхронизация DataSet/DBGrid на клиентах по сети

СообщениеДобавлено: 19.07.2020 22:20:56
Снег Север
wwswowsogon
https://stackoverflow.com/questions/351 ... delphi-app

и используйте пакет IBX, у него больше возможностей для firebird

Re: Синхронизация DataSet/DBGrid на клиентах по сети

СообщениеДобавлено: 19.07.2020 23:45:29
zoltanleo
wwswowsogon писал(а):Подозреваю, что в Lazarus это SQLTransaction.Action = (caNone, caCommit, caCommitRetaining, caRollback,
caRollbackRetaining);

Пробую менять эти значения, но пока особого эффекта не заметил. Скорее всего, я чего-то не понимаю

"Чукча не читатель, чукча - писатель" ©

Код: Выделить всё
with SQLTransaction1 do
begin
  ...
  Params.Add('read');
  Params.Add('read_committed');
  Params.Add('rec_version');
  ...
end;


Настоятельно советую почитать выше приведенную ссылку про транзакции на ibase.ru или для FIBplus (хотя компоненты уже не поддерживаются, но написано очень понятно)

Не пользуйте встроенные компоненты Лазаря для работы с Firebird/IB - они сырые (я, например, так и не смог добиться загрузки библиотеки клиента, когда сервер был запущен в виде приложения на нестандартном порте).

Пользуйте хотя бы это IBX4Laz (если после его установки при загрузке среды компонент будет кричать, что не находит fbclient.dll/fbclient.so в системной папке и потому показываться не будет, подложите в корень установленного Лазаруса этот самый fbclient.dll/fbclient.so - для x32 Лазаря - от 32-битного FB, для x64 Лазаря - соответственно от 64-битного FB)

Re: Синхронизация DataSet/DBGrid на клиентах по сети

СообщениеДобавлено: 20.07.2020 17:07:09
wwswowsogon
Снег Север писал(а):wwswowsogonи используйте пакет IBX, у него больше возможностей для firebird


Спасибо, IBX скачал, мануал скачал, да, на вид там много полезного, буду изучать.
Увы, на начало работы над проектом знал только о существовании ZeOS, но решил делать без него.

zoltanleo писал(а):
Код: Выделить всё
with SQLTransaction1 do
begin
  ...
  Params.Add('read');
  Params.Add('read_committed');
  Params.Add('rec_version');
  ...
end;



Вах, вот оно как делается. А что такое rec_version?

zoltanleo писал(а):Не пользуйте встроенные компоненты Лазаря для работы с Firebird/IB - они сырые (я, например, так и не смог добиться загрузки библиотеки клиента, когда сервер был запущен в виде приложения на нестандартном порте).


Спасибо за подсказку.

zoltanleo писал(а):подложите в корень установленного Лазаруса этот самый fbclient.dll/fbclient.so - для x32 Лазаря - от 32-битного FB, для x64 Лазаря - соответственно от 64-битного FB)


Уже лежит, с самого начала. :)

Re: Синхронизация DataSet/DBGrid на клиентах по сети

СообщениеДобавлено: 20.07.2020 17:16:35
zoltanleo
wwswowsogon писал(а):Вах, вот оно как делается. А что такое rec_version?

Повторю в третий раз: читайте литературу, статья на 30 мин вдумчивого прочтения. Нет смысла пересказывать статью. Ссылки в предыдущих моих постах

Re: Синхронизация DataSet/DBGrid на клиентах по сети

СообщениеДобавлено: 21.07.2020 07:11:59
Снег Север
wwswowsogon писал(а):А что такое rec_version?

Это RECORD_VERSION, один из параметров транзакции, подробности по нему читайте в документации FB.
http://www.ibase.ru/transactions/