Потокобезопасность в Slick

У меня есть общий вопрос о том, как Slick/база данных управляет асинхронными операциями. Когда я составляю запрос или действие, скажем

(for {
  users <- UserDAO.findUsersAction(usersInput.map(_.email))
  addToInventoriesResult <- insertOrUpdate(inventoryInput, user)  
  deleteInventoryToUsersResult <- inventoresToUsers.filter(_.inventoryUuid === inventoryInput.uuid).delete if addToInventoriesResult == 1
  addToInventoryToUsersResult <- inventoresToUsers ++= users.map(u => DBInventoryToUser(inventoryInput.uuid, u.uuid)) if addToInventoriesResult == 1 
} yield(addToInventoriesResult)).transactionally

Есть ли вероятность того, что другой пользователь может, например, удалить пользователей сразу после выполнения первого действия UserDAO.findUsersAction(usersInput.map(_.email)), но до остальных, так что вставка не удастся (из-за ошибки внешнего ключа)? Или сценарий, который может привести к потере обновления, например: транзакция A читает данные, затем транзакция B обновляет эти данные, затем транзакция A выполняет обновление на основе того, что она прочитала, она не увидит обновление B и перезапишет его.

Я думаю, что это, вероятно, зависит от реализации базы данных или, возможно, JDBC, поскольку это отправляется в базу данных в виде блока SQL, но, возможно, Slick играет в этом роль. Я использую MySQL.

Если здесь есть проблемы с синхронизацией, как лучше всего это решить? Я читал о таких подходах, как фоновая очередь, которая последовательно обрабатывает операции (как семантические единицы), но не устранит ли это частично преимущество возможности асинхронного доступа к базе данных -> плохая производительность?


person User    schedule 16.12.2015    source источник


Ответы (1)


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

Ходили разговоры о внедрении неблокирующих драйверов для Oracle и SQL Server (в рамках платной подписки Typesafe), но это не произойдет в ближайшее время, AFAICT. Существует несколько проектов, которые предоставляют неблокирующие драйверы для Postegres и MySQL, но YMMV, еще первые дни.

С учетом этого, когда вы вызываете transactionally, Slick берет пакет запросов для выполнения и оборачивает их в блок try-catch с флагом autocommit базового соединения, установленным в false. После успешного выполнения запросов транзакция фиксируется путем установки autocommit обратно в значение по умолчанию, true. В случае возникновения исключения вызывается метод соединения rollback. Просто стандартный шаблон сеанса JDBC, который Slick удобно абстрагирует.

Что касается вашего сценария удаления пользователя в середине транзакции и его правильной обработки, то это работа базовой базы данных/драйвера.

person virtualeyes    schedule 19.12.2015
comment
поток будет потребляться и блокироваться это означает, что по-прежнему может быть n потоков, одновременно обращающихся к базе данных (где max n, вероятно, является размером моего пула потоков)? Меня беспокоит только перезапись обновлений, которую, я думаю, нельзя решить только с помощью транзакций. Если транзакция A читает какие-то данные, то транзакция B обновляет эти данные, затем транзакция A выполняет обновление на основе того, что она прочитала, она не увидит обновление B и перезапишет его. Может ли это случиться? Если да, как это решается с помощью Slick? - person User; 19.12.2015
comment
Slick поддерживает свой собственный внутренний пул потоков (поверх пула по умолчанию, HikariCP), который, например, зависит от DBIO для выполнения своих потоковых/пакетных операций. Slick сам по себе не предотвращает возникновение сценария транзакции A/транзакции B, поскольку они (предположительно) являются отдельными невложенными транзакциями. Пока базовая база данных является потокобезопасной, она должна быть в состоянии правильно управлять вашим сценарием транзакции A/транзакции B (т.е. сбой транзакции B) - person virtualeyes; 19.12.2015
comment
Знаете ли вы, является ли MySQL потокобезопасным? Я читал, что иногда требуется блокировка таблицы, поэтому не уверен в этом. Если это не потокобезопасно, правильно ли я понимаю, что я ничего не могу сделать со стороны Slick? Спасибо за всю информацию. - person User; 19.12.2015
comment
да, я предполагал, что через HikariCP (и другие пулы соединений JDBC, которые правильно реализуют спецификацию) безопасность потоков гарантируется на уровне драйвера (т.е. один поток на соединение). Но, согласно этой проблеме github, это не всегда так. В любом случае, вы можете отправить запрос автору Slick на github или gitter, чтобы узнать, какие меры принимает Slick, чтобы гарантировать потокобезопасный доступ. - person virtualeyes; 20.12.2015
comment
Хорошо, из этой проблемы похоже, что в конце концов безопасность потоков исходит от Slick? Я спрошу в гиттере, когда у меня будет время. +1, но пусть это откроется, так как было бы неплохо получить полный ответ, а также чтобы другие люди могли его прочитать. - person User; 20.12.2015