Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Delphi Новый топик    Ответить
Топик располагается на нескольких страницах: Ctrl  назад   1 .. 35 36 37 38 39 40 41 42 43 [44]
 Re: FireDAC  [new]
PalychXX
Member

Откуда:
Сообщений: 142
Здравствуйте!
Delphi 10.2, штатный FireDac, БД SQLite
Пишу Windows-службу, которая реализует TCP/IP-сервер, принимающий от клиентов данные, которые затем парсит, сохраняет в локальной БД, а отдельный поток периодически отправляет данные из этой БД POST-запросом PHP-скрипту для просмотра через web.
Может быть, надо было выбрать вместо SQLite какой-нить FB или MS SQL, но в обычных приложениях без многопоточной работы с БД проблем со связкой FireDac+SQLite не наблюдал, решил и в службе попробовать ее, разрулив доступ к БД из нескольких потоков критическими секциями (одновременных клиентов не так много и высокая скорость доступа не сильно критична).
Столкнулся с несколькими проблемами.
Перед подключением к БД (событие BeforeConnect) я пытаюсь сделать валидацию БД примерно так:
procedure Tdm.ConnectionBeforeConnect(Sender: TObject);
const
  dbname = 'name.db';
var
  s: string;
  f: Boolean;
begin
  try
    s := TPath.Combine(ExtractFilePath(ParamStr(0)), dbname);
    Connection.ConnectionString:='DriverID=SQLite;Database=' + s;
    // валидация БД
    SQLiteValidate.Database := s;
    if FileExists(s) then
    begin
      SQLog('Валидация БД...');
      cs.Enter;
      try
        f := SQLiteValidate.CheckOnly;
      finally
        cs.Leave;
      end;
      if (not f) then
      begin
        SQLog('Внимание! БД повреждена');
      end
      else
        SQLog('БД в порядке');
    end;}
  except
    on e: Exception do
    begin
      SQLog(e.Message, True);
    end;
  end;
end;

А после подключения к БД - проверяю существование таблиц и статичных данных справочников в некоторых из них, затем вызываю VACUUM:
procedure Tdm.ConnectionAfterConnect(Sender: TObject);
var
  s: string;
  f: Boolean;
begin
  try
    // создаем таблицы если не существуют
    sql :=
      'PRAGMA foreign_keys = off;' +
      'BEGIN TRANSACTION;' +
      // создаем таблицы
      'CREATE TABLE If Not Exists table1 (' +
      '    idx  INTEGER PRIMARY KEY ASC' +
      '                 UNIQUE' +
      '                 NOT NULL,' +
      '    name TEXT    NOT NULL' +
      ');' +
      ...
      // подтверждаем транзакцию
      'COMMIT TRANSACTION;' +
      'PRAGMA foreign_keys = on;'
      ;
    try
      cs.Enter;
      try
        Connection.ExecSQL(sql);
      finally
        cs.Leave;
      end;
    except
      on e: Exception do
      begin
        SQLog(e.Message, True);
      end;
    end;
    // заполняем таблицы значениями по умолчанию (если отсутствуют)
    FillDefaultValues;
    ClearTable(); // удаление старых записей
    Vacuum; // сжатие
  except
    on e: Exception do
    begin
      SQLog(e.Message, True);
      // raise Exception.Create(e.Message);
    end;
  end;
end;

procedure Tdm.Vacuum;
begin
  try
    ChkConnect;
    SQLog('Vacuum...');
    cs.Enter;
    try
      Connection.ExecSQL('VACUUM;');
    finally
      cs.Leave;
    end;
    SQLog(sOK);
  except
    on e: Exception do
    begin
      SQLog(e.Message, True);
    end;
  end;
end;

И в onDestroy удаляю записи старше определенного времени, после чего делаю Backup:
procedure Tdm.DataModuleDestroy(Sender: TObject);
var
  dt: TDateTime;
  dir: string;
begin
  try
    ClearTable;
    dt := Now;
    dir := TPath.Combine(ExtractFilePath(ParamStr(0)), 'backup');
    DbBackup(TPath.Combine(dir, Format('db_%.4u_%.2u_%.2u.bak', [YearOf(dt), MonthOf(dt), DayOf(dt)])));
  except
    on e: Exception do
    begin
      SQLog(e.Message, True);
    end;
  end;
  if Assigned(cs) then FreeAndNil(cs);
end;

procedure Tdm.DbBackup(const FileName: string);
var
  s: string;
begin
  try
    SQLog(Format('DbBackup (FileName = "%s")...', [FileName]));
    s := ExtractFilePath(FileName);
    if not DirectoryExists(s) then
    begin
      SQLog(Format('Создание папки: "%s"...', [s]));
      ForceDirectories(s);
      SQLog(sOK);
    end;
    SQLog('Создание резервной копии БД...');
    SQLiteBackup.Database := Connection.Params.Database;
    SQLiteBackup.DestDatabase := FileName;
    cs.Enter;
    try
      SQLiteBackup.Backup;
    finally
      cs.Leave;
    end;
    SQLog(sOK);
  except
    on e: Exception do
    begin
      SQLog(e.Message, True);
    end;
  end;
end;

Запускаю, работает, наполняю данными. Через несколько дней замечаю, что после ребута ПК служба не стартовала, в логах последняя запись "Валидация БД..." и все, похоже, что на валидации случился вылет. Комментирую код валидации, делаю ребут, теперь в логах все останавливается на "Vacuum...". Комментирую и вызов этого метода, служба успешно стартует после перезагрузок. Возвращаю оба метода, удаляю БД, чтобы служба создала ее самостоятельно, опять пару-тройку дней все работает и после очередного ребута те же грабли. ЧЯДНТ? Не так делаю валидацию, не так вызываю VACUUM (кстати, попробовал в Connection.Params.SQLiteAdvanced прописать auto_vacuum=1, но не заметил, чтобы файл БД после удаления записей уменьшился в размерах, а вот вызов аналогичного пункта меню в SQLiteStudio сразу сжал его раза в 3), не при том событии делаю бэкап, как-то повреждаю БД?
Еще при тесте интенсивной одновременной работы нескольких клиентов словил ситуацию, что полученные от них данные в течение одной минуты от всех отсутствуют, хотя после записи в БД ответ всеми клиентами был получен. Все остальное время все писалось в БД хорошо, поочередно обрабатывались по 1 пакету от каждого клиента. При записи данных из каждого потока дергаю процедуру дата-модуля, динамически создающую TFDQuery и формирующую запрос, ну и вдобавок qr.ExecSQL снова обернут критической секцией. Переключил LockingMode и Synchronous на Normal, попробую еще потестировать. Или в службе с многопоточным доступом к БД не стоит использовать SQLite и лучше сразу мигрировать на FB? Вообщем, буду признателен за критику и советы. :)
14 мар 19, 12:45    [21832387]     Ответить | Цитировать Сообщить модератору
 Re: FireDAC  [new]
PalychXX
Member

Откуда:
Сообщений: 142
З.Ы. Да, еще на случай багов FireDac попробовал скомпилить проект в 10.1, а также дома на самой крайней 10.3.1 CE, во всех случаях ситуация ровно такая же.
14 мар 19, 12:55    [21832399]     Ответить | Цитировать Сообщить модератору
 Re: FireDAC  [new]
wadman
Member

Откуда: Санкт-Петербург
Сообщений: 25056
PalychXX
Вообщем, буду признателен за критику и советы. :)

Ребуты жесткие?
PalychXX
также дома на самой крайней

Я-бы сказал, что такое "крайняя"...
14 мар 19, 12:57    [21832403]     Ответить | Цитировать Сообщить модератору
 Re: FireDAC  [new]
Игорь_UUS
Member

Откуда: г. Екатеринбург
Сообщений: 558
Michael Longneck
Если кто хочет таки использовать TVP в MSSQL с FireDac - пусть исправит в файле FireDac.Phys.ODBCWrapper в процедуре procedure TODBCVariable.UpdateFlags;

такой код

  else if SQLDataType = SQL_SS_TABLE then begin
    FScale := 0;
    FColumnSize := MaxInt; //было 1.
    FDataSize := 0;
  end


И всё заработает как надо



Мучительно больно использовать TVP в mssql. Багов много... все тянуться с XE7!

1.Нельзя использовать более одной переменной табличного типа в MSSQL. Вторая заполненная данными табличная переменная попросту приходит пустой в хранимку mssql.
2.Если в хранимой процедуре используется тип NVARCHAR(MAX), и за ней идёт табличная переменная, то последняя запись внесённая в табличную переменную не доходит до MSSQL. Т.е. чтоб работало корректно, нужно чтоб все переменные хранимки табличного типа шли первыми а за ними уже все переменные имеющие тип NVARCHAR(MAX).
3.С драйвером "неатив11" табличные переменные и вовсе не работают!!! (ПРОСТО ЖЕСТЬ!!!).

