Anonymous
PgCat иногда направляет запросы на запись в реплики.
Сообщение
Anonymous » 29 окт 2025, 20:44
Я тестирую
кластер TimescaleDB (1 основная + 2 реплики) на базе
PgCat , работающий в Docker Swarm.
Иногда пишу запросы (
, INSERT) завершается ошибкой:
Код: Выделить всё
psycopg.errors.ReadOnlySqlTransaction: cannot execute INSERT in a read-only transaction
В других случаях один и тот же сценарий запускается правильно и записывает данные в основной.
Это происходит по-разному при каждом запуске.
Примеры журналов
Код: Выделить всё
# python
p.py Traceback (most recent call last):
File "//p.py", line 27, in
cur.execute("""
psycopg.errors.ReadOnlySqlTransaction: cannot execute INSERT in a read-only transaction
# python p.py
Inserted rows:
(1, 'alpha', 10)
(2, 'beta', 20)
(3, 'gamma', 30)
Queried rows:
id=1, name=alpha, value=10
id=2, name=beta, value=20
id=3, name=gamma, value=30
# python p.py
Traceback (most recent call last):
File "//p.py", line 27, in
cur.execute("""
psycopg.errors.ReadOnlySqlTransaction: cannot execute INSERT in a read-only transaction
# python p.py
Inserted rows:
(4, 'alpha', 10)
(5, 'beta', 20)
(6, 'gamma', 30)
Queried rows:
id=1, name=alpha, value=10
id=2, name=beta, value=20
id=3, name=gamma, value=30
id=4, name=alpha, value=10
id=5, name=beta, value=20
id=6, name=gamma, value=30
# python p.py
psycopg.errors.ReadOnlySqlTransaction: cannot execute CREATE TABLE in a read-only transaction
Таким образом, PgCat иногда направляет запросы на запись в реплики, хотя конфигурация должна это предотвращать.
Конфигурация PgCat (
)
Код: Выделить всё
[general]
host = "0.0.0.0"
port = 6432
admin_username = "---TIMESCALEDB_PGCAT_USER---"
admin_password = "---TIMESCALEDB_PGCAT_PASSWORD---"
pool_mode = "transaction"
server_lifetime = 3600
idle_timeout = 30000
connect_timeout = 5000
healthcheck_timeout = 1000
healthcheck_delay = 30000
log_level = "info"
log_client_connections = true
log_client_disconnections = true
[pools.dev]
pool_size = 15
database = "---TIMESCALEDB_DB_NAME---"
default_role = "primary"
query_parser_enabled = true
primary_reads_enabled = true
[pools.dev.users.0]
username = "---TIMESCALEDB_USER---"
password = "---TIMESCALEDB_PASSWORD---"
pool_size = 15
[pools.dev.shards.0]
database = "---TIMESCALEDB_DB_NAME---"
servers = [
[ "timescaledb-master", 5432, "primary" ],
[ "timescaledb-replica1", 5432, "replica" ],
[ "timescaledb-replica2", 5432, "replica" ]
]
Тестовый скрипт (
)
Код: Выделить всё
import psycopg
conn = psycopg.connect(
dbname="dev",
user="wizabot",
password="crptx123",
host="timescaledb_proxy",
port=6432,
)
cur = conn.cursor()
cur.execute("""
CREATE TABLE IF NOT EXISTS test_table (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
value INTEGER NOT NULL
);
""")
conn.commit()
cur.execute("""
INSERT INTO test_table (name, value)
VALUES ('alpha', 10), ('beta', 20), ('gamma', 30)
RETURNING id, name, value;
""")
print("Inserted rows:")
for row in cur.fetchall():
print(row)
conn.commit()
cur.execute("SELECT id, name, value FROM test_table ORDER BY id;")
for row in cur.fetchall():
print(f"id={row[0]}, name={row[1]}, value={row[2]}")
cur.close()
conn.close()
Конфигурация docker Compose (режим Swarm):
Код: Выделить всё
...
timescaledb-master:
image: timescaledb:latest
networks:
- wizabot_internal_net
environment:
POSTGRES_USER: ${TIMESCALEDB_USER?}
POSTGRES_PASSWORD: ${TIMESCALEDB_PASSWORD?}
POSTGRES_DB: ${TIMESCALEDB_DB_NAME?}
POSTGRES_HOST_AUTH_METHOD: md5
POSTGRES_INITDB_ARGS: "--auth-host=md5"
command: >
postgres
-c wal_level=replica
-c max_wal_senders=10
-c max_replication_slots=10
-c hot_standby=on
-c shared_preload_libraries=timescaledb
volumes:
- wizabot_timescaledb_master:/var/lib/postgresql/data
deploy:
replicas: 1
placement:
constraints:
- node.labels.db_vm == true
- node.hostname == wiza-swarm-manager
timescaledb-replica1:
image: timescaledb:latest
networks:
- wizabot_internal_net
environment:
POSTGRES_USER: ${TIMESCALEDB_USER?}
POSTGRES_PASSWORD: ${TIMESCALEDB_PASSWORD?}
POSTGRES_DB: ${TIMESCALEDB_DB_NAME?}
PGUSER: ${TIMESCALEDB_USER?}
PGPASSWORD: ${TIMESCALEDB_PASSWORD?}
POSTGRES_MASTER_HOST: timescaledb-master
POSTGRES_MASTER_PORT: 5432
volumes:
- wizabot_timescaledb_replica1:/var/lib/postgresql/data
depends_on:
- timescaledb-master
timescaledb-replica2:
image: timescaledb:latest
networks:
- wizabot_internal_net
environment:
POSTGRES_USER: ${TIMESCALEDB_USER?}
POSTGRES_PASSWORD: ${TIMESCALEDB_PASSWORD?}
POSTGRES_DB: ${TIMESCALEDB_DB_NAME?}
PGUSER: ${TIMESCALEDB_USER?}
PGPASSWORD: ${TIMESCALEDB_PASSWORD?}
POSTGRES_MASTER_HOST: timescaledb-master
POSTGRES_MASTER_PORT: 5432
volumes:
- wizabot_timescaledb_replica2:/var/lib/postgresql/data
depends_on:
- timescaledb-master
timescaledb_proxy:
image: pgcat:latest
networks:
- wizabot_internal_net
depends_on:
- timescaledb-master
- timescaledb-replica1
- timescaledb-replica2
environment:
TIMESCALEDB_USER: ${TIMESCALEDB_USER?}
TIMESCALEDB_PASSWORD: ${TIMESCALEDB_PASSWORD?}
TIMESCALEDB_DB_NAME: ${TIMESCALEDB_DB_NAME?}
deploy:
replicas: 2
placement:
preferences:
- spread: node.id
...
Среда
PgCat: последняя версия ()
TimescaleDB: 2.x (PostgreSQL 15)
Python: psycopg 3.x
Развертывание: Docker Swarm
Вопрос
Почему PgCat иногда направляет запросы на запись в реплики, даже если:
Известна ли проблема с маршрутизацией запросов или повторным использованием соединений между репликами и первичными узлами?
Подробнее здесь:
https://stackoverflow.com/questions/798 ... o-replicas
1761759865
Anonymous
Я тестирую [b]кластер TimescaleDB[/b] (1 основная + 2 реплики) на базе [b]PgCat[/b], работающий в Docker Swarm. Иногда пишу запросы ([code]CREATE TABLE[/code], INSERT) завершается ошибкой: [code]psycopg.errors.ReadOnlySqlTransaction: cannot execute INSERT in a read-only transaction [/code] В других случаях один и тот же сценарий запускается правильно и записывает данные в основной. Это происходит по-разному при каждом запуске. Примеры журналов [code]# python p.py Traceback (most recent call last): File "//p.py", line 27, in cur.execute(""" psycopg.errors.ReadOnlySqlTransaction: cannot execute INSERT in a read-only transaction # python p.py Inserted rows: (1, 'alpha', 10) (2, 'beta', 20) (3, 'gamma', 30) Queried rows: id=1, name=alpha, value=10 id=2, name=beta, value=20 id=3, name=gamma, value=30 # python p.py Traceback (most recent call last): File "//p.py", line 27, in cur.execute(""" psycopg.errors.ReadOnlySqlTransaction: cannot execute INSERT in a read-only transaction # python p.py Inserted rows: (4, 'alpha', 10) (5, 'beta', 20) (6, 'gamma', 30) Queried rows: id=1, name=alpha, value=10 id=2, name=beta, value=20 id=3, name=gamma, value=30 id=4, name=alpha, value=10 id=5, name=beta, value=20 id=6, name=gamma, value=30 # python p.py psycopg.errors.ReadOnlySqlTransaction: cannot execute CREATE TABLE in a read-only transaction [/code] Таким образом, PgCat иногда направляет запросы на запись в реплики, хотя конфигурация должна это предотвращать. Конфигурация PgCat ([code]pgcat.toml[/code]) [code][general] host = "0.0.0.0" port = 6432 admin_username = "---TIMESCALEDB_PGCAT_USER---" admin_password = "---TIMESCALEDB_PGCAT_PASSWORD---" pool_mode = "transaction" server_lifetime = 3600 idle_timeout = 30000 connect_timeout = 5000 healthcheck_timeout = 1000 healthcheck_delay = 30000 log_level = "info" log_client_connections = true log_client_disconnections = true [pools.dev] pool_size = 15 database = "---TIMESCALEDB_DB_NAME---" default_role = "primary" query_parser_enabled = true primary_reads_enabled = true [pools.dev.users.0] username = "---TIMESCALEDB_USER---" password = "---TIMESCALEDB_PASSWORD---" pool_size = 15 [pools.dev.shards.0] database = "---TIMESCALEDB_DB_NAME---" servers = [ [ "timescaledb-master", 5432, "primary" ], [ "timescaledb-replica1", 5432, "replica" ], [ "timescaledb-replica2", 5432, "replica" ] ] [/code] Тестовый скрипт ([code]psycopg[/code]) [code]import psycopg conn = psycopg.connect( dbname="dev", user="wizabot", password="crptx123", host="timescaledb_proxy", port=6432, ) cur = conn.cursor() cur.execute(""" CREATE TABLE IF NOT EXISTS test_table ( id SERIAL PRIMARY KEY, name TEXT NOT NULL, value INTEGER NOT NULL ); """) conn.commit() cur.execute(""" INSERT INTO test_table (name, value) VALUES ('alpha', 10), ('beta', 20), ('gamma', 30) RETURNING id, name, value; """) print("Inserted rows:") for row in cur.fetchall(): print(row) conn.commit() cur.execute("SELECT id, name, value FROM test_table ORDER BY id;") for row in cur.fetchall(): print(f"id={row[0]}, name={row[1]}, value={row[2]}") cur.close() conn.close() [/code] Конфигурация docker Compose (режим Swarm): [code]... timescaledb-master: image: timescaledb:latest networks: - wizabot_internal_net environment: POSTGRES_USER: ${TIMESCALEDB_USER?} POSTGRES_PASSWORD: ${TIMESCALEDB_PASSWORD?} POSTGRES_DB: ${TIMESCALEDB_DB_NAME?} POSTGRES_HOST_AUTH_METHOD: md5 POSTGRES_INITDB_ARGS: "--auth-host=md5" command: > postgres -c wal_level=replica -c max_wal_senders=10 -c max_replication_slots=10 -c hot_standby=on -c shared_preload_libraries=timescaledb volumes: - wizabot_timescaledb_master:/var/lib/postgresql/data deploy: replicas: 1 placement: constraints: - node.labels.db_vm == true - node.hostname == wiza-swarm-manager timescaledb-replica1: image: timescaledb:latest networks: - wizabot_internal_net environment: POSTGRES_USER: ${TIMESCALEDB_USER?} POSTGRES_PASSWORD: ${TIMESCALEDB_PASSWORD?} POSTGRES_DB: ${TIMESCALEDB_DB_NAME?} PGUSER: ${TIMESCALEDB_USER?} PGPASSWORD: ${TIMESCALEDB_PASSWORD?} POSTGRES_MASTER_HOST: timescaledb-master POSTGRES_MASTER_PORT: 5432 volumes: - wizabot_timescaledb_replica1:/var/lib/postgresql/data depends_on: - timescaledb-master timescaledb-replica2: image: timescaledb:latest networks: - wizabot_internal_net environment: POSTGRES_USER: ${TIMESCALEDB_USER?} POSTGRES_PASSWORD: ${TIMESCALEDB_PASSWORD?} POSTGRES_DB: ${TIMESCALEDB_DB_NAME?} PGUSER: ${TIMESCALEDB_USER?} PGPASSWORD: ${TIMESCALEDB_PASSWORD?} POSTGRES_MASTER_HOST: timescaledb-master POSTGRES_MASTER_PORT: 5432 volumes: - wizabot_timescaledb_replica2:/var/lib/postgresql/data depends_on: - timescaledb-master timescaledb_proxy: image: pgcat:latest networks: - wizabot_internal_net depends_on: - timescaledb-master - timescaledb-replica1 - timescaledb-replica2 environment: TIMESCALEDB_USER: ${TIMESCALEDB_USER?} TIMESCALEDB_PASSWORD: ${TIMESCALEDB_PASSWORD?} TIMESCALEDB_DB_NAME: ${TIMESCALEDB_DB_NAME?} deploy: replicas: 2 placement: preferences: - spread: node.id ... [/code] Среда [list] [*][b]PgCat:[/b] последняя версия ([code]postgresml/pgcat[/code]) [*][b]TimescaleDB:[/b] 2.x (PostgreSQL 15) [*][b]Python:[/b] psycopg 3.x [*][b]Развертывание:[/b] Docker Swarm [/list] Вопрос Почему PgCat иногда направляет запросы на запись в реплики, даже если: [list] [*][code]pool_mode = "transaction"[/code] [*][code]query_parser_enabled = true[/code] [*][code]default_role = "primary"[/code] [*][code]primary_reads_enabled = true[/code] [/list] Известна ли проблема с маршрутизацией запросов или повторным использованием соединений между репликами и первичными узлами? Подробнее здесь: [url]https://stackoverflow.com/questions/79804089/pgcat-sometimes-routes-write-queries-to-replicas[/url]