Комментарии 23
Стать полностью реактивными получится только если полностью перейти на базу данных, имеющую асинхронный драйвер.
А если пул акторов (через роутер) в специальном ExecutionContext (количество потоков == количество акторов) каждый со своим коннектом? При заполнении запроса параметрами — запоминать их и отправлять вместе с текстом запроса акторам для исполнения.
Выходные параметры получать через `Future[Map[String, Any]]` (если извратиться, то, полагаю и типизированно через HList можно).
Для обработки курсоров — дополнительно передавать, например, `Iteratee`.
Должно получится вполне реактивно.
Так драйвер Oracle всё равно блокирует. Будут эти потоки (по количеству акторов) висеть и ждать, а контексты их занимать память сервера. Чтобы этого не было, нужно, чтобы драйвер был неблокирующий изначально.
Все так, но это в общем-то копейки по ресурсам. Зато код можно получить в духе Reactive Manifesto. Без блокировки «основного» потока. С агрегацией Future и параллельностью.
Можно, конечно, и с ними, но совершенно не обязательно. Достаточно чтобы вот это:
Возвращало:
Сделать это не сложно и при этом полностью строго типизированно.
Вот чтобы типизировать курсоры — тут да, нужны Type Providers. Но они, по сути, уже есть. Правда придется парсить sql чтоб разобрать какие поля из каких таблиц, но это осуществимо.
sql"""
begin
${output[String]()} := ${"abc"};
${output[Int]()} := ${1};
end;
"""
Возвращало:
SqlExpr[String :: Int :: HNil](
sql = "begin ? = ?; ? = ?; end;",
params = Seq( OutputString, InputString("abc"), OutputInt, InputInt(1))
)
Сделать это не сложно и при этом полностью строго типизированно.
Вот чтобы типизировать курсоры — тут да, нужны Type Providers. Но они, по сути, уже есть. Правда придется парсить sql чтоб разобрать какие поля из каких таблиц, но это осуществимо.
Slick, scalikejdbc, squeryl? м?
Опять же вот есть scalikejdbc-async — https://github.com/scalikejdbc/scalikejdbc-async/tree/develop
Опять же вот есть scalikejdbc-async — https://github.com/scalikejdbc/scalikejdbc-async/tree/develop
Кто из них поддерживает pl/sql блоки, out-параметры хранимок и тому подобное?
Хм, да, с этим я думаю посложнее. Как-то я не внимательно прочитал первый абзац. :)
scalikejdbc ближе всего к тому, что описано в данном посте. Я думаю его query DSL можно было бы дописать до необходимого уровня поддержки этих фич.
scalikejdbc ближе всего к тому, что описано в данном посте. Я думаю его query DSL можно было бы дописать до необходимого уровня поддержки этих фич.
Ну-ну, сидите дальше на своём Oracle. Настоящие базы данных давно имеют асинхронные драйверы, и даже по несколько разных реализаций. Чтобы не быть голословным, вот вам ссылка на postgresql-async.
Я че-т не понял — вы после каждого запроса возвращаете коннект в пул? И его может после этого захватить любой другой поток?
Вроде это обычная практика, нет? Позволяет сделать так, чтобы количество соединений с базой не зависело от количества клиентов. Можно нагрузку на базу регулировать.
Честно сказать, я по другому видел только в однопользовательских приложениях.
Честно сказать, я по другому видел только в однопользовательских приложениях.
Ну SQL то он вроде как statefull. Опять-же транзакции там.
Условно говоря (с синтаксисом не знаком, так что псевдокод):
Такое провернуть не удастся, т.к. каждый запрос может выполняться в совершенно разных соединениях. Правильно понял?
Условно говоря (с синтаксисом не знаком, так что псевдокод):
database execute query ("BEGIN")
database execute query (sql"select balance from users where id=${one}")
if balance >= amount:
database execute query (sql"update users set balance=balance - ${amount} where id=${one}")
database execute query (sql"update users set balance=balance + ${amount} where id=${two}")
database execute query ("COMMIT")
Такое провернуть не удастся, т.к. каждый запрос может выполняться в совершенно разных соединениях. Правильно понял?
А зачем 2 отдельных метода database.execute() и database.query()? разве нельзя connection получать/освобождать внутри query?
Или вы еще и batch query используете?
Или вы еще и batch query используете?
Иногда хочется композировать запросы:
или даже так можно:
batch update тоже есть, да.
database.execute { connection =>
val result = database.query(sql"select 1 from dual")(connection)
database.update(sql"update users set result = ${result}")(connection)
}
или даже так можно:
database.execute { connection =>
database.update(sql"update table1 set field1 = 1 where key1 = 1") ~
database.update(sql"update table2 set field2 = 2 where key2 = 2")
}
batch update тоже есть, да.
Интересно было почитать. Смешно, когда я прочитал слово Oracle, который вы любите и умеете готовить, я в первую очередь подумал о джаве, а совсем не о СУБД.
Скальный паттерн освобождения ресурсов, который вы использовали в execute, называется Loan: wiki.scala-lang.org/display/SYGN/Loan. Необходимая вещь, конечно же.
Еще очень правильным решением было отказаться от ORM, вот и ребята из Слика считают, что ORM — большая ошибка для крупных проектов, что реляционные абстракции и абстракции ОО плохо соответствуют друг другу, и поэтому не стоит маскировать реляционные таблички и кортежи под объекты: slick.typesafe.com/talks/scalax2012/Slick_ScalaExchange_2012.pdf, слайд 11.
Мне кажется, что соединение с БД проще сделать неявным параметром у запроса, чтобы не передавать его каждый раз в дополнительных скобках.
Я сильно подозреваю, что Идея поняла вашу строковую интерполяцию для sql потому, что в других движках тоже есть похожие с такими же названиями. В Слике есть. Вообще, я остаюсь большим поклонником Слика, жалко, что Оракл-плагин у него платный, но для «Тинькова» наверно это не так важно. И для Монго они тоже активно разрабатывают плагинчик.
Реактивность для jdbc обычно достигается выделением фиксированого количества тредов под соединения с БД, а реактивный код к этим тредам обращается. Это позволяет снять пиковую нагрузку на БД, когда количество замороженных тредов начинает зашкаливать. Этим активно пользуются play-разработчики, например.
Скальный паттерн освобождения ресурсов, который вы использовали в execute, называется Loan: wiki.scala-lang.org/display/SYGN/Loan. Необходимая вещь, конечно же.
Еще очень правильным решением было отказаться от ORM, вот и ребята из Слика считают, что ORM — большая ошибка для крупных проектов, что реляционные абстракции и абстракции ОО плохо соответствуют друг другу, и поэтому не стоит маскировать реляционные таблички и кортежи под объекты: slick.typesafe.com/talks/scalax2012/Slick_ScalaExchange_2012.pdf, слайд 11.
Мне кажется, что соединение с БД проще сделать неявным параметром у запроса, чтобы не передавать его каждый раз в дополнительных скобках.
Я сильно подозреваю, что Идея поняла вашу строковую интерполяцию для sql потому, что в других движках тоже есть похожие с такими же названиями. В Слике есть. Вообще, я остаюсь большим поклонником Слика, жалко, что Оракл-плагин у него платный, но для «Тинькова» наверно это не так важно. И для Монго они тоже активно разрабатывают плагинчик.
Реактивность для jdbc обычно достигается выделением фиксированого количества тредов под соединения с БД, а реактивный код к этим тредам обращается. Это позволяет снять пиковую нагрузку на БД, когда количество замороженных тредов начинает зашкаливать. Этим активно пользуются play-разработчики, например.
Вот страничка Slick-плагина для Play, на которой написано как авторы плагина решили эту проблему в своем случае: github.com/freekh/play-slick/wiki/ScalaSlickThreads. А вот код для DBAction, который там описывается, но сам я этот код не стал читал github.com/freekh/play-slick/blob/master/src/main/scala/play/api/db/slick/DBAction.scala
Спасибо за идею про неявный параметр. Пока не вкручивал, но попробую обязательно, жаль что вылезает слово
От ORM не отказывались, его у нас не было ;)
Про пулы — да, это не про реактивность, а вообще про то, как справляться с нагрузкой. Но таки это ограничивает количество одновременно обрабатываемых запросов, а с реактивностью за счёт асинхронности такого быть не должно. Короче, принципиально улучшить архитектуру поможет только переход на другую БД или реверс инжиниринг драйвера, второе очень неконструктивно по ряду факторов.
implicit
в прикладном коде…От ORM не отказывались, его у нас не было ;)
Про пулы — да, это не про реактивность, а вообще про то, как справляться с нагрузкой. Но таки это ограничивает количество одновременно обрабатываемых запросов, а с реактивностью за счёт асинхронности такого быть не должно. Короче, принципиально улучшить архитектуру поможет только переход на другую БД или реверс инжиниринг драйвера, второе очень неконструктивно по ряду факторов.
Модель у нас не очень сложная, поэтому сознательно отказались от ORM.Но я конечно понял твою мысль.
Я так понимаю, что реактивность не просто в пуле тредов, а еще в том, как с ним взаимодействовать (можно использовать акторы например, или просто фьючи). А еще я не знал, но к некоторым СУБД разрабатываются асинхронные коннекторы (Postgres и MySQL)
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Работа с реляционными базами данных в Scala