I wrote a shell.nix for my project, and it uses shellHook to initialize/start a postgres server when you enter the shell, and shutdown postgres when you exit.
If I start a second nix-shell when there’s one already running (in a different terminal), pg_ctl start complains that postgres is already running. This was just a minor annoyance, but was easy enough to fix:
pg_ctl status >/dev/null
if (( $? == 3 )); then
setsid pg_ctl start --silent --log $PWD/log/pg.log
fi
The more interesting problem is fixing the shutdown logic. If I have two nix-shells running, whichever one exits first will shutdown postgres, leaving the other without a database.
Solutions I’m considering:
pid files in a pids/ directory
on entering nix-shell, create pids/$$
on exiting, remove pids/$$ and check for any remaining files in pids/
use postgres to maintain a table of pids
on entering nix-shell, insert pid
on exiting, delete pid and check for any remaining rows in the table
use ps to query for nix-shell processes
I’d need a way to distinguish which directory the nix-shell processes were run from
Or maybe there’s another approach I’m overlooking? I wonder if there’s a way to leverage process groups or sessions to register the relationship between these shells.
I’ve written something like this for Hydra once, the solution I’ve choosen is a semaphore that uses the PID of the shells as the key. That way when the last shell stops, the database is also shut down and if another shell is already running with a database, it’s not started again. So it’s similar to your first point.
Cool, thanks for sharing. It looks like we ran into some of the same issues. I like your use of PGDATABASE too, I didn’t know about that.
I ended up implementing the first two solutions in order to compare. The first one was essentially the same as your semaphores . I like your $(find "$hydraDevDir/sema" -prune -empty), btw. The second was pretty straightforward too – I maintain a table of pids in postgres, rather than a directory of pidfiles on the filesystem, but the end result is the same. I liked the fact that this avoided the need to create a pids directory.
I haven’t found anything to argue for one approach over the other. It’s nice to have options, I guess : )
I also ran into the Control-C thing. I was solving it with setsid, though I’d overlooked setsid's -w option. In one spot, I had:
So despite telling pg_ctl to wait (-w), the setsid call would return immediately and createuser could be invoked before the server’s up. I never actually ran into this, though.
I also overlooked the fact that setsid isn’t available on macOS. I found another solution that doesn’t require setsid:
set -m
pg_ctl start ...
I’m still mystified as to why this works, since the m shell option is already on according to $-. I ended up running it in a subshell just in case.
I also noticed you’re using Unix domain sockets, rather than TCP sockets. It sounds like that might be more performant on localhost so I’m going to try it.
Hm, that’s weird, because all which set -m does is setting SHELLOPTS accordingly, no other magic involved from reading the source, so it shouldn’t make any difference when monitor is already set.