Чтоб обойти все эти баги приходиться придумывать всякие хитрости... табличные переменные сильно не доведены до ума.
14 мар 19, 13:27    [21832456]     Ответить | Цитировать Сообщить модератору
 Re: FireDAC  [new]
Игорь_UUS
Member

Откуда: г. Екатеринбург
Сообщений: 558
Michael Longneck
Dmitry Arefiev
Что значит "победил" ?


Это значит нашёл второй баг, из-за которого второй и далее параметры TVP не заполнялись данными. Первый баг- тотЮ что я приводил ранее.


unit FireDAC.Phys.ODBCWrapper;

Первый
   else if SQLDataType = SQL_SS_TABLE then begin
    FScale := 0;
    FColumnSize := MaxInt; //было 1
    FDataSize := 0;
  end


Второй
function TODBCCommandStatement.PutLongParams: SQLReturn;
var
  pParamNum: SQLPointer;
  oPar: TODBCParameter;
  iCurNum: SQLSmallint;
  lFirst: Boolean;
  iIndex: Integer;
begin
  iCurNum := -1;
  oPar := nil;
  FFocusedParam := nil;
  iIndex := 0;
  lFirst := True;
  repeat
    pParamNum := nil;
    Result := Lib.SQLParamData(FHandle, pParamNum);
    if Result = SQL_NEED_DATA then begin
      // MSSQL: subsequent SQLParamData calls for TVP return pParamNum=nil
      if pParamNum <> nil then
      begin
        oPar := ParamList.FindByToken(pParamNum) as TODBCParameter;
        lFirst := True; //новый параметр, заначит надо DataReader сделать Reset  
      end;



Когда я озвучивал Dmitry Arefiev данные баги года три назад, услышал в ответ, что это недоработка со стороны майкрасофт...

теперь... заставить бы работать TVP ещё для последнего драйвера "неатив 11-го"
14 мар 19, 13:32    [21832471]     Ответить | Цитировать Сообщить модератору
 Re: FireDAC  [new]
PalychXX
Member

Откуда:
Сообщений: 142
wadman
Ребуты жесткие?

Раз в сутки в планировщике прописано задание "shutdown -r -f". Ну и обычная перезагрузка при тестах.
wadman
Я-бы сказал, что такое "крайняя"...

10.3.1 (Version 26.0.33219.4899).
14 мар 19, 13:34    [21832475]     Ответить | Цитировать Сообщить модератору
 Re: FireDAC  [new]
Michael Longneck
Member

Откуда: Москва
Сообщений: 2277
Игорь_UUS
Мучительно больно использовать TVP в mssql. Багов много... все тянуться с XE7!

1.Нельзя использовать более одной переменной табличного типа в MSSQL. Вторая заполненная данными табличная переменная попросту приходит пустой в хранимку mssql.
2.Если в хранимой процедуре используется тип NVARCHAR(MAX), и за ней идёт табличная переменная, то последняя запись внесённая в табличную переменную не доходит до MSSQL. Т.е. чтоб работало корректно, нужно чтоб все переменные хранимки табличного типа шли первыми а за ними уже все переменные имеющие тип NVARCHAR(MAX).
3.С драйвером "неатив11" табличные переменные и вовсе не работают!!! (ПРОСТО ЖЕСТЬ!!!).

Чтоб обойти все эти баги приходиться придумывать всякие хитрости... табличные переменные сильно не доведены до ума.


На текущий момент я практически всё довёл до ума, и никаких проблем не ощущаю, 2 пункт не проверял. Исправлений пришлось сделать побольше чем я раньше описал. Native Client 11 сильно не новый драйвер.
14 мар 19, 14:32    [21832627]     Ответить | Цитировать Сообщить модератору
 Re: FireDAC  [new]
Игорь_UUS
Member

Откуда: г. Екатеринбург
Сообщений: 558
Michael Longneck
Игорь_UUS
Мучительно больно использовать TVP в mssql. Багов много... все тянуться с XE7!

1.Нельзя использовать более одной переменной табличного типа в MSSQL. Вторая заполненная данными табличная переменная попросту приходит пустой в хранимку mssql.
2.Если в хранимой процедуре используется тип NVARCHAR(MAX), и за ней идёт табличная переменная, то последняя запись внесённая в табличную переменную не доходит до MSSQL. Т.е. чтоб работало корректно, нужно чтоб все переменные хранимки табличного типа шли первыми а за ними уже все переменные имеющие тип NVARCHAR(MAX).
3.С драйвером "неатив11" табличные переменные и вовсе не работают!!! (ПРОСТО ЖЕСТЬ!!!).

Чтоб обойти все эти баги приходиться придумывать всякие хитрости... табличные переменные сильно не доведены до ума.


На текущий момент я практически всё довёл до ума, и никаких проблем не ощущаю, 2 пункт не проверял. Исправлений пришлось сделать побольше чем я раньше описал. Native Client 11 сильно не новый драйвер.


Что касается Native Client 11, новый/не новый... он идёт в комплекте установки mssql2016-2017... других просто не пробовал, и тут другой вопрос, идёт ли поддержка FD более нового чем 11-й драйвер?!
14 мар 19, 15:07    [21832682]     Ответить | Цитировать Сообщить модератору
 Re: FireDAC  [new]
Michael Longneck
Member

Откуда: Москва
Сообщений: 2277
Не знаю. "Поддержка" это просто константы в файле. Дописал и у меня на XE7 поддерживается до ODBC Driver 17 включительно.
14 мар 19, 15:15    [21832696]     Ответить | Цитировать Сообщить модератору
 Re: FireDAC  [new]
PalychXX
Member

Откуда:
Сообщений: 142
Хочу для сравнения попробовать то же самое с FB, пока время позволяет. Правильно я понимаю, что если установить сервер FB, то FireDac из Delphi редакции Pro без купленного Client/Server пака не даст подключиться даже к серверу на 127.0.0.1 и в этом случае мне нужно работать через IBX или другие альтернативные компоненты? А то в табличке с редакциями Delphi фраза "Создание приложений для баз данных с локальным и встроенным подключением" вызывает вопрос, чем локальное подключение отличается от встроенного, является ли оно подключением к серверу на локалхосте или это что-то другое.
15 мар 19, 09:43    [21833311]     Ответить | Цитировать Сообщить модератору
 Re: FireDAC  [new]
Vlad F
Member

Откуда:
Сообщений: 692
PalychXX,

Говорят, что всеже даст, просто это будет нарушением соглашения.
Но если это не смущает, то флаг те в руки проверить/доложить.
15 мар 19, 10:18    [21833362]     Ответить | Цитировать Сообщить модератору
 Re: FireDAC  [new]
PalychXX
Member

Откуда:
Сообщений: 142
Vlad F
Говорят, что всеже даст, просто это будет нарушением соглашения.
Но если это не смущает, то флаг те в руки проверить/доложить.

А в случае с IBX не будет нарушением?
Про ADO в свое время сотрудник Эмбаркадеры отвечал, что можно по сети.
З.Ы. Наконец, словил на тестах в логах ошибку:
DBLOG
[FireDAC][Phys][SQLite] ERROR: database disk image is malformed

Мда, с SQLite в данном случае, видимо, лучше мигрировать на клиент-серверную БД...
15 мар 19, 10:24    [21833376]     Ответить | Цитировать Сообщить модератору
 Re: FireDAC  [new]
Vlad F
Member

Откуда:
Сообщений: 692
PalychXX,

А в случае с IBX, говорят, что не будет.)) Да, и реализовывай в своих экспериментах сразу по отдельному
подключению на каждый поток, что бы не дискредитировать попусту используемые библиотеки.
15 мар 19, 10:37    [21833386]     Ответить | Цитировать Сообщить модератору
 Re: FireDAC  [new]
PalychXX
Member

Откуда:
Сообщений: 142
Vlad F
А в случае с IBX, говорят, что не будет.)) Да, и реализовывай в своих экспериментах сразу по отдельному
подключению на каждый поток, что бы не дискредитировать попусту используемые библиотеки.

Чудесно, тогда попробую FB + IBX, чтобы наверняка. Про отдельные подключения весьма дельный совет, спасибо! Так и сделаю.
15 мар 19, 16:30    [21834048]     Ответить | Цитировать Сообщить модератору
 Re: FireDAC  [new]
Arioch
Member

Откуда:
Сообщений: 10598
PalychXX
тогда попробую FB + IBX


http://www.loginovprojects.ru/index.php?page=ibxfbutils
15 мар 19, 18:23    [21834195]     Ответить | Цитировать Сообщить модератору
 Re: FireDAC  [new]
Vlad F
Member

Откуда:
Сообщений: 692
PalychXX
Чудесно, тогда попробую FB + IBX, чтобы наверняка. Про отдельные подключения весьма дельный совет, спасибо! Так и сделаю.

Мы ждем от тебя заключения.))
15 мар 19, 22:11    [21834367]     Ответить | Цитировать Сообщить модератору
Топик располагается на нескольких страницах: Ctrl  назад   1 .. 35 36 37 38 39 40 41 42 43 [44]
Все форумы / Delphi Ответить