PostgreSQL — More on DBA Basics

Monowar Mukul
5 min readApr 18, 2020

PostgreSQL Database Session and Deadlock Management

In my last article, I went through first with PostgreSQL user, role and schema management and then I have shown you how we can manage Database, Tablespace, Tables and Vacuum process.

In the article first, I will go through PostgreSQL session — to enable and disable, then I will go through Deadlock and long-running

First, connect to the database server as a PostgreSQL user
$sudo su — postgres
Then set up a session
> psql

Session Enable and Disable — Database Level or System Level

Database Level — To disable session
+++++++++++++++++++
postgres=# alter database dbadb with allow_connections false;
postgres=# \c dbadb
FATAL: database “dbadb” is not currently accepting connections
Previous connection kept

Database Level — To enable session
+++++++++++++++++++
postgres=# alter database dbadb with allow_connections true;
ALTER DATABASE
postgres=# \c dbadb
You are now connected to database “dbadb” as user “postgres”.

For system level
+++++++++
To disable access at the system level
- set pg_hba.conf (by commenting client connection entries or setting method=reject)

Deadlock

In a database, a deadlock is a situation that occurs when two or more different database sessions are waiting for each other. It caused some data locked, as illustrated in the following diagram (Reference: https://vladmihalcea.com/database-deadlock/).

Reference: Database Deadlock

Lets set up a deadlock session —

First, we see the deadlock timeout parameter value,

postgres=# show deadlock_timeout ;
deadlock_timeout
— — — — — — — — —
1s
(1 row)

From the first session, Going to run an update on the ‘dedlktst’ table. Here we use BEGIN to avoid auto-commit -

postgres=# BEGIN;
BEGIN
postgres=# UPDATE dedlktst SET region=’Asia’ WHERE code=’BNG’;
UPDATE 1

Then from the second session, execute below commands
postgres=# begin;
BEGIN
postgres=# UPDATE dedlktst SET region=’Oceania’ WHERE code=’AUS’;
UPDATE 1
postgres=# UPDATE dedlktst SET population=4901 WHERE code=’BNG’;
— — — Hanging

Now if I go back to the first session and execute below command -it will prompt a message -
postgres=# UPDATE dedlktst SET population=9001 WHERE code=’AUS’;
ERROR: deadlock detected
DETAIL: Process 91836 waits for ShareLock on transaction 600; blocked by process 91896.
Process 91896 waits for ShareLock on transaction 598; blocked by process 91836.
HINT: See server log for query details.
CONTEXT: while updating tuple (0,2) in relation “dedlktst”

To get more in details we can see the log in our PostgreSQL server
$ tail -30 postgresql-Mon.log
— — — —
2019–11–11 15:38:47.325 AEDT [91896] STATEMENT: UPDATE country SET population=15864001 WHERE code=’BNG’;
2019–11–11 15:39:01.242 AEDT [91896] ERROR: current transaction is aborted, commands ignored until end of transaction block
2019–11–11 15:39:01.242 AEDT [91896] STATEMENT: UPDATE dedlktst SET population=15864001 WHERE code=’BNG’;
2019–11–11 15:39:19.098 AEDT [91896] ERROR: current transaction is aborted, commands ignored until end of transaction block
2019–11–11 15:39:19.098 AEDT [91896] STATEMENT: UPDATE dedlktst SET population=15864001 WHERE code=’BNG’;
2019–11–11 15:40:05.402 AEDT [91836] ERROR: deadlock detected
2019–11–11 15:40:05.402 AEDT [91836] DETAIL: Process 91836 waits for ShareLock on transaction 600; blocked by process 91896.
Process 91896 waits for ShareLock on transaction 598; blocked by process 91836.
Process 91836: UPDATE dedlktst SET population=18886001 WHERE code=’AUS’;
Process 91896: UPDATE dedlktst SET population=15864001 WHERE code=’BNG’;
2019–11–11 15:40:05.402 AEDT [91836] HINT: See server log for query details.
2019–11–11 15:40:05.402 AEDT [91836] CONTEXT: while updating tuple (0,2) in relation “dedlktst”
2019–11–11 15:40:05.402 AEDT [91836] STATEMENT: UPDATE dedlktst SET population=18886001 WHERE code=’AUS’;

Long running queries

Finding long running queries
+++++++++++++++++

SELECT
pid,
now() — pg_stat_activity.query_start AS duration,
query,
state
FROM pg_stat_activity
WHERE (now() — pg_stat_activity.query_start) > interval ‘1 minutes’;

Killing long-running queries
++++++++++++++++
To cancel long-running queries either we can stop the query and then terminate the session more safely or kill the process from the OS level:
i) To stop the query we can execute below command
SELECT pg_cancel_backend(<pid>);

ii) If we see that the process is hanging for a long even after stopping the query then we can kill the process executing below command
SELECT pg_terminate_backend (<pid>);

or by executing kill command at the OS level:

kill -9 <PostgreSQL Process Id>

Now we will go through a lab setup
For our testing purpose, we consider for 5 minutes. In a real scenario, it will be based on the much higher job duration time compare to the normal run time.

i. From the first session, we can execute a SQL to run for 5 minutes.

$ psql
postgres=# select pg_sleep(5 * 60);

ii. From the second session, we are executing the below query to a session running more than a minute.

SELECT
pid,
now() — pg_stat_activity.query_start AS duration,
query,
state
FROM pg_stat_activity
WHERE (now() — pg_stat_activity.query_start) > interval ‘1 minutes’;

pid | duration | query | state
— — — -+ — — — — — — — — -+ — — — — — — — — — — — — — + — — — —
93443 | 00:02:24.981683 | select pg_sleep(5 * 60); | active
(1 row)

Cancel the session
===========
postgres=# SELECT pg_cancel_backend(93443);
pg_cancel_backend
— — — — — — — — — -
t
(1 row)
postgres=# SELECT
postgres-# pid,
postgres-# now() — pg_stat_activity.query_start AS duration,
postgres-# query,
postgres-# state
postgres-# FROM pg_stat_activity
postgres-# WHERE (now() — pg_stat_activity.query_start) > interval ‘1 minutes’;
pid | duration | query | state
— — — -+ — — — — — — — -+ — — — — — — — — — — — — — + — — — -
93443 | 00:04:10.8239 | select pg_sleep(5 * 60); | idle
(1 row)

Now terminate the session
==========================
postgres=# SELECT pg_terminate_backend (93443);
pg_terminate_backend
— — — — — — — — — — —
t
(1 row)

postgres=# SELECT
postgres-# pid,
postgres-# now() — pg_stat_activity.query_start AS duration,
postgres-# query,
postgres-# state
postgres-# FROM pg_stat_activity
postgres-# WHERE (now() — pg_stat_activity.query_start) > interval ‘1 minutes’;
pid | duration | query | state
— — -+ — — — — — + — — — -+ — — — -
(0 rows)

​In this article, I have gone through how we can manage a deadlock and long-running session in the PostgreSQL database.​

--

--

Monowar Mukul

Monowar Mukul is a Cloud Solution Architect Professional. /*The statements and opinions expressed here are my own & nothing with my present or past employer*/