Skip to content

Commit

Permalink
Utility improvements (#85)
Browse files Browse the repository at this point in the history
* Utility improvements

* Add connect & transact methods directly on Transactor
* Add commonly used classes under magnum.common.* export, so users don't get swamped by every class living in the magnum package
* Add magzio.Transactor.semaphore to save memory when not using the Virtual-Thread based blocking executor

* actually use semaphore

* remove .uninterruptible from zio connect method

* Review: Prefer layers to build `Transactor` instances (#88)

* use ZLayer.fromZIO instead of Semaphore.unsafe

* null check in zio Transactor.releaseConnection.. just in case

* try fix ci

* formatting

---------

Co-authored-by: Jules Ivanic <[email protected]>
  • Loading branch information
AugustNagro and guizmaii authored Jan 15, 2025
1 parent db93b32 commit 31de10b
Show file tree
Hide file tree
Showing 34 changed files with 501 additions and 280 deletions.
7 changes: 3 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,12 @@ permissions:
jobs:
ci:
runs-on: ubuntu-latest
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
- uses: olafurpg/setup-scala@v14
with:
distribution: temurin
java-version: 17
check-latest: true
cache: 'sbt'
- name: run tests
run: sbt 'clean; scalafmtCheckAll; test'
18 changes: 12 additions & 6 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ addCommandAlias("fmt", "scalafmtAll")

val testcontainersVersion = "0.41.4"
val circeVersion = "0.14.10"
val munitVersion = "1.0.2"
val postgresDriverVersion = "42.7.4"

lazy val root = project
.in(file("."))
Expand All @@ -51,10 +53,10 @@ lazy val magnum = project
Test / fork := true,
publish / skip := false,
libraryDependencies ++= Seq(
"org.scalameta" %% "munit" % "1.0.2" % Test,
"org.scalameta" %% "munit" % munitVersion % Test,
"com.dimafeng" %% "testcontainers-scala-munit" % testcontainersVersion % Test,
"com.dimafeng" %% "testcontainers-scala-postgresql" % testcontainersVersion % Test,
"org.postgresql" % "postgresql" % "42.7.4" % Test,
"org.postgresql" % "postgresql" % postgresDriverVersion % Test,
"com.dimafeng" %% "testcontainers-scala-mysql" % testcontainersVersion % Test,
"com.mysql" % "mysql-connector-j" % "9.0.0" % Test,
"com.h2database" % "h2" % "2.3.232" % Test,
Expand All @@ -73,8 +75,8 @@ lazy val magnumPg = project
Test / fork := true,
publish / skip := false,
libraryDependencies ++= Seq(
"org.postgresql" % "postgresql" % "42.7.4" % "provided",
"org.scalameta" %% "munit" % "1.0.2" % Test,
"org.postgresql" % "postgresql" % postgresDriverVersion % "provided",
"org.scalameta" %% "munit" % munitVersion % Test,
"com.dimafeng" %% "testcontainers-scala-munit" % testcontainersVersion % Test,
"com.dimafeng" %% "testcontainers-scala-postgresql" % testcontainersVersion % Test,
"io.circe" %% "circe-core" % circeVersion % Test,
Expand All @@ -85,11 +87,15 @@ lazy val magnumPg = project

lazy val magnumZio = project
.in(file("magnum-zio"))
.dependsOn(magnum % "compile->compile;test->test")
.dependsOn(magnum)
.settings(
Test / fork := true,
publish / skip := false,
libraryDependencies ++= Seq(
"dev.zio" %% "zio" % "2.1.12" % Provided
"dev.zio" %% "zio" % "2.1.12" % Provided,
"org.scalameta" %% "munit" % munitVersion % Test,
"com.dimafeng" %% "testcontainers-scala-munit" % testcontainersVersion % Test,
"com.dimafeng" %% "testcontainers-scala-postgresql" % testcontainersVersion % Test,
"org.postgresql" % "postgresql" % postgresDriverVersion % Test
)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
package com.augustnagro.magnum.magzio

import com.augustnagro.magnum.{DbCon, DbTx, SqlException, SqlLogger}
import zio.{Semaphore, Task, Trace, UIO, ULayer, ZIO, ZLayer}

import java.sql.Connection
import javax.sql.DataSource
import scala.util.control.NonFatal

class Transactor private (
dataSource: DataSource,
sqlLogger: SqlLogger,
connectionConfig: Connection => Unit,
semaphore: Option[Semaphore]
):

def withSqlLogger(sqlLogger: SqlLogger): Transactor =
new Transactor(
dataSource,
sqlLogger,
connectionConfig,
semaphore
)

def withConnectionConfig(connectionConfig: Connection => Unit): Transactor =
new Transactor(
dataSource,
sqlLogger,
connectionConfig,
semaphore
)

def connect[A](f: DbCon ?=> A)(using Trace): Task[A] =
val zio = ZIO.blocking(
ZIO.acquireReleaseWith(acquireConnection)(releaseConnection)(cn =>
ZIO.attempt {
connectionConfig(cn)
f(using DbCon(cn, sqlLogger))
}
)
)
semaphore.fold(zio)(_.withPermit(zio))

def transact[A](f: DbTx ?=> A)(using Trace): Task[A] =
val zio = ZIO.blocking(
ZIO.acquireReleaseWith(acquireConnection)(releaseConnection)(cn =>
ZIO.attempt {
connectionConfig(cn)
cn.setAutoCommit(false)
try
val res = f(using DbTx(cn, sqlLogger))
cn.commit()
res
catch
case NonFatal(t) =>
cn.rollback()
throw t
}.uninterruptible
)
)
semaphore.fold(zio)(_.withPermit(zio))

private def acquireConnection(using Trace): Task[Connection] =
ZIO
.attempt(dataSource.getConnection())
.mapError(t => SqlException("Unable to acquire DB Connection", t))

private def releaseConnection(con: Connection)(using Trace): UIO[Unit] =
if con eq null then ZIO.unit
else
ZIO
.attempt(con.close())
.orDieWith(t =>
SqlException("Unable to close DB Connection, will die", t)
)
end Transactor

object Transactor:
private val noOpConnectionConfig: Connection => Unit = _ => ()

/** Construct a Transactor
*
* @param dataSource
* Datasource to be used
* @param sqlLogger
* Logging configuration
* @param connectionConfig
* Customize the underlying JDBC Connections
* @param maxBlockingThreads
* Number of threads in your connection pool. This helps magzio be more
* memory efficient by limiting the number of blocking pool threads used.
* Not needed if using the ZIO virtual-thread based blocking executor
* @return
* Transactor UIO
*/
def layer(
dataSource: DataSource,
sqlLogger: SqlLogger,
connectionConfig: Connection => Unit,
maxBlockingThreads: Option[Int]
): ULayer[Transactor] =
ZLayer.fromZIO {
ZIO
.fromOption(maxBlockingThreads)
.flatMap(threads => Semaphore.make(threads))
.unsome
.map(semaphoreOpt =>
new Transactor(
dataSource,
sqlLogger,
connectionConfig,
semaphoreOpt
)
)
}

/** Construct a Transactor
*
* @param dataSource
* Datasource to be used
* @param sqlLogger
* Logging configuration
* @param connectionConfig
* Customize the underlying JDBC Connections
* @return
* Transactor UIO
*/
def layer(
dataSource: DataSource,
sqlLogger: SqlLogger,
connectionConfig: Connection => Unit
): ULayer[Transactor] =
layer(
dataSource,
sqlLogger,
connectionConfig,
None
)

/** Construct a Transactor
*
* @param dataSource
* Datasource to be used
* @param sqlLogger
* Logging configuration
* @return
* Transactor UIO
*/
def layer(dataSource: DataSource, sqlLogger: SqlLogger): ULayer[Transactor] =
layer(dataSource, sqlLogger, noOpConnectionConfig, None)

/** Construct a Transactor
*
* @param dataSource
* Datasource to be used
* @return
* Transactor UIO
*/
def layer(dataSource: DataSource): ULayer[Transactor] =
layer(dataSource, SqlLogger.Default, noOpConnectionConfig, None)

/** Construct a Transactor
*
* @param dataSource
* Datasource to be used
* @param connectionConfig
* Customize the underlying JDBC Connections
* @return
* Transactor UIO
*/
def layer(
dataSource: DataSource,
connectionConfig: Connection => Unit
): ULayer[Transactor] =
layer(dataSource, SqlLogger.Default, connectionConfig, None)

end Transactor
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.augustnagro.magnum.magzio

export com.augustnagro.magnum.{
sql,
batchUpdate,
DbCon,
DbTx,
DbType,
Id,
ImmutableRepo,
NullOrder,
Repo,
SeekDir,
SortOrder,
Spec,
SqlName,
SqlNameMapper,
Table,
TableInfo,
DbCodec,
Frag,
ClickhouseDbType,
OracleDbType,
PostgresDbType,
SqliteDbType,
MySqlDbType,
H2DbType
}
Loading

0 comments on commit 31de10b

Please sign in to comment.