gaggle
Hey all,
Newish to Elixir and trying to deepen my knowledge by experimenting, and I got caught out by how Ecto.Repo behaves. I’m hoping someone here can explain why Ecto works how it does, and perhaps can offer ways of accomplishing what I had in mind (even if it’s a bit weird what I’m trying, but it’s to learn!)
Using a normal Phoenix app, if I shut down the database and start the server it immediately floods with repeated error messages:
[error] Postgrex.Protocol (#PID<0.332.0>) failed to connect: ** (DBConnection.ConnectionError) tcp connect (localhost:5432): connection refused - :econnrefused
[error] Postgrex.Protocol (#PID<0.334.0>) failed to connect: ** (DBConnection.ConnectionError) tcp connect (localhost:5432): connection refused - :econnrefused
...
I assumed that was MyApp.Repo failing, but the PIDs are all different and don’t match Process.whereis(MyApp.Repo). So… I guess those errors are probably connection-pool processes crashing, maybe supervised by MyApp.Repo?
What I had wanted to do was to take manual control over MyApp.Repo, and then when it crashed I wanted to let the app slip into “maintenance mode”, and I could manually restart the repo. But that’s a no-go, since the repo isn’t actually crashing.
- Can anyone provide some context or link to resources that explain the repo behavior? I’ve looked around hexdocs but have so far not come away with more knowledge on how the sub-processes work and if there’s a mechanism for observing them.
- Anyone have suggestions for how to react to the database dropping out? I do understand Repo auto-reconnects and I can just leave it as-is, but I’m on a learning-journey and I figured a nice experiment was to prevent the wall of error-messages that floods in from disconnects.
Trending in Questions
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixirconf-us
- #ai
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming










Showing Posts 1 to 6- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
D4no0
The first thing you need to read about to understand what is going on is Supervisors.
What you are experiencing is a default behavior of Ecto (this has nothing to do with Phoenix). In 99% of production apps, inability to connect to a data source is considered as a fault the system should not recover from, critical data for business logic and application runtime is stored in database usually.
The core resources that will guide you towards customizing this behavior is:
Feel free to ask more focused questions as you progress along in this direction.
dimitarvp
After reading on dynamic repositories I’d look seriously into using that. Then you can supervise it in a more lax manner – it’s very doable and I saw it done once but of course I can’t keep my customers’ code so I have no example in hand.
But yes you can do it, and no you likely shouldn’t do it the normal way. You can and should still create a new module and have
use Ecto.Repoinside but f.ex. you shouldn’t put it inside your supervision tree because these children there are non-negotiable startup dependencies.gaggle
D4no0, thanks for links, I understood from them how the repo has an underlying dynamic supervisor process that gets started, which controls the connection pool processes.
I need to learn if/how I can dive into that supervising process, and then… maybe I can get a status from that process, or maybe I have to scan its children’s statuses? I’m still chasing some way of reacting to those database connection errors by then stopping the
MyApp.Repoprocess (implying I’ve taken it out of main supervisor and control its lifecycle myself), but I’m not even sure yet which questions to ask to dig into that journey. And I still feel like it’s 50/50 if I’m just completely misunderstanding the whole thingI’ll start experimenting, but of course if you or anyone else have specific ideas/pointers for reacting to those connection pool process errors I’m all ears.
D4no0
One way of doing it, is to monitor the connection processes using
Process.monitor/1. You can read about a potential approach dealing with this here: How can I monitor when a process is restarted.If you are only interested in disabling the repo when there is no connection to the database, a much easier solution is to create a process that pings the database and either starts or stops the repo supervisor.
garrison
If you’re just looking for Ecto-related stuff to read through you should also check out the docs for
Ecto.Adapters.SQLandDBConnection(which are in separate repositories).Of particular interest to me was the code for connection sandboxing in tests which can be found here and the connection ownership code here - it’s a neat trick, and the abstraction holds for a surprisingly long time before it starts to leak. I remember having to track this code down when I was trying to write tests for functions which start async processes which then access the DB. Unfortunately the
Sandboxdocs don’t mention the$callerstrick used byDBConnection.Ownershipoutright, and I remember it taking me a while to confirm that’s actually how it worked.gaggle
I ended up exploring and learning which has been a lot of fun! Thanks for the suggestions and links to documentations.
I ended up finding Ecto can be configured with
backoff_type: :stop, which disables its reconnection strategy. That means the repo just crashes when disconnecting, which let me put a monitor on the repo process and react by opening a circuit breaker. I hacked together a rough proof of concept where I register my guard instead of the repo:And then I can use
MyApp.GuardedRepolike this:This
GuardedRepomodule can also attempt a reconnect, which will close the circuit if the connection succeeds and let queries go through again. It doesn’t have all the proper circuit-breaker logic to automatically close, etc., I figure it’s good enough for a quick hackathon to get this far and the rest would be kind of icing on the cake that could be added in later iterations.This is literally the first time I’ve used GenServers or Supervisors so I assume the code for this might be hot fire, but I’d be happy to hear reactions if someone wants to type in their spit-takes
:
As I hope is clear from this thread I don’t mean for this solution to be some viable new great approach, I just think its a good way to learn by pushing against default patterns to see why a system is put together the way it is. If someone can spot easier ways to solve what I’ve done above I’d love to hear it, and don’t assume I know what might seem obvious to you because I’m just beginning my dive into Elixir and I probably have blindspots the size of all of Elixir. So any form of feedback is appreciated.