How To Get Advisory Lock in Shell
How To Get Advisory Lock in Shell
Recently we had interesting problem. There are some maintenance tools, that do stuff that touch database. One can be calling repack, another can be specific
dump, and yet another can be applying migrations from application.
The problem is that they can step over each other toes, and cause issues.
So we needed to add some way to prevent them from running at the same time…
Naturally since the problem is when they do something to database, the natural choice was having some kind of lock in database.
But how? Write in some table that it's locked, and check? It simple, but if the tool that would get such lock would die, the lock would stay in place. Ideally we'd
like to have lock that we can obtain, and then it will disappear once the program that ran it finishes.
There is a thing in PostgreSQL that matches this specific criteria: advisory lock.
This is lock that you can obtain on a number (or pair of numbers). These numbers don't represent anything for PostgreSQL – they are just for you and your apps.
You can tell Pg: lock number 1 for me, and then, until you will free this lock, number 1 will be locked, and no other process will be able to get lock on it.
Amazing. Solves the problem. We picked random number, and apps were modified to get this lock when starting process.
But then we realized that there is a shell script that does stuff to database, that should get this lock too.
The thing is – shell scripts access database only via psql, and once psql will finish – it doesn't stay there, but rather closes connection. And lock is gone.
SELECT pg_advisory_lock(12345);
But then keep the connection open. Simple. I can put psql to background. But then – how will I know that I got the lock?
Also – we have a program that might kill idle session. So it shouldn't be idle for too long.
obtain lock
inform about it parent script, somehow
keep connection open, doing something, anything, every so often
when parent script will die, the psql and connection should also end
Line 1 makes temp file, and stores it's path in locker_file variable.
printf in line 2 prints this:
\o :x
select 'LOCKED', pg_advisory_lock(:l);
\o /dev/null
select :'m' \watch 60
Side note: in our example, there was more logic in the final loop, as we were checking for some side-stuff, as maintenance tools can only run within specific hours,
but that's not important for now.
Nothing magical, but does the trick. Hope you'll find it useful
2021-09-03 at 15:30
Hello.
Why not to take the problem the other way round ?
Assume my_work.sh is the shell to run after lock is obtained.
printf “select pg_advisory_lock(12345);\n\\!my_work.sh” | psql
2. depesz says:
2021-09-06 at 09:11
@Marc:
well, you glossed over the problem that I mentioned: “we have a program that might kill idle session”