-
Notifications
You must be signed in to change notification settings - Fork 13
Update to Aff 4.0 #19
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
Conversation
src/Database/Postgres.js
Outdated
@@ -4,40 +4,21 @@ | |||
var pg = require('pg'); | |||
|
|||
exports["connect'"] = function (conString) { | |||
return function(success, error) { | |||
return function(error, success) { | |||
var client = new pg.Client(conString); | |||
client.connect(function(err) { | |||
if (err) { | |||
error(err); | |||
} else { | |||
success(client); | |||
} |
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.
Note, that these functions still need to return something for a canceler, even if it just invokes a callback (please consult the example). If it's possible to cancel the effect, then it should be done so we don't accidentally leak resources. Does pg.Client
support some way to close the pending connection?
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.
For reference: https://github.com/slamdata/purescript-aff/blob/master/src/Control/Monad/Aff/Compat.purs#L35-L38
The canceler isn't optional, even through the FFI, and it's an important part of Aff's semantics.
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.
Ok pushed one for connect'
that I think is correct, but not sure about the query
ones. I don't think those are cancellable .... does that mean to just call onCancelerSuccess
?
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.
If there is no way to cancel a pending request, then yes, the only thing that can be done is call onCancelerSuccess
.
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.
cool, I think this is all set then
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.
There is, or at least these was a difference between withClient
and withConnection
. withClient
would pull a connection from the connection pool and withConnection
would make a new connection.
Now I'm looking at the pg documentation and they have a new pooling API so I'm not sure how client.connect
works currently.
When we've taken a connection from the pool, and we cancel the Aff
we should try to cancel the query and return the connection to the pool (there's a pending PR to add query canceling to pg brianc/node-postgres#1392). With single connections to the db without a pool we can just close the connection.
I can take a closer look at the pg API during the weekend, as I'm not sure the API we have here is relevant anymore.
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.
@anttih What do you think about passing that off to the user? Like export the Client
and Pool
constructors and let the user of this lib choose which to use and pass them into the query functions?
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.
@tonyd256 I'd like to keep it simple for now and concentrate on Pool
only. That means we would expose the Pool
constructor with something like mkPool :: PoolConfig
where the PoolConfig
would have all the config flags available. I think we can ditch the connectionstring
for now.
We could then replace withClient
with connect
:
connect :: forall eff a
. Pool
-> (Client -> Aff (db :: DB | eff) a)
-> Aff (db :: DB | eff) a
What do you think? And thanks for working on this!
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.
@anttih I think focusing on Pool makes sense. I'd like to not ditch connection string though since that's the main way I'd like to interact with the api.
src/Database/Postgres.js
Outdated
return function(cancelError, onCancelerError, onCancelerSuccess) { | ||
client.end(function(err) { | ||
if (err) { | ||
onCancelerError(); |
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.
This should be called with the err
, but this is otherwise correct I think.
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.
The types say this, but it should be documented in a better example.
@anttih I did a bunch of work here to abstract the query functions using a type class. Since both client and pool have a query function it seems like they can be abstracted as such. This also switches to more complex connection info objects. I think they should probably all be |
Sorry, what I meant by concentrating on the |
Ya I figured that's not what you meant. I got excited and went overboard haha. I'll revert back to just the pool client. What do you think about the nullable settings though? A lot of the defaults for those settings are different types altogether (yay javascript) so we couldn't replicate the default in purescript. |
Ok updated. One thing about using the pool is that we need to call |
I think we could separate foreign import data ConnectionInfo :: Type
type ClientConfig =
{ host :: String
, database :: String
, port :: Int
, user :: String
, password :: String
, ssl :: Boolean
}
type PoolConfig =
{ connectionTimeoutMillis :: Int
, idleTimeoutMillis :: Int
, max :: Int
}
defaultPoolConfig :: PoolConfig
defaultPoolConfig = { ... }
connectionInfoFromConfig :: ClientConfig -> PoolConfig -> ConnectionInfo
connectionInfoFromConfig = unsafeCoerce
{ -- all the props from both here...
}
newtype ConnectionString = ConnectionString String
connectionInfoFromString :: ConnectionString -> ConnectionInfo
connectionInfoFromString cs = unsafeCoerce { connectionString: cs } We could also have |
@tonyd256 I think we need to turn the |
Ok updated the config building code. I'm not clear on how to do what you're saying with the callback and done function. We're already using Aff with |
@tonyd256 Actually we don't have to use any callbacks, all we need to do is call
which we can use in |
@anttih I think we still need to end the pool at some point right? Every time we call |
Ah, right. I think se should create the pool separately and pass into withClient. Then have separate end which should be called “manually” once you’re done. |
Gotcha, I'll make some changes then. |
Ok, what do you think about this? |
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.
Looks good! Just a few comments.
src/Database/Postgres.purs
Outdated
withClient :: forall eff a | ||
. ConnectionInfo -> (Client -> Aff (db :: DB | eff) a) -> Aff (db :: DB | eff) a | ||
withClient c p = | ||
withPool :: forall eff a |
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.
We can still use the name withClient
here. It tries to describe that your callback is called with the Client
that we are pulling out from the Pool
.
src/Database/Postgres.purs
Outdated
<> ci.host <> ":" | ||
<> show ci.port <> "/" | ||
<> ci.db | ||
mkDefaultPoolConfig :: ClientConfig -> ConnectionInfo |
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.
Let's actually remove this for now. I think the API of calling either connectionInfoFromString ...
or connectionInfoFromConfig {...} defaultPoolConfig
is good enough.
test/Main.purs
Outdated
@@ -1,14 +1,13 @@ | |||
module Test.Main where | |||
|
|||
import Prelude | |||
import Prelude (class Eq, class Show, Unit, bind, const, discard, pure, show, unit, ($), (<$>), (<*>), (<>), (=<<), (>>=)) |
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.
Was there a particular reason for this? You can just open import this like it was, PS allows one open import without any warnings.
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.
ha I didn't know that. It must have given me an error at some point when I had another import in there.
I'm finding that I don't like this API. I want to do this |
Wrapping in another bracket does work fine. Not sure if you'd like a convenience function for that, otherwise it's usage will be similar to: bracket
(liftEff $ mkPool connectionInfo)
(liftEff <<< end)
(_ `withClient` (\c-> do
-- do stuff here
)) or with a transaction bracket
(liftEff $ mkPool connectionInfo)
(liftEff <<< end)
(_ `withClient` withTransaction (\c-> do
-- do stuff here
)) Once we nail down the API I'll make sure to update the docs too. |
@anttih Any thoughts on what's left here? We're so close to getting this done :) |
@tonyd256 what is your use case for these ad-hoc queries? It seems to me that the most common use case is in long running processes where you would always have a pool handy. |
For our API we want a transaction per request. This is useful for rollback if anything in that request fails. One transaction means one client. We don't persist the pool over the API application. |
Ok, maybe just call it |
So we could have |
So after a chat with coworkers we do want to persist that so you're right. |
Yes, most likely you want to persist it if it's any way possible. |
Would you like me to bring back |
No, I think we should just have the |
Thanks a lot! |
This updates the dependencies and code to Aff 4.0. There were some breaking changes that Aff no longer supports the success/failure callback method. There is still support under
Control.Monad.Aff.Compat
usingEffFnAff
andfromEffFnAff
. The update was replacing the oldAff
s withEffFnAff
, switching the success and error functions around, and removing thewithClient
function as it seemed like a duplicate ofwithConnection
. This builds on the changes made in #18 and fixes #17.