Skip to content
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

Add support for flushing the pool #16

Merged
merged 5 commits into from
Aug 29, 2022
Merged

Conversation

robx
Copy link
Contributor

@robx robx commented Aug 8, 2022

"Flushing the pool" is close to what "releasing the pool" did in versions < 0.6, except it also ensures that in-flight connections don't get returned to the pool.

Does this seem like a useful feature? I fully understand if you'd prefer to keep the library simpler.

For context, I'm trying to upgrade PostgREST from hasql-pool 0.5, and PostgREST relies (brokenly) on being able to flush the pool via release. That's because it sets some per-connection variables, and needs to be able to update those values for new requests. (I make no claims as to whether that's a particularly sensible approach.)

This isn't necessarily the simplest/cleanest way to do it. An alternative I'm aware of would be to track e.g. a poolGeneration :: TVar Int and compare the acquisition generation to the current generation to determine whether to return a connection to the pool. That has the theoretical downside that Int is bounded...

@nikita-volkov
Copy link
Owner

Sorry for the delayed response.

Have you considered leaving the pool package as it is, but wrap it in something like the following:

data FlushablePool =
  FlushablePool
    {-| Pool size. -}
    Int
    {-| Connection settings. -}
    Hasql.Connection.Settings
    {-| Pool var. -}
    (MVar Hasql.Pool.Pool)

flush :: FlushablePool -> IO ()
flush (FlushablePool size settings var) = do
  pool <- takeMVar var
  Hasql.Pool.release pool
  newPool <- Hasql.Pool.acquire size settings
  putMVar var newPool

@robx
Copy link
Contributor Author

robx commented Aug 26, 2022

Have you considered leaving the pool package as it is, but wrap it in something like the following:

Yes, I went for a similar approach (using an IORef) first here: https://github.com/PostgREST/postgrest/pull/2391/files#r939930348

But the approach is fundamentally flawed in that the effective connection limit becomes (number of pools) x (pool size). Given that postgresql connections are a pretty scarce resource, that doesn't really feel like a good solution.

EDIT: To be a bit more explicit: If the connection pool is fully loaded, and there are some long running queries, we'd exceed the limit on the connection count while any of the long running queries prior to releasing the pool are in flight.

Congrats on the pgenie release btw! (And I understand you've been busy)

@nikita-volkov
Copy link
Owner

But the approach is fundamentally flawed in that the effective connection limit becomes (number of pools) x (pool size). Given that postgresql connections are a pretty scarce resource, that doesn't really feel like a good solution.

Makes sense. Give me some time to review the PR.

Congrats on the pgenie release btw! (And I understand you've been busy)

Thanks :)

@robx
Copy link
Contributor Author

robx commented Aug 26, 2022

Oh I should add that there's a follow-up change I want to make to allow timing out connection acquisition (I've filed it here PostgREST#4 for now, didn't want to keep piling on.)

I'd understand in general if you want to keep this package more bare-bones, particularly considering its recent simplification. It would be ok (even if not ideal) for us to "properly" fork as say postgrest-hasql-pool for a more fully featured (and likely less well considered) hasql pool library.

(Just mentioning this to warn you that this might not be the end of it, since that information might factor into deciding whether you want to merge this individual change.)

@nikita-volkov
Copy link
Owner

I'm okay with merging the so far discussed updates to the library. I see no point in breeding libraries when we agree on the direction.

Also since clearly you have a popular and actively maintained use-case if you wish I can add you as a co-maintainer. Let's just agree to keep initiating discussions like the one we're having now before making design changes.

Back to the PR. Here's a thought: why don't we make release be flush? Namely, make it so that using connections after releasing will guarantee that the connection will be fresh and won't fail with PoolIsReleasedUsageError (IOW, get rid of that error type altogether). If the pool is never used after releasing, it effectively means that all associated resources get released.

Regarding the timeout. Give a try to the 0.6 version. It was a major version that fixed the bugs and got rid of the resource-pool dependency, yet it retained the timeout functionality. There were no issues detected with that version, it just lacked testing. It was lingering as an unreleased branch for some time and then I forgot about it and accidentally wrote 0.7. So I released both giving the preference to the latest developments. 😅 Yeah. I know.

Any way, please give it some testing. Or even better, PR with some tests for it, if you will. If we discover that 0.6 is reliable, I'll be happy to use it as the base for future developments and extend it with flushing in whatever form we choose.

@robx
Copy link
Contributor Author

robx commented Aug 29, 2022

Back to the PR. Here's a thought: why don't we make release be flush? Namely, make it so that using connections after releasing will guarantee that the connection will be fresh and won't fail with PoolIsReleasedUsageError (IOW, get rid of that error type altogether). If the pool is never used after releasing, it effectively means that all associated resources get released.

Yes that sounds great, somehow I took it as a given that release should keep working that way. I pushed a change to that effect.

Regarding the timeout. Give a try to the 0.6 version. It was a major version that fixed the bugs and got rid of the resource-pool dependency, yet it retained the timeout functionality.

I think it's a different kind of timeout: We'd like to be able to bail out of waiting for a connection to be made available by the pool, while to my understanding the old timeout behaviour was about timing out idle connections. I'll file my change for that so we can discuss it there.

-- | Alive.
poolAlive :: TVar Bool
-- | Whether to return a connection to the pool.
poolReuseToggle :: TVar (TVar ReuseConnection)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm at a bit of a loss how to name this well. I went for very explicit naming for the purposes of making the PR understandable for now, hopefully we can find something better?

@robx robx mentioned this pull request Aug 29, 2022
@nikita-volkov
Copy link
Owner

Alright! I've merged all the changes and released it as 0.8. Nice work and thanks for your contributions!

@robx
Copy link
Contributor Author

robx commented Aug 29, 2022

Whoa! Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants