refactor: unify pglogical region schema (#498)
This commit is contained in:
parent
5de1e32dd4
commit
5678975cb8
101
account/Makefile
101
account/Makefile
@ -4,8 +4,7 @@ PORT ?= 8080
|
||||
OS := $(shell uname -s)
|
||||
SCHEMA_FILE := ./sql/schema.sql
|
||||
PGLOGICAL_SCHEMA_FILE := ./sql/schema_pglogical_init.sql
|
||||
PGLOGICAL_REGION_CN_SCHEMA_FILE := ./sql/schema_pglogical_region_cn.sql
|
||||
PGLOGICAL_REGION_GLOBAL_SCHEMA_FILE := ./sql/schema_pglogical_region_global.sql
|
||||
PGLOGICAL_REGION_SCHEMA_FILE := ./sql/schema_pglogical_region.sql
|
||||
MIGRATION_FILES := $(shell ls -1 sql/migrations/*.up.sql 2>/dev/null | sort)
|
||||
|
||||
|
||||
@ -28,7 +27,7 @@ SUPERADMIN_EMAIL ?= admin@svc.plus
|
||||
|
||||
export PATH := /usr/local/go/bin:$(PATH)
|
||||
|
||||
.PHONY: all init init-go init-db init-pglogical-region-cn init-pglogical-region-global migrate-db dump-schema build start stop restart clean help dev test create-super-admin account-export account-import
|
||||
.PHONY: all init init-go init-db init-pglogical-region init-pglogical-region-cn init-pglogical-region-global migrate-db dump-schema build start stop restart clean help dev test create-super-admin account-export account-import
|
||||
|
||||
all: build
|
||||
|
||||
@ -77,49 +76,65 @@ init-db:
|
||||
fi; \
|
||||
fi
|
||||
|
||||
init-pglogical-region:
|
||||
@if [ -z "$(strip $(REGION_DB_URL))" ]; then \
|
||||
echo "请通过 REGION_DB_URL 指定节点数据库连接字符串"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@if [ -z "$(strip $(NODE_NAME))" ]; then \
|
||||
echo "请通过 NODE_NAME 指定当前节点名称"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@if [ -z "$(strip $(NODE_DSN))" ]; then \
|
||||
echo "请通过 NODE_DSN 指定当前节点 DSN"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@if [ -z "$(strip $(SUBSCRIPTION_NAME))" ]; then \
|
||||
echo "请通过 SUBSCRIPTION_NAME 指定订阅名称"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@if [ -z "$(strip $(PROVIDER_DSN))" ]; then \
|
||||
echo "请通过 PROVIDER_DSN 指定 Provider DSN"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@if [ ! -f $(PGLOGICAL_REGION_SCHEMA_FILE) ]; then \
|
||||
echo "未找到 pglogical schema 文件: $(PGLOGICAL_REGION_SCHEMA_FILE)"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@if ! command -v psql >/dev/null 2>&1; then \
|
||||
echo "未检测到 psql,请先安装 PostgreSQL 客户端"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@echo "使用数据库连接: $(REGION_DB_URL)"
|
||||
@if psql "$(REGION_DB_URL)" -Atc "SELECT rolsuper FROM pg_roles WHERE rolname = current_user" | grep -qx 't'; then \
|
||||
psql "$(REGION_DB_URL)" -v ON_ERROR_STOP=1 \
|
||||
-v NODE_NAME="$(NODE_NAME)" \
|
||||
-v NODE_DSN="$(NODE_DSN)" \
|
||||
-v SUBSCRIPTION_NAME="$(SUBSCRIPTION_NAME)" \
|
||||
-v PROVIDER_DSN="$(PROVIDER_DSN)" \
|
||||
-f $(PGLOGICAL_REGION_SCHEMA_FILE); \
|
||||
else \
|
||||
echo "跳过 pglogical 初始化: 当前数据库用户缺少超级用户权限"; \
|
||||
echo "提示: 请使用具有超级用户权限的连接或由管理员预先创建 pglogical 扩展"; \
|
||||
fi
|
||||
|
||||
init-pglogical-region-cn:
|
||||
@echo ">>> 初始化 CN 节点 pglogical 配置"
|
||||
@if [ -z "$(strip $(REGION_CN_DB_URL))" ]; then \
|
||||
echo "请通过 REGION_CN_DB_URL 指定 CN 节点数据库连接字符串"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@if [ ! -f $(PGLOGICAL_REGION_CN_SCHEMA_FILE) ]; then \
|
||||
echo "未找到 pglogical schema 文件: $(PGLOGICAL_REGION_CN_SCHEMA_FILE)"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@if ! command -v psql >/dev/null 2>&1; then \
|
||||
echo "未检测到 psql,请先安装 PostgreSQL 客户端"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@echo "使用数据库连接: $(REGION_CN_DB_URL)"
|
||||
@if psql "$(REGION_CN_DB_URL)" -Atc "SELECT rolsuper FROM pg_roles WHERE rolname = current_user" | grep -qx 't'; then \
|
||||
psql "$(REGION_CN_DB_URL)" -v ON_ERROR_STOP=1 -f $(PGLOGICAL_REGION_CN_SCHEMA_FILE); \
|
||||
else \
|
||||
echo "跳过 pglogical 初始化: 当前数据库用户缺少超级用户权限"; \
|
||||
echo "提示: 请使用具有超级用户权限的连接或由管理员预先创建 pglogical 扩展"; \
|
||||
fi
|
||||
@echo ">>> 初始化 CN 节点 pglogical 配置"
|
||||
@$(MAKE) init-pglogical-region \
|
||||
REGION_DB_URL="$(REGION_CN_DB_URL)" \
|
||||
NODE_NAME="node_cn" \
|
||||
NODE_DSN="host=cn-homepage.svc.plus port=5432 dbname=account user=pglogical password=xxxx" \
|
||||
SUBSCRIPTION_NAME="sub_from_global" \
|
||||
PROVIDER_DSN="host=global-homepage.svc.plus port=5432 dbname=account user=pglogical password=xxx"
|
||||
|
||||
init-pglogical-region-global:
|
||||
@echo ">>> 初始化 Global 节点 pglogical 配置"
|
||||
@if [ -z "$(strip $(REGION_GLOBAL_DB_URL))" ]; then \
|
||||
echo "请通过 REGION_GLOBAL_DB_URL 指定 Global 节点数据库连接字符串"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@if [ ! -f $(PGLOGICAL_REGION_GLOBAL_SCHEMA_FILE) ]; then \
|
||||
echo "未找到 pglogical schema 文件: $(PGLOGICAL_REGION_GLOBAL_SCHEMA_FILE)"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@if ! command -v psql >/dev/null 2>&1; then \
|
||||
echo "未检测到 psql,请先安装 PostgreSQL 客户端"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@echo "使用数据库连接: $(REGION_GLOBAL_DB_URL)"
|
||||
@if psql "$(REGION_GLOBAL_DB_URL)" -Atc "SELECT rolsuper FROM pg_roles WHERE rolname = current_user" | grep -qx 't'; then \
|
||||
psql "$(REGION_GLOBAL_DB_URL)" -v ON_ERROR_STOP=1 -f $(PGLOGICAL_REGION_GLOBAL_SCHEMA_FILE); \
|
||||
else \
|
||||
echo "跳过 pglogical 初始化: 当前数据库用户缺少超级用户权限"; \
|
||||
echo "提示: 请使用具有超级用户权限的连接或由管理员预先创建 pglogical 扩展"; \
|
||||
fi
|
||||
@echo ">>> 初始化 Global 节点 pglogical 配置"
|
||||
@$(MAKE) init-pglogical-region \
|
||||
REGION_DB_URL="$(REGION_GLOBAL_DB_URL)" \
|
||||
NODE_NAME="node_global" \
|
||||
NODE_DSN="host=global-homepage.svc.plus port=5432 dbname=account user=pglogical password=xxxx" \
|
||||
SUBSCRIPTION_NAME="sub_from_cn" \
|
||||
PROVIDER_DSN="host=cn-homepage.svc.plus port=5432 dbname=account user=pglogical password=xxxx"
|
||||
|
||||
# =========================================
|
||||
# migrate-db target
|
||||
|
||||
@ -38,13 +38,48 @@ GRANT USAGE ON SCHEMA pglogical TO shenlan;
|
||||
|
||||
\q
|
||||
⚙️ 执行顺序建议
|
||||
步骤 节点 脚本 说明
|
||||
1️⃣ Global schema_base_bidirectional_enhanced.sql 创建业务结构(含 version/origin_node)
|
||||
2️⃣ CN schema_base_bidirectional_enhanced.sql 创建相同业务结构
|
||||
3️⃣ Global schema_pglogical_region_global.sql 定义 Global provider + 订阅 CN
|
||||
4️⃣ CN schema_pglogical_region_cn.sql 定义 CN provider + 订阅 Global
|
||||
|
||||
💡 CN 节点执行 `schema_pglogical_region_cn.sql` 或 `make init-pglogical-region-cn` 前,请确保连接用户拥有 PostgreSQL 超级用户权限。
|
||||
| 步骤 | 节点 | 脚本 / 命令 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| 1️⃣ | Global | schema_base_bidirectional_enhanced.sql | 创建业务结构(含 version/origin_node) |
|
||||
| 2️⃣ | CN | schema_base_bidirectional_enhanced.sql | 创建相同业务结构 |
|
||||
| 3️⃣ | Global | schema_pglogical_region.sql + 参数 | 定义 Global provider + 订阅 CN |
|
||||
| 4️⃣ | CN | schema_pglogical_region.sql + 参数 | 定义 CN provider + 订阅 Global |
|
||||
|
||||
💡 执行 `schema_pglogical_region.sql` 或对应的 `make init-pglogical-region-*` 目标前,请确保连接用户拥有 PostgreSQL 超级用户权限。
|
||||
|
||||
### 手动执行模版脚本
|
||||
|
||||
使用相同的 `schema_pglogical_region.sql` 模版即可初始化 Global 与 CN 两个节点,只需传入不同的变量:
|
||||
|
||||
```bash
|
||||
# Global 节点示例
|
||||
psql "$REGION_GLOBAL_DB_URL" -v ON_ERROR_STOP=1 \
|
||||
-v NODE_NAME=node_global \
|
||||
-v NODE_DSN='host=global-homepage.svc.plus port=5432 dbname=account user=pglogical password=xxxx' \
|
||||
-v SUBSCRIPTION_NAME=sub_from_cn \
|
||||
-v PROVIDER_DSN='host=cn-homepage.svc.plus port=5432 dbname=account user=pglogical password=xxxx' \
|
||||
-f account/sql/schema_pglogical_region.sql
|
||||
|
||||
# CN 节点示例
|
||||
psql "$REGION_CN_DB_URL" -v ON_ERROR_STOP=1 \
|
||||
-v NODE_NAME=node_cn \
|
||||
-v NODE_DSN='host=cn-homepage.svc.plus port=5432 dbname=account user=pglogical password=xxxx' \
|
||||
-v SUBSCRIPTION_NAME=sub_from_global \
|
||||
-v PROVIDER_DSN='host=global-homepage.svc.plus port=5432 dbname=account user=pglogical password=xxx' \
|
||||
-f account/sql/schema_pglogical_region.sql
|
||||
```
|
||||
|
||||
也可以通过新的 `make init-pglogical-region` 目标自定义变量,例如:
|
||||
|
||||
```bash
|
||||
make init-pglogical-region \
|
||||
REGION_DB_URL="$REGION_DB_URL" \
|
||||
NODE_NAME=node_example \
|
||||
NODE_DSN="host=example port=5432 dbname=account user=pglogical password=secret" \
|
||||
SUBSCRIPTION_NAME=sub_from_peer \
|
||||
PROVIDER_DSN="host=peer port=5432 dbname=account user=pglogical password=secret"
|
||||
```
|
||||
|
||||
- 若使用业务账号(如 `shenlan`)执行初始化,PostgreSQL 会提示缺少超级用户权限并跳过 `pglogical` 初始化。
|
||||
- 建议改用 `postgres` 等超级用户连接执行,或由管理员预先安装 `pglogical` 扩展并授予业务用户访问权限。
|
||||
|
||||
@ -1,9 +1,35 @@
|
||||
-- =========================================
|
||||
-- schema_pglogical_region_cn.sql
|
||||
-- pglogical configuration for CN node
|
||||
-- schema_pglogical_region.sql
|
||||
-- pglogical configuration template for regional nodes
|
||||
-- PostgreSQL 16+, 双向复制 (provider + subscriber)
|
||||
-- 使用前请通过 psql -v NODE_NAME=... -v NODE_DSN=... -v SUBSCRIPTION_NAME=... -v PROVIDER_DSN=...
|
||||
-- 提供所需参数。
|
||||
-- =========================================
|
||||
|
||||
\if :{?NODE_NAME}
|
||||
\else
|
||||
\echo 'ERROR: 未设置 NODE_NAME 变量。请通过 -v NODE_NAME=... 传入节点名称。'
|
||||
\quit 1
|
||||
\endif
|
||||
|
||||
\if :{?NODE_DSN}
|
||||
\else
|
||||
\echo 'ERROR: 未设置 NODE_DSN 变量。请通过 -v NODE_DSN=... 传入当前节点 DSN。'
|
||||
\quit 1
|
||||
\endif
|
||||
|
||||
\if :{?SUBSCRIPTION_NAME}
|
||||
\else
|
||||
\echo 'ERROR: 未设置 SUBSCRIPTION_NAME 变量。请通过 -v SUBSCRIPTION_NAME=... 传入订阅名称。'
|
||||
\quit 1
|
||||
\endif
|
||||
|
||||
\if :{?PROVIDER_DSN}
|
||||
\else
|
||||
\echo 'ERROR: 未设置 PROVIDER_DSN 变量。请通过 -v PROVIDER_DSN=... 传入 Provider DSN。'
|
||||
\quit 1
|
||||
\endif
|
||||
|
||||
-- 🏗️ 确保 pglogical schema 及扩展存在
|
||||
DO $$
|
||||
BEGIN
|
||||
@ -20,13 +46,13 @@ CREATE EXTENSION IF NOT EXISTS pglogical WITH SCHEMA pglogical;
|
||||
-- 🧭 清理旧节点(可安全重入)
|
||||
DO $$
|
||||
BEGIN
|
||||
PERFORM pglogical.drop_subscription('sub_from_global', true);
|
||||
PERFORM pglogical.drop_subscription(:'SUBSCRIPTION_NAME', true);
|
||||
EXCEPTION WHEN others THEN NULL;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
PERFORM pglogical.drop_node('node_cn');
|
||||
PERFORM pglogical.drop_node(:'NODE_NAME');
|
||||
EXCEPTION WHEN others THEN NULL;
|
||||
END $$;
|
||||
|
||||
@ -34,8 +60,8 @@ END $$;
|
||||
-- 创建本节点 (Provider)
|
||||
-- =========================================
|
||||
SELECT pglogical.create_node(
|
||||
node_name := 'node_cn',
|
||||
dsn := 'host=cn-homepage.svc.plus port=5432 dbname=account user=pglogical password=xxxx'
|
||||
node_name := :'NODE_NAME',
|
||||
dsn := :'NODE_DSN'
|
||||
);
|
||||
|
||||
-- =========================================
|
||||
@ -45,11 +71,11 @@ SELECT pglogical.create_replication_set('rep_all');
|
||||
SELECT pglogical.replication_set_add_all_tables('rep_all', ARRAY['public']);
|
||||
|
||||
-- =========================================
|
||||
-- 创建订阅 (订阅 Global 节点)
|
||||
-- 创建订阅 (订阅远端节点)
|
||||
-- =========================================
|
||||
SELECT pglogical.create_subscription(
|
||||
subscription_name := 'sub_from_global',
|
||||
provider_dsn := 'host=global-homepage.svc.plus port=5432 dbname=account user=pglogical password=xxx',
|
||||
subscription_name := :'SUBSCRIPTION_NAME',
|
||||
provider_dsn := :'PROVIDER_DSN',
|
||||
replication_sets := ARRAY['rep_all'],
|
||||
synchronize_structure := false,
|
||||
synchronize_data := true,
|
||||
@ -1,65 +0,0 @@
|
||||
-- =========================================
|
||||
-- schema_pglogical_region_global.sql
|
||||
-- pglogical configuration for GLOBAL node
|
||||
-- PostgreSQL 16+, 双向复制 (provider + subscriber)
|
||||
-- =========================================
|
||||
|
||||
-- 🏗️ 确保 pglogical schema 及扩展存在
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_namespace WHERE nspname = 'pglogical'
|
||||
) THEN
|
||||
EXECUTE format('CREATE SCHEMA pglogical AUTHORIZATION %I', current_user);
|
||||
END IF;
|
||||
END;
|
||||
$$;
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS pglogical WITH SCHEMA pglogical;
|
||||
|
||||
-- 🧭 清理旧节点(可安全重入)
|
||||
DO $$
|
||||
BEGIN
|
||||
PERFORM pglogical.drop_subscription('sub_from_cn', true);
|
||||
EXCEPTION WHEN others THEN NULL;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
PERFORM pglogical.drop_node('node_global');
|
||||
EXCEPTION WHEN others THEN NULL;
|
||||
END $$;
|
||||
|
||||
-- =========================================
|
||||
-- 创建本节点 (Provider)
|
||||
-- =========================================
|
||||
SELECT pglogical.create_node(
|
||||
node_name := 'node_global',
|
||||
dsn := 'host=global-homepage.svc.plus port=5432 dbname=account user=pglogical password=xxxx'
|
||||
);
|
||||
|
||||
-- =========================================
|
||||
-- 定义复制集
|
||||
-- =========================================
|
||||
SELECT pglogical.create_replication_set('rep_all');
|
||||
SELECT pglogical.replication_set_add_all_tables('rep_all', ARRAY['public']);
|
||||
|
||||
-- =========================================
|
||||
-- 创建订阅 (订阅 CN 节点)
|
||||
-- =========================================
|
||||
SELECT pglogical.create_subscription(
|
||||
subscription_name := 'sub_from_cn',
|
||||
provider_dsn := 'host=cn-homepage.svc.plus port=5432 dbname=account user=pglogical password=xxxx',
|
||||
replication_sets := ARRAY['rep_all'],
|
||||
synchronize_structure := false,
|
||||
synchronize_data := true,
|
||||
forward_origins := '{}'
|
||||
);
|
||||
|
||||
-- =========================================
|
||||
-- 验证状态
|
||||
-- =========================================
|
||||
-- 运行以下命令检查同步是否正常:
|
||||
-- SELECT * FROM pglogical.show_subscription_status();
|
||||
-- 若 status = 'replicating' 表示复制成功。
|
||||
-- =========================================
|
||||
@ -4,7 +4,7 @@
|
||||
# 用于清理指定文件的历史提交记录,但保留当前版本
|
||||
#
|
||||
# 使用示例:
|
||||
# ./clean_git_history.sh account/sql/schema_pglogical_region_cn.sql account/sql/schema_pglogical_region_global.sql
|
||||
# ./clean_git_history.sh account/sql/schema_pglogical_region.sql
|
||||
#
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
Loading…
Reference in New Issue
Block a user