Непонятное для меня поведение кода PHP

12
D
На сайте с 28.06.2008
Offline
1108
202

Есть скрипт парсера, структура примерно такая

$level1_url = $conn->query('SELECT * FROM `table`  WHERE `sid` = 0 limit 20');
$result = $level1_url->fetchAll(PDO::FETCH_ASSOC);


if (!empty($result)){

    foreach ($result as $value) {
        $sqlSID = "UPDATE `table` SET `sid` = '2'  where id = '{$value['id']}';";
        $conn->query($sqlSID);
    }


    $rez_count = count($result);
    for ($k=0; $k < $rez_count; $k++) {
            //логика парсинга 

   тут в самом низу запрос на UPDATE `table` и там же SET `sid` = '1'
  }
}

Лимит 20 подобран примерно чтобы успеть с гарантией отработать за минуту. Скрипт запускается по крону раз в минуту. Но чтобы еще себя обезопасить от дублей я всем выбранным записям ставлю SET `sid` = '2' чтобы если вдруг скрипт все же будет выполняться более 1 минуты следующий заход по крону не запросил опять эти же данные.

В итоге я получаю более 20% данных в таблице в которых пустые поля и `sid` = '2' и я не могу объяснить как такое может произойти.

Т.е. скрипт начал выполняться, запись получила  `sid` = '2'   но до конца до строки

тут в самом низу запрос на UPDATE `table` и там же SET `sid` = '1'

почему-то работа не дошла.

Причем если заново взять и тем записям у которых в базе остался  `sid` = '2' поставить  `sid` = '0' и заново по ним пройтись - то опять 80-90% из них обработает нормально. Т.е. проблема не в конкретных записях, косяк где-то в логике, но я не могу понять где?

Алеандр
На сайте с 08.12.2010
Offline
202
#1
В нормальных условиях ставят файл-локер, который проверяет, отработал ли предыдущий скрипт и, если отработал и файла нет, то запускается, а если он есть - то не запускается. А эти вот "гарантированно вложиться в минуту" - никакой гарантии не дают. Зачем себя так мучать то?
D
На сайте с 28.06.2008
Offline
1108
#2
Алеандр #:
В нормальных условиях ставят файл-локер, который проверяет, отработал ли предыдущий скрипт и, если отработал и файла нет, то запускается, а если он есть - то не запускается. А эти вот "гарантированно вложиться в минуту" - никакой гарантии не дают. Зачем себя так мучать то?

А можно подробнее про локер, как это делается? А то я говнокодер новичок, что пришло первое в голову то и реализовал

Алеандр
На сайте с 08.12.2010
Offline
202
#3
Dram #:

А можно подробнее про локер, как это делается? А то я говнокодер новичок, что пришло первое в голову то и реализовал

Ну уж точно не новичок )

При старте скрипта проверяется, есть ли в наличие файл-локер, любое имя, в любом удобном месте. Просто пустой файл, главное - это его наличие или отсутствие. Если он уже есть, то запуск скрипта не производится, банальный die(). Если же файла нет, то сначала создается этот файл-локер и дальше запускается скрипт. Когда скрипт закончил свое выполнение, то файл-локер просто удаляется. Таким образом две копии одного скрипта не будут запущены одновременно. И не нужно городить огороды.

Скрипт - это маркер, что предыдущая копия этого скрипта еще не отработала, т.е. этот запуск требуется пропустить.

--

Есть файл? -> Нет -> Создать файл -> Работа скрипта -> Удалить файл
Есть файл? -> Да -> Выход

lutskboy
На сайте с 22.11.2013
Offline
185
#4

зачем пачками парсить?

парсим по одному разу.

скрипт отвалился. а где у вас set_time_limit, ignore_user_abort?

файл локер нужно от крвого крона кторый может запустится не раз как вы предположили а 2 или даже 3

lealhost
На сайте с 07.06.2014
Offline
136
#5

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

Помню писал скрипт, к которому 20-30 человек обращаются практически одновременно, так как скрипт не в режиме демона, то на каждый запрос по процессу.  Первые несколько запросов стабильно выполнялись одновременно. Решил проблему созданием локера-переменной в Memcached, отрабатывает в 100% случаев правильно (веду журналы). Будь это поточный демон, конечно бы использовал мьютексы.

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

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

Solmyr
На сайте с 10.09.2007
Offline
501
#6
Алеандр #:
При старте скрипта проверяется, есть ли в наличие файл-локер, любое имя, в любом удобном месте. Просто пустой файл, главное - это его наличие или отсутствие. Если он уже есть, то запуск скрипта не производится, банальный die().
Хороший способ положить сервис совсем :)

На самом деле надо в файл-локер писать pid запустившего его процесса.

При новом запуске считываем из локера pid   и проверяем существует ли этот процесс. Если да, то умираем. А если нет - то запускаемся и перетираем локер.
ArbNet
На сайте с 27.10.2019
Offline
139
#7
Алеандр #:
файл-локер

Вот это реально велосипед 😀 я когда в 2000 начинал сайты делать и то лучше способ был. А сейчас есть паттерн singleton один экземпляр класса и не надо никакого файла-локера 😁

LEOnidUKG
На сайте с 25.11.2006
Offline
1762
#8
Solmyr #:
Хороший способ положить сервис совсем :)

На самом деле надо в файл-локер писать pid запустившего его процесса.

При новом запуске считываем из локера pid   и проверяем существует ли этот процесс. Если да, то умираем. А если нет - то запускаемся и перетираем локер.

Нельзя юзать PID, об этом написано в мануале:

https://www.php.net/manual/ru/function.getmypid.php

PHP: getmypid - Manual
  • www.php.net
Внимание Идентификаторы процессов в системе не уникальны, поэтому являются слабым источником энтропии. Мы не рекомендуем полагаться на pid в контекстах, влияющих на безопасность.
✅ Мой Телеграм канал по SEO, оптимизации сайтов и серверов: https://t.me/leonidukgLIVE ✅ Качественное и рабочее размещение SEO статей СНГ и Бурж: https://getmanylinks.ru/ ✅ Настройка и оптимизация серверов https://getmanyspeed.ru/
LEOnidUKG
На сайте с 25.11.2006
Offline
1762
#9
ArbNet #:

Вот это реально велосипед 😀 я когда в 2000 начинал сайты делать и то лучше способ был. А сейчас есть паттерн singleton один экземпляр класса и не надо никакого файла-локера 😁

Вместо хвастовства, ТС-у бы ответили на его вопрос.

Dreammaker
На сайте с 20.04.2006
Offline
569
#10

Если проблема именно в блокировке и в том, что скрипт запускается в нескольких экземплярах из-за крона, то поставьте на сервере flock 

и запускайте скрипты примерно так:


*/1 * * * * /usr/bin/flock -n /tmp/ert.textphp.lockfile /usr/bin/php /path/to/file/test.php

Flock будет сам отпускать локфайл, когда скрипт завершится. Это несколько лучше, чем внутренний для скрипта обработчик локеров.

12

Авторизуйтесь или зарегистрируйтесь, чтобы оставить комментарий