longevity
longevity copied to clipboard
Expose underlying database connections to the users
Let users have a back door to the database to do whatever they need to do. They can always open up another connection themselves; Having two database connections open from the same app to the same database is workable, but less than ideal. (Actually, in SQLite, this probably isn't even workable, as SQLite has single-writer model.)
This is slightly trickier than you might expect, because each back end has their own connection API, and these connection APIs are defined in optional dependencies. So we have to be careful where to expose these methods so that things don't break when the optional dependency is missing. We've handled this kind of thing before. See for example implicit def longevity.persistence.repoToAkkaStreamsRepo
.
Probably the best signatures for these methods would be something like:
def mongoClientOpt: Option[com.mongodb.MongoClient]
def mongoDatabaseOpt: Option[com.mongodb.client.MongoDatabase]
def jdbcConnectionOpt: Option[java.sql.Connection]
def cassandraSessionOpt: Option[com.datastax.driver.core.Session]
A couple of notes here:
- Normally the
MongoClient
should be sufficient, but in some circumstances, theMongoDatabase
instance may be needed. (In case they need to connect to the admin database for some reason.) - We could choose to not wrap in Options, and just throw exception if the connection is closed. (See e.g.
private lazy val session
inMongoRepo
.) But it's probably better to wrap in an option than introduce an API method that throws exception. (Note it would be quite inconvenient for, say,Repo.create
to return anF[Option[PState[P]]]
, where the option wasNone
if the connection was closed. So it's less unreasonable forRepo.create
to throw aConnectionClosedException
. (This exception is thrown within the effectF
, of course.) There are probably some good functional approaches that could avoid exception throwing cleanly here...) - It's important that the end user plays nice with the exposed session, i.e., doesn't close it. This would put
Repo
internal state out of sync. (Again, probably some good functional approaches to get rid of this internal state. But certainly not obvious.) This "plays nice" requirement should be mentioned in the Scaladocs for the method.
@mardo you might be interested in taking on this low hanging fruit 😄 :1st_place_medal:
Not sure how much of the internals I need to have, but I'll give it a shot when I have a chance