pg_readonly

This Release
pg_readonly 1.0.3
Date
Status
Stable
Other Releases
Abstract
set all cluster databases in read only
Released By
pifor
License
PostgreSQL
Resources
Special Files
Tags

Extensions

pg_readonly 1.0.3
set all cluster databases in read only

README

pg_readonly

pg_readonly is a PostgreSQL extension which allows to set all cluster databases read only.

Installation

Compiling

This module can be built using the standard PGXS infrastructure. For this to work, the pg_config program must be available in your $PATH:

git clone https://github.com/pierreforstmann/pg_readonly.git
cd pg_readonly
make
make install

This extension has been validated with PostgreSQL 9.5, 9.6, 10, 11, 12, 13, 14 and 15.

PostgreSQL setup

Extension must be loaded at server level with shared_preload_libraries parameter:

shared_preload_libraries = 'pg_readonly'

and it must be created with following SQL statement at server level:

create extension pg_readonly;

Usage

pg_readonly has no specific GUC.

The read-only status is managed only in (shared) memory with a global flag. SQL functions are provided to set the flag, to unset the flag and to query the flag. The current version of the extension does not allow to store the read-only status in a permanent way.

The flag is at cluster level: either all databases are read-only or all database are read-write (the usual setting).

The read-only mode is implemented by filtering SQL statements: SELECT statements are allowed but INSERT, UPDATE, DELETE and DDL statements are not allowed. This means that the databases are in read-only mode at SQL level: however, the checkpointer, background writer, walwriter and the autovacuum launcher are still running; this means that the database files are not read-only and that in some cases the database may still write to disk.

Example

To query the cluster status, call the function get_cluster_readonly which returns true is the cluster is read-only and false if not:

# select get_cluster_readonly();
get_cluster_readonly
----------------------
f
(1 row)

To set the cluster read-only, call the function set_cluster_readonly:
# select set_cluster_readonly();
set_cluster_readonly
----------------------
t
(1 row)

The cluster is now read-only and only SELECT statements are allowed:

pierre=# select * from t;
x | y
----+-----
32 | abc
(1 row)

# update t set x=33 where y='abc';
ERROR: pg_readonly: invalid statement because cluster is read-only
# select 1 into tmp;
ERROR: pg_readonly: invalid statement because cluster is read-only
# create table tmp(c text);
ERROR: pg_readonly: invalid statement because cluster is read-only

To set the cluster on read-write, call the function unset_cluster_readonly:

# select unset_cluster_readonly();
unset_cluster_readonly
------------------------
t
(1 row)

The cluster is now read-write and any DML or DDL statement is allowed:
# update t set x=33 where y='abc';
UPDATE 1
# select * from t;
x | y
----+-----
33 | abc
(1 row)

Note that any open transaction is cancelled by set_cluster_readonly function.
The client is disconnected and gets the following message:
FATAL: terminating connection due to conflict with recovery
DETAIL: User query might have needed to see row versions that must be removed.
HINT: In a moment you should be able to reconnect to the database and repeat your command.
In PostgreSQL log, following messages are written:

2020-04-14 16:00:14.531 CEST [29578] LOG: pg_readonly: killing all transactions ...
2020-04-14 16:00:14.531 CEST [29578] LOG: pg_readonly: PID 29569 signalled
2020-04-14 16:00:14.531 CEST [29578] LOG: pg_readonly: ... done.
2020-04-14 16:00:14.531 CEST [29569] FATAL: terminating connection due to conflict with recovery
2020-04-14 16:00:14.531 CEST [29569] DETAIL: User query might have needed to see row versions that must be removed.
2020-04-14 16:00:14.531 CEST [29569] HINT: In a moment you should be able to reconnect to the database and repeat your command.