Странное поведение сервера FB
-
- Сообщения: 30
- Зарегистрирован: 03 апр 2009, 21:10
Странное поведение сервера FB
Исходные данные. Сервер 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'ом. Отключение никак не влияет.
Вопросы стандартные - кто виноват и что делать...
В программе (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'ом. Отключение никак не влияет.
Вопросы стандартные - кто виноват и что делать...
-
- Заслуженный разработчик
- Сообщения: 1436
- Зарегистрирован: 15 сен 2005, 09:05
Re: Странное поведение сервера FB
Убери коммит транзакции первого потока с таймера, коммить всегда железно после обработки данных.
Второй поток переведи на транзакцию concurrency и прекрати его тормозить на время работы первого потока. Коммить эту транзакцию опять же сразу по завершению обработки. Неясно зачем там вообще два коннекта и две транзакции...
Второй поток переведи на транзакцию concurrency и прекрати его тормозить на время работы первого потока. Коммить эту транзакцию опять же сразу по завершению обработки. Неясно зачем там вообще два коннекта и две транзакции...
-
- Сообщения: 30
- Зарегистрирован: 03 апр 2009, 21:10
Re: Странное поведение сервера FB
Спасибо за ответ.
>Коммить эту транзакцию опять же сразу по завершению обработки.
Нужно что бы данные в реальном времени изменялись. Второй тред - это очередь переноса файлов с одного места на другое. В базе хранятся ссылки на файлы - они должны быть всегда валидными. Кроме этих двух тредов есть еще коннект, который читает данные из базы. Но в описанной проблеме он не учавствует.
>Неясно зачем там вообще два коннекта и две транзакции...
Вторая введена для того, что бы не закрывался читающий датасет во время коммита. Он открывается длинным по времени запросом. Да и позиционировать его потом - тоже лишний процесс.
>Убери коммит транзакции первого потока с таймера, коммить всегда железно после обработки данных.
Данные поступают от юзеров неравномерно (приложение, грубо, файл-сервер). И когда наберется 200 файлов - невозможно предположить - иногда валится массово, иногда - по 1-2 файлов. И, если данные об файлах не коммитить по таймеру, то они могут быть видны только на завтра. Что неприемлемо. Коммит же после каждого файла тоже неудобен - скорость обработки в несколько раз падает при массовой пересылке (бывает - за день 100-200 тысяч сваливается).
Может быть насчет таймера не совсем ясно. Тайм-аут возникает не во время процесса работы потока, а после приёма очередной партии файлов. Т.е. перестало падать (и, если меньше 200-та упало) - 30 секунд - коммит. После каждой итерации первого потока таймер перезапускаю.
Сделал пишущую транзакцию второго треда concurrency. Получаю: Deadlock update conflicts with concurrent update. Происходит так: стартую второй поток. Работает. Начинает работать первый (программа начинат принимать файлы и записывать данные о них в базу). Открывает транзакцию. Второй поток приостанавливается (базой). Первый делает свои дела. Закрывает транзакцию. Второй пытается начать работать - дедлок.
>Коммить эту транзакцию опять же сразу по завершению обработки.
Нужно что бы данные в реальном времени изменялись. Второй тред - это очередь переноса файлов с одного места на другое. В базе хранятся ссылки на файлы - они должны быть всегда валидными. Кроме этих двух тредов есть еще коннект, который читает данные из базы. Но в описанной проблеме он не учавствует.
>Неясно зачем там вообще два коннекта и две транзакции...
Вторая введена для того, что бы не закрывался читающий датасет во время коммита. Он открывается длинным по времени запросом. Да и позиционировать его потом - тоже лишний процесс.
>Убери коммит транзакции первого потока с таймера, коммить всегда железно после обработки данных.
Данные поступают от юзеров неравномерно (приложение, грубо, файл-сервер). И когда наберется 200 файлов - невозможно предположить - иногда валится массово, иногда - по 1-2 файлов. И, если данные об файлах не коммитить по таймеру, то они могут быть видны только на завтра. Что неприемлемо. Коммит же после каждого файла тоже неудобен - скорость обработки в несколько раз падает при массовой пересылке (бывает - за день 100-200 тысяч сваливается).
Может быть насчет таймера не совсем ясно. Тайм-аут возникает не во время процесса работы потока, а после приёма очередной партии файлов. Т.е. перестало падать (и, если меньше 200-та упало) - 30 секунд - коммит. После каждой итерации первого потока таймер перезапускаю.
Сделал пишущую транзакцию второго треда concurrency. Получаю: Deadlock update conflicts with concurrent update. Происходит так: стартую второй поток. Работает. Начинает работать первый (программа начинат принимать файлы и записывать данные о них в базу). Открывает транзакцию. Второй поток приостанавливается (базой). Первый делает свои дела. Закрывает транзакцию. Второй пытается начать работать - дедлок.
Re: Странное поведение сервера FB
Она должна быть ещё и read-onlyDmitryBelkevich писал(а):Их список открыт (ibquery) в отельной (читающей, read_committed rec_version) транзакции.
Второй коммитит свою пишущую тр-цию перед приостановкой ?DmitryBelkevich писал(а):Первый поток, перед открытием транзакции, приостанавливает второй.
-
- Сообщения: 30
- Зарегистрирован: 03 апр 2009, 21:10
Re: Странное поведение сервера FB
Читающую лучше сделать read_committed rec_version read?hvlad писал(а):Она должна быть ещё и read-only
Да. Приостановка стоит в начале цикла. Пишущая коммитися в конце. Первый тред ожидает второй, пока тот приостановится - для этого есть отдельный флаг во втором треде. Читающая второго треда открыта всё время.DmitryBelkevich писал(а):Второй коммитит свою пишущую тр-цию перед приостановкой ?
-
- Заслуженный разработчик
- Сообщения: 1436
- Зарегистрирован: 15 сен 2005, 09:05
Re: Странное поведение сервера FB
Похоже, налицо непонимание транзакций. Ссылки на файлы при такой работе по опредеелнию не будут валидными всегда. В тот момент когда файл уже перемещён, а информация об этом ещё не закоммичена все остальные читатели будут получать дохлую ссылку.DmitryBelkevich писал(а):Нужно что бы данные в реальном времени изменялись. Второй тред - это очередь переноса файлов с одного места на другое. В базе хранятся ссылки на файлы - они должны быть всегда валидными.
Это про транзакции, пусть будут (хотя логика выглядит подозрительной). А зачем второй коннект?DmitryBelkevich писал(а):Вторая введена для того, что бы не закрывался читающий датасет во время коммита. Он открывается длинным по времени запросом. Да и позиционировать его потом - тоже лишний процесс.
И пачка в 200 файлов - догма? Коммитить транзакцию когда кончились файлы для обработки запрещено? Упало 200 - коммитишь 200. Упало 3, коммитишь 3. Своим таймером ты создаёшь область непредсказуемого поведения системы.DmitryBelkevich писал(а):Данные поступают от юзеров неравномерно (приложение, грубо, файл-сервер). И когда наберется 200 файлов - невозможно предположить - иногда валится массово, иногда - по 1-2 файлов.
Значит ты что-то делаешь неправильно. 100-200 тысяч операций в день - маленькие объёмы и частый коммит не должен вляить так сильно.DmitryBelkevich писал(а):Коммит же после каждого файла тоже неудобен - скорость обработки в несколько раз падает при массовой пересылке (бывает - за день 100-200 тысяч сваливается).
И это лишний раз подтверждает, что где-то в базовой логике есть ошибка. Возможно при проектировании БД или схемы работы системы.DmitryBelkevich писал(а):Сделал пишущую транзакцию второго треда concurrency. Получаю: Deadlock update conflicts with concurrent update.
-
- Сообщения: 30
- Зарегистрирован: 03 апр 2009, 21:10
Re: Странное поведение сервера FB
Не всё так просто, конечно. Файл перемещается в два этапа. Вначале он копируется на новое место. При благополополучном копировании делается попытка обновить базу. Когда базе удалось благополучно обновится, старый файл удаляется.Dimitry Sibiryakov писал(а):Похоже, налицо непонимание транзакций. Ссылки на файлы при такой работе по опредеелнию не будут валидными всегда. В тот момент когда файл уже перемещён, а информация об этом ещё не закоммичена все остальные читатели будут получать дохлую ссылку.
Обычно создаю парами. Хотя можно попробовать повесить на один.Dimitry Sibiryakov писал(а):Это про транзакции, пусть будут (хотя логика выглядит подозрительной). А зачем второй коннект?
Как же мне узнать, что падение файлов прекратилось, кроме как по таймеру? 30 секунд файлы не падали - предпологаю, что 'пачка' окончилась. 200 файлов - не догма. Выбиралось из времени добавления в базу. 200 файлов поток успевает добавлять за 30-50 секунд, в среднем.Dimitry Sibiryakov писал(а):И пачка в 200 файлов - догма? Коммитить транзакцию когда кончились файлы для обработки запрещено? Упало 200 - коммитишь 200. Упало 3, коммитишь 3. Своим таймером ты создаёшь область непредсказуемого поведения системы.
Так я нигде и не говорю, что проблема с двумя тредами именно в коммите (хотя, нельзя исключить). Просто коммит после каждого файла всегда стабильно замедляет очередь в 2-3 раза, вне зависимости от условий. Что замедляет работу и добавление записей в базу.Dimitry Sibiryakov писал(а):Значит ты что-то делаешь неправильно. 100-200 тысяч операций в день - маленькие объёмы и частый коммит не должен вляить так сильно.
Пока неясно почему такое происходит. Как я понимаю, если записи, которые пытается изменить второй поток, в таблице параллельный поток не изменял - то такого быть не должно.DmitryBelkevich писал(а):И это лишний раз подтверждает, что где-то в базовой логике есть ошибка. Возможно при проектировании БД или схемы работы системы.
-
- Сообщения: 30
- Зарегистрирован: 03 апр 2009, 21:10
Re: Странное поведение сервера FB
Нашел.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^
Как такое разруливать без ручной синхронизации тредов?
Последний раз редактировалось DmitryBelkevich 10 апр 2009, 20:12, всего редактировалось 1 раз.
Re: Странное поведение сервера FB
похоже, ты попал на конкуретнтное обновление агрегатов. оно не может производиться не монопольно.
-
- Сообщения: 30
- Зарегистрирован: 03 апр 2009, 21:10
Re: Странное поведение сервера FB
В любом случае - конкуренция мне не особенно важна. Мне нужно, что бы первый тред отработал как можно быстрее. Второй тред является фоновой задачей.
-
- Сообщения: 144
- Зарегистрирован: 16 фев 2006, 22:36
Re: Странное поведение сервера FB
Доброго времени суток!
То DmitryBelkevich
Похоже, первый поток пытается собрать мусор, порожденный вторым, и это ему не удается, поскольку его держит "читающая" транзакция. См. выше совет hvlad.
С другой стороны, неясно, почему 100% загрузка процессора - сборка мусора была существенно ускорена в 2.0, а в 2.1 фоновый сборщик не должен читать версии записей, порожденные активными транзакциями. И в 100 раз обработку это замедлять не должно.
По поводу "sleep в цикле" - многопоточные приложения вообще-то принято писать несколько иначе - см. цикл статей
То DmitryBelkevich
Похоже, первый поток пытается собрать мусор, порожденный вторым, и это ему не удается, поскольку его держит "читающая" транзакция. См. выше совет hvlad.
С другой стороны, неясно, почему 100% загрузка процессора - сборка мусора была существенно ускорена в 2.0, а в 2.1 фоновый сборщик не должен читать версии записей, порожденные активными транзакциями. И в 100 раз обработку это замедлять не должно.
По поводу "sleep в цикле" - многопоточные приложения вообще-то принято писать несколько иначе - см. цикл статей
Re: Странное поведение сервера FB
Где-то ты нас обманываешь. И себя.DmitryBelkevich писал(а):В любом случае - конкуренция мне не особенно важна. Мне нужно, что бы первый тред отработал как можно быстрее. Второй тред является фоновой задачей.
а) Ты говоришь, что второй поток приостанавилавается, когда работает первый
б) Они у тебя редактируют одни и те же записи одновременно
Ась ?
Последний раз редактировалось hvlad 10 апр 2009, 22:50, всего редактировалось 1 раз.
-
- Сообщения: 144
- Зарегистрирован: 16 фев 2006, 22:36
Re: Странное поведение сервера FB
Да он, наверное, уже совету "Второй поток переведи на транзакцию concurrency и прекрати его тормозить на время работы первого потока." успел последоватьhvlad писал(а):Где-то ты нас обманываешь. И себя.
а) Ты говоришь, что второй поток приостанавилавается, когда раобтает первый
б) Они у тебя редактируют одни и те же записи одновременно
Ась ?
--
С уважением, Евгений
-
- Сообщения: 30
- Зарегистрирован: 03 апр 2009, 21:10
Re: Странное поведение сервера FB
Спасибо всем отвечающим.
Нужно придерживать поток в опеределенной точке до снятия двух задержек (от первого потока, или ручной - из гуя) или до его терминирования.
Успел В целях эксперимента. Затем откатился. Без конкуренции транзакции не мешают друг другу (по крайней мере - точно одну запись не редактируют).Кузнецов Евгений писал(а):Да он, наверное, уже совету "Второй поток переведи на транзакцию concurrency и прекрати его тормозить на время работы первого потока." успел последоватьhvlad писал(а):Где-то ты нас обманываешь. И себя.
а) Ты говоришь, что второй поток приостанавилавается, когда раобтает первый
б) Они у тебя редактируют одни и те же записи одновременно
Ась ?
--
С уважением, Евгений
Интересная мысль, спасибо. Попробую читающую второго потока закрыть.Кузнецов Евгений писал(а):Похоже, первый поток пытается собрать мусор, порожденный вторым, и это ему не удается, поскольку его держит "читающая" транзакция.
Спасибо за линк, интересно. Мне, честно говоря, sleep'ы самому не нравятся. Может подскажете, с помощью чего вот такую функциональность ораганизовать лучше:Кузнецов Евгений писал(а):По поводу "sleep в цикле" - многопоточные приложения вообще-то принято писать несколько иначе
Код: Выделить всё
while FPaused or FManPaused do
begin
FPausedState := True;
if Terminated then
Exit;
Sleep(1000);
end;
-
- Сообщения: 30
- Зарегистрирован: 03 апр 2009, 21:10
Re: Странное поведение сервера FB
По линку: "В целом же проще писать потоки, которые регулярно проверяют свойство Terminated". Удастся ли с объектами синхронизации его проверять?
-
- Сообщения: 144
- Зарегистрирован: 16 фев 2006, 22:36
Re: Странное поведение сервера FB
Доброго времени суток!
--
С уважением, Евгений
Там сначала наиболее простые случаи рассматриваются. В общем случае, например, для продолжительного SQL-запроса в потоке, проверить это невозможно.DmitryBelkevich писал(а):По линку: "В целом же проще писать потоки, которые регулярно проверяют свойство Terminated". Удастся ли с объектами синхронизации его проверять?
Вам hvlad выше писал, что достаточно сделать ее read-only (т.е. read_committed rec_version read). О транзакциях посмотрите здесь.Интересная мысль, спасибо. Попробую читающую второго потока закрыть.
Задержки можно реализовать как события (взведено - нет задержки), а ожидание - через WaitForMultipleObjects. Terminated тогда, конечно, проверить не удастся, но можно попробовать перекрыть SetTerminated в потомке TThread и опять же формировать событие.Нужно придерживать поток в опеределенной точке до снятия двух задержек (от первого потока, или ручной - из гуя) или до его терминирования.
--
С уважением, Евгений
-
- Сообщения: 30
- Зарегистрирован: 03 апр 2009, 21:10
Re: Странное поведение сервера FB
Это понятно. Просто Terminated проверяю в конце цикла. Ну и при приостановке.Кузнецов Евгений писал(а):В общем случае, например, для продолжительного SQL-запроса в потоке, проверить это невозможно.
Ясно. Сделаю.Кузнецов Евгений писал(а):Вам 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
Жаль, она даже не виртуальная. Тогда только собственная процедура завершения потока.DmitryBelkevich писал(а):Увы:
procedure TThread.Terminate;
begin
FTerminated := True;
end;
--
С уважением, Евгений
Re: Странное поведение сервера FB
Тот, кто завершает поток, должен выставить и соотв. сигнал :
Код: Выделить всё
// главный поток
secondaryThread.Terminate;
SetEvent (hWakeUp);
// вторичный поток
while (not Terminated) do
begin
WaitForSingleObject(hWakeUp, INIFINITE);
if (Terminated)
then Break;
...
end
-
- Сообщения: 30
- Зарегистрирован: 03 апр 2009, 21:10
Re: Странное поведение сервера FB
Да. Как-то так и сделал.hvlad писал(а):Тот, кто завершает поток, должен выставить и соотв. сигнал :