-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cats-Effect intergation (WIP) #89
Open
KaranAhlawat
wants to merge
11
commits into
AugustNagro:master
Choose a base branch
from
KaranAhlawat:cats-effect-integration
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+561
−0
Open
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
4db6753
add: Cats-Effect intergation for Magnum (WIP)
KaranAhlawat 06dd9e5
remove: dependencey on natchez
KaranAhlawat 8130545
remove trace implicit, convert to Tagless Final style using Sync class
KaranAhlawat 994fd9e
setup Transactor class as entry point for connect and transact
KaranAhlawat 261a419
add: Add apply methods for Transactor, update tests
KaranAhlawat c31ff3a
refactor: simplify some context bounds
KaranAhlawat a171f6c
refactor: Simplify context bounds
KaranAhlawat 831253f
Use munit-cats-effect for tests, add some more apply methods
KaranAhlawat de569f2
use a Mutex if only 1 blocking thread is requested, minor refactorings
KaranAhlawat 8a48810
fix: getConnection and conn.close are blocking calls
KaranAhlawat e7dce61
make transactor fields private vals
KaranAhlawat File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
205 changes: 205 additions & 0 deletions
205
magnum-cats-effect/src/main/scala/com/augustnagro/magnum/magcats/Transactor.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
package com.augustnagro.magnum.magcats | ||
|
||
import cats.* | ||
import cats.effect.kernel.* | ||
import cats.effect.kernel.Outcome.* | ||
import cats.effect.std.* | ||
import cats.effect.syntax.all.* | ||
import cats.syntax.all.* | ||
import com.augustnagro.magnum.* | ||
|
||
import java.sql.Connection | ||
import javax.sql.DataSource | ||
import scala.util.control.NonFatal | ||
|
||
class Transactor[F[_]: Sync] private ( | ||
private val dataSource: DataSource, | ||
private val sqlLogger: SqlLogger, | ||
private val connectionConfig: Connection => Unit, | ||
private val rateLimiter: Option[Resource[F, Unit]] | ||
): | ||
private val makeConn = Resource.make(acquireConnection)(releaseConnection) | ||
|
||
def withSqlLogger(sqlLogger: SqlLogger): Transactor[F] = | ||
new Transactor( | ||
dataSource, | ||
sqlLogger, | ||
connectionConfig, | ||
rateLimiter | ||
) | ||
|
||
def withConnectionConfig( | ||
connectionConfig: Connection => Unit | ||
): Transactor[F] = | ||
new Transactor( | ||
dataSource, | ||
sqlLogger, | ||
connectionConfig, | ||
rateLimiter | ||
) | ||
|
||
def connect[A](f: DbCon ?=> A): F[A] = | ||
useRateLimitedConnection: cn => | ||
Sync[F].delay(connectionConfig(cn)) >> | ||
Sync[F].interruptible(f(using DbCon(cn, sqlLogger))) | ||
|
||
def transact[A](f: DbTx ?=> A): F[A] = | ||
useRateLimitedConnection: cn => | ||
Sync[F] | ||
.delay { | ||
connectionConfig(cn) | ||
cn.setAutoCommit(false) | ||
} >> | ||
Sync[F] | ||
.interruptible(f(using DbTx(cn, sqlLogger))) | ||
.guaranteeCase { | ||
case Succeeded(_) => Sync[F].blocking(cn.commit()) | ||
case Errored(_) | Canceled() => | ||
Sync[F].blocking(cn.rollback()) | ||
} | ||
|
||
private def useRateLimitedConnection[A](program: Connection => F[A]): F[A] = | ||
val io = makeConn.use(program) | ||
rateLimiter.fold(io)(_.surround(io)) | ||
|
||
private def acquireConnection: F[Connection] = | ||
KaranAhlawat marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Sync[F] | ||
.blocking(dataSource.getConnection()) | ||
.adaptError(t => SqlException("Unable to acquire DB Connection", t)) | ||
|
||
private def releaseConnection(conn: Connection): F[Unit] = | ||
KaranAhlawat marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if conn eq null then Sync[F].unit | ||
KaranAhlawat marked this conversation as resolved.
Show resolved
Hide resolved
|
||
else | ||
Sync[F] | ||
.blocking(conn.close()) | ||
.adaptError(t => SqlException("Unable to close DB connection", 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 magcats be more | ||
* memory efficient by limiting the number of blocking pool threads used. | ||
* Not needed if using a virtual-thread based blocking executor (e.g. via | ||
* evalOn) | ||
* @return | ||
* F[Transactor[F]] | ||
*/ | ||
def apply[F[_]: Async]( | ||
dataSource: DataSource, | ||
sqlLogger: SqlLogger, | ||
connectionConfig: Connection => Unit, | ||
maxBlockingThreads: Int | ||
): F[Transactor[F]] = | ||
assert(maxBlockingThreads > 0) | ||
|
||
val rateLimiter = | ||
if maxBlockingThreads == 1 then Mutex[F].map(_.lock) | ||
else Semaphore[F](maxBlockingThreads).map(_.permit) | ||
|
||
rateLimiter.map: rl => | ||
new Transactor( | ||
dataSource, | ||
sqlLogger, | ||
connectionConfig, | ||
Some(rl) | ||
) | ||
|
||
/** Construct a Transactor | ||
* | ||
* @param dataSource | ||
* Datasource to be used | ||
* @param sqlLogger | ||
* Logging configuration | ||
* @param maxBlockingThreads | ||
* Number of threads in your connection pool. This helps magcats be more | ||
* memory efficient by limiting the number of blocking pool threads used. | ||
* Not needed if using a virtual-thread based blocking executor (e.g. via | ||
* evalOn) | ||
* @return | ||
* F[Transactor[F]] | ||
*/ | ||
def apply[F[_]: Async]( | ||
dataSource: DataSource, | ||
sqlLogger: SqlLogger, | ||
maxBlockingThreads: Int | ||
): F[Transactor[F]] = | ||
apply(dataSource, sqlLogger, noOpConnectionConfig, maxBlockingThreads) | ||
|
||
/** Construct a Transactor | ||
* | ||
* @param dataSource | ||
* Datasource to be used | ||
* @param maxBlockingThreads | ||
* Number of threads in your connection pool. This helps magcats be more | ||
* memory efficient by limiting the number of blocking pool threads used. | ||
* Not needed if using a virtual-thread based blocking executor (e.g. via | ||
* evalOn) | ||
* @return | ||
* F[Transactor[F]] | ||
*/ | ||
def apply[F[_]: Async]( | ||
dataSource: DataSource, | ||
maxBlockingThreads: Int | ||
): F[Transactor[F]] = | ||
apply( | ||
dataSource, | ||
SqlLogger.Default, | ||
noOpConnectionConfig, | ||
maxBlockingThreads | ||
) | ||
|
||
/** Construct a Transactor | ||
* | ||
* @param dataSource | ||
* Datasource to be used | ||
* @param sqlLogger | ||
* Logging configuration | ||
* @param connectionConfig | ||
* Customize the underlying JDBC Connections | ||
* @return | ||
* F[Transactor[F]] | ||
*/ | ||
def apply[F[_]: Sync]( | ||
dataSource: DataSource, | ||
sqlLogger: SqlLogger, | ||
connectionConfig: Connection => Unit | ||
): F[Transactor[F]] = | ||
Sync[F].pure(new Transactor(dataSource, sqlLogger, connectionConfig, None)) | ||
|
||
/** Construct a Transactor | ||
* | ||
* @param dataSource | ||
* Datasource to be used | ||
* @param sqlLogger | ||
* Logging configuration | ||
* @return | ||
* F[Transactor[F]] | ||
*/ | ||
def apply[F[_]: Sync]( | ||
dataSource: DataSource, | ||
sqlLogger: SqlLogger | ||
): F[Transactor[F]] = | ||
apply(dataSource, sqlLogger, noOpConnectionConfig) | ||
|
||
/** Construct a Transactor | ||
* | ||
* @param dataSource | ||
* Datasource to be used | ||
* @return | ||
* F[Transactor[F]] | ||
*/ | ||
def apply[F[_]: Sync]( | ||
dataSource: DataSource | ||
): F[Transactor[F]] = | ||
apply(dataSource, SqlLogger.Default, noOpConnectionConfig) | ||
end Transactor |
28 changes: 28 additions & 0 deletions
28
magnum-cats-effect/src/main/scala/com/augustnagro/magnum/magcats/exports.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package com.augustnagro.magnum.magcats | ||
|
||
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
drop table if exists big_dec cascade; | ||
|
||
create table big_dec ( | ||
id int primary key, | ||
my_big_dec numeric | ||
); | ||
|
||
insert into big_dec values | ||
(1, 123), | ||
(2, null); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
DROP TABLE IF EXISTS car; | ||
|
||
CREATE TABLE car ( | ||
model VARCHAR(50) NOT NULL, | ||
id bigint PRIMARY KEY, | ||
top_speed INT NOT NULL, | ||
vin INT, | ||
color TEXT NOT NULL CHECK (color IN ('Red', 'Green', 'Blue')), | ||
created TIMESTAMP WITH TIME ZONE NOT NULL | ||
); | ||
|
||
INSERT INTO car (model, id, top_speed, vin, color, created) VALUES | ||
('McLaren Senna', 1, 208, 123, 'Red', '2024-11-24T22:17:30.000000000Z'::timestamptz), | ||
('Ferrari F8 Tributo', 2, 212, 124, 'Green', '2024-11-24T22:17:31.000000000Z'::timestamptz), | ||
('Aston Martin Superleggera', 3, 211, null, 'Blue', '2024-11-24T22:17:32.000000000Z'::timestamptz); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
drop table if exists my_user cascade; | ||
|
||
create table my_user ( | ||
first_name text not null, | ||
id bigint primary key generated always as identity | ||
); | ||
|
||
insert into my_user (first_name) values | ||
('George'), | ||
('Alexander'), | ||
('John'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
drop table if exists no_id; | ||
|
||
create table no_id ( | ||
created_at timestamptz not null default now(), | ||
user_name text not null, | ||
user_action text not null | ||
); | ||
|
||
insert into no_id values | ||
(timestamp '1997-08-15', 'Josh', 'clicked a button'), | ||
(timestamp '1997-08-16', 'Danny', 'opened a toaster'), | ||
(timestamp '1997-08-17', 'Greg', 'ran some QA tests'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
drop table if exists person cascade; | ||
|
||
create table person ( | ||
id bigint primary key, | ||
first_name varchar(50), | ||
last_name varchar(50) not null, | ||
is_admin boolean not null, | ||
created timestamptz not null, | ||
social_id UUID | ||
); | ||
|
||
insert into person (id, first_name, last_name, is_admin, created, social_id) values | ||
(1, 'George', 'Washington', true, now(), 'd06443a6-3efb-46c4-a66a-a80a8a9a5388'), | ||
(2, 'Alexander', 'Hamilton', true, now(), '529b6c6d-7228-4da5-81d7-13b706f78ddb'), | ||
(3, 'John', 'Adams', true, now(), null), | ||
(4, 'Benjamin', 'Franklin', true, now(), null), | ||
(5, 'John', 'Jay', true, now(), null), | ||
(6, 'Thomas', 'Jefferson', true, now(), null), | ||
(7, 'James', 'Madison', true, now(), null), | ||
(8, null, 'Nagro', false, timestamp '1997-08-12', null); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you see any downside of transact being interruptible @guizmaii ?
I can see the benefit if the user has a request timeout that triggers during a long-running transaction.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see any downside. That's even something I might want to backport to the ZIO module 🤔
Note that this interruption is quite brutal: the only way for Cats to interrupt the
f
call is to callThread.interrupt
, which will stop the thread running the code.Could this be an issue? 🤔
I don't think so 🤔