Опрос: как у вас решается проблема синхронизации параллельных запросов на PHP?

Долгое время я пытаюсь понять, сколько места отведено вопросам параллельности и конкурентности выполнения кода в повседневной практике среднестатистического PHP-программиста. С одной стороны, разрабатывая серверное приложение, программист автоматически пишет код, который будет выполняться параллельно. С другой, в на практике в PHP все проблемы этой области решались инструментами, которыми все пользовали — веб-сервер, сессия и СУБД.Обращают ли на ваших проектах внимание на на проблемы синхронизации параллельно обрабатывающихся HTTP-запросов? Они решаются через транзакции, блокировки? Какие способы блокирования вы используете? Да и вообще, нужно об этом париться, или тема бесполезная? Узнаем мнение аудитории. Этот пост не дает ответов на вопросы. Здесь ведется разведка.

***

В мире PHP исторически принято не обращать много внимания на параллельность выполнения кода. Под многопоточность не заточен сам PHP (нет потокобезопасности внутри движка). Я ни разу не встречал проекты или программистов, которые использовали pthreads (а вы встречали? тогда расскажите об этом в коменнтах). И не очень-то именно многопоточность нужна на практике в веб-приложениях, где параллельно нужно выполнять прежде всего запросы, а не отдельные части кода в рамках одного запроса. А так как параллельное выполнение входящих запросов в отдельных процессах организуется сервером приложений (php-fpm или apache), программисту об этом думать не нужно — все работает из коробки.

В PHP есть механизм сессий, которым пользуются в подавляющем большинстве случаев. А сессия с дефолтовыми настройками блокирует параллельное выполнение запросов в рамках одной сессии. Это «прикрывает» некоторые дыры и приводит к тому, что на практике можно никогда и не столкнуться с явными проблемами. Т.е. пока пользователь не начнет хакерить, работая, например, в двух браузерах одновременно, ничего не сломается из-за отсутствия блокировок и транзакций.

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

Ну и, наконец, если какие-то проблемы параллельности все же всплывают, то самый простой способ их решения — транзакция в БД с достаточным уровнем изоляции. Так как почти все сайты, которые разрабатываются на PHP, используют в качестве хранилища транзакционную СУБД, достаточно просто начать использовать транзакции для решения проблем конкуренции выполнения запросов, которые приводят к неконсистентности данных. Даже не вникая глубоко в тему синхронизации процессов, просто используя транзакции, проблему можно решить.

Все это приводит к тому, что на практике среднестатистический PHP-программист почти никогда не сталкивается с проблемами параллельного выполнения кода. Большинство вообще мало что знает про параллельное программирование, синхронизацию и блокировки. Это четко прослеживается на собеседованиях. А на сколько это востребовано, я и хочу узнать в этом опросе.

Отчасти все это и хорошо — низкий порог вхождения, что и является одним из главных преимуществ PHP. Быстрая разработка за счет экономии времени на проработку всех узким мест. Но рано или поздно, многие начинают сталкиваться с проблемой синхронизации процессов лицом к лицу. А разработка надежных, а не рассчитанных на обстоятельства, приложений требует определенного уровня изучения этого вопроса.

Самый простой пример, когда не поможет транзакция, — прогрев кэша. Чтобы данные, которые кэшируются, не генерировались параллельно, конкурентные запросы нужно блокировать, дав заполнить кэш тому запросу, который начал это делать первым. Без блокировки тут не обойтись. Причем, если серверов несколько, блокировка должна быть централизованной. Другой пример, файловый хостинг. У пользователя ограничено количество файлов, которые он может закачать. При добавлении файла нужно сравнить количество закачанных файлов с лимитом и принять файл, если лимит не исчерпан. Хотя тут можно сделать финт ушами и обойтись без блокировок, проще всего будет заблокироваться перед проверкой по пользователю, проверить счетчик, занять слот под файл, снять блокировку, после чего принять само тело файла.

И с использованием транзакций тоже есть свои проблемы. Как минимум то, что их надо несколько раз рестартовать, если присутствует race-condition и транзакция откатывается по причине коллизии. Есть вопросы при работе с внешними для БД ресурсами — файлами, кэшем, запросами к удаленному API.

***

На самом деле все PHP-программисты пишут код, который работает в конкурентной среде. Зачастую даже в очень выскоконкурентной. И доступ к общим ресурсам из параллельно выполняющихся процессов синхронизировать приходится. Я думаю, что многим, как и мне, будет интересно узнать, как на эту проблему смотрят коллеги по цеху. Как на ваших проектах решают проблему синхронизации доступа к общим ресурсам?

Как эта проблема решается у нас? У нас используются транзацкии и блокировки. Транзакции помогают сохранить консистентность данных, если выполнение задачи сводится к серии запросов в БД. Когда же нужно синхронизировать код, который работает не только с базой, или вообще с ней не работает, мы используем блокировки через мою библиотеку абстракции над способом блокировки. Если бэкэнд работает на одном сервере, достаточно использовать драйвер для flock (), если нужно блокироваться распределенно, то можно использовать драйвера для Redis или Memcache. Если у вас есть хорошие материалы по этой теме, делитесь ссылками в комментах.

P.S. Любителям других языков программирования: если вы хотите рассказать, как проблема успешно решается в вашем языке/фреймворке, you are welcome. В противном случае, обратите внимание на хаб, в котором размещена публикация.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

© Habrahabr.ru