Skip to content
In this article, we will see the solution for error like PostgreSQL Error: cannot execute in a read-only transaction.
ERROR: cannot execute CREATE DATABASE in a read-only transaction Error: cannot execute DROP DATABASE in a read-only transaction ERROR: cannot execute CREATE TABLE in a read-only transaction Error: cannot execute DROP TABLE in a read-only transaction cannot execute INSERT in a read-only transaction
Reason:
Reason is simple, PostgreSQL server is recovery(read-only) or standby mode.
Resolution:
1. We have to remove recovery(read-only) or standby mode.
2. Go to your data or main directory then remove standby.signal and recovery.signal files.
Before performing following action, take backup of these files in some other location.
File location in(Ubuntu change your respective version nuymber)
/var/lib/postgresql/13/main
3. After removing signal files, restart PostgreSQL cluster.
sudo systemctl restart postgresql
4. Then, you will able to connect to the server and perform all actions as usual.
I’m trying to setup the pgexercises data in my local machine. When I run: psql -U <username> -f clubdata.sql -d postgres -x
I get the error: psql:clubdata.sql:6: ERROR: cannot execute CREATE SCHEMA in a read-only transaction
.
Why did it create a read-only database on my local machine? Can I change this?
asked Jul 2, 2015 at 13:48
3
Normally the most plausible reasons for this kind of error are :
-
trying create statements on a read-only replica (the entire instance is read-only).
-
<username>
hasdefault_transaction_read_only
set to ON -
the database has
default_transaction_read_only
set to ON
The script mentioned has in its first lines:
CREATE DATABASE exercises;
c exercises
CREATE SCHEMA cd;
and you report that the error happens with CREATE SCHEMA
at line 6, not before.
That means that the CREATE DATABASE
does work, when run by <username>
.
And it wouldn’t work if any of the reasons above was directly applicable.
One possibility that would technically explain this would be that default_transaction_read_only
would be ON
in the postgresql.conf
file, and set to OFF
for the database postgres
, the one that the invocation of psql connects to, through an ALTER DATABASE
statement that supersedes the configuration file.
That would be why CREATE DATABASE
works, but then as soon as it connects to a different database with c
, the default_transaction_read_only
setting of the session would flip to ON
.
But of course that would be a pretty weird and unusual configuration.
answered Jul 2, 2015 at 16:32
Daniel VéritéDaniel Vérité
56.1k15 gold badges125 silver badges150 bronze badges
1
Reached out to pgexercises.com and they were able to help me.
I ran these commands(separately):
psql -U <username> -d postgres
begin;
set transaction read write;
alter database exercises set default_transaction_read_only = off;
commit;
q
Then I dropped the database from the terminal dropdb exercises
and ran script again psql -U <username> -f clubdata.sql -d postgres -x -q
answered Jul 4, 2015 at 4:45
ltrainprltrainpr
2,9653 gold badges28 silver badges40 bronze badges
1
I was having getting cannot execute CREATE TABLE in a read-only transaction
, cannot execute DELETE TABLE in a read-only transaction
and others.
They all followed a cannot execute INSERT in a read-only transaction
. It was like the connection had switched itself over to read-only in the middle of my batch processing.
Turns out, I was running out of storage!
Write access was disabled when the database could no longer write anything. I am using Postgres on Azure. I don’t know if the same effect would happen if I was on a dedicated server.
alecxe
455k116 gold badges1061 silver badges1180 bronze badges
answered Jan 15, 2019 at 4:52
Brad MathewsBrad Mathews
1,4532 gold badges20 silver badges45 bronze badges
1
I had same issue for Postgre
Update statement
SQL Error: 0, SQLState: 25006 ERROR: cannot execute UPDATE in a read-only transaction
Verified Database
access by running below query and it will return either true
or false
SELECT pg_is_in_recovery()
true
-> Database has only Read Access
false
-> Database has full Access
if returns true then check with DBA team for the full access and also try for ping
in command prompt
and ensure the connectivity.
ping <database hostname or dns>
Also verify if you have primary and standby node for the database
answered Jun 16, 2021 at 16:22
BharathirajaBharathiraja
6441 gold badge11 silver badges20 bronze badges
In my case I had a master and replication nodes, and the master node became replication node, which I believe switched it into hot_standby
mode. So I was trying to write data into a node that was meant only for reading, therefore the «read-only» problem.
You can query the node in question with SELECT pg_is_in_recovery()
, and if it returns True
then it is «read-only», and I suppose you should switch to using whatever master node you have now.
I got this information from: https://serverfault.com/questions/630753/how-to-change-postgresql-database-from-read-only-to-writable.
So full credit and my thanks goes to Craig Ringer!
answered May 6, 2021 at 9:12
vladimirrorvladimirror
65014 silver badges7 bronze badges
Dbeaver: In my case
This was on.
answered Oct 4, 2022 at 4:00
This doesn’t quite answer the original question, but I received the same error and found this page, which ultimately led to a fix.
My issue was trying to run a function with temp tables being created and dropped. The function was created with SECURITY DEFINER
privileges, and the user had access locally.
In a different environment, I received the cannot execute DROP TABLE in a read-only transaction
error message. This environment was AWS Aurora, and by default, non-admin developers were given read-only privileges. Their server connections were thus set up to use the read-only node of Aurora (-ro-
is in the connection url), which must put the connection in the read-only state. Running the same function with the same user against the write node worked.
Seems like a good use case for table variables like SQL Server has! Or, at least, AWS should modify their flow to allow temp tables to be created and dropped on read nodes.
answered May 7, 2020 at 15:35
ps2goatps2goat
7,8971 gold badge33 silver badges68 bronze badges
This occurred when I was restoring a production database locally, the database is still doing online recovery from the WAL records.
A little bit unexpected as I assumed pgbackgrest was creating instantly recoverable restores, perhaps not.
91902 postgres 20 0 1445256 14804 13180 D 4.3 0.3 0:28.06 postgres: startup recovering 000000010000001E000000A5
answered Dec 6, 2019 at 15:28
Kevin ParkerKevin Parker
16.8k20 gold badges76 silver badges103 bronze badges
If like me you are trying to create DB on heroku and are stuck as this message shows up on the dataclip tab
I did this,
Choose Resources from(Overview Resources Deploy Metrics Activity Access Settings)
Choose Settings out of (Overview, Durability, Settings, Dataclip)
Then in Administration->Database Credentials choose View Credentials...
then open terminal and fill that info here and enter
psql --host=***************.amazonaws.com --port=5432 --username=*********pubxl --password --dbname=*******lol
then it’ll ask for password, copy-paste from there and you can run Postgres cmds.
answered May 27, 2021 at 18:42
shekhar g hshekhar g h
1,1751 gold badge9 silver badges12 bronze badges
I suddenly started facing this error on postgres installed on my windows machine, when I was running alter
query from dbeaver, all I did was deleted the connection of postgres from dbeaver and created a new connection
answered Apr 8, 2022 at 9:55
Muhammad AwaisMuhammad Awais
1,3501 gold badge17 silver badges20 bronze badges
I just had this error. My cause was not granting permission to the SEQUENCE
GRANT ALL ON SEQUENCE word_mash_word_cube_template_description_reference_seq TO ronshome_user;
answered Oct 13, 2022 at 16:18
Ron PiggottRon Piggott
7031 gold badge8 silver badges26 bronze badges
If you are facing this issue with an RDS instance cluster, please check your endpoint and use the Writer instance endpoint. Then it should work now.
answered Nov 23, 2022 at 11:46
Aditya YAditya Y
5015 silver badges9 bronze badges
You could always double check your problem by issuing a command such as
/usr/bin/pgsql/bin/createdb test
You can frequently get better/clearer error messages and/or other information from the command line than from GUI tools such as pgAdmin III.
But, in order to change a system parameter, you have to reload the postgresql.conf
file after making changes. You can do this by either
-
rebooting the server or
-
reloading the .conf file or
-
change on the client side
From here, some parameters always need a reboot and some can be dynamically changed by simply reloading.
I tried to find a clear list of those which required reboot and which required reload, but failed (grateful for input on this!).
For a system reboot,
pgctl restart
To reload, you could also try these commands (from here — or check the documentation page given above).
Option 1: From the command-line shell
su - postgres /path/to/pg_ctl reload
Option 2: Using SQL
SELECT pg_reload_conf();
You can verify that the setting has «taken» from the table pg_settings. Also of interest is this page.
For the default_transaction_read_only
parameter, you can also set it directly from the client (see here). Check the links here for how to set all parameters — reboot, reload or client.
test=# SELECT name, setting FROM pg_settings WHERE name = 'default_transaction_read_only';
name | setting
-------------------------------+---------
default_transaction_read_only | on
(1 row)
test=#
test=# `SET default_transaction_read_only = OFF;`
SET
test=#
test=# SELECT name, setting FROM pg_settings WHERE name = 'default_transaction_read_only';
name | setting
-------------------------------+---------
default_transaction_read_only | off
(1 row)
test=#
test=# SET default_transaction_read_only = ON;
SET
Данная статья — продолжение статьи «Инкрементальные бэкапы postgresql с pgbackrest — курс молодого бойца от разработчика».
В первой части мы научились делать инкрементальные бэкапы, загружать их на удаленный сервер (репозиторий с бэкапами) и откатываться на последний бэкап.
В этой статье мы научимся шифровать бэкапы, загружать их в S3-совместимое хранилище (вместо второго сервера-репозитория), восстанавливаться на чистый кластер и, наконец, восстанавливаться на определенный момент времени (point in time recovery, PITR).
Момент
Автор не претендует на роль DBA, но иногда любит настроить и посмотреть все сам.
Подготовка
Для воспроизведения данного мануала нам понадобится:
- Cервер с базой данных (на него мы установим pgbackrest);
- S3 хранилище (вы можете использовать Amazon или любое S3 совместимое, я буду использовать Amazon S3);
- Третий сервер. По желанию. На нем мы потренируемся разворачивать postgresql и pgbackrest с нуля, разворачивая существующий бекап из S3 хранилища (сервер сгорел, переезд и т.п.);
Подразумевается, что postgresql у вас уже установлен, а следовательно пользователь postgres тоже имеется.
Процесс установки pgbackrest я описал в предыдущей статье, но на всякий случай продублирую еще раз (напоминаю: перед установкой создайте себе sudo пользователя — я буду использовать sudo пользователя с юзернеймом pgbackrest).
Чтобы у вас было меньше проблем при воспроизведении инструкции — курсивом я прописываю где, каким пользователем и с какими правами я исполнял команду во время написания и проверки статьи.
Пойдем мы по такому пути:
Настроим postgresql и pgbackrest
Настроим шифрование бэкапов (две строчки)
Научимся делать бэкап и отправлять в S3 хранилище (пять строчек)
Сделаем бэкап
Представим, что сломали кластер, развернемся на новом сервере, подключим существующий S3 репозиторий и накатим бэкап
Посмотрим на PITR (Point In Time Recovery): восстановимся на определенный момент времени (допустим, pgbackrest забэкапил уже drop table, а нам нужно откатиться на 4 часа назад, когда табличка еще существовала)
Поехали!
Установка pgBackRest
sudo пользователь или root:
1. Скачиваем архив с pgbackrest и переносим его содержимое в папку /build:
sudo mkdir /build
sudo wget -q -O -
https://github.com/pgbackrest/pgbackrest/archive/release/2.18.tar.gz |
sudo tar zx -C /build
2. Устанавливаем необходимые для сборки зависимости:
sudo apt-get update
sudo apt-get install build-essential libssl-dev libxml2-dev libperl-dev zlib1g-dev
libpq-dev
3. Собираем pgbackrest:
cd /build/pgbackrest-release-2.18/src && sudo ./configure
sudo make -s -C /build/pgbackrest-release-2.18/src
4. Копируем исполняемый файл в директорию /usr/bin:
sudo cp /build/pgbackrest-release-2.18/src/pgbackrest /usr/bin
sudo chmod 755 /usr/bin/pgbackrest
5. pgBackRest требует наличие perl. Устанавливаем:
sudo apt-get install perl
6. Создаем директории для логов, даем им определенные права:
sudo mkdir -p -m 770 /var/log/pgbackrest
sudo chown postgres:postgres /var/log/pgbackrest
sudo mkdir -p /etc/pgbackrest
sudo mkdir -p /etc/pgbackrest/conf.d
sudo touch /etc/pgbackrest/pgbackrest.conf
sudo chmod 640 /etc/pgbackrest/pgbackrest.conf
sudo chown postgres:postgres /etc/pgbackrest/pgbackrest.conf
7. Проверяем:
pgbackrest version
Настройка postgresql и pgBackRest
sudo пользователь или root:
1. Внесем необходимые настройки в postgresql.conf (он находится в папке /etc/postgresql/11/main) для работы pgBackRest:
archive_command = 'pgbackrest --stanza=main archive-push %p' # Где main - название кластера. При установке postgres автоматически создает кластер main.
archive_mode = on
max_wal_senders = 3
wal_level = replica
2. Внесем необходимые настройки в файл конфигурации pgbackrest (/etc/pgbackrest/pgbackrest.conf):
[main]
pg1-path=/var/lib/postgresql/11/main
[global]
log-level-file=detail
repo1-cipher-pass=tr5+BXdfdoxeyUqfo6AzLTrW+c+Jfd/1QbQj2CDMMBwtB0YGH3EJajry4+Eeen6D
repo1-cipher-type=aes-256-cbc
repo1-path=/var/lib/pgbackrest
repo1-retention-full=2 # Параметр, указывающий сколько хранить полных бэкапов. Т.е. если у вас есть два полных бэкапа и вы создаете третий, то самый старый бэкап будет удален. Можно произносить как "хранить не более двух полных бэкапов" - по аналогии с ротациями логов.
repo1-type=s3
repo1-s3-bucket=pgbackrest-part2-tutorial
repo1-s3-endpoint=s3.us-east-1.amazonaws.com
repo1-s3-region=us-east-1
repo1-s3-key=9wdS3G8U5wz7kNsFWVGck7DDZ7DtVDtbM
repo1-s3-key-secret=A9zRmW16zXKt2vVA8mmNsFWy2mUAPYHa
start-fast=y
[global:archive-push]
compress-level=3
Как вы поняли, тут мы сразу настроили шифрование и настроили поддержку S3 хранилища.
P.S. Комментариев в конфиге быть не должно.
P.S. Чтобы сгенерировать надежный ключ шифрования можно воспользоваться командой:
openssl rand -base64 48
Создание хранилища
sudo пользователь или root:
sudo mkdir -m 770 /var/lib/pgbackrest
sudo chown -R postgres /var/lib/pgbackrest/
sudo -u postgres pgbackrest --stanza=main stanza-create
Если все получилось, то в S3-бакете вы увидите файлы, которые сгенерировал pgbackrest.
Делаем бэкап:
sudo пользователь или root:
sudo -u postgres pgbackrest --log-level-console=info --stanza=main backup
pgBackRest создаст первый полный бэкап. При желании вы можете запустить команду бэкапа повторно и убедиться, что система создаст инкрементальный бэкап.
Если вы хотите повторно сделать полный бэкап, то укажите дополнительный флаг:
sudo -u postgres pgbackrest --log-level-console=info --stanza=main --type=full backup
Посмотреть список бэкапов можно с помощью команды:
sudo -u postgres pgbackrest --stanza=main info
Восстанавливаем бэкап:
sudo пользователь или root:
1. Останавливаем работающий кластер:
sudo pg_ctlcluster 11 main stop
2. Восстанавливаемся из бэкапа:
sudo -u postgres pgbackrest --stanza=main --log-level-console=info --delta --recovery-option=recovery_target=immediate restore
Чтобы восстановить базу в состояние последнего ПОЛНОГО бэкапа используйте команду без указания recovery_target:
sudo -u postgres pgbackrest --stanza=main --log-level-console=info --delta restore
Важно! После восстановления может оказаться так, что база зависнет в режиме восстановления (будут ошибки в духе ERROR: cannot execute DROP DATABASE in a read-only transaction). Решается следующим образом (нужно будет малость подождать после исполнения команды):
sudo -u postgres psql -c "select pg_wal_replay_resume()"
UPD: Как я понял, данная ошибка возникает из-за того, что после восстановления с указанием recovery_target=immediate pgbackrest ставит БД на паузу. Чтобы этого не было, нужно дополнительно указать некоторые флаги (их значение можете прочитать тут и тут):
sudo -u postgres pgbackrest --stanza=main --log-level-console=info --delta --recovery-option=recovery_target=immediate --target-action=promote --type=immediate restore
На самом деле, есть возможность восстановить конкретный бэкап по его имени. Здесь я лишь укажу ссылку на описание данной фичи в документации. Разработчики советуют использовать данный параметр с осторожностью и объясняют почему. От себя могу добавить, что я его использовал. Главное убедитесь, что после восстановления база вышла из recovery mode (select pg_is_in_recovery() должен показать «f») и на всякий случай сделайте полный бэкап после восстановления.
3. Запускаем кластер:
sudo pg_ctlcluster 11 main start
После восстановления бэкапа выполним повторный бэкап:
sudo -u postgres pgbackrest --log-level-console=info --stanza=main backup
Восстанавливаем бэкап на чистый кластер:
Давайте представим, что произошло что-то ужасное — сервер сгорел (упал, затопило, потеряли доступ). Нужно восстановить все на новый, чистый сервер. На новом сервере мы уже завели sudo пользователя, установили pgbackrest, установили postgresql и у нас появился чистый и свежий main кластер.
Все, что нам нужно сделать — настроить конфиг postgresql и pgBackRest аналогично тем, что были на прежнем сервере, перезапустить postgresql и выполнить порядок команд, аналогичный восстановлению:
sudo пользователь или root:
1. Останавливаем работающий кластер:
sudo pg_ctlcluster 11 main stop
2. Восстанавливаемся из бэкапа:
sudo -u postgres pgbackrest --stanza=main --log-level-console=info --delta --recovery-option=recovery_target=immediate --target-action=promote --type=immediate restore
P.S. Нюансы и тонкости данной команды я описал выше.
3. Запускаем кластер:
sudo pg_ctlcluster 11 main start
После восстановления бэкапа нам необходимо выполнить повторный бэкап:
sudo -u postgres pgbackrest --log-level-console=info --stanza=main backup
Вот, собственно, и все. Пожар потушен.
Point In Time Recovery (PITR)
Давайте представим такую ситуацию, что в 16:00 был создан бэкап, а в 18:05 мы случайно стерли важную табличку в которую за 2 часа успело попасть очень много важных данных, которые не хотелось бы потерять. PgBackRest за счет PostgreSQL предоставляет нам такую возможность: мы можем откатиться на конкретный момент времени, при условии, что у нас достаточное кол-во информации для восстановления.
Работает это так:
- Мы говорим, что хотим откатиться на момент времени 18:04;
- pgBackRest ищет последний актуальный бэкап (16:00);
- Восстанавливает его, но накатывает не все, а лишь то, что мы успели сделать до 18.04;
P.S. построен этот механизм на WAL логах. Почитать можно здесь.
Выполнить восстановление на конкретный момент времени (предварительно остановив кластер) можно данной командой:
sudo -u postgres pgbackrest --stanza=main --log-level-console=info --delta --type=time "--target=2020-09-06 18:27:24.561458+02" --target-action=promote restore
Важное замечание — PostgreSQL может воспроизводить WAL логи только вперед, но не назад. Что это значит на практике?
Допустим, в 16:00 был создан бэкап. В 18:05 мы случайно удалили таблицу, а в 18:10 снова был создан бэкап с уже удаленной таблицей. Когда вы попытаетесь сделать PITR — pgBackRest возьмет бэкап, который был создан в 16:00 и накатит сверху все то, что было до 18:05, а не возьмет бэкап, который был создал в 18:10 и будет откатывать все назад. Это важно понимать. Более подробно данный механизм описан здесь.
Предположим, вы сделали бэкап, в который уже попала информации о удалении таблицы. С помощью флага —set скажем, какой бэкап нужно использовать за базу (тот, в котором таблица еще была). pgBackRest возьмет данный бэкап и накатит все то, что было до удаления таблицы.
sudo -u postgres pgbackrest --stanza=main --log-level-console=info --delta --type=time --set=20200905-183838F_20200906-182612I "--target=2020-09-06 18:27:24.561458+02" --target-action=promote restore
P.S. Команду для получения списка бэкапов я показал выше.
Запустим кластер. После запуска изучим файл /var/log/postgresql/postgresql-11-main.log. В нем нас интересуют следующие строки, показывающие, что восстановление на указанный момент времени произошло успешно:
starting point-in-time recovery to 2020-09-07 11:26:52.493127+02
...
recovery stopping before commit of transaction 576, time 2020-09-07 11:27:14.584496+02
...
last completed transaction was at log time 2020-09-07 11:24:09.583761+02
Вот и все. Настоятельно советую поэкспериментировать с данным инструментом перед применением его в бою.
Normally the most plausible reasons for this kind of error are :
-
trying create statements on a read-only replica (the entire instance is read-only).
-
<username>
hasdefault_transaction_read_only
set to ON -
the database has
default_transaction_read_only
set to ON
The script mentioned has in its first lines:
CREATE DATABASE exercises;
c exercises
CREATE SCHEMA cd;
and you report that the error happens with CREATE SCHEMA
at line 6, not before.
That means that the CREATE DATABASE
does work, when run by <username>
.
And it wouldn’t work if any of the reasons above was directly applicable.
One possibility that would technically explain this would be that default_transaction_read_only
would be ON
in the postgresql.conf
file, and set to OFF
for the database postgres
, the one that the invocation of psql connects to, through an ALTER DATABASE
statement that supersedes the configuration file.
That would be why CREATE DATABASE
works, but then as soon as it connects to a different database with c
, the default_transaction_read_only
setting of the session would flip to ON
.
But of course that would be a pretty weird and unusual configuration.
Reached out to pgexercises.com and they were able to help me.
I ran these commands(separately):
psql -U <username> -d postgres
begin;
set transaction read write;
alter database exercises set default_transaction_read_only = off;
commit;
q
Then I dropped the database from the terminal dropdb exercises
and ran script again psql -U <username> -f clubdata.sql -d postgres -x -q
I had same issue for Postgre
Update statement
SQL Error: 0, SQLState: 25006 ERROR: cannot execute UPDATE in a read-only transaction
Verified Database
access by running below query and it will return either true
or false
SELECT pg_is_in_recovery()
true
-> Database has only Read Access
false
-> Database has full Access
if returns true then check with DBA team for the full access and also try for ping
in command prompt
and ensure the connectivity.
ping <database hostname or dns>
Also verify if you have primary and standby node for the database
I was having getting cannot execute CREATE TABLE in a read-only transaction
, cannot execute DELETE TABLE in a read-only transaction
and others.
They all followed a cannot execute INSERT in a read-only transaction
. It was like the connection had switched itself over to read-only in the middle of my batch processing.
Turns out, I was running out of storage! Write access was disabled when the database could no longer write anything. I am using Postgres on Azure. I don’t know if the same effect would happen if I was on a dedicated server.
Я пытаюсь настроить данные pgexercises на моем локальном компьютере. Когда я запускаю: psql -U <username> -f clubdata.sql -d postgres -x
, я получаю сообщение об ошибке: psql:clubdata.sql:6: ERROR: cannot execute CREATE SCHEMA in a read-only transaction
.
Почему на моем локальном компьютере была создана база данных, доступная только для чтения? Могу я это изменить?
5 ответов
Лучший ответ
Обратился к pgexercises.com, и они смогли мне помочь.
Я выполнил эти команды (отдельно):
psql -U <username> -d postgres
begin;
set transaction read write;
alter database exercises set default_transaction_read_only = off;
commit;
q
Затем я сбросил базу данных с терминала dropdb exercises
и снова запустил скрипт psql -U <username> -f clubdata.sql -d postgres -x -q
17
ltrainpr
4 Июл 2015 в 04:45
Это произошло, когда я восстанавливал производственную базу данных локально, база данных все еще выполняет восстановление в оперативном режиме из записей WAL.
Немного неожиданно, поскольку я предположил, что pgbackgrest создает мгновенно восстанавливаемые восстановления, возможно, нет.
91902 postgres 20 0 1445256 14804 13180 D 4.3 0.3 0:28.06 postgres: startup recovering 000000010000001E000000A5
0
Kevin Parker
6 Дек 2019 в 15:28
Это не совсем ответ на исходный вопрос, но я получил ту же ошибку и нашел эту страницу, что в конечном итоге привело к исправлению.
Моя проблема заключалась в попытке запустить функцию с созданием и удалением временных таблиц. Функция была создана с правами SECURITY DEFINER
, и пользователь имел доступ локально.
В другой среде я получил сообщение об ошибке cannot execute DROP TABLE in a read-only transaction
. Этой средой была AWS Aurora, и по умолчанию разработчикам, не являющимся администраторами, были предоставлены права только для чтения. Таким образом, их серверные соединения были настроены на использование доступного только для чтения узла Aurora (-ro-
находится в URL-адресе соединения), который должен перевести соединение в состояние только для чтения. Выполнение той же функции с тем же пользователем для узла записи сработало.
Похоже, хороший вариант использования табличных переменных, таких как SQL Server! Или, по крайней мере, AWS следует изменить свой поток, чтобы разрешить создание и удаление временных таблиц на узлах чтения.
1
ps2goat
7 Май 2020 в 15:35
Я получал cannot execute CREATE TABLE in a read-only transaction
, cannot execute DELETE TABLE in a read-only transaction
и другие.
Все они следовали cannot execute INSERT in a read-only transaction
. Как будто соединение переключилось в режим только для чтения в середине моей пакетной обработки.
Оказывается, у меня закончилась память! Доступ для записи был отключен, когда база данных больше не могла ничего писать. Я использую Postgres в Azure. Я не знаю, случился бы такой же эффект, если бы я был на выделенном сервере.
7
Brad Mathews
15 Янв 2019 в 04:52
Обычно наиболее вероятными причинами такого рода ошибок являются:
-
попытка создания операторов на реплике, доступной только для чтения (весь экземпляр доступен только для чтения).
-
<username>
имеетdefault_transaction_read_only
значение ON -
в базе данных установлено значение «
default_transaction_read_only
«
В первых строках упомянутого сценария:
CREATE DATABASE exercises;
c exercises
CREATE SCHEMA cd;
и вы сообщаете, что ошибка возникает с CREATE SCHEMA
в строке 6, а не ранее.
Это означает, что CREATE DATABASE
действительно работает, когда запускается <username>
. И это не сработало бы, если бы любая из вышеперечисленных причин была прямо применима.
Одна из возможностей, которая технически объяснила бы это, заключалась в том, что default_transaction_read_only
будет ON
в файле postgresql.conf
и установлен на OFF
для базы данных postgres
, той самой к которому подключается вызов psql через оператор ALTER DATABASE
, заменяющий файл конфигурации.
Вот почему CREATE DATABASE
работает, но как только он подключается к другой базе данных с помощью c
, параметр default_transaction_read_only
сеанса переключается на ON
.
Но, конечно, это была бы довольно странная и необычная конфигурация.
29
Daniel Vérité
2 Июл 2015 в 16:37
Дисклеймер
Я — разработчик. Я пишу код, с базой данных взаимодействую лишь как пользователь. Я ни в коем случае не претендую на должность системного администратора и, тем более, dba. Но…
Так вышло, что мне нужно было организовать резервное копирование postgresql базы данных. Никаких облаков — держи SSH и сделай, чтобы все работало и не просило денег. Что мы делаем в таких случаях? Правильно, пихаем pgdump в cron, каждый день бэкапим все в архив и если совсем разошлись — отправляем этот архив куда-нибудь подальше.
В этот раз сложность состояла в том, что по планам база должна была расти примерно на +- 100 МБ в день. Разумеется, уже через пару недель желание бэкапить все pgdump’ом отпадет. Тут на помощь приходят инкрементальные бэкапы.
Интересно? Добро пожаловать под кат.
Инкрементальный бэкап — разновидность резервной копии, когда копируются не все файлы источника, а только новые и измененные с момента создания предыдущей копии.
Как и любой разработчик, СОВЕРШЕННО не желающий (на тот момент) разбираться в тонкостях postgres я хотел найти зеленую кнопку. Ну, знаете, как в AWS, DigitalOcean: нажал одну кнопку — получил репликацию, нажал вторую — настроил бэкапы, третью — все откатил на пару часов назад. Кнопки и красивого GUIшного инструмента я не нашел. Если вы знаете такой (бесплатный или дешевый) — напишите об этом в комментариях.
Погуглив я нашел два инструмента pgbarman и pgbackrest. С первым у меня просто не задалось (очень скудная документация, пытался все поднять по старинным мануалам), а вот у второго документация оказалась на уровне, но и не без изъяна. Чтобы упростить работу тем, кто столкнется с подобной задачей и была написана данная статья.
Дочитав данную статью вы научитесь делать инкрементальные бекапы, сохранять их на удаленный сервер (репозиторий с бэкапами) и восстанавливать их в случае утери данных или иных проблем на основном сервере.
Подготовка
Для воспроизведения мануала вам понадобятся два VPS. Первый будет хранилищем (репозиторием, на котором будут лежат бэкапы), а второй, собственно, сам сервер с postgres (в моем случае 11 версия postgres).
Подразумевается, что на сервере с postgres у вас есть root, sudo пользователь, пользователь postgres и сам postgres установлен (пользователь postgres создается автоматически при установке postgresql), а на сервере-репозитории есть root и sudo пользователь (в мануале будет использоваться имя пользователя pgbackrest).
Чтобы у вас было меньше проблем при воспроизведении инструкции — курсивом я прописываю где, каким пользователем и с какими правами я исполнял команду во время написания и проверки статьи.
Установка pgbackrest
Репозиторий (пользователь pgbackrest):
1. Скачиваем архив с pgbackrest и переносим его содержимое в папку /build:
sudo mkdir /build
sudo wget -q -O -
https://github.com/pgbackrest/pgbackrest/archive/release/2.18.tar.gz |
sudo tar zx -C /build
2. Устанавливаем необходимые для сборки зависимости:
sudo apt-get update
sudo apt-get install build-essential libssl-dev libxml2-dev libperl-dev zlib1g-dev
libpq-dev
3. Собираем pgbackrest:
cd /build/pgbackrest-release-2.18/src && sudo ./configure
sudo make -s -C /build/pgbackrest-release-2.18/src
4. Копируем исполняемый файл в директорию /usr/bin:
sudo cp /build/pgbackrest-release-2.18/src/pgbackrest /usr/bin
sudo chmod 755 /usr/bin/pgbackrest
5. Pgbackrest требует наличие perl. Устанавливаем:
sudo apt-get install perl
6. Создаем директории для логов, даем им определенные права:
sudo mkdir -p -m 770 /var/log/pgbackrest
sudo chown pgbackrest:pgbackrest /var/log/pgbackrest
sudo mkdir -p /etc/pgbackrest
sudo mkdir -p /etc/pgbackrest/conf.d
sudo touch /etc/pgbackrest/pgbackrest.conf
sudo chmod 640 /etc/pgbackrest/pgbackrest.conf
sudo chown pgbackrest:pgbackrest /etc/pgbackrest/pgbackrest.conf
7. Проверяем:
pgbackrest version
Postgres сервер (sudo пользователь или root):
Процесс установки pgbackrest на сервере с postgres аналогичен процессу установки на репозитории (да, pgbackrest должен стоять на обоих серверах), но в 6-ом пункте вторую и последнюю команды:
sudo chown pgbackrest:pgbackrest /var/log/pgbackrest
sudo chown pgbackrest:pgbackrest /etc/pgbackrest/pgbackrest.conf
заменяем на:
sudo chown postgres:postgres /var/log/pgbackrest
sudo chown postgres:postgres /etc/pgbackrest/pgbackrest.conf
Настройка взаимодействия между серверами через passwordless SSH
Для того, чтобы pgbackrest корректно работал, необходимо настроить взаимодействие между postgres сервером и репозиторием по файлу-ключу.
Репозиторий (пользователь pgbackrest):
Создаем пару ключей:
mkdir -m 750 /home/pgbackrest/.ssh
ssh-keygen -f /home/pgbackrest/.ssh/id_rsa
-t rsa -b 4096 -N ""
Внимание! Указанные выше команды выполняем без sudo.
Postgres сервер (sudo пользователь или root):
Создаем пару ключей:
sudo -u postgres mkdir -m 750 -p /var/lib/postgresql/.ssh
sudo -u postgres ssh-keygen -f /var/lib/postgresql/.ssh/id_rsa
-t rsa -b 4096 -N ""
Репозиторий (sudo пользователь):
Копируем публичный ключ postgres сервера на сервер-репозиторий:
(echo -n 'no-agent-forwarding,no-X11-forwarding,no-port-forwarding,' &&
echo -n 'command="/usr/bin/pgbackrest ${SSH_ORIGINAL_COMMAND#* }" ' &&
sudo ssh [email protected]<postgres_server_ip> cat /var/lib/postgresql/.ssh/id_rsa.pub) |
sudo -u pgbackrest tee -a /home/pgbackrest/.ssh/authorized_keys
На данном шаге попросит пароль от root пользователя. Вводить нужно именно пароль root пользователя postgres сервера!
Postgres сервер (sudo пользователь):
Копируем публичный ключ репозитория на сервер с postgres:
(echo -n 'no-agent-forwarding,no-X11-forwarding,no-port-forwarding,' &&
echo -n 'command="/usr/bin/pgbackrest ${SSH_ORIGINAL_COMMAND#* }" ' &&
sudo ssh [email protected]<repository_server_ip> cat /home/pgbackrest/.ssh/id_rsa.pub) |
sudo -u postgres tee -a /var/lib/postgresql/.ssh/authorized_keys
На данном шаге попросит пароль от root пользователя. Вводить нужно именно пароль root пользователя репозитория!
Проверяем:
Репозиторий (root пользователь, для чистоты эксперимента):
sudo -u pgbackrest ssh [email protected]<postgres_server_ip>
Postgres сервер (root пользователь, для чистоты эксперимента):
sudo -u postgres ssh [email protected]<repository_server_ip>
Убеждаемся, что без проблем получаем доступ.
Настройка postgres сервера
Postgres сервер (sudo пользователь или root):
1. Разрешим «стучаться» на postgres сервер с внешних ip. Для этого отредактируем файл postgresql.conf (находится в папке /etc/postgresql/11/main), добавив в него строчку:
listen_addresses = '*'
Если такая строка уже есть — либо раскомментируйте ее, либо установите значение параметра как ‘*’.
В файле pg_hba.conf (так же находится в папке /etc/postgresql/11/main) добавляем следующие строчки:
hostssl all all 0.0.0.0/0 md5
host all all 0.0.0.0/0 md5
где:
hostssl/host - подключаемся через SSL (или нет)
all - разрешаем подключение ко всем базам
all - имя пользователя, которому разрешаем подключение (всем)
0.0.0.0/0 - маска сети с которой можно подключаться
md5 - способ шифрования пароля
2. Внесем необходимые настройки в postgresql.conf (он находится в папке /etc/postgresql/11/main) для работы pgbackrest:
archive_command = 'pgbackrest --stanza=main archive-push %p' # Где main - название кластера. При установке postgres автоматически создает кластер main.
archive_mode = on
max_wal_senders = 3
wal_level = replica
3. Внесем необходимые настройки в файл конфигурации pgbackrest (/etc/pgbackrest/pgbackrest.conf):
[main]
pg1-path=/var/lib/postgresql/11/main
[global]
log-level-file=detail
repo1-host=<repository_server_ip>
4. Перезагрузим postgresql:
sudo service postgresql restart
Настройка сервера-репозитория
Репозиторий (pgbackrest пользователь):
Внесем необходимые настройки в файл конфигурации pgbackrest
(/etc/pgbackrest/pgbackrest.conf):
[main]
pg1-host=<postgres_server_ip>
pg1-path=/var/lib/postgresql/11/main
[global]
repo1-path=/var/lib/pgbackrest
repo1-retention-full=2 # Параметр, указывающий сколько хранить полных бэкапов. Т.е. если у вас есть два полных бэкапа и вы создаете третий, то самый старый бэкап будет удален. Можно произносить как "хранить не более двух бэкапов" - по аналогии с ротациями логов. Спасибо @Aytuar за исправление ошибки.
start-fast=y # Начинает резервное копирование немедленно, прочитать про этот параметр можно тут https://postgrespro.ru/docs/postgrespro/9.5/continuous-archiving
Создание хранилища
Репозиторий (pgbackrest пользователь):
Создаем новое хранилище для кластера main:
sudo mkdir -m 770 /var/lib/pgbackrest
sudo chown -R pgbackrest /var/lib/pgbackrest/
sudo -u pgbackrest pgbackrest --stanza=main stanza-create
Проверка
Postgres сервер (sudo пользователь или root):
Проверяем на postgres сервере:
sudo -u postgres pgbackrest --stanza=main --log-level-console=info check
Репозиторий (pgbackrest пользователь):
Проверяем на сервере-репозитории:
sudo -u pgbackrest pgbackrest --stanza=main --log-level-console=info check
Убеждаемся, что в выводе видим строку «check command end: completed successfully».
Устали? Переходим к самому интересному.
Делаем бэкап
Репозиторий (pgbackrest пользователь):
1. Выполняем резервное копирование:
sudo -u pgbackrest pgbackrest --stanza=main backup
2. Убеждаемся, что бэкап был создан:
ls /var/lib/pgbackrest/backup/main/
Pgbackrest создаст первый полный бэкап. При желании вы можете запустить команду бэкапа повторно и убедиться, что система создаст инкрементальный бэкап.
Если вы хотите повторно сделать полный бэкап, то укажите дополнительный флаг:
sudo -u pgbackrest pgbackrest --stanza=main --type=full backup
Если вы хотите подробный вывод в консоль, то также укажите:
sudo -u pgbackrest pgbackrest --stanza=main --type=full --log-level-console=info backup
Восстанавливаем бэкап
Postgres сервер (sudo пользователь или root):
1. Останавливаем работающий кластер:
sudo pg_ctlcluster 11 main stop
2. Восстанавливаемся из бэкапа:
sudo -u postgres pgbackrest --stanza=main --log-level-console=info --delta --recovery-option=recovery_target=immediate restore
Чтобы восстановить базу в состояние последнего ПОЛНОГО бэкапа используйте команду без указания recovery_target:
sudo -u postgres pgbackrest --stanza=main --log-level-console=info --delta restore
Важно! После восстановления может оказаться так, что база зависнет в режиме восстановления (будут ошибки в духе ERROR: cannot execute DROP DATABASE in a read-only transaction). Честно говоря, я еще не понял, с чем это связано. Решается следующим образом (нужно будет малость подождать после исполнения команды):
sudo -u postgres psql -c "select pg_wal_replay_resume()"
На самом деле, есть возможность восстановить конкретный бэкап по его имени. Здесь я лишь укажу ссылку на описание данной фичи в документации. Разработчики советуют использовать данный параметр с осторожностью и объясняют почему. От себя могу добавить, что я его использовал. Если очень нужно — убедитесь, что после восстановления база вышла из recovery mode (select pg_is_in_recovery() должен показать «f») и на всякий случай сделайте полный бэкап после восстановления.
3. Запускаем кластер:
sudo pg_ctlcluster 11 main start
После восстановления бэкапа нам необходимо выполнить повторный бэкап:
Репозиторий (pgbackrest пользователь):
sudo pgbackrest --stanza=main backup
На этом все. В заключение хочу напомнить, что я ни в коем случае не пытаюсь строить из себя senior dba и при малейшей возможности буду использовать облака. В настоящее время сам начинаю изучать различные темы вроде резервного копирования, репликаций, мониторинга и т.п. и о результатах пишу небольшие отчеты, дабы сделать небольшой вклад в сообщество и оставить для себя небольшие шпаргалки.
В следующих статьях постараюсь рассказать о дополнительных фичах — восстановление данных на чистый кластер, шифрование бэкапов и публикацию на S3, бэкапы через rsync.
Источник: habr.com
I have created a stored procedure in postgresql database and am calling this from my jasper report and have set the fields in report.
However since a temp table is created during execution, the report fails when ran from report server with the error below.
org.postgresql.util.PSQLException: ERROR: cannot execute DROP TABLE in a read-only transaction Where: SQL statement «DROP TABLE IF EXISTS msg_stats»
How to fix this error? is there a configuration change i could make to allow reportserver execute the stored procedure call?
Am using a JDBC datasource and passing readonly=true as part of the JDBC URL did not help.
Please advise, thanks.
CREATE OR REPLACE FUNCTION app_summary(
IN startdate timestamp without time zone,
IN enddate timestamp without time zone,
OUT param1 character varying,
OUT param2 character varying,
OUT param3 bigint,
OUT param4 bigint,
OUT param5 bigint,
OUT param6 bigint,
OUT param7 bigint,
OUT param8 bigint,
OUT param9 bigint,
OUT param10 bigint,
OUT param11 bigint,
OUT param12 bigint)
RETURNS SETOF record AS
$BODY$
DECLARE
rec RECORD;
BEGIN
DROP TABLE IF EXISTS msg_stats;
CREATE TEMP TABLE msg_stats (param1 varchar(100),
param2 varchar(20),
param3 bigint,
param4 bigint,
param5 bigint,
param6 bigint,
param7 bigint,
param8 bigint,
param9 bigint,
param10 bigint,
param11 bigint,
param12 bigint);
insert into msg_stats(param1,param2, param3,
param4 ,
param5 ,
param6 ,
param7 ,
param8 ,
param9 ,
param10,
param11,
param12) (select c.param1 param1, c.param2 param2,
sum(case when a.messagetype='D' then 1 else 0 end) param3,
sum(case when a.messagetype='MMixed' then 1 else 0 end) param4,
sum(case when a.messagetype='Merge' then 1 else 0 end) param5,
sum(case when a.messagetype='MMessage' then 1 else 0 end) param6,
sum(case when a.messagetype='MApp' then 1 else 0 end) param7,
sum(case when a.messagetype='NRepeat' then 1 else 0 end) param8,
sum(case when a.messagetype='LRepeat' then 1 else 0 end) param9,
sum(case when a.messagetype='Expiring' then 1 else 0 end) param10,
from table2 c
inner join table4 b on c.username=b.pid
inner join table5 a on a.pid=b.pid and a.datereceived >= $1 and a.datereceived <= $2
group by c.username);
for rec in select a.pid p1, p.pname p2, count(b.activated) activations from
pharmacies p
inner join table4 a on p.username=a.pid
inner join table5 b on a.pid=b.id
and b.activated=True and b.datecreated >= $1 and b.datecreated <= $2
group by a.pid, p.pname
LOOP
IF EXISTS (select * from msg_stats where msg_stats.pid=rec.pid) THEN
update msg_stats set param11=rec.activations where msg_stats.pid=rec.pid;
ELSE
insert into msg_stats(pid, pname, param11) values (rec.pid, rec.pname, rec.activations);
END IF;
END LOOP;
for rec in select b.pid pid, p.pname, count(1) totalactivations from pharmacies p
inner join table4 b on p.uname=b.pid
inner join table5 c on b.id=c.pid
and c.activated=True group by b.pid, p.pname
LOOP
IF EXISTS (select * from msg_stats where msg_stats.pid=rec.pid) THEN
update msg_stats set param12=rec.totalactivations where msg_stats.pid=rec.pid;
ELSE
insert into msg_stats(pid, pharmacyname, param12) values (rec.pid, rec.pname, rec.totalactivations);
END IF;
END LOOP;
RETURN QUERY SELECT * FROM msg_stats;
END
$BODY$
LANGUAGE plpgsql;