Биллинговая система Nodeny
22 Ноября 2024, 11:22:35 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
Новости: Прекращена поддержка версии Nodeny 49
 
   Начало   Помощь Поиск Войти Регистрация  
Страниц: [1]
  Печать  
Автор Тема: Снова о DEADLOCK  (Прочитано 7203 раз)
Efendy
Администратор
Спец
*****

Карма: 138
Offline Offline

Сообщений: 4790



Просмотр профиля
« : 28 Ноября 2021, 15:50:51 »

Снова по deadlock Улыбающийся Я постарась быть максимально кратким, насколько это возможно.

Deadlock - это состояние базы данных, когда, условно говоря, она зависает. Почему так происходило? Я говорю, происходило, потому что вероятность данной проблемы я максимально свел к нулю. Если у кого-то не так, это обычно частные случаи неправильной настройки, которые я бывает объясняю в личке и это не всегда видится как ответ в форуме.

Итак, почему происходил deadlock? Если сеть большая, то часто к NoDeny идут параллельно несколько запросов на получение свободных динамических ip. И когда база данных "захватывает" (назначает юзеру) один и тот же ip разным пользователям, то возникает конфликт, который не разруливается.

Может возникнуть вопрос: может что-то не так делаю с процедурами раз это происходит? Я отвечу: все правильно ибо это проблема mysql Улыбающийся Почему я перекидываю ответственность на mysql? Потому что в 8й версии они эту проблему исправили. Но по-порядку...

Функция get_ip занимается выдачей ip по id абонента. Если ip статический, то это просто прекрасно, у вас никогда не будет дедлоков (рекомендую Улыбающийся). Если динамический, то из таблицы ip_pool выбирается любой ip, который не назначен никому (uid=0). Чтобы в один момент не было попытки выдать один и тот же свободный ip разным юзерам, get_ip выбрает ip по случайному id. Тут есть свои нюансы, не хочу забивать вам голову, но скажу, что order by rand() не катит и сделано это немного иначе. Но в любом случае нет 100%-й гарантии выборки одного и того же ip разным абонентам, поэтому если вы посмотрите процедуру get_ip, то увидите, что она довольно мудреная. Как раз из-за разруливания этой ситуации. Честно скажу, она работает, но у меня "болит сердце" от ее корявого вида. Хотя, повторюсь, она работает нормально.

По документации я рекомендовал (я ее изменил буквально несколько дней назад) ставить 5.7 версию mysql. У меня есть данные, что довольно большая сеть пользуется этой версией несколько месяцев, так что противопоказаний к вакцинации 8й версией нет. Но, главное, что есть в 8й версии, это штука, которая позволяет обходить deadlock-и.

Что нам это дает?

1) mysql-процедуры nodeny становятся более понятными и простыми
2) все

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

Уловили главное слово "планирую"? Да, пока я планирую ибо не тестил досконально - к сожалению, у меня нет сети уже несколько лет, поэтому такие важные вещи протестировать в бою сам лично не могу, за меня это делают другие админы, с которыми я взаимодейтсвую, спасибо им.

Но, если кто хочет попробовать (на самом деле я бы хотел чтоб кто-то попробовал и стал первопроходцем), то суть такая:
1) функция get_ip становится процедурой get_ip. Не получилось оставить функцию т.к. mysql-функции не умеют в транзакции
2) везде, где вызывается get_ip как

Код:
SELECT get_ip(usr_id) INTO usr_ip;

нужно заменить на:

Код:
CALL get_ip(usr_id, usr_ip);

Сама процедура get_ip:

Код:
DROP PROCEDURE IF EXISTS `get_ip`;
DELIMITER $$
CREATE PROCEDURE `get_ip` (IN user_id INTEGER UNSIGNED, OUT res_ip VARCHAR(15))
BEGIN
    DECLARE user_ip VARCHAR(15);
    DECLARE real_ip INTEGER;
    DECLARE row_cnt INTEGER;
    DECLARE ip_id INTEGER;

    SELECT INET_NTOA(ip) INTO user_ip FROM ip_pool
        WHERE uid = user_id AND type='static' LIMIT 1;

    IF( user_ip IS NULL ) THEN
        SELECT 1 INTO real_ip FROM users_services WHERE uid = user_id AND tags LIKE '%,realip,%';
        SELECT IF(ROW_COUNT()>0, 1, 0) INTO real_ip;

        SELECT id, INET_NTOA(ip) INTO ip_id, user_ip FROM ip_pool
            WHERE uid = user_id AND type = 'dynamic' AND realip = real_ip
            LIMIT 1;

        IF( ip_id IS NOT NULL) THEN
            UPDATE ip_pool SET `release` = UNIX_TIMESTAMP() + 3600
                WHERE id = ip_id AND uid = user_id AND `release` < UNIX_TIMESTAMP() + 3601;
        ELSE
            START TRANSACTION;
            SELECT id, INET_NTOA(ip) INTO ip_id, user_ip
                FROM ip_pool
                WHERE uid = 0
                    AND type = 'dynamic'
                    AND realip = real_ip
                    AND `release` < UNIX_TIMESTAMP()
                    ORDER BY id
                LIMIT 1 FOR UPDATE SKIP LOCKED;

            IF( user_ip IS NOT NULL) THEN
                UPDATE ip_pool SET uid = user_id, `release` = UNIX_TIMESTAMP() + 3600
                    WHERE id = ip_id
                    AND uid = 0
                    AND type = 'dynamic'
                    AND realip = real_ip
                    AND `release` < UNIX_TIMESTAMP();
            END IF;
        COMMIT;
        END IF;
    END IF;
    SELECT user_ip INTO res_ip;
END$$
DELIMITER ;
Записан
Warlock
NoDeny
Старожил
*

Карма: 8
Offline Offline

Сообщений: 367


Просмотр профиля
« Ответ #1 : 04 Февраля 2022, 11:19:02 »

2) везде, где вызывается get_ip как
Код:
SELECT get_ip(usr_id) INTO usr_ip;
Можно уточнить, где это указывается, чтоб ничего не пропустить?
Записан
Pa4ka
Старожил
****

Карма: 4
Offline Offline

Сообщений: 281

591884591
Просмотр профиля Email
« Ответ #2 : 05 Февраля 2022, 11:00:51 »

2) везде, где вызывается get_ip как
Код:
SELECT get_ip(usr_id) INTO usr_ip;
Можно уточнить, где это указывается, чтоб ничего не пропустить?

Зависит от того какие процедуры и функции у вас используються. Если используете радиус sql то ищите их в файле ...raddb/mods-enabled/sql все вызовы и потом пройдитесь по всем тем процедурам в базе, например будет call radupdate.
Заходите в базу и show create procedure radupdate там ищите get_ip.
Ну и так далее
Записан
Efendy
Администратор
Спец
*****

Карма: 138
Offline Offline

Сообщений: 4790



Просмотр профиля
« Ответ #3 : 05 Февраля 2022, 12:14:15 »

Еще можно в доке поискать
Записан
Страниц: [1]
  Печать  
 
Перейти в:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.20 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!