diff --git a/account/Makefile b/account/Makefile index d8da6e1..c0ff25b 100644 --- a/account/Makefile +++ b/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 diff --git a/account/sql/readme.md b/account/sql/readme.md index b68d5f4..37f2764 100644 --- a/account/sql/readme.md +++ b/account/sql/readme.md @@ -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` 扩展并授予业务用户访问权限。 diff --git a/account/sql/schema_pglogical_region_cn.sql b/account/sql/schema_pglogical_region.sql similarity index 59% rename from account/sql/schema_pglogical_region_cn.sql rename to account/sql/schema_pglogical_region.sql index 94ac4d6..bfeeb50 100644 --- a/account/sql/schema_pglogical_region_cn.sql +++ b/account/sql/schema_pglogical_region.sql @@ -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, diff --git a/account/sql/schema_pglogical_region_global.sql b/account/sql/schema_pglogical_region_global.sql deleted file mode 100644 index 6606f44..0000000 --- a/account/sql/schema_pglogical_region_global.sql +++ /dev/null @@ -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' 表示复制成功。 --- ========================================= diff --git a/scripts/clean_git_history.sh b/scripts/clean_git_history.sh index 737ba69..0df8b6d 100644 --- a/scripts/clean_git_history.sh +++ b/scripts/clean_git_history.sh @@ -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