@stolet2016
Пытаюсь быть хорошим программистом

Подробнее про блокировки и в laravel в частности?

Всем доброго времени суток!
Я правда внимательно читаю документацию laravel про блокировки ( https://laravel.com/docs/5.1/queries#pessimistic-l... ), но т.к. до этого никогда не работал с блокировками, тяжело понять как правильно нужно себя вести в приложении. Можно привести какой-нибудь пример? Например, когда нужно использовать sharedLock, а когда lockForUpdate.

Что делать, если, например, у меня есть 2 связанные таблицы, например, со связью 1 к 1.
К примеру, есть таблица Книга (Book) и есть таблица с подробной информацией про эту книгу (BookInfo).
Как мне сделать так, чтобы о Book в BookInfo была только одна запись, что пока я буду делать проверку в приложении, пока подготовлю запись, в этот момент другой поток ничего не добавил в BookInfo про эту книгу?

Пример таблиц:
book
id, name

book_info
id, book_id, year

Хочу сразу уточнить: я понимаю, что данный пример неудачный, и что поставленной проблемы легко избежать, но я хочу рассмотреть пример именно с учетом блокировок.

Желательно, с примером как это делать в laravel.
  • Вопрос задан
  • 4277 просмотров
Пригласить эксперта
Ответы на вопрос 3
@xfg
Вам нужны мьютексы. Можно реализовать разными способами. Но если у вас планируется распределенное приложение, то можно сделать на основе mysql, используя GET_LOCK() и RELEASE_LOCK().

К сожалению, не нашел готового для laravel фреймворка, но можно сделать самому подсмотрев как сделано у других.

Задача добиться выполнения приложения в один поток. Мьютекс можно захватывать перед началом работы экшена и отпускать после выполнения.

mysql> SELECT GET_LOCK('name', 10); //первая сессия.
mysql> SELECT GET_LOCK('name', 5) //вторая сессия. будет висеть, пока не истечет таймаут в 10 секунд или пока первая сессия не выпустит SELECT RELEASE_LOCK('name');


Блокировки можно именовать как "controllerName:ActionName". Это значит, что если первый поток взял GET_LOCK("controllerName:ActionName", 10); то второй поток к тому же самому экшену подвиснет и подождет, пока первый поток снимет блокировку. Таким образом, мы добиваемся того, что все запросы к серверу будут выполняться синхронно.

Также, можно воспроизвести состояние гонки и посмотреть, что будет происходить с приложением, без мьютексов и с ними. Этого можно добиться используя curl в терминале:
curl -d 'param1=value1&param2=value2' http://mylaravel.app/addBookInfo & curl -d 'param1=value1&param2=value2' http://mylaravel.app/addBookInfo & wait

В итоге к приложению будет отправлено два одновременных запроса.

Также стоит упомянуть нативные сессии, если речь касается PHP. Так как они реализованы с помощью файлов, то один из запросов подвиснет, так как не сможет получить доступ на запись в файл сессии, пока с ним работает другой поток. Это может создать у разработчика ложное впечатление о том, что его код синхронный и соответственно не подвержен состоянию гонки, но это не так. Здесь нужно детальнее изучать код, в каких точках файл сессии блокируется, а в каких точках программы блокировка файла отпускается.
Ответ написан
Комментировать
VGrabko
@VGrabko
Golang, Php, Js
он и так не добавит. протокольная бд же
Ответ написан
@dimbo
Если в используете PostgreSQL, я бы рекомендовал использовать advisory locks (см. https://www.postgresql.org/docs/current/static/exp..., в конце страницы). По сути, это реализация мьютексов на уровне базы данных.

Трейт для PHP, использующий advisory locks, можно посмотреть здесь: https://gist.github.com/dfbag7/75dcd78a33a6a6d8b61... Этот трейт можно подключать к классам моделей. Также см. реализацию scope: https://gist.github.com/dfbag7/75a0a1ca843e5e14258... Код проверялся (и реально используется) в Laravel 4.2.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы