Странное поведение сервера FB

Запросы, планы, оптимизация запросов, ...

Модераторы: kdv, CyberMax

DmitryBelkevich
Сообщения: 30
Зарегистрирован: 03 апр 2009, 21:10

Странное поведение сервера FB

Сообщение DmitryBelkevich » 09 апр 2009, 01:40

Исходные данные. Сервер FB 2.1.1 (был 2.0.3, ситуация аналогичная), SS. Операционка - Win 2003 server. Железо - двухядерный Xeon, ОЗУ 2 Гб.

В программе (Delphi) есть два треда (потомки TThread).

Первый тред создаётся при старте программы, использует отдельный коннект через tcp (localhost, база локальная) и отдельную транзиакцию (read_committed rec_version nowait). В нормальном состоянии спит (sleep в цикле). Периодически просыпается (занимается 'распихиванием' данных о файлах, которые принимаются программой, в базу), по мере появляния файлов в очереди, открывает транзакцию, делает несколько селектов, делит и инсерт, в цикле, пока файлы не окончатся. И уходит дальше в спячку, до появления новых данных для обработки. Периодически (раз на 200 итераций) делает коммит. Либо этот же коммит происходит по тайм-ауту (при срабатывании таймера - 30 секунд).

Второй тред. Создаётся, когда юзер нажмёт соответсвующий GUI контрол. Использует два отдельных коннекта и две транзакции (разделил чтение/запись). Поток, если бы ему не мешал первый, спать не должен. Он открывает пишущую транзакцию (read_committed rec_version nowait), делает 2 апдейта и селект, закрывает транзакцию, уходит дальше в цикл, пока не 'пробежит' все из нужных (2-3 миллиона) записей. Их список открыт (ibquery) в отельной (читающей, read_committed rec_version) транзакции.

