За последние 24 часа нас посетили 22822 программиста и 1259 роботов. Сейчас ищут 703 программиста ...

Laravel делает записи в БД не по порядку

Тема в разделе "Laravel", создана пользователем Anaksagor, 5 июл 2018.

Метки:
  1. Anaksagor

    Anaksagor Активный пользователь

    С нами с:
    21 ноя 2017
    Сообщения:
    21
    Симпатии:
    0
    Приветствую всех.

    Столкнулся с задачей: есть контроллер, который принимает ajax запрос и передаёт данные из запроса в job, а затем job уже записывает эти данные в MySQL DB.

    В контроллере:
    PHP:
    1. $this->dispatch(new WriteMessageToDB($userId, $roomId, $body, $arrived_at));
    Job:

    PHP:
    1. class WriteMessageToDB implements ShouldQueue
    2. {
    3.     use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
    4.  
    5.     protected $userId;
    6.  
    7.     protected $roomId;
    8.  
    9.     protected $body;
    10.  
    11.     protected $arrived_at;
    12.  
    13.     /**
    14.      * WriteMessageToDB constructor.
    15.      * @param $userId
    16.      * @param $roomId
    17.      * @param $body
    18.      * @param $arrived_at
    19.      */
    20.  
    21.     public function __construct($userId, $roomId, $body, $arrived_at)
    22.     {
    23.         $this->userId = $userId;
    24.  
    25.         $this->roomId = $roomId;
    26.  
    27.         $this->body = $body;
    28.  
    29.         $this->arrived_at = $arrived_at;
    30.     }
    31.  
    32.     /**
    33.      * Execute the job.
    34.      *
    35.      * @return mixed
    36.      */
    37.  
    38.     public function handle()
    39.     {
    40.         //persisting to DB
    41.         $cue = Message::create([
    42.             'user_id' => $this->userId,
    43.             'room_id' => $this->roomId,
    44.             'body'    => $this->body,
    45.             'arrived_at' => $this->arrived_at,
    46.         ]);
    47.         return $cue;  
    48.     }
    В консоли запускаю:
    Код (Text):
    1. php artisan queue:work database --tries=1
    Проблема в том, что сообщения в базу данных не всегда записываются в том порядке, в каком пришли: иногда те, что пришли раньше записываются в базу позже. От front-end'а запросы идут в правильном порядке, ошибок при выполнении job'ов тоже нет. Я думал очередь поправит это дело, но похоже, я что-то упускаю из-виду. Буду рад любой помощи. Спасибо.
     
  2. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.554
    Симпатии:
    1.754
    Очереди для того, чтобы одну запись в БД сделать - это слишком. Про порядок - сортируйте по arrived_at и вам по барабану должно быть
     
  3. Anaksagor

    Anaksagor Активный пользователь

    С нами с:
    21 ноя 2017
    Сообщения:
    21
    Симпатии:
    0
    Почему слишком, разве это не поможет приложению быстрее работать?

    Неправильный порядок при уже отсортированных по arrived_at записях: более поздние сообщения оказываются записанными на несколько сот миллисекунд раньше, чем более ранние.
     
  4. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.554
    Симпатии:
    1.754
    Не поможет, тем более у тебя очереди через базу, т.е. ты вместо одной операции вставки делаешь две (в таблицу очередей, а потом в таблицу с сообщениями)
    Ну тут уже, если это супер-пупер критично, надо какие-нибудь изобретать Mutex-ы, ведь скрипт сайта запускается параллельно в нескольких экземплярах. Я даже не сильно себе такой mutex представляю, честно говоря
     
    Anaksagor нравится это.
  5. Anaksagor

    Anaksagor Активный пользователь

    С нами с:
    21 ноя 2017
    Сообщения:
    21
    Симпатии:
    0
    Существенное замечание, спасибо.
     
  6. romach

    romach Старожил

    С нами с:
    26 окт 2013
    Сообщения:
    2.904
    Симпатии:
    719
    Хотелось бы заметить, что в SQL, как в стандарте, порядок гарантируется только при задании инструкции сортировки в запросе. Потому полагаться на порядок добавления записей - нельзя. Более того, если вы станете перебирать большую таблицу через LIMIT OFFSET без ORDER BY, то теоретически, рискуете нарваться на дубликаты.

    Другое дело, что MySQL, используемый в большинстве случаев, как правило отдает строки именно в порядке добавления и потому эту деталь многие пропускают, пока не столкнутся с другим движком.
    --- Добавлено ---
    Это поможет быстрее отдать фронту ответ, но у фронта нет гарантии, что данные действительно были записаны, очередь отработалось и не выстрелил при этом один из эксепшенов. Т.к. добавление / изменение данных по запросу с фронта обычно дешевая операция (вставил запись / отредактировал / изменил), то время работы с БД будет незначительным по сравнению с бутстрапом фреймворка и сетевой частью - экономить тут нечего и очереди смысла не имеют. Потому что если перестает держать БД или фронт - нужно балансировать именно их, вместо внедрения новой сущности, которая забирает на себя ресурсы выполняя лишнее действие.

    С другой стороны, если при выполнении экшена делается ещё что-то: расчеты, обращение к другим api, обработка данных, короче что-то, что увеличивает время выполнения запроса до неприлично долгого для данного кейса, вот тогда и надо переносить действие в очередь, предоставив при этом фронту возможность получить результат выполнения тем или иным способом.

    В теории, как-то так )
     
    Anaksagor нравится это.
  7. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.554
    Симпатии:
    1.754
    Вот на что хочется ещё обратить внимание. Для обработки множества входящих запросов сервер использует несколько параллельных процессов с PHP, и после того, как эти параллельные процессы запущены, нам уже не проконтролировать, как ОС распределить между ними процессорное время. И если у тебя запросы приходят с разницей в миллисекунды, то вполне может такое быть, что запущенный позже процесс доберётся до записи в базу раньше, чем запущенный на миллисекунду раньше. Поэтому тут нужны мютексы - т.е. такие флаги, к которым имеют доступ оба процесса, и которые могут сказать: подожди, я ещё не записал в базу, потом ты запишешь. Можно реализовать через какой-нибудь Redis, а можно и просто с помощью средств MySQL (см GET_LOCK). Но с этим тоже не всё так просто, потому что могут быть так называемые дэдлоки, короче, это вообще отдельная песня. А что у тебя за чат такой, в который люди пишут с разницей в миллисекунды, и это важно? Ведь даже если нагрузка очень большая, в конкретный один чат люди никак не могут писать с такой скоростью