Pool.cr - A generic pool library

This is just a simple library, I post it here to gather feedback. Lots of nice features are present in crystal-db/pool.cr at master · crystal-lang/crystal-db · GitHub, which may be worth to add (on a new child class).


A pool library was already existing: GitHub - ysbaddaden/pool: Generic (connection) pools for Crystal. However, I was not plainly satisfied on several aspects. I consider all the Fiber logic unnecessary, as the developer can just get one instance from the pool then use it/pass it around, instead of doing so with the Pool itself (did I miss an other advantage?).

Other added features are thread-safety and pool resizing. The later allows to add custom strategies on top to shrink back the pool after a traffic burst, or at the contrary grow it to prepare for an increased traffic for example.

Link to the repository: GitHub - j8r/pool.cr: A simple thread-safe generic pool.

4 Likes

It looks like a great way to use generic resource pools. Specifically, I’m thinking about how HTTP::Server reuses instances of HTTP::Server::Response or how Fiber stacks are reused by the Crystal runtime. You could probably also use it to maintain a Fiber pool for doing things concurrently without the risk of unbounded fiber proliferation (thinking along the lines of Sidekiq’s static thread pool).

For connection pooling specifically, DB::Pool from crystal-lang/crystal-db has been decoupled from DB-specific connections and can be used as a pool for any type of connection as of 0.10.0, and it’s really well optimized. For example, it allows concurrent connection creation to reduce the effect of latency (it unlocks the mutex while connecting) and shedding excess idle connections after a volume spike for elasticity. If you need a connection pool specifically, I’d stick with that.

Examples of DB::Pool used outside of DB:

3 Likes

Yes, I’m taking inspiration from DB::Pool. However, some disadvantages:

  • we don’t usually want all the other SQL features coming with the crystal-db shard
  • features are aimed for SQL connections, and not all are always needed for all pools, and some are missing for other cases.

On more little thing: a Mutex is not necessary on single threaded apps.

Another thing I don’t like much is to ensure to add back the resource, whatever happens. It means if a connection raised an IO error, it will be still added back. I prefer no to. If the error is not “important”, it can be rescued before.

I think mutex are needed if the resource creation can reschedule the running fiber and you want to have an upper limit in the concurrent existing resources.

2 Likes

Funny you mention that. Before that PR to make it usable for other connections, I suggested extracting it from crystal-db entirely for exactly this reason. Due to concerns about long-term cohesion, I took the smaller step of just making it usable outside of DB. Ideally these libraries wouldn’t depend on the full DB library but so far it works okay.

Yeah, a connection that isn’t self-healing becomes a poison pill if it goes back in. I did have to add reconnect functionality to my Redis shard to make it usable with DB::Pool specifically for this reason. HTTP::Clients also automatically reconnect on the next request if they get disconnected.

For database or service clients, it’s often a good idea to support reconnections in general. If that’s not feasible, though, I agree that throwing them out and starting fresh ones is preferred over sticking them back in the pool. :slightly_smiling_face:

1 Like