Первый поток, перед открытием транзакции, приостанавливает второй (цикл со sleep'ом), открывает транзакцию, делает свои дела. Закрывает транзакцию, снимает флаг занятости у второго треда. Второй продолжает дальше крутить цикл.

При работе с базой любого из тредов по отдельности не врозникает никаких проблем или ошибок.

При работе обоих тредов по описанной схеме возникает странное поведение сервера. Через некоторое, неопределенное, время (1 - 15 минут) запросы в первом треде начинают работать существенно медленнее. Вначале - в 2 раза, и чем дальше - тем медленее, в 100 раз могут замедлится (измерял журналированием продолжительности). При этом сервер FB на 100% грузит ядро. Второй тред работает всегда нормально.

Работа идёт с 3-мя таблицами, кроме запросов и таблиц в действе участвуют еще несколько триггеров и хп (достаточно простых). Вроде ничего выдающегося.

После перезапуска программы нормальная работа восстанавливается. Пробовал пересоздавать каждый из тредов или сразу оба - ничего не изменяется.

Рекомендовали поиграться со sweep'ом. Отключение никак не влияет.

Вопросы стандартные - кто виноват и что делать...

Dimitry Sibiryakov
Заслуженный разработчик
Сообщения: 1436
Зарегистрирован: 15 сен 2005, 09:05

Re: Странное поведение сервера FB

Сообщение Dimitry Sibiryakov » 09 апр 2009, 14:33

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

DmitryBelkevich
Сообщения: 30
Зарегистрирован: 03 апр 2009, 21:10

Re: Странное поведение сервера FB

Сообщение DmitryBelkevich » 09 апр 2009, 16:12

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

>Коммить эту транзакцию опять же сразу по завершению обработки.

Нужно что бы данные в реальном времени изменялись. Второй тред - это очередь переноса файлов с одного места на другое. В базе хранятся ссылки на файлы - они должны быть всегда валидными. Кроме этих двух тредов есть еще коннект, который читает данные из базы. Но в описанной проблеме он не учавствует.

>Неясно зачем там вообще два коннекта и две транзакции...

Вторая введена для того, что бы не закрывался читающий датасет во время коммита. Он открывается длинным по времени запросом. Да и позиционировать его потом - тоже лишний процесс.

>Убери коммит транзакции первого потока с таймера, коммить всегда железно после обработки данных.

Данные поступают от юзеров неравномерно (приложение, грубо, файл-сервер). И когда наберется 200 файлов - невозможно предположить - иногда валится массово, иногда - по 1-2 файлов. И, если данные об файлах не коммитить по таймеру, то они могут быть видны только на завтра. Что неприемлемо. Коммит же после каждого файла тоже неудобен - скорость обработки в несколько раз падает при массовой пересылке (бывает - за день 100-200 тысяч сваливается).

Может быть насчет таймера не совсем ясно. Тайм-аут возникает не во время процесса работы потока, а после приёма очередной партии файлов. Т.е. перестало падать (и, если меньше 200-та упало) - 30 секунд - коммит. После каждой итерации первого потока таймер перезапускаю.

Сделал пишущую транзакцию второго треда concurrency. Получаю: Deadlock update conflicts with concurrent update. Происходит так: стартую второй поток. Работает. Начинает работать первый (программа начинат принимать файлы и записывать данные о них в базу). Открывает транзакцию. Второй поток приостанавливается (базой). Первый делает свои дела. Закрывает транзакцию. Второй пытается начать работать - дедлок.

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

Re: Странное поведение сервера FB

Сообщение hvlad » 10 апр 2009, 01:29

DmitryBelkevich писал(а):Их список открыт (ibquery) в отельной (читающей, read_committed rec_version) транзакции.
Она должна быть ещё и read-only
DmitryBelkevich писал(а):Первый поток, перед открытием транзакции, приостанавливает второй.
Второй коммитит свою пишущую тр-цию перед приостановкой ?

DmitryBelkevich
Сообщения: 30
Зарегистрирован: 03 апр 2009, 21:10

Re: Странное поведение сервера FB

Сообщение DmitryBelkevich » 10 апр 2009, 01:57

hvlad писал(а):Она должна быть ещё и read-only
Читающую лучше сделать read_committed rec_version read?
DmitryBelkevich писал(а):Второй коммитит свою пишущую тр-цию перед приостановкой ?
Да. Приостановка стоит в начале цикла. Пишущая коммитися в конце. Первый тред ожидает второй, пока тот приостановится - для этого есть отдельный флаг во втором треде. Читающая второго треда открыта всё время.

Dimitry Sibiryakov
Заслуженный разработчик
Сообщения: 1436
Зарегистрирован: 15 сен 2005, 09:05

Re: Странное поведение сервера FB

Сообщение Dimitry Sibiryakov » 10 апр 2009, 13:41

DmitryBelkevich писал(а):Нужно что бы данные в реальном времени изменялись. Второй тред - это очередь переноса файлов с одного места на другое. В базе хранятся ссылки на файлы - они должны быть всегда валидными.
Похоже, налицо непонимание транзакций. Ссылки на файлы при такой работе по опредеелнию не будут валидными всегда. В тот момент когда файл уже перемещён, а информация об этом ещё не закоммичена все остальные читатели будут получать дохлую ссылку.
DmitryBelkevich писал(а):Вторая введена для того, что бы не закрывался читающий датасет во время коммита. Он открывается длинным по времени запросом. Да и позиционировать его потом - тоже лишний процесс.
Это про транзакции, пусть будут (хотя логика выглядит подозрительной). А зачем второй коннект?
DmitryBelkevich писал(а):Данные поступают от юзеров неравномерно (приложение, грубо, файл-сервер). И когда наберется 200 файлов - невозможно предположить - иногда валится массово, иногда - по 1-2 файлов.
И пачка в 200 файлов - догма? Коммитить транзакцию когда кончились файлы для обработки запрещено? Упало 200 - коммитишь 200. Упало 3, коммитишь 3. Своим таймером ты создаёшь область непредсказуемого поведения системы.
DmitryBelkevich писал(а):Коммит же после каждого файла тоже неудобен - скорость обработки в несколько раз падает при массовой пересылке (бывает - за день 100-200 тысяч сваливается).
Значит ты что-то делаешь неправильно. 100-200 тысяч операций в день - маленькие объёмы и частый коммит не должен вляить так сильно.
DmitryBelkevich писал(а):Сделал пишущую транзакцию второго треда concurrency. Получаю: Deadlock update conflicts with concurrent update.
И это лишний раз подтверждает, что где-то в базовой логике есть ошибка. Возможно при проектировании БД или схемы работы системы.

DmitryBelkevich
Сообщения: 30
Зарегистрирован: 03 апр 2009, 21:10

Re: Странное поведение сервера FB

Сообщение DmitryBelkevich » 10 апр 2009, 16:30

Dimitry Sibiryakov писал(а):Похоже, налицо непонимание транзакций. Ссылки на файлы при такой работе по опредеелнию не будут валидными всегда. В тот момент когда файл уже перемещён, а информация об этом ещё не закоммичена все остальные читатели будут получать дохлую ссылку.
Не всё так просто, конечно. Файл перемещается в два этапа. Вначале он копируется на новое место. При благополополучном копировании делается попытка обновить базу. Когда базе удалось благополучно обновится, старый файл удаляется.
Dimitry Sibiryakov писал(а):Это про транзакции, пусть будут (хотя логика выглядит подозрительной). А зачем второй коннект?
Обычно создаю парами. Хотя можно попробовать повесить на один.
Dimitry Sibiryakov писал(а):И пачка в 200 файлов - догма? Коммитить транзакцию когда кончились файлы для обработки запрещено? Упало 200 - коммитишь 200. Упало 3, коммитишь 3. Своим таймером ты создаёшь область непредсказуемого поведения системы.
Как же мне узнать, что падение файлов прекратилось, кроме как по таймеру? 30 секунд файлы не падали - предпологаю, что 'пачка' окончилась. 200 файлов - не догма. Выбиралось из времени добавления в базу. 200 файлов поток успевает добавлять за 30-50 секунд, в среднем.
Dimitry Sibiryakov писал(а):Значит ты что-то делаешь неправильно. 100-200 тысяч операций в день - маленькие объёмы и частый коммит не должен вляить так сильно.
Так я нигде и не говорю, что проблема с двумя тредами именно в коммите (хотя, нельзя исключить). Просто коммит после каждого файла всегда стабильно замедляет очередь в 2-3 раза, вне зависимости от условий. Что замедляет работу и добавление записей в базу.
DmitryBelkevich писал(а):И это лишний раз подтверждает, что где-то в базовой логике есть ошибка. Возможно при проектировании БД или схемы работы системы.
Пока неясно почему такое происходит. Как я понимаю, если записи, которые пытается изменить второй поток, в таблице параллельный поток не изменял - то такого быть не должно.

DmitryBelkevich
Сообщения: 30
Зарегистрирован: 03 апр 2009, 21:10

Re: Странное поведение сервера FB

Сообщение DmitryBelkevich » 10 апр 2009, 18:17

Dimitry Sibiryakov писал(а):Пока неясно почему такое происходит. Как я понимаю, если записи, которые пытается изменить второй поток, в таблице параллельный поток не изменял - то такого быть не должно.
Нашел.

ХП, которая обновляет данные о файлах на томах пытается обновить данные, которые были обновлены в другом потоке другой ХП (она тоже SHARE_CURR_SIZE обновляет).

Код ХП, которая вызывает дедлок:

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

create or alter procedure MOVE_FILES_LENGTH (
    OLD_SHARE_UID integer,
    NEW_SHARE_UID integer,
    FILES_LENGTH double precision) 
as
begin
  update SHARES 
  set SHARE_CURR_SIZE =(select SHARE_SIZE 
                        from GET_SHARE_SIZE(:OLD_SHARE_UID)) - :FILES_LENGTH 
  where SHARE_UID = :OLD_SHARE_UID;
  update SHARES 
  set SHARE_CURR_SIZE =(select SHARE_SIZE 
                        from GET_SHARE_SIZE(:NEW_SHARE_UID)) + :FILES_LENGTH 
  where SHARE_UID = :NEW_SHARE_UID;
end^
С синхронизацией всё просто. Две транзакции из двух тредов никогда не изменяют поле SHARE_CURR_SIZE параллельно. С concurency получается, что это поле становится невалидным у пишущей транзакции второго треда.

Как такое разруливать без ручной синхронизации тредов?
Последний раз редактировалось DmitryBelkevich 10 апр 2009, 20:12, всего редактировалось 1 раз.

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

Re: Странное поведение сервера FB

Сообщение kdv » 10 апр 2009, 19:07

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

DmitryBelkevich
Сообщения: 30
Зарегистрирован: 03 апр 2009, 21:10

Re: Странное поведение сервера FB

Сообщение DmitryBelkevich » 10 апр 2009, 22:08

В любом случае - конкуренция мне не особенно важна. Мне нужно, что бы первый тред отработал как можно быстрее. Второй тред является фоновой задачей.

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

Re: Странное поведение сервера FB

Сообщение Кузнецов Евгений » 10 апр 2009, 22:36

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

То DmitryBelkevich
Похоже, первый поток пытается собрать мусор, порожденный вторым, и это ему не удается, поскольку его держит "читающая" транзакция. См. выше совет hvlad.
С другой стороны, неясно, почему 100% загрузка процессора - сборка мусора была существенно ускорена в 2.0, а в 2.1 фоновый сборщик не должен читать версии записей, порожденные активными транзакциями. И в 100 раз обработку это замедлять не должно.

По поводу "sleep в цикле" - многопоточные приложения вообще-то принято писать несколько иначе - см. цикл статей

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

Re: Странное поведение сервера FB

Сообщение hvlad » 10 апр 2009, 22:38

DmitryBelkevich писал(а):В любом случае - конкуренция мне не особенно важна. Мне нужно, что бы первый тред отработал как можно быстрее. Второй тред является фоновой задачей.
Где-то ты нас обманываешь. И себя.
а) Ты говоришь, что второй поток приостанавилавается, когда работает первый
б) Они у тебя редактируют одни и те же записи одновременно

Ась ?
Последний раз редактировалось hvlad 10 апр 2009, 22:50, всего редактировалось 1 раз.

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

Re: Странное поведение сервера FB

Сообщение Кузнецов Евгений » 10 апр 2009, 22:42

hvlad писал(а):Где-то ты нас обманываешь. И себя.
а) Ты говоришь, что второй поток приостанавилавается, когда раобтает первый
б) Они у тебя редактируют одни и те же записи одновременно

Ась ?
Да он, наверное, уже совету "Второй поток переведи на транзакцию concurrency и прекрати его тормозить на время работы первого потока." успел последовать :)
--
С уважением, Евгений

DmitryBelkevich
Сообщения: 30
Зарегистрирован: 03 апр 2009, 21:10

Re: Странное поведение сервера FB

Сообщение DmitryBelkevich » 11 апр 2009, 13:52

Спасибо всем отвечающим.
Кузнецов Евгений писал(а):
hvlad писал(а):Где-то ты нас обманываешь. И себя.
а) Ты говоришь, что второй поток приостанавилавается, когда раобтает первый
б) Они у тебя редактируют одни и те же записи одновременно

Ась ?
Да он, наверное, уже совету "Второй поток переведи на транзакцию concurrency и прекрати его тормозить на время работы первого потока." успел последовать :)
--
С уважением, Евгений
Успел ;) В целях эксперимента. Затем откатился. Без конкуренции транзакции не мешают друг другу (по крайней мере - точно одну запись не редактируют).
Кузнецов Евгений писал(а):Похоже, первый поток пытается собрать мусор, порожденный вторым, и это ему не удается, поскольку его держит "читающая" транзакция.
Интересная мысль, спасибо. Попробую читающую второго потока закрыть.
Кузнецов Евгений писал(а):По поводу "sleep в цикле" - многопоточные приложения вообще-то принято писать несколько иначе
Спасибо за линк, интересно. Мне, честно говоря, sleep'ы самому не нравятся. Может подскажете, с помощью чего вот такую функциональность ораганизовать лучше:

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

    while FPaused or FManPaused do
    begin
     FPausedState := True;
     if Terminated then
      Exit;
     Sleep(1000);
    end;
