I would like to run PureScript code in AWS Lambda. Normally, if a lambda function has to call the database, the connection is stored in a global variable. Excuse me if the following JavaScript code is syntactically wrong as I don't write JavaScript but something like below is what I want(this is the code expected by AWS Lambda):
function createConnection() {
connection = SomeDBLibrary.createConnection(username, pwd)
return connection;
}
global.db_connection = createConnection();
handler = (event, context, callback) => {
// TODO use global.db_connection here to call the database
};
module.exports {
handler: handler
};
How do I write the equivalent PureScript to get such code? Currently, I call the createConnection() function in each of my CRUD methods which is wasteful.
Edit 1:
My actual PureScript code is below. The function which I want to call only once is aPool. However, I believe it's being called everytime one hits the /list endpoint. I would like it to be hit only once when the handler is loaded for the first time.
module Main where
import Prelude hiding (apply)
import Data.Bifunctor (lmap)
import Data.Either (Either)
import Database.Postgres (Client, ClientConfig, ConnectionInfo, Pool, Query(Query), connectionInfoFromConfig, defaultPoolConfig, mkPool, query_, withClient)
import Effect (Effect)
import Effect.Aff (Aff)
import Effect.Aff.Class (liftAff)
import Effect.Class (class MonadEffect, liftEffect)
import Effect.Console (log)
import Effect.Exception (Error, error)
import Foreign (Foreign)
import Node.Express.App (App, get)
import Node.Express.Handler (Handler)
import Node.Express.Response (sendJson)
import Simple.JSON as JSON
import Network.AWS.Lambda.Express as Lambda
clientConfig :: ClientConfig
clientConfig =
{ host: "localhost"
, database: "test"
, port: 5432
, user: "testuser"
, password: "test"
, ssl: false
}
type IndexedTodo = { id :: Int, description :: String, isdone :: Boolean }
connectionInfo :: ConnectionInfo
connectionInfo = connectionInfoFromConfig clientConfig defaultPoolConfig
println :: forall a. MonadEffect a => String -> a Unit
println str = liftEffect $ log str
aPool :: Effect Pool
aPool = do
println "In Pooler"
mkPool connectionInfo
withConnectionPool :: forall a. Effect Pool -> (Client -> Aff a) -> Aff a
withConnectionPool somePool f = do
pool <- liftEffect somePool
withClient pool \c -> f c
withConnection :: forall a. (Client -> Aff a) -> Aff a
withConnection = withConnectionPool aPool
read' :: forall a. JSON.ReadForeign a => Foreign -> Either Error a
read' = lmap (error <<< show) <<< JSON.read
readTodoList :: Aff (Array IndexedTodo)
readTodoList = do
let selectQuery = Query "select * from todos" :: Query IndexedTodo
withConnection $ query_ read' selectQuery
listTodosHandler :: Handler
listTodosHandler = do
userdata <- liftAff readTodoList
sendJson userdata
app :: App
app = do
get "/list" listTodosHandler
-- Define the AWS Lambda handler
handler :: Lambda.HttpHandler
handler = Lambda.makeHandler app
I think that this requires a FFI like:
foreign import dbConnection :: Effect Connection
with implementation:
exports.dbConnection = function() {
if(globals.dbConnection === undefined) {
globals.dbConnection = createConnection();
}
return globals.dbConnection;
}
Related
How is the createUser or updateUser using the unmarshalValueJs function and passing arguments.
package controllers.user
import play.api._
import play.api.mvc._
import play.api.libs.json._
import play.api.libs.json.Reads._
import play.api.libs.functional.syntax._
import services.user.UserServiceComponent
import domain.user.User
trait UserController extends Controller {
self: UserServiceComponent =>0
def emailAlreadyExists(implicit reads: Reads[String]) =
Reads[String](js => reads.reads(js).flatMap { e =>
userService.tryFindByEmail(e).map(_ => JsError("error.custom.emailAlreadyExists")).getOrElse(JsSuccess(e))
})
implicit val userReads = (__ \ "email").read[String](email andKeep emailAlreadyExists)
.map(resource => UserResource(resource))
implicit val userWrites = new Writes[User] {
override def writes(user: User): JsValue = {
Json.obj(
"id" -> user.id,
"email" -> user.email
)
}
}
What create User passes to the unmarshalJsValue?
Where does resource come from?
def createUser = Action(parse.json) {request =>
unmarshalJsValue(request) { resource: UserResource =>
val user = User(Option.empty, resource.email)
userService.createUser(user)
Created
}
}
def updateUser(id: Long) = Action(parse.json) {request =>
unmarshalJsValue(request) { resource: UserResource =>
val user = User(Option(id), resource.email)
userService.updateUser(user)
NoContent
}
}
def findUserById(id: Long) = Action {
val user = userService.tryFindById(id)
if (user.isDefined) {
Ok(Json.toJson(user))
} else {
NotFound
}
}
def deleteUser(id: Long) = Action {
userService.delete(id)
NoContent
}
What is R over here?
What get passed back to the createUser?
def unmarshalJsValue[R](request: Request[JsValue])(block: R => Result)(implicit rds : Reads[R]): Result =
request.body.validate[R](rds).fold(
valid = block,
invalid = e => {
val error = e.mkString
Logger.error(error)
BadRequest(error)
}
)
}
case class UserResource(email: String)
R is a type variable. The definition def unmarshalJsValue[R](request: Request[JsValue])(block: R => Result)(implicit rds : Reads[R]) reads:
I'm a method called unmarshallJsValue.
I require a type parameter that could potentially be anything.
I require a value of Request[JsValue] (presumably a request contianin a JSON entity).
I require a function that, given some value of type R, produces a Result.
I require a value to be in implicit scope that is a Reads for type R. In other words, I need a way to convert a JsValue to an R, whatever R might happen to be. (If you look at the docs for Reads, you'll notice that its only method, reads, has this effect.)
To put it all together, entire function just says, give me some block of code that produces a Result from some piece of data I know how to convert to from JSON. The body of the function simply attempts to convert the JSON to the desired type (known as unmarshalling). If this succeeds, it runs the block. Otherwise, you get a BadRequest client error.
The compiler can't know whether the JSON that gets provided in real life by the client will be convertible to some type R, but it can require a JSON -> R converter, and some error handling code if it fails on real data.
I'm trying to build a REST API using play 2.0. I have a User case class that contains some fields (like username & password) that shouldn't be updatable by the updateMember method.
Is there a good, functional way, of dealing with multiple Options somehow, because request.body.asJson returns an Option[JsValue], and my user lookup also returns an Option:
package controllers.api
import org.joda.time.LocalDate
import play.api.Play.current
import play.api.db.slick.{DB, Session}
import play.api.mvc._
import play.api.libs.json._
import play.api.libs.functional.syntax._
import models.{Gender, User, UserId}
import repositories.UserRepository
object Member extends Controller {
def updateMember(id: Long) = Action {
DB.withSession {
implicit session: Session =>
val json: JsValue = request.body.asJson // how to deal with this?
val repository = new UserRepository
repository.findById(new UserId(id)).map {
user =>
def usernameAppender = __.json.update(
__.read[JsObject].map { o => o ++ Json.obj("username" -> user.username) }
)
json.transform(usernameAppender) // transform not found
Ok("updated")
}.getOrElse(NotFound)
}
}
}
I could move the map call to where I try to parse the request, but then inside there I guess I'd need another map over the user Option like I already have. So in that style, I'd need a map per Option.
Is there a better way of dealing with multiple Options like this in FP?
You're basically dealing with a nested monad, and the main tool for working with such is flatMap, particularly if both options being None has the same semantic meaning to your program:
request.body.asJson.flatMap { requestJson =>
val repository = new UserRepository
repository.findById(new UserId(id)).map { user =>
def usernameAppender = __.json.update(
__.read[JsObject].map { o => o ++ Json.obj("username" -> user.username) }
)
requestJson.transform(usernameAppender)
Ok("updated") // EDIT: Do you not want to return the JSON?
}
}.getOrElse(NotFound)
But it might also be the case that your Nones have different meanings, in which case, you probably just want to pattern match, and handle the error cases separately:
request.body.asJson match {
case Some(requestJson) =>
val repository = new UserRepository
repository.findById(new UserId(id)).map { user =>
def usernameAppender = __.json.update(
__.read[JsObject].map { o => o ++ Json.obj("username" -> user.username) }
)
requestJson.transform(usernameAppender)
Ok("updated")
}.getOrElse(NotFound)
case None => BadRequest // Or whatever you response makes sense for this case
}
I am writing a Play (2.2) controller in Scala, which should return the result of a query against OrientDB. Now, I have succeeded in writing a synchronous version of said controller, but I'd like to re-write it to work asynchronously.
My question is; given the below code (just put together for demonstration purposes), how do I re-write my controller to interact asynchronously with OrientDB (connecting and querying)?
import play.api.mvc.{Action, Controller}
import play.api.libs.json._
import com.orientechnologies.orient.`object`.db.OObjectDatabasePool
import java.util
import com.orientechnologies.orient.core.sql.query.OSQLSynchQuery
import scala.collection.JavaConverters._
object Packages extends Controller {
def packages() = Action { implicit request =>
val db = OObjectDatabasePool.global().acquire("http://localhost:2480", "reader", "reader")
try {
db.getEntityManager().registerEntityClass(classOf[models.Package])
val packages = db.query[util.List[models.Package]](new OSQLSynchQuery[models.Package]("select from Package")).asScala.toSeq
Ok(Json.obj(
"packages" -> Json.toJson(packages)
))
}
finally {
db.close()
}
}
}
EDIT:
Specifically, I wish to use OrientDB's asynchronous API. I know that asynchronous queries are supported by the API, though I'm not sure if you can connect asynchronously as well.
Attempted Solution
Based on Jean's answer, I've tried the following asynchronous implementation, but it fails due to a compilation error value execute is not a member of Nothing possible cause: maybe a semicolon is missing before 'value execute'?:
def getPackages(): Future[Seq[models.Package]] = {
val db = openDb
try {
val p = promise[Seq[models.Package]]
val f = p.future
db.command(
new OSQLAsynchQuery[ODocument]("select from Package",
new OCommandResultListener() {
var acc = List[ODocument]()
#Override
def result(iRecord: Any): Boolean = {
val doc = iRecord.asInstanceOf[ODocument]
acc = doc :: acc
true
}
#Override
def end() {
// This is just a dummy
p.success(Seq[models.Package]())
}
// Fails
})).execute()
f
}
finally {
db.close()
}
}
One way could be to start a promise, return the future representing the result of that promise, locally accumulate the results as they come and complete de promise ( thus resolving the future ) when orient db notifies you that the command has completed.
def executeAsync(osql: String, params: Map[String, String] = Map()): Future[List[ODocument]] = {
import scala.concurrent._
val p = promise[List[ODocument]]
val f =p.future
val req: OCommandRequest = database.command(
new OSQLAsynchQuery[ODocument]("select * from animal where name = 'Gipsy'",
new OCommandResultListener() {
var acc = List[ODocument]()
#Override
def result(iRecord:Any):Boolean= {
val doc = iRecord.asInstanceOf[ODocument]
acc=doc::acc
true
}
#Override
def end() {
p.success(acc)
}
}))
req.execute()
f
}
Be careful though, to enable graph navigation and field lazy loading, orientdb objects used to keep an internal reference to the database instance they were loaded from ( or to depend on a threadlocal database connected instance ) for lazily loading elements from the database. Manipulating these objects asynchronously may result in loading errors. I haven't checked changes from 1.6 but that seemed to be deeply embedded in the design.
It's as simple as wrapping the blocking call in a Future.
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import scala.concurrent.Future
object Packages extends Controller {
def packages = Action.async { implicit request =>
val db = OObjectDatabasePool.global().acquire("http://localhost:2480", "reader", "reader")
db.getEntityManager().registerEntityClass(classOf[models.Package])
val futureResult: Future[Result] = Future(
db.query[util.List[models.Package]](new OSQLSynchQuery[models.Package]("select from Package")).asScala.toSeq
).map(
queryResult => Ok(Json.obj("packages" -> Json.toJson(packages)))
).recover {
// Handle each of the exception cases legitimately
case e: UnsupportedOperationException => UnsupportedMediaType(e.getMessage)
case e: MappingException => BadRequest(e.getMessage)
case e: MyServiceException => ServiceUnavailable(e.toString)
case e: Throwable => InternalServerError(e.toString + "\n" + e.getStackTraceString)
}
futureResult.onComplete { case _ =>
db.close()
}
futureResult
}
}
Note that I did not compile the code. There is a lot of room to improve the code.
Here is code in Scala:
def write() = {
try {
val out = new PrintWriter(new BufferedWriter(new FileWriter(fileName, true)))
out.println("123")
out.close
} catch {
case e: IOException => {}
}
//finally {
//out.close // ops, it's not visible in this context
//}
}
It would be better to have "out.close" in finally block, isn't it? But I don't want to use var.
My question is, how do I achieve that?
A variable defined in a block is local to that block. So if you insist on using try/finally manually you will have to move the val out of the block.
However, what you are trying to achieve is to create a resource, use it in a block, and call a close method on it when leaving the block, no matter whether you leave the block normally or abnormally via an exception. This is an extremely common problem, so there is already a library for it, called Scala ARM. ARM stands for automatic resource management.
Here is the basic usage:
import resource._
for(input <- managed(new FileInputStream("test.txt")) {
// Code that uses the input as a FileInputStream
}
There was some talk of moving this construct to the scala standard library, so in the future you probably won't even need an external dependency.
I would recommend using a library for something like this. It is just one more line in your build.sbt. But for educational purposes, here is how you would roll your own:
def managed[T <: AutoCloseable](resource:T) = new Traversable[T] {
def foreach[U](f:T=>U) {
try {
f(resource)
} finally {
resource.close()
}
}
}
And here is how to use it
scala> for(reader<-managed(new java.io.FileReader("/etc/passwd"))) { println(reader.read()) }
114
scala> for(reader<-managed(new java.io.FileReader("/etc/shadow"))) { println(reader.read()) }
java.io.FileNotFoundException: /etc/shadow (Permission denied)
...
You will still get the exception, but close will be called. Of course if close throws an exception as well you this will hide the original exception. Little details like this are probably handled better in scala ARM.
This is what I use to manage closable resources passed to function returning and not retuning futures
def withClosable[ T, C <: Closeable ]( closable: C )( f: C ⇒ T ) = try { f( closable ) } finally { IOUtils closeQuietly closable }
def withFutureClosable[ T <: Future[Any], C <: Closeable ]( closable: C )( f: C ⇒ T ) = f( closable ) andThen {
case _ => IOUtils closeQuietly closable
}
}
I use IOUtils from commons-io to simplify the call to actually close the resource. A simple try { closable.close() } catch { case _ => /* blah */ } would do
Example usage:
withClosable(new FileInpustream("f")) { stream => /* read the stream */ }
The loan pattern is more usual for this use case, but since anything goes on Stack Overflow, you can construct the expression you're looking for with Try.
Try deserves more exposure as a handy tool.
scala> import util._
import util._
scala> import io._
import io._
Try to open the file --
scala> def f =
| Try (Source.fromFile("foo.text")) map { in =>
then do something with it, packaging the result in a tuple with the i/o source --
note that when you do a value definition in a for-comprehension, this is what it does --
| (in, Try(in.getLines.mkString("/")))
| } flatMap {
then close the source and yield the result of the computation --
| case (in, res) =>
| in.close()
| res
| }
f: scala.util.Try[String]
Uncommented:
scala> def f =
| Try (Source.fromFile("foo.text")) map { in =>
| (in, Try(in.getLines.mkString("/")))
| } flatMap {
| case (in, res) =>
| in.close()
| res
| }
f: scala.util.Try[String]
scala> f
res1: scala.util.Try[String] = Failure(java.io.FileNotFoundException: foo.text (No such file or directory))
Create the test file with some classic humour text, then try again:
scala> f
res2: scala.util.Try[String] = Success(Now is the time/for all good dogs/to lie.)
You can sugarcoat it as a for-comprehension, though observe the extra flatten, since you get a map instead of flatMap from the yield:
scala> def g = (for {
| in <- Try (Source.fromFile("foo.text"))
| res = Try(in.getLines.mkString("/"))
| } yield {
| in.close()
| res
| }).flatten
g: scala.util.Try[String]
scala> g
res2: scala.util.Try[String] = Success(Now is the time/for all good dogs/to lie.)
What if we want to fail if the close fails?
I don't want to type in all that stuff into the REPL again!
scala> :hi // :history
[snip]
2490 def g = (for {
2491 in <- Try (Source.fromFile("foo.text"))
2492 res = Try(in.getLines.mkString("/"))
2493 } yield {
2494 in.close()
2495 res
2496 }).flatten
2497 :hi
scala> :edit 2490+7 // or just :edit 2490-
+import util._
+import io._
+def g = (for {
+ in <- Try (Source.fromFile("foo.text"))
+ res = Try(in.getLines.mkString("/"))
+} yield {
+ val ok = Try(in.close())
+ res transform (s => ok map (_ => s), new Failure(_))
+}).flatten
+
import util._
import io._
g: scala.util.Try[String]
The transform says that if the computation succeeded, convert that success to failure if the result of the close, ok, is a failure; and on a failed computation, keep that failure, though some people prefer to add up their failures.
Don't you know try/catch is so 1990s. </droll> (droll does not mean troll.)
Or just:
val is = new FileInputStream(file)
val result = try {
// do stuff
} finally {
is.close()
}
Because there's no way is can be null.
In your question code, just move val out outside try block. That way it will be identical to what Java AutoCloseable does, except for null case. But in Scala you don't have to deal with nullables.
I am using anorm to access data on my DB. The DB is written to using another service, which is made in Java, and persist using ebean.
I have the following scala object
import java.sql.Connection
import scala.concurrent.{ Future, blocking, future }
import scala.concurrent.ExecutionContext.Implicits.global
import anorm.{ SQL, SqlQuery, SqlRow, sqlToSimple, toParameterValue }
import play.api.Logger
import play.api.Play.current
import play.api.db.DB
object Queries {
private val readDataSource: String = play.Configuration.root().getString("data.provider.api.source", "default")
//better IO execution context
import play.api.libs.concurrent.Execution.Implicits.defaultContext
private val dataSetDescription: SqlQuery = SQL("SELECT DISTINCT platform, name FROM data_nugget")
private val identityCreationTime: SqlQuery = SQL("SELECT i.creation_time FROM identity i WHERE platform = {pfm} AND userid = {uid};")
private val identityData: SqlQuery = SQL("SELECT n.name, n.value FROM data_nugget n WHERE platform = {pfm} AND userid = {uid};")
private val playerData: SqlQuery = SQL("SELECT n.platform, n.name, n.value, r.userid, r.registration_time FROM data_nugget n JOIN registration r ON n.platform=r.platform AND n.userid=r.userid WHERE r.playerid = {pid} AND r.application = {app};")
private def withAsyncAnormConnection(function: Connection => Stream[SqlRow]): Future[List[SqlRow]] = {
future {
blocking {
DB.withConnection(readDataSource)(c => function(c)).toList
}
}
}
def fetchDistinctDataNames(): Future[List[SqlRow]] = {
withAsyncAnormConnection(implicit c => dataSetDescription())
}
def fetchIdentityCreationTime(platform: String, userid: String): Future[List[SqlRow]] = {
withAsyncAnormConnection(implicit c => identityCreationTime.on("pfm" -> platform, "uid" -> userid)())
}
def fetchIdentityData(platform: String, userid: String): Future[List[SqlRow]] = {
withAsyncAnormConnection(implicit c => identityData.on("pfm" -> platform, "uid" -> userid)())
}
def fetchRegistrationData(game: String, playerid: String): Future[List[SqlRow]] = {
withAsyncAnormConnection(implicit c => playerData.on("app" -> game, "pid" -> playerid)())
}
}
I use it to wrap my SQL queries executions within futures.
Everytime I run any of those queries I obtain an error with this following stack trace :
(Error,com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1073)
com.mysql.jdbc.SQLError.createSQLException(SQLError.java:987)
com.mysql.jdbc.SQLError.createSQLException(SQLError.java:982)
com.mysql.jdbc.SQLError.createSQLException(SQLError.java:927)
com.mysql.jdbc.ResultSetImpl.checkClosed(ResultSetImpl.java:794)
com.mysql.jdbc.ResultSetImpl.next(ResultSetImpl.java:7139)
anorm.Sql$$anonfun$resultSetToStream$1.apply(Anorm.scala:527)
anorm.Sql$$anonfun$resultSetToStream$1.apply(Anorm.scala:527)
anorm.Useful$.unfold(Anorm.scala:315)
anorm.Useful$$anonfun$unfold$1.apply(Anorm.scala:317)
anorm.Useful$$anonfun$unfold$1.apply(Anorm.scala:317)
scala.collection.immutable.Stream$Cons.tail(Stream.scala:1078)
scala.collection.immutable.Stream$Cons.tail(Stream.scala:1070)
scala.collection.immutable.Stream.foreach(Stream.scala:548)
scala.collection.generic.Growable$class.$plus$plus$eq(Growable.scala:48)
scala.collection.mutable.ListBuffer.$plus$plus$eq(ListBuffer.scala:178)
scala.collection.mutable.ListBuffer.$plus$plus$eq(ListBuffer.scala:45)
scala.collection.TraversableLike$class.to(TraversableLike.scala:629)
scala.collection.AbstractTraversable.to(Traversable.scala:105)
scala.collection.TraversableOnce$class.toList(TraversableOnce.scala:243)
scala.collection.AbstractTraversable.toList(Traversable.scala:105)
controllers.dataprovider.data.Queries$$anonfun$withAsyncAnormConnection$1$$anonfun$apply$1.apply(Queries.scala:31)
controllers.dataprovider.data.Queries$$anonfun$withAsyncAnormConnection$1$$anonfun$apply$1.apply(Queries.scala:31)
scala.concurrent.impl.ExecutionContextImpl$DefaultThreadFactory$$anon$2$$anon$3.block(ExecutionContextImpl.scala:44)
scala.concurrent.forkjoin.ForkJoinPool.managedBlock(ForkJoinPool.java:2803)
scala.concurrent.impl.ExecutionContextImpl$DefaultThreadFactory$$anon$2.blockOn(ExecutionContextImpl.scala:41)
scala.concurrent.package$.blocking(package.scala:50)
controllers.dataprovider.data.Queries$$anonfun$withAsyncAnormConnection$1.apply(Queries.scala:30)
controllers.dataprovider.data.Queries$$anonfun$withAsyncAnormConnection$1.apply(Queries.scala:30)
scala.concurrent.impl.Future$PromiseCompletingRunnable.liftedTree1$1(Future.scala:24)
scala.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:24)
scala.concurrent.forkjoin.ForkJoinTask$AdaptedRunnableAction.exec(ForkJoinTask.java:1417)
scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:262)
scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:975)
scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1478)
scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:104))
I already encountered those on previous Java services using jdbc but here I am not touching the ResultSet, and I am even returning a list asap from the Stream of rows I receive from the connection.
What is happening? Where am I closing the ResultSet? What did I refactor wrong?
As a note, on the prototype of this service (when everything was in the controller) I used to have the SQL("...") directly in the code with something like that:
future {
blocking {
DB.withConnection(implicit c => {
SQL("SELECT DISTINCT platform, name FROM data_nugget")().map(row => (row[String]("platform"), row[String]("name"))).toArray
})
}
}
and it worked just fine.
PS : Sorry for the long copy/paste of stacktrace and the code ... trying to be detailed.
I solved it myself and it is a very fine line.
I changed this function
private def withAsyncAnormConnection(function: Connection => Stream[SqlRow]): Future[List[SqlRow]] = {
future {
blocking {
DB.withConnection(readDataSource)(c => function(c)).toList
}
}
}
to THIS:
private def withAsyncAnormConnection(function: Connection => Stream[SqlRow]): Future[List[SqlRow]] = {
future {
blocking {
DB.withConnection(readDataSource)(c => function(c).toList)
}
}
}
The trick is that I am using the "loan-pattern" of withConnection, so I need to iter through the Stream to get all the rows before I release the connection.
The connection is alive only within this round brackets (c => function(c).toList)
There's a difference between the code that is working for you and the code that is not working. In your working example, you are calling map on the lazy Stream of Row instances. In the non-working example, you are calling toList without using map. Maybe map is forcing the full processing of the underlying ResultSet within the withConnection block and toList is not, leaving it lazy until you get outside of the withConnection block after which the underlying ResultSet is closed. Maybe you can modify your new code to try and map the results (mapping the Row to itself, no actual mapping logic) and see if this fixes anything.