How would you deal with the connection pool as connections a set to specific databases at startup?
On Thursday, June 18, 2026 at 2:45:49 PM UTC-4 [email protected] wrote: > > > What I'm toying with is a fork of *Ecto.Adapters.SQL.Sandbox* that > implements test isolation by doing a full clean copy of the test database > during setup per test > > Ah yes we've run into this exact thing doing tests in Clickhouse. Even > for postgres, this would be amazing for testing features like pg_notify or > other postgres features that depend on an actual COMMIT. > > Ben, if you are interested, what I am toying with is ultimately bringing > pytest-postgresql <https://pypi.org/project/pytest-postgresql/> to all of > Ecto: a clone-based transaction-free sandbox mechanism for any > *db_connection*-powered Ecto adapter (not just for *Ecto.SQL*): > > - Adding a new optional *Ecto.Storage.storage_clone* type-callback > - Implementing this for adapters where the storage has underlying > conveniences for this, ex > - easy to implement (built-in): > - postgres: *CREATE DATABASE TEMPLATE test_db* > - sqlite3: *File.copy("test_db")* > - cheap to implement (copy on write semantics) > - snowflake: *CREATE DATABASE CLONE test_db* > - clickhouse: *CREATE DATABASE test_db *and loop* CLONE TABLE* > - possibly high-value but laborious to get right adapters: > - MyXQL, TDS, etc > - Rewriting the *Ecto.Adapters.SQL.Sandbox *to work for any ecto > adapter powered by *db_connection*, not just from *Ecto.SQL* > - since *storage_clone/storage_down* is sufficient for any > compatible adapter to guarantee isolation, we could offer sandboxing to > any > compatible ecto storage > - Allowing connections to specify the isolation strategy as > *:transactional* vs *:clone* > - falling back preferentially to *:transactional* and the existing > sandbox mechanism for supported *Ecto.SQL* adapters, *:clone* for > everything else if *storage_clone* is supported > > I don't know what the final form of this experiment will be, but when I > get some anticipated downtime in a couple of weeks to work on it I'd love > to pick your brains if you have immediate use-cases! This proposal is just > setting the stage for that effort. José has brought up a viable > alternative for libraries > <https://github.com/elixir-lang/elixir/pull/15496#discussion_r3433840637> > in the PR, so it's non-blocking, but I'd still like to get a blessed API > for this kind of cleanup into ExUnit if I can anyways as it seems useful > and less brittle than hooking into the ExUnit process lifecycle. > > On Wednesday, June 17, 2026 at 8:55:23 PM UTC-5 [email protected] wrote: > >> Ah yes we've run into this exact thing doing tests in Clickhouse. Even >> for postgres, this would be amazing for testing features like pg_notify or >> other postgres features that depend on an actual COMMIT. >> >> >> On Tuesday, June 16, 2026 at 10:06:05 AM UTC-4 [email protected] wrote: >> >>> > What we typically do is that we never delete it by default, instead we >>> clean up when the next test runs. The rationale is: >>> > >>> > 1. Leave resources for debugging >>> > 2. If tests are interrupted (ctrl+c or whatever other reason), you >>> need to deal with trailing resources anyway >>> > >>> > Would that be a problem here? >>> >>> What I'm toying with is a fork of *Ecto.Adapters.SQL.Sandbox* that >>> implements test isolation by doing a full clean copy of the test database >>> during setup per test, each copy named after the test in question, and >>> redirecting all test interaction to the copy, rather than using >>> transactions to get isolation. The goal is to support concurrent tests for >>> Sqlite and to allow introspecting database state on test failure for Sqlite >>> and Postgres. So in this case, >>> >>> 1. Tearing down the resource unconditionally *on_exit* is not desired >>> (to have a debuggable state left) >>> 2. Leaving behind every resource on successful tests is not desirable >>> (that's a lot of wasted disk space after running 2000 stateful tests) >>> 3. Keeping resources for failed tests and juggling *N* extra copies on >>> disk where test parallelism is *N* is just about manageable >>> >>> Under this mechanism, leaving test failure resources behind is >>> manageable, as is leaving *N* copies behind on test interruption, and >>> all leftover state is overwritable on next test run, but leaving *all >>> copies* behind is not viable for non-trivial test suites sizes or large >>> seed databases. The worst case then becomes some errant global >>> configuration causing every stateful test to fail—the mostly likely way >>> that would happen is by providing a bad database configuration itself, >>> which would prevent creation in the first place, preventing disk bloat from >>> happening to begin with. >>> On Tuesday, June 16, 2026 at 3:19:03 AM UTC-5 José Valim wrote: >>> >>>> > However, I would also like to leave the resource in-place on test >>>> failure for inspection. (My *prepare_resource* function can handle the >>>> situation where a resource already exists on setup. This is opposite to >>>> the >>>> common tmpdir pattern where the resource cleans itself up automatically >>>> upon test conclusion but would have unexpected side effects if already >>>> setup.) >>>> >>>> What we typically do is that we never delete it by default, instead we >>>> clean up when the next test runs. The rationale is: >>>> >>>> 1. Leave resources for debugging >>>> 2. If tests are interrupted (ctrl+c or whatever other reason), you need >>>> to deal with trailing resources anyway >>>> >>>> Would that be a problem here? >>>> >>>> *José Valimhttps://dashbit.co/ <https://dashbit.co/>* >>>> >>>> >>>> On Tue, Jun 16, 2026 at 1:31 AM Christopher Keele <[email protected]> >>>> wrote: >>>> >>>>> Assuming, of course I wrote this proposal without the typo in the >>>>> final sentence and properly used an anonymous function for *on_exit(fn >>>>> state -> #... end)* in my examples. >>>>> >>>>> On Monday, June 15, 2026 at 6:27:48 PM UTC-5 Christopher Keele wrote: >>>>> >>>>>> I am creating a resource during test setup based on a test's context >>>>>> (namely, its module and name): >>>>>> >>>>>> >>>>>> >>>>>> *setup context do prepare_resource_for_test_context(context)end* >>>>>> >>>>>> I would like to be able to teardown this resource upon test >>>>>> conclusion. This is possible today: >>>>>> >>>>>> >>>>>> *setup context do prepare_resource_for_test_context(context)* >>>>>> * on_exit do* >>>>>> * cleanup_resource_for_test_context(context)* >>>>>> * end* >>>>>> *end* >>>>>> >>>>>> *Proposal* >>>>>> >>>>>> However, I would also like to leave the resource in-place on test >>>>>> failure for inspection. (My *prepare_resource* function can handle >>>>>> the situation where a resource already exists on setup. This is opposite >>>>>> to >>>>>> the common tmpdir pattern where the resource cleans itself up >>>>>> automatically >>>>>> upon test conclusion but would have unexpected side effects if already >>>>>> setup.) >>>>>> >>>>>> As far as I know, this is not possible today. I would like to do >>>>>> something like receiving the *ExUnit.state()* >>>>>> <https://ex-unit.hexdocs.pm/ExUnit.html#t:state/0> in the *on_exit* >>>>>> callback: >>>>>> >>>>>> >>>>>> *setup context do prepare_resource_for_test_context(context)* >>>>>> * on_exit state do* >>>>>> *case state do* >>>>>> * {:failed, _} -> :ok* >>>>>> * _ -> cleanup_resource_for_test_context(context)* >>>>>> * end* >>>>>> * end* >>>>>> *end* >>>>>> >>>>>> Are there reasons to not entertain this functionality? Is there >>>>>> another way to accomplish it that doesn't rely on a test formatter to >>>>>> notice *{:test_finished, test}* and do the cleanup at a global level? >>>>>> >>>>>> *Implementation* >>>>>> >>>>>> AFAICT we could enable this usecase trivially by threading >>>>>> *test_or_case.state* into >>>>>> <https://github.com/elixir-lang/elixir/blob/1d599978f7d36ea6896b294b3d26d110212be7ab/lib/ex_unit/lib/ex_unit/runner.ex#L541>*ExUnit.OnExitHandler.run >>>>>> >>>>>> <https://github.com/elixir-lang/elixir/blob/1d599978f7d36ea6896b294b3d26d110212be7ab/lib/ex_unit/lib/ex_unit/runner.ex#L541> >>>>>> *and >>>>>> on to its helper functions. >>>>>> >>>>>> We could retain backwards-compatibility by having >>>>>> *exec_callback(callback, >>>>>> state)* >>>>>> <https://github.com/elixir-lang/elixir/blob/1d599978f7d36ea6896b294b3d26d110212be7ab/lib/ex_unit/lib/ex_unit/on_exit_handler.ex#L139> >>>>>> >>>>>> check the arity of the callback before invoking it. >>>>>> >>>>>> Documentation could describe the optional callback parameter and >>>>>> elaborate that an *on_exit* callback defined in a *setup_all* would >>>>>> have some other behaviour (raise an error, receive *nil* or the case >>>>>> name instead of a state, etc—open to ideas). >>>>>> >>>>>> Are would such an implementation be welcome? >>>>>> >>>>> -- >>>>> You received this message because you are subscribed to the Google >>>>> Groups "elixir-lang-core" group. >>>>> To unsubscribe from this group and stop receiving emails from it, send >>>>> an email to [email protected]. >>>>> To view this discussion visit >>>>> https://groups.google.com/d/msgid/elixir-lang-core/71b223e9-3815-4b8d-9ac0-cb32b98b05e8n%40googlegroups.com >>>>> >>>>> <https://groups.google.com/d/msgid/elixir-lang-core/71b223e9-3815-4b8d-9ac0-cb32b98b05e8n%40googlegroups.com?utm_medium=email&utm_source=footer> >>>>> . >>>>> >>>> -- You received this message because you are subscribed to the Google Groups "elixir-lang-core" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To view this discussion visit https://groups.google.com/d/msgid/elixir-lang-core/1bb7129a-0c95-445a-86a8-b16e6f9758den%40googlegroups.com.