Нужно придерживать поток в опеределенной точке до снятия двух задержек (от первого потока, или ручной - из гуя) или до его терминирования.

DmitryBelkevich
Сообщения: 30
Зарегистрирован: 03 апр 2009, 21:10

Re: Странное поведение сервера FB

Сообщение DmitryBelkevich » 11 апр 2009, 14:35

По линку: "В целом же проще писать потоки, которые регулярно проверяют свойство Terminated". Удастся ли с объектами синхронизации его проверять?

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

Re: Странное поведение сервера FB

Сообщение Кузнецов Евгений » 11 апр 2009, 15:01

Доброго времени суток!
DmitryBelkevich писал(а):По линку: "В целом же проще писать потоки, которые регулярно проверяют свойство Terminated". Удастся ли с объектами синхронизации его проверять?
Там сначала наиболее простые случаи рассматриваются. В общем случае, например, для продолжительного SQL-запроса в потоке, проверить это невозможно.
Интересная мысль, спасибо. Попробую читающую второго потока закрыть.
Вам hvlad выше писал, что достаточно сделать ее read-only (т.е. read_committed rec_version read). О транзакциях посмотрите здесь.
Нужно придерживать поток в опеределенной точке до снятия двух задержек (от первого потока, или ручной - из гуя) или до его терминирования.
Задержки можно реализовать как события (взведено - нет задержки), а ожидание - через WaitForMultipleObjects. Terminated тогда, конечно, проверить не удастся, но можно попробовать перекрыть SetTerminated в потомке TThread и опять же формировать событие.
--
С уважением, Евгений

DmitryBelkevich
Сообщения: 30
Зарегистрирован: 03 апр 2009, 21:10

Re: Странное поведение сервера FB

Сообщение DmitryBelkevich » 11 апр 2009, 15:15

Кузнецов Евгений писал(а):В общем случае, например, для продолжительного SQL-запроса в потоке, проверить это невозможно.
Это понятно. Просто Terminated проверяю в конце цикла. Ну и при приостановке.
Кузнецов Евгений писал(а):Вам hvlad выше писал, что достаточно сделать ее read-only (т.е. read_committed rec_version read).
Ясно. Сделаю.
Кузнецов Евгений писал(а):Задержки можно реализовать как события (взведено - нет задержки), а ожидание - через WaitForMultipleObjects. Terminated тогда, конечно, проверить не удастся, но можно попробовать перекрыть SetTerminated в потомке TThread и опять же формировать событие.
Увы:

procedure TThread.Terminate;
begin
FTerminated := True;
end;

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

Re: Странное поведение сервера FB

Сообщение Кузнецов Евгений » 11 апр 2009, 15:35

DmitryBelkevich писал(а):Увы:

procedure TThread.Terminate;
begin
FTerminated := True;
end;
Жаль, она даже не виртуальная. Тогда только собственная процедура завершения потока.

--
С уважением, Евгений

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

Re: Странное поведение сервера FB

Сообщение hvlad » 11 апр 2009, 22:28

Тот, кто завершает поток, должен выставить и соотв. сигнал :

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

// главный поток
secondaryThread.Terminate;
SetEvent (hWakeUp);

// вторичный поток
while (not Terminated) do
begin
  WaitForSingleObject(hWakeUp, INIFINITE);
  if (Terminated)
  then Break;
...
end

DmitryBelkevich
Сообщения: 30
Зарегистрирован: 03 апр 2009, 21:10

Re: Странное поведение сервера FB

Сообщение DmitryBelkevich » 12 апр 2009, 20:00

hvlad писал(а):Тот, кто завершает поток, должен выставить и соотв. сигнал :
Да. Как-то так и сделал.

Ответить