Configure read/write splitting with Pgpool

Updated at:

This article explains how to use pgpool on an ECS instance to configure read-write splitting for PostgreSQL. For a simpler managed solution, you can use ApsaraDB RDS for PostgreSQL with its read-only instances.

Background

When not used for database high availability, pgpool functions as a stateless, horizontally scalable middleware with minimal performance overhead. This setup lets you easily implement read/write splitting by pairing pgpool with an ApsaraDB RDS for PostgreSQL instance, which provides its own high-availability architecture.

Deployment environment

If you have already purchased a PostgreSQL 12 instance with high-performance local disks and a read-only instance (for details, see Quickly create an ApsaraDB RDS for PostgreSQL instance and Create a PostgreSQL read-only instance), you only need to install pgpool. Then, skip to Configure pgpool.

Note

Read-only instances for PostgreSQL on cloud disks will be available in a future release.

Test environment:

  • ECS instance: 16-core CPU, 64 GB memory, and a 1.8 TB SSD cloud disk.

  • ECS instance OS: CentOS 7.7 x64.

Follow these steps:

  1. Modify the sysctl.conf file by running the following command:

    sudo vi /etc/sysctl.conf                
    fs.aio-max-nr = 1048576                
    fs.file-max = 76724600                
    # Optional: kernel.core_pattern = /data01/corefiles/core_%e_%u_%t_%s.%p                         
    # Create the /data01/corefiles directory with 777 permissions in advance. If it is a symbolic link, change the permissions of the target directory to 777.       
    kernel.sem = 4096 2147483647 2147483646 512000                    
    # Semaphores. Check with `ipcs -l` or `ipcs -u`. Each group of 16 processes requires 17 semaphores.                
    kernel.shmall = 107374182                      
    # The limit on the total size of all shared memory segments (recommended: 80% of memory). Unit: pages.                
    kernel.shmmax = 274877906944                   
    # The maximum size of a single shared memory segment (recommended: 50% of memory). In PostgreSQL versions after 9.2, shared memory usage is significantly reduced. Unit: bytes.                
    kernel.shmmni = 819200                         
    # The total number of shared memory segments that can be created. Each PostgreSQL cluster requires at least two shared memory segments.
    net.core.netdev_max_backlog = 10000                
    net.core.rmem_default = 262144                       
    # Default socket receive buffer size in bytes.                
    net.core.rmem_max = 4194304                          
    # The maximum receive socket buffer size in bytes.                
    net.core.wmem_default = 262144                       
    # Default socket send buffer size in bytes.                
    net.core.wmem_max = 4194304                          
    # The maximum send socket buffer size in bytes.                
    net.core.somaxconn = 4096                
    net.ipv4.tcp_max_syn_backlog = 4096                
    net.ipv4.tcp_keepalive_intvl = 20                
    net.ipv4.tcp_keepalive_probes = 3                
    net.ipv4.tcp_keepalive_time = 60                
    net.ipv4.tcp_mem = 8388608 12582912 16777216                
    net.ipv4.tcp_fin_timeout = 5                
    net.ipv4.tcp_synack_retries = 2                
    net.ipv4.tcp_syncookies = 1                    
    # Enable SYN cookies. When a SYN queue overflow occurs, cookies are used to handle requests, which can mitigate minor SYN flood attacks.                
    net.ipv4.tcp_timestamps = 1                    
    # Reduce TIME_WAIT sockets.
    net.ipv4.tcp_tw_recycle = 0                    
    # If set to 1, this enables fast recycling of TIME_WAIT sockets, but it may cause connection failures in NAT environments. Disabling this on the server is recommended.
    net.ipv4.tcp_tw_reuse = 1                      
    # Enable reuse. This allows TIME_WAIT sockets to be reused for new TCP connections.
    net.ipv4.tcp_max_tw_buckets = 262144                
    net.ipv4.tcp_rmem = 8192 87380 16777216                
    net.ipv4.tcp_wmem = 8192 65536 16777216                
    net.nf_conntrack_max = 1200000                
    net.netfilter.nf_conntrack_max = 1200000                
    vm.dirty_background_bytes = 409600000                       
    # When the amount of dirty data in the system reaches this value, a background process like pdflush writes dirty pages older than (dirty_expire_centisecs/100) seconds to the disk.                
    # The default is 10%. For machines with large memory, we recommend setting a specific byte value.                
    vm.dirty_expire_centisecs = 3000                             
    # Dirty pages older than this value are flushed to the disk. A value of 3000 means 30 seconds.                
    vm.dirty_ratio = 95                                          
    # If the system's background flushing is too slow and dirty pages exceed 95% of memory, user processes that perform disk writes (such as fsync or fdatasync calls) must proactively flush dirty pages.                
    # This is highly effective in preventing user processes from flushing dirty data, especially in multi-instance environments where IOPS are limited by cgroups.                  
    vm.dirty_writeback_centisecs = 100                            
    # The wake-up interval for the background flushing process (such as pdflush). A value of 100 means 1 second.                
    vm.swappiness = 0                
    # Do not use the swap partition.                
    vm.mmap_min_addr = 65536                
    vm.overcommit_memory = 0                     
    # Allows a small amount of memory over-allocation. If set to 1, the system assumes there is always enough memory. A value of 1 can be used in test environments with limited memory.
    vm.overcommit_ratio = 90                     
    # Used to calculate the allowable memory size when overcommit_memory is set to 2.                
    vm.zone_reclaim_mode = 0                     
    # Disable NUMA, or disable it in vmlinux.            
    net.ipv4.ip_local_port_range = 40000 65535                    
    # The range of TCP and UDP port numbers that are automatically allocated locally.                
    fs.nr_open=20480000                
    # The maximum number of file handles a single process can open.                
    #vm.extra_free_kbytes = 4096000   # Do not set this to a large value on a machine with limited memory, as it may fail to boot.
    #vm.min_free_kbytes = 6291456    # For vm.min_free_kbytes, we recommend allocating 1 GB for every 32 GB of memory.     
    # We do not recommend setting the preceding two values on machines with limited memory.                
    # vm.nr_hugepages = 66536                    
    # We recommend using huge pages when shared_buffers is larger than 64 GB. To check page size, run `cat /proc/meminfo | grep Hugepagesize`.                
    #vm.lowmem_reserve_ratio = 1 1 1                
    # Recommended for systems with more than 64 GB of memory. Otherwise, use the default values of 256 256 32.    
  2. Modify the limits.conf configuration file by running the following command:

    sudo vi /etc/security/limits.conf  
    * soft    nofile  1024000                
    * hard    nofile  1024000                
    * soft    nproc   unlimited                
    * hard    nproc   unlimited                
    * soft    core    unlimited                
    * hard    core    unlimited                
    * soft    memlock unlimited                
    * hard    memlock unlimited    
    # Comment out other settings.  
    # Also, comment out the contents of /etc/security/limits.d/20-nproc.conf.  
  3. Disable transparent huge pages, configure huge pages, and enable auto-start for PostgreSQL by running the following commands:

    sudo chmod +x /etc/rc.d/rc.local  
    sudo vi /etc/rc.local  
    # Disable transparent huge pages.  
    if test -f /sys/kernel/mm/transparent_hugepage/enabled; then                
       echo never > /sys/kernel/mm/transparent_hugepage/enabled                
    fi    
    # Two instances, each with a 16 GB shared buffer.  
    #sysctl -w vm.nr_hugepages=17000  
    # Auto-start the two instances.  
    su - postgres -c "pg_ctl start -D /data01/pg12_3389/pg_data"  
    su - postgres -c "pg_ctl start -D /data01/pg12_8002/pg_data"  
  4. Create a file system by running the following commands:

    Warning

    This step is for new disks only. Before you proceed, confirm that the new disk is mounted (for example, as /dev/vdb and not /dev/vda). Otherwise, you might format the wrong disk and lose all data.

    parted -a optimal -s /dev/vdb mklabel gpt mkpart primary 1MiB 100%FREE     
    mkfs.ext4 /dev/vdb1 -m 0 -O extent,uninit_bg -E lazy_itable_init=1 -b 4096 -T largefile -L vdb1  
    vi /etc/fstab   
    LABEL=vdb1 /data01 ext4 defaults,noatime,nodiratime,nodelalloc,barrier=0,data=writeback 0 0  
    mkdir /data01  
    mount -a  
  5. Start the irqbalance service by running the following commands:

    sudo systemctl status irqbalance     
    sudo systemctl enable irqbalance        
    sudo systemctl start irqbalance       
    sudo systemctl status irqbalance    
  6. Install PostgreSQL 12 and the pgpool tools by running the following commands:

    sudo yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm        
    sudo yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm       
    sudo yum search all postgresql  
    sudo yum search all pgpool  
    sudo yum install -y postgresql12*    
    sudo yum install -y pgpool-II-12-extensions      
  7. Initialize the database data directory by running the following commands:

    mkdir /data01/pg12_3389  
    sudo chown postgres:postgres /data01/pg12_3389  
  8. Configure environment variables for the postgres user by running the following commands:

    su - postgres  
    vi .bash_profile  
    # Append the following content:    
    export PS1="$USER@`/bin/hostname -s`-> "      
    export PGPORT=3389  
    export PGDATA=/data01/pg12_$PGPORT/pg_data     
    export LANG=en_US.utf8      
    export PGHOME=/usr/pgsql-12      
    export LD_LIBRARY_PATH=$PGHOME/lib:/lib64:/usr/lib64:/usr/local/lib64:/lib:/usr/lib:/usr/local/lib:$LD_LIBRARY_PATH      
    export DATE=`date +"%Y%m%d%H%M"`      
    export PATH=$PGHOME/bin:$PATH:.      
    export MANPATH=$PGHOME/share/man:$MANPATH      
    export PGHOST=$PGDATA      
    export PGUSER=postgres      
    export PGDATABASE=db1  
    alias rm='rm -i'      
    alias ll='ls -lh'      
    unalias vi      
  9. Initialize the primary instance by running the following command:

    initdb -D $PGDATA -U postgres -E UTF8 --lc-collate=C --lc-ctype=en_US.utf8  
  10. Modify the postgresql.conf file with the following example settings:

    listen_addresses = '0.0.0.0'  
    port = 3389  
    max_connections = 1500  
    superuser_reserved_connections = 13  
    unix_socket_directories = '., /var/run/postgresql, /tmp'  
    tcp_keepalives_idle = 60  
    tcp_keepalives_interval = 10  
    tcp_keepalives_count = 10  
    shared_buffers = 16GB  
    huge_pages = on  
    work_mem = 8MB  
    maintenance_work_mem = 1GB  
    dynamic_shared_memory_type = posix  
    vacuum_cost_delay = 0  
    bgwriter_delay = 10ms  
    bgwriter_lru_maxpages = 1000  
    bgwriter_lru_multiplier = 10.0  
    bgwriter_flush_after = 512kB  
    effective_io_concurrency = 0  
    max_worker_processes = 128  
    max_parallel_maintenance_workers = 3  
    max_parallel_workers_per_gather = 4  
    parallel_leader_participation = off  
    max_parallel_workers = 8  
    backend_flush_after = 256  
    wal_level = replica  
    synchronous_commit = off  
    full_page_writes = on  
    wal_compression = on  
    wal_buffers = 16MB  
    wal_writer_delay = 10ms  
    wal_writer_flush_after = 1MB  
    checkpoint_timeout = 15min  
    max_wal_size = 64GB  
    min_wal_size = 8GB  
    checkpoint_completion_target = 0.2  
    checkpoint_flush_after = 256kB  
    random_page_cost = 1.1  
    effective_cache_size = 48GB  
    log_destination = 'csvlog'  
    logging_collector = on  
    log_directory = 'log'  
    log_filename = 'postgresql-%a.log'  
    log_truncate_on_rotation = on  
    log_rotation_age = 1d  
    log_rotation_size = 0  
    log_min_duration_statement = 1s  
    log_checkpoints = on  
    log_connections = on  
    log_disconnections = on  
    log_line_prefix = '%m [%p] '  
    log_statement = 'ddl'  
    log_timezone = 'Asia/Shanghai'  
    autovacuum = on  
    log_autovacuum_min_duration = 0  
    autovacuum_vacuum_scale_factor = 0.1  
    autovacuum_analyze_scale_factor = 0.05  
    autovacuum_freeze_max_age = 800000000  
    autovacuum_multixact_freeze_max_age = 900000000  
    autovacuum_vacuum_cost_delay = 0  
    vacuum_freeze_table_age = 750000000  
    vacuum_multixact_freeze_table_age = 750000000  
    datestyle = 'iso, mdy'  
    timezone = 'Asia/Shanghai'  
    lc_messages = 'en_US.utf8'  
    lc_monetary = 'en_US.utf8'  
    lc_numeric = 'en_US.utf8'  
    lc_time = 'en_US.utf8'  
    default_text_search_config = 'pg_catalog.english'  
  11. Modify the pg_hba.conf file with the following example settings:

    Note

    Because pgpool-II and the database server are on the same ECS instance, login requires a password when the host is set to 127.0.0.1.

    # "local" is for Unix domain socket connections only  
    local   all             all                                     trust  
    # IPv4 local connections:  
    host    all             all             127.0.0.1/32            md5  
    # IPv6 local connections:  
    host    all             all             ::1/128                 trust  
    # Allow replication connections from localhost, by a user with the  
    # replication privilege.  
    local   replication     all                                     trust  
    host    replication     all             127.0.0.1/32            trust  
    host    replication     all             ::1/128                 trust  
    host db123 digoal 0.0.0.0/0 md5  
  12. Create a streaming replication user. Example:

    create role rep123 login replication encrypted password 'xxxxxxx';  
    CREATE ROLE  
  13. Create an application user. Example:

    create role digoal login encrypted password 'xxxxxxx';  
    CREATE ROLE  
    create database db123 owner digoal;  
    CREATE DATABASE  
  14. Create a health check user for pgpool. This user, which requires only login permissions, is used with specific pgpool parameters to check the WAL replay lag on read-only nodes. Example:

    create role nobody login encrypted password 'xxxxxxx';  

Create a standby instance

To simplify testing, create the standby instance on the same ECS instance.

  1. Create the standby instance online using the pg_basebackup command.

    pg_basebackup -D /data01/pg12_8002/pg_data -F p --checkpoint=fast -P -h 127.0.0.1 -p 3389 -U rep123  
  2. Modify the standby instance's postgresql.conf file.

    cd /data01/pg12_8002/pg_data   
    vi postgresql.conf  
    # Compared to the primary's configuration, make the following changes:  
    port = 8002  
    primary_conninfo = 'hostaddr=127.0.0.1 port=3389 user=rep123' # A password is not required because the primary uses trust authentication.  
    hot_standby = on  
    wal_receiver_status_interval = 1s  
    wal_receiver_timeout = 10s  
    recovery_target_timeline = 'latest'  
  3. Create the standby.signal marker file.

    cd /data01/pg12_8002/pg_data   
    touch standby.signal  
  4. Check the replication status on the primary instance.

    db1=# select * from pg_stat_replication ;  
    -[ RECORD 1 ]----+------------------------------  
    pid              | 21065  
    usesysid         | 10  
    usename          | rep123  
    application_name | walreceiver  
    client_addr      | 127.0.0.1  
    client_hostname  |   
    client_port      | 47064  
    backend_start    | 2020-02-29 00:26:28.485427+08  
    backend_xmin     |   
    state            | streaming  
    sent_lsn         | 0/52000060  
    write_lsn        | 0/52000060  
    flush_lsn        | 0/52000060  
    replay_lsn       | 0/52000060  
    write_lag        |   
    flush_lag        |   
    replay_lag       |   
    sync_priority    | 0  
    sync_state       | async  
    reply_time       | 2020-02-29 01:32:40.635183+08  

Configure pgpool

  1. Run the following commands to find the pgpool installation files:

    rpm -qa|grep pgpool  
    pgpool-II-12-extensions-4.1.1-1.rhel7.x86_64  
    pgpool-II-12-4.1.1-1.rhel7.x86_64  
    rpm -ql pgpool-II-12-4.1.1  
  2. Modify the configuration file pgpool.conf:

    cd /etc/pgpool-II-12/  
    cp pgpool.conf.sample-stream pgpool.conf  
    vi pgpool.conf  
    # ----------------------------  
    # pgPool-II configuration file  
    # ----------------------------  
    #  
    # This file consists of lines of the form:  
    #  
    #   name = value  
    #  
    # Whitespace may be used. Comments are introduced with "#" anywhere on a line.  
    # The complete list of parameter names and allowed values can be found in the  
    # pgPool-II documentation.  
    #  
    # If you edit this file on a running system, SIGHUP the server or run "pgpool reload" for the changes to take effect.  
    # Some parameters, as marked below, require a server restart.  
    #  
    #------------------------------------------------------------------------------  
    # Connections  
    #------------------------------------------------------------------------------  
    # - Pgpool connection settings -  
    listen_addresses = '0.0.0.0'  
                                       # Hostname or IP address to listen on:  
                                       # '*' for all, '' for no TCP/IP connections  
                                       # (change requires restart)  
    port = 8001   
                                       # Port number  
                                       # (change requires restart)  
    socket_dir = '/tmp'  
                                       # Unix domain socket path  
                                       # The Debian package defaults to  
                                       # /var/run/postgresql  
                                       # (change requires restart)  
    reserved_connections = 0  
                                       # Number of reserved connections.  
                                       # Pgpool-II rejects connections if the number of clients exceeds `num_init_children` - `reserved_connections`.  
    # - Pgpool communication manager connection settings -  
    pcp_listen_addresses = ''  
                                       # Hostname or IP address for the PCP process to listen on:  
                                       # '*' for all, '' for no TCP/IP connections  
                                       # (change requires restart)  
    pcp_port = 9898  
                                       # Port number for PCP  
                                       # (change requires restart)  
    pcp_socket_dir = '/tmp'  
                                       # Unix domain socket path for PCP  
                                       # The Debian package defaults to  
                                       # /var/run/postgresql  
                                       # (change requires restart)  
    listen_backlog_multiplier = 2  
                                       # Sets the backlog parameter of listen(2) to  
                                       # num_init_children * listen_backlog_multiplier.  
                                       # (change requires restart)  
    serialize_accept = off  
                                       # Specifies whether to serialize `accept()` calls to prevent the thundering herd problem.  
                                       # (change requires restart)  
    # - Backend connection settings -  
    backend_hostname0 = '127.0.0.1'  
                                       # Hostname or IP address to connect to for backend 0  
    backend_port0 = 3389   
                                       # Port number for backend 0  
    backend_weight0 = 1  
                                       # Weight for backend 0 (only in load balancing mode)  
    backend_data_directory0 = '/data01/pg12_3389/pg_data'  
                                       # Data directory for backend 0  
    backend_flag0 = 'ALWAYS_MASTER'  
                                       # Controls various backend behavior  
                                       # ALLOW_TO_FAILOVER, DISALLOW_TO_FAILOVER  
                                       # or ALWAYS_MASTER  
    backend_application_name0 = 'server0'  
                                       # walsender's application_name, used for "show pool_nodes" command  
    backend_hostname1 = '127.0.0.1'  
    backend_port1 = 8002  
    backend_weight1 = 1  
    backend_data_directory1 = '/data01/pg12_8002/pg_data'  
    backend_flag1 = 'DISALLOW_TO_FAILOVER'  
    backend_application_name1 = 'server1'  
    # - Authentication -  
    enable_pool_hba = on   
                                       # Use pool_hba.conf for client authentication  
    pool_passwd = 'pool_passwd'  
                                       # Filename of pool_passwd for md5 authentication.  
                                       # "" disables pool_passwd.  
                                       # (change requires restart)  
    authentication_timeout = 60  
                                       # Delay in seconds to complete client authentication.  
                                       # 0 means no timeout.  
    allow_clear_text_frontend_auth = off  
                                       # Allow Pgpool-II to use cleartext password authentication  
                                       # with clients when pool_passwd does not  
                                       # contain the user password.  
    # - SSL connections -  
    ssl = off  
                                       # Enable SSL support  
                                       # (change requires restart)  
    #ssl_key = './server.key'  
                                       # Path to the SSL private key file  
                                       # (change requires restart)  
    #ssl_cert = './server.cert'  
                                       # Path to the SSL public certificate file  
                                       # (change requires restart)  
    #ssl_ca_cert = ''  
                                       # Path to a single PEM format file  
                                       # containing CA root certificate(s)  
                                       # (change requires restart)  
    #ssl_ca_cert_dir = ''  
                                       # Directory containing CA root certificate(s)  
                                       # (change requires restart)  
    ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL'  
                                       # Allowed SSL ciphers  
                                       # (change requires restart)  
    ssl_prefer_server_ciphers = off  
                                       # Use the server's SSL cipher preferences  
                                       # rather than the client's.  
                                       # (change requires restart)  
    ssl_ecdh_curve = 'prime256v1'  
                                       # Name of the curve to use in ECDH key exchange  
    ssl_dh_params_file = ''  
                                       # Name of the file containing Diffie-Hellman parameters used  
                                       # for the ephemeral DH family of SSL ciphers.  
    #------------------------------------------------------------------------------  
    # Pools  
    #------------------------------------------------------------------------------  
    # - Concurrent session and pool size -  
    num_init_children = 128   
                                       # Number of concurrent sessions allowed  
                                       # (change requires restart)  
    max_pool = 4  
                                       # Number of connection pool caches per connection  
                                       # (change requires restart)  
    # - Lifetime -  
    child_life_time = 300  
                                       # A child process exits after being idle for this many seconds.  
    child_max_connections = 0  
                                       # A child process exits after accepting this many connections.  
                                       # 0 means no exit.  
    connection_life_time = 0  
                                       # Connection to a backend closes after being idle for this many seconds.  
                                       # 0 means no close.  
    client_idle_limit = 0  
                                       # Client is disconnected after being idle for this many seconds  
                                       # (even inside explicit transactions).  
                                       # 0 means no disconnection.  
    #------------------------------------------------------------------------------  
    # Logs  
    #------------------------------------------------------------------------------  
    # - Where to log -  
    log_destination = 'syslog'  
                                       # Where to log.  
                                       # Valid values are combinations of stderr  
                                       # and syslog. Default is stderr.  
    # - What to log -  
    log_line_prefix = '%t: pid %p: '   # printf-style string to output at the beginning of each log line.  
    log_connections = on  
                                       # Log connections  
    log_hostname = off  
                                       # Hostname will be shown in ps status  
                                       # and in logs if connections are logged.  
    log_statement = off  
                                       # Log all statements  
    log_per_node_statement = off  
                                       # Log all statements  
                                       # with node and backend information.  
    log_client_messages = off  
                                       # Log any client messages  
    log_standby_delay = 'if_over_threshold'  
                                       # Log standby delay.  
                                       # Valid values are always,  
                                       # if_over_threshold, or none.  
    # - Syslog specific -  
    syslog_facility = 'LOCAL0'  
                                       # Syslog local facility. Default is LOCAL0.  
    syslog_ident = 'pgpool'  
                                       # Syslog program identification string.  
                                       # Default is 'pgpool'.  
    # - Debug -  
    #log_error_verbosity = default          # terse, default, or verbose messages  
    #client_min_messages = notice           # values in order of decreasing detail:  
                                            #   debug5  
                                            #   debug4  
                                            #   debug3  
                                            #   debug2  
                                            #   debug1  
                                            #   log  
                                            #   notice  
                                            #   warning  
                                            #   error  
    #log_min_messages = warning             # values in order of decreasing detail:  
                                            #   debug5  
                                            #   debug4  
                                            #   debug3  
                                            #   debug2  
                                            #   debug1  
                                            #   info  
                                            #   notice  
                                            #   warning  
                                            #   error  
                                            #   log  
                                            #   fatal  
                                            #   panic  
    #------------------------------------------------------------------------------  
    # File locations  
    #------------------------------------------------------------------------------  
    pid_file_name = '/var/run/pgpool-II-12/pgpool.pid'  
                                       # PID filename.  
                                       # Can be specified as a path relative to the `pgpool.conf` file or  
                                       # as an absolute path.  
                                       # (change requires restart)  
    logdir = '/tmp'  
                                       # Directory of the pgPool status file  
                                       # (change requires restart)  
    #------------------------------------------------------------------------------  
    # Connection pooling  
    #------------------------------------------------------------------------------  
    connection_cache = on  
                                       # Enables connection pools.  
                                       # (change requires restart)  
                                       # Semicolon-separated list of queries  
                                       # to be issued at the end of a session.  
                                       # The default is for 8.3 and later.  
    reset_query_list = 'ABORT; DISCARD ALL'  
                                       # The following is for 8.2 and earlier:  
    #reset_query_list = 'ABORT; RESET ALL; SET SESSION AUTHORIZATION DEFAULT'  
    #------------------------------------------------------------------------------  
    # Replication mode  
    #------------------------------------------------------------------------------  
    replication_mode = off  
                                       # Enables replication mode.  
                                       # (change requires restart)  
    replicate_select = off  
                                       # Replicate SELECT statements  
                                       # when in replication mode.  
                                       # replicate_select has higher priority than  
                                       # load_balance_mode.  
    insert_lock = off  
                                       # Automatically locks a dummy row or a table  
                                       # with INSERT statements to keep SERIAL data  
                                       # consistency.  
                                       # Without SERIAL, no lock will be issued.  
    lobj_lock_table = ''  
                                       # When rewriting the lo_create command in  
                                       # replication mode, specify the table name to  
                                       # lock.  
    # - Degenerate handling -  
    replication_stop_on_mismatch = off  
                                       # On a packet kind mismatch  
                                       # from the backend, degenerate the node  
                                       # that is most likely the "minority".  
                                       # If off, pgpool forces the session to exit.  
    failover_if_affected_tuples_mismatch = off  
                                       # On disagreement with the number of affected  
                                       # tuples in UPDATE/DELETE queries,  
                                       # degenerate the node that is most likely  
                                       # the "minority".  
                                       # If off, pgpool aborts the transaction to  
                                       # maintain consistency.  
    #------------------------------------------------------------------------------  
    # Load balancing mode  
    #------------------------------------------------------------------------------  
    load_balance_mode = on  
                                       # Enables load balancing mode.  
                                       # (change requires restart)  
    ignore_leading_white_space = on  
                                       # Ignore leading white spaces in each query.  
    white_function_list = ''  
                                       # Comma-separated list of function names  
                                       # that do not write to the database.  
                                       # Regular expressions are accepted.  
    black_function_list = 'currval,lastval,nextval,setval'  
                                       # Comma-separated list of function names  
                                       # that write to the database.  
                                       # Regular expressions are accepted.  
    black_query_pattern_list = ''  
                                       # Semicolon-separated list of query patterns  
                                       # that should be sent to the primary node.  
                                       # Regular expressions are accepted.  
                                       # Valid for streaming replication mode only.  
    database_redirect_preference_list = ''  
                                       # Comma-separated list of database and node ID pairs.  
                                       # Example: postgres:primary,mydb[0-4]:1,mydb[5-9]:2  
                                       # Valid for streaming replication mode only.  
    app_name_redirect_preference_list = ''  
                                       # Comma-separated list of application name and node ID pairs.  
                                       # Example: 'psql:primary,myapp[0-4]:1,myapp[5-9]:standby'  
                                       # Valid for streaming replication mode only.  
    allow_sql_comments = off  
                                       # If on, ignore SQL comments when determining if load balancing or  
                                       # query caching is possible.  
                                       # If off, SQL comments prevent this determination  
                                       # (pre-3.4 behavior).  
    disable_load_balance_on_write = 'transaction'  
                                       # Load balancing behavior when a write query is issued  
                                       # in an explicit transaction.  
                                       # Any query not in an explicit transaction  
                                       # is not affected by this parameter.  
                                       # 'transaction' (default): If a write query is issued,  
                                       # subsequent read queries will not be load balanced  
                                       # until the transaction ends.  
                                       # 'trans_transaction': If a write query is issued,  
                                       # subsequent read queries in an explicit transaction  
                                       # will not be load balanced until the session ends.  
                                       # 'always': If a write query is issued, read queries will  
                                       # not be load balanced until the session ends.  
    statement_level_load_balance = off  
                                       # Enables statement-level load balancing.  
    #------------------------------------------------------------------------------  
    # Primary/standby mode  
    #------------------------------------------------------------------------------  
    master_slave_mode = on  
                                       # Activates primary/standby mode.  
                                       # (change requires restart)  
    master_slave_sub_mode = 'stream'  
                                       # Primary/standby sub-mode.  
                                       # Valid values are stream, slony,  
                                       # or logical. The default is stream.  
                                       # (change requires restart)  
    # - Streaming -  
    sr_check_period = 3   
                                       # Streaming replication check period.  
                                       # Disabled (0) by default.  
    sr_check_user = 'nobody'  
                                       # Streaming replication check user.  
                                       # This is necessary even if you disable the streaming  
                                       # replication delay check by setting sr_check_period = 0.  
    sr_check_password = ''  
                                       # Password for the streaming replication check user.  
                                       # If empty, Pgpool-II first looks for the  
                                       # password in the pool_passwd file before using an empty password.  
    sr_check_database = 'postgres'  
                                       # Database name for the streaming replication check.  
    delay_threshold = 512000  
                                       # Threshold in bytes before not dispatching a query to the standby node.  
                                       # Disabled (0) by default.  
    # - Special commands -  
    follow_master_command = ''  
                                       # Executes this command after a primary failover.  
                                       # Special values:  
                                       #   %d = failed node id  
                                       #   %h = failed node hostname  
                                       #   %p = failed node port number  
                                       #   %D = failed node database cluster path  
                                       #   %m = new master node id  
                                       #   %H = new master node hostname  
                                       #   %M = old master node id  
                                       #   %P = old primary node id  
                                       #   %r = new master port number  
                                       #   %R = new master database cluster path  
                                       #   %N = old primary node hostname  
                                       #   %S = old primary node port number  
                                       #   %% = '%' character  
    #------------------------------------------------------------------------------  
    # Health check global parameters  
    #------------------------------------------------------------------------------  
    health_check_period = 5  
                                       # Health check period.  
                                       # Disabled (0) by default.  
    health_check_timeout = 10  
                                       # Health check timeout.  
                                       # 0 means no timeout.  
    health_check_user = 'nobody'  
                                       # Health check user.  
    health_check_password = ''  
                                       # Password for the health check user.  
                                       # If empty, Pgpool-II first looks for the  
                                       # password in the pool_passwd file before using an empty password.  
    health_check_database = ''  
                                       # Database name for the health check. If empty, 'postgres' is tried first.   
    health_check_max_retries = 60   
                                       # Maximum number of times to retry a failed health check before giving up.  
    health_check_retry_delay = 1  
                                       # Amount of time in seconds to wait between retries.  
    connect_timeout = 10000  
                                       # Timeout in milliseconds before giving up on connecting to a backend.  
                                       # Default is 10000 ms (10 seconds). Users on unstable networks may need to increase this value.  
                                       # 0 means no timeout.  
                                       # Note that this value is used for ordinary connections to the backend,  
                                       # not just for health checks.  
    #------------------------------------------------------------------------------  
    # Health check per-node parameters (optional)  
    #------------------------------------------------------------------------------  
    #health_check_period0 = 0  
    #health_check_timeout0 = 20  
    #health_check_user0 = 'nobody'  
    #health_check_password0 = ''  
    #health_check_database0 = ''  
    #health_check_max_retries0 = 0  
    #health_check_retry_delay0 = 1  
    #connect_timeout0 = 10000  
    #------------------------------------------------------------------------------  
    # Failover and failback  
    #------------------------------------------------------------------------------  
    failover_command = ''  
                                       # Executes this command on failover.  
                                       # Special values:  
                                       #   %d = failed node id  
                                       #   %h = failed node hostname  
                                       #   %p = failed node port number  
                                       #   %D = failed node database cluster path  
                                       #   %m = new master node id  
                                       #   %H = new master node hostname  
                                       #   %M = old master node id  
                                       #   %P = old primary node id  
                                       #   %r = new master port number  
                                       #   %R = new master database cluster path  
                                       #   %N = old primary node hostname  
                                       #   %S = old primary node port number  
                                       #   %% = '%' character  
    failback_command = ''  
                                       # Executes this command on failback.  
                                       # Special values:  
                                       #   %d = failed node id  
                                       #   %h = failed node hostname  
                                       #   %p = failed node port number  
                                       #   %D = failed node database cluster path  
                                       #   %m = new primary node id  
                                       #   %H = new primary node hostname  
                                       #   %M = old primary node id  
                                       #   %P = old primary node id  
                                       #   %r = new primary port number  
                                       #   %R = new primary database cluster path  
                                       #   %N = old primary node hostname  
                                       #   %S = old primary node port number  
                                       #   %% = '%' character  
    failover_on_backend_error = off  
                                       # Initiates a failover when reading/writing to the  
                                       # backend communication socket fails.  
                                       # If set to off, pgpool reports an  
                                       # error and disconnects the session.  
    detach_false_primary = off  
                                       # Detach a false primary if on. Only  
                                       # valid in streaming replication  
                                       # mode and with PostgreSQL 9.6 or  
                                       # later.  
    search_primary_node_timeout = 300  
                                       # Timeout in seconds to search for the  
                                       # primary node when a failover occurs.  
                                       # 0 means no timeout; keeps searching  
                                       # for a primary node forever.  
    #------------------------------------------------------------------------------  
    # Online recovery  
    #------------------------------------------------------------------------------  
    recovery_user = 'nobody'  
                                       # Online recovery user.  
    recovery_password = ''  
                                       # Online recovery password.  
                                       # If empty, Pgpool-II first looks for the  
                                       # password in the pool_passwd file before using an empty password.  
    recovery_1st_stage_command = ''  
                                       # Executes a command in the first stage.  
    recovery_2nd_stage_command = ''  
                                       # Executes a command in the second stage.  
    recovery_timeout = 90  
                                       # Timeout in seconds to wait for the  
                                       # recovering node's postmaster to start up.  
                                       # 0 means no wait.  
    client_idle_limit_in_recovery = 0  
                                       # Client is disconnected after being idle  
                                       # for this many seconds in the second stage  
                                       # of online recovery.  
                                       # 0 means no disconnection.  
                                       # -1 means immediate disconnection.  
    auto_failback = off  
                                       # A detached backend node automatically reattaches if its `replication_state` is 'streaming'.  
    auto_failback_interval = 60  
                                       # Minimum interval in seconds for executing auto_failback.  
    #------------------------------------------------------------------------------  
    # Watchdog  
    #------------------------------------------------------------------------------  
    # - Enabling -  
    use_watchdog = off  
                                        # Activates the watchdog.  
                                        # (change requires restart)  
    # - Connection to upstream servers -  
    trusted_servers = ''  
                                        # Trusted server list used  
                                        # to confirm network connection  
                                        # (hostA,hostB,hostC,...).  
                                        # (change requires restart)  
    ping_path = '/bin'  
                                        # Path to the ping command.  
                                        # (change requires restart)  
    # - Watchdog communication settings -  
    wd_hostname = ''  
                                        # Hostname or IP address of this watchdog.  
                                        # (change requires restart)  
    wd_port = 9000  
                                        # Port number for the watchdog service.  
                                        # (change requires restart)  
    wd_priority = 1  
                                        # Priority of this watchdog in leader election.  
                                        # (change requires restart)  
    wd_authkey = ''  
                                        # Authentication key for watchdog communication.  
                                        # (change requires restart)  
    wd_ipc_socket_dir = '/tmp'  
                                        # Unix domain socket path for the watchdog IPC socket.  
                                        # The Debian package defaults to  
                                        # /var/run/postgresql.  
                                        # (change requires restart)  
    # - Virtual IP control settings -  
    delegate_IP = ''  
                                        # The virtual IP address.  
                                        # If this is empty, the virtual IP is not configured.  
                                        # (change requires restart)  
    if_cmd_path = '/sbin'  
                                        # Path to the directory where if_up/down_cmd exists.  
                                        # If if_up/down_cmd starts with "/", if_cmd_path will be ignored.  
                                        # (change requires restart)  
    if_up_cmd = '/usr/bin/sudo /sbin/ip addr add $_IP_$/24 dev eth0 label eth0:0'  
                                        # The command to bring up the virtual IP.  
                                        # (change requires restart)  
    if_down_cmd = '/usr/bin/sudo /sbin/ip addr del $_IP_$/24 dev eth0'  
                                        # The command to bring down the virtual IP.  
                                        # (change requires restart)  
    arping_path = '/usr/sbin'  
                                        # Path to the arping command.  
                                        # If arping_cmd starts with "/", if_cmd_path will be ignored.  
                                        # (change requires restart)  
    arping_cmd = '/usr/bin/sudo /usr/sbin/arping -U $_IP_$ -w 1 -I eth0'  
                                        # The arping command.  
                                        # (change requires restart)  
    # - Behavior on escalation settings -  
    clear_memqcache_on_escalation = on  
                                        # Clear all query cache in shared memory  
                                        # when a standby pgpool escalates to active pgpool  
                                        # (= virtual IP holder).  
                                        # This should be off if clients connect to pgpool  
                                        # without using the virtual IP.  
                                        # (change requires restart)  
    wd_escalation_command = ''  
                                        # Executes this command on escalation on the new active pgpool.  
                                        # (change requires restart)  
    wd_de_escalation_command = ''  
                                        # Executes this command when the primary pgpool resigns from being primary.  
                                        # (change requires restart)  
    # - Watchdog consensus settings for failover -  
    failover_when_quorum_exists = on  
                                        # Only perform a backend node failover  
                                        # when the watchdog cluster holds a quorum.  
                                        # (change requires restart)  
    failover_require_consensus = on  
                                        # Perform a failover when a majority of Pgpool-II nodes  
                                        # agrees on the backend node status change.  
                                        # (change requires restart)  
    allow_multiple_failover_requests_from_node = off  
                                        # A Pgpool-II node can cast multiple votes  
                                        # for building consensus on a failover.  
                                        # (change requires restart)  
    enable_consensus_with_half_votes = off  
                                        # When enabled, quorum and failover consensus are achieved with exactly half the votes.  
                                        # Otherwise, they require more than half the votes.  
                                        # (change requires restart)  
    # - Lifecheck settings -  
    # -- Common --  
    wd_monitoring_interfaces_list = ''  # Comma-separated list of interface names to monitor.  
                                        # If any interface from the list is active, the watchdog will  
                                        # consider the network to be fine.  
                                        # 'any' enables monitoring on all interfaces except loopback.  
                                        # '' disables monitoring.  
                                        # (change requires restart)  
    wd_lifecheck_method = 'heartbeat'  
                                        # Method of watchdog lifecheck ('heartbeat', 'query', or 'external').  
                                        # (change requires restart)  
    wd_interval = 10  
                                        # Lifecheck interval in seconds (> 0).  
                                        # (change requires restart)  
    # -- Heartbeat mode --  
    wd_heartbeat_port = 9694  
                                        # Port number for receiving heartbeat signals.  
                                        # (change requires restart)  
    wd_heartbeat_keepalive = 2  
                                        # Interval in seconds for sending heartbeat signals.  
                                        # (change requires restart)  
    wd_heartbeat_deadtime = 30  
                                        # Dead-time interval for heartbeat signals (in seconds).  
                                        # (change requires restart)  
    heartbeat_destination0 = 'host0_ip1'  
                                        # Hostname or IP address of destination 0  
                                        # for sending heartbeat signals.  
                                        # (change requires restart)  
    heartbeat_destination_port0 = 9694   
                                        # Port number of destination 0 for sending  
                                        # heartbeat signals. Usually, this is the  
                                        # same as wd_heartbeat_port.  
                                        # (change requires restart)  
    heartbeat_device0 = ''  
                                        # Name of the NIC device (e.g., 'eth0')  
                                        # used for sending/receiving heartbeat  
                                        # signals to/from destination 0.  
                                        # This only works if it is not empty  
                                        # and pgpool has root privileges.  
                                        # (change requires restart)  
    #heartbeat_destination1 = 'host0_ip2'  
    #heartbeat_destination_port1 = 9694  
    #heartbeat_device1 = ''  
    # -- Query mode --  
    wd_life_point = 3  
                                        # Lifecheck retry times.  
                                        # (change requires restart)  
    wd_lifecheck_query = 'SELECT 1'  
                                        # Lifecheck query to pgpool from the watchdog.  
                                        # (change requires restart)  
    wd_lifecheck_dbname = 'template1'  
                                        # Database name to connect to for lifecheck.  
                                        # (change requires restart)  
    wd_lifecheck_user = 'nobody'  
                                        # Watchdog user for monitoring pgpools in lifecheck.  
                                        # (change requires restart)  
    wd_lifecheck_password = ''  
                                        # Password for the watchdog user in lifecheck.  
                                        # If empty, Pgpool-II first looks for the  
                                        # password in the pool_passwd file before using an empty password.  
                                        # (change requires restart)  
    # - Other pgpool connection settings -  
    #other_pgpool_hostname0 = 'host0'  
                                        # Hostname or IP address to connect to for other pgpool 0.  
                                        # (change requires restart)  
    #other_pgpool_port0 = 5432  
                                        # Port number for other pgpool 0.  
                                        # (change requires restart)  
    #other_wd_port0 = 9000  
                                        # Port number for other watchdog 0.  
                                        # (change requires restart)  
    #other_pgpool_hostname1 = 'host1'  
    #other_pgpool_port1 = 5432  
    #other_wd_port1 = 9000  
    #------------------------------------------------------------------------------  
    # Others  
    #------------------------------------------------------------------------------  
    relcache_expire = 0  
                                       # Lifetime of the relation cache in seconds.  
                                       # 0 means no cache expiration (the default).  
                                       # The relation cache is used to cache  
                                       # query results against the PostgreSQL system  
                                       # catalog to obtain information,  
                                       # such as table structures or if a table is temporary.  
                                       # The cache is maintained in a pgpool child's local memory  
                                       # and is kept for the life of the process.  
                                       # If a table is modified (e.g., with ALTER TABLE), the cache may become inconsistent.  
                                       # For this purpose, relcache_expire  
                                       # controls the lifetime of the cache.  
    relcache_size = 8192  
                                       # Number of relation cache  
                                       # entries. If you frequently see:  
                                       # "pool_search_relcache: cache replacement happened"  
                                       # in the pgpool log, you should increase this value.  
    check_temp_table = catalog  
                                       # Temporary table check method: catalog, trace, or none.  
                                       # The default is catalog.  
    check_unlogged_table = on  
                                       # If on, enables unlogged table check in SELECT statements.  
                                       # This initiates queries against the system catalog of the primary node,  
                                       # thus increasing the load on the primary node.  
                                       # If you are certain that your system never uses unlogged tables  
                                       # and want to reduce access to the primary node, you can turn this off.  
                                       # Default is on.  
    enable_shared_relcache = on  
                                       # If on, the relation cache is stored in memory cache  
                                       # and shared among child processes.  
                                       # Default is on.  
                                       # (change requires restart)  
    relcache_query_target = master     # Target node for relcache queries. The default is the primary node.  
                                       # If a load-balanced node is specified, queries are sent to a load-balanced node.  
    #------------------------------------------------------------------------------  
    # In-memory query cache  
    #------------------------------------------------------------------------------  
    memory_cache_enabled = off  
                                       # If on, enables the memory cache functionality. Off by default.  
                                       # (change requires restart)  
    memqcache_method = 'shmem'  
                                       # Cache storage method: 'shmem' (shared memory) or  
                                       # 'memcached'. 'shmem' is the default.  
                                       # (change requires restart)  
    memqcache_memcached_host = 'localhost'  
                                       # Memcached hostname or IP address. Mandatory if  
                                       # memqcache_method = 'memcached'.  
                                       # Defaults to localhost.  
                                       # (change requires restart)  
    memqcache_memcached_port = 11211  
                                       # Memcached port number. Mandatory if memqcache_method = 'memcached'.  
                                       # Defaults to 11211.  
                                       # (change requires restart)  
    memqcache_total_size = 67108864  
                                       # Total memory size in bytes for storing the memory cache.  
                                       # Mandatory if memqcache_method = 'shmem'.  
                                       # Defaults to 64 MB.  
                                       # (change requires restart)  
    memqcache_max_num_cache = 1000000  
                                       # Total number of cache entries. Mandatory  
                                       # if memqcache_method = 'shmem'.  
                                       # Each cache entry consumes 48 bytes of shared memory.  
                                       # Defaults to 1,000,000 (45.8 MB).  
                                       # (change requires restart)  
    memqcache_expire = 0  
                                       # The lifetime in seconds for a query cache entry.  
                                       # A value of 0 means an infinite lifetime (default).  
                                       # (change requires restart)  
    memqcache_auto_cache_invalidation = on  
                                       # If on, invalidation of the query cache is triggered by a corresponding  
                                       # DDL/DML/DCL (and memqcache_expire). If off, it is only triggered  
                                       # by memqcache_expire. On by default.  
                                       # (change requires restart)  
    memqcache_maxcache = 409600  
                                       # Maximum SELECT result size in bytes.  
                                       # Must be smaller than memqcache_cache_block_size. Defaults to 400 KB.  
                                       # (change requires restart)  
    memqcache_cache_block_size = 1048576  
                                       # Cache block size in bytes. Mandatory if memqcache_method = 'shmem'.  
                                       # Defaults to 1 MB.  
                                       # (change requires restart)  
    memqcache_oiddir = '/var/log/pgpool/oiddir'  
                                       # Temporary working directory to record table OIDs.  
                                       # (change requires restart)  
    white_memqcache_table_list = ''  
                                       # A comma-separated list of tables whose SELECT queries are candidates for caching.  
                                       # Regular expressions are accepted.  
    black_memqcache_table_list = ''  
                                       # A comma-separated list of tables whose SELECT queries should not be cached.  
                                       # Regular expressions are accepted.  

    Key configuration changes include:

    Análisis: la entrada es un bloque de código (<code>), no un documento HTML con encabezados o tablas. Por lo tanto, las reglas de procesamiento de HTML no se aplican directamente. La instrucción principal es "optimizar el borrador en inglés basándose en el original en chino". Mi rol es ser un experto en localización de textos extremadamente conciso.
    
    Mi estrategia es la siguiente:
    1.  Utilizar el "borrador en inglés" como base.
    2.  Comparar los comentarios (#) con los del "original en chino".
    3.  Adoptar los comentarios de la versión "original" cuando sean más concisos y directos, siempre que mantengan la claridad y la corrección gramatical.
    4.  Mantener los comentarios del "borrador" cuando sean más claros, gramaticalmente superiores o cuando la versión "original" contenga errores.
    5.  Conservar intactos todo el código, los valores de los parámetros y la estructura del archivo.
    
    Este enfoque se alinea con la intención del usuario de optimizar el texto y con mi perfil de experto en concisión, aplicando el juicio profesional en lugar de un reemplazo ciego.
    
    html
    
    listen_addresses = '0.0.0.0'  
    port = 8001  
    socket_dir = '/tmp'  
    reserved_connections = 0  
    pcp_listen_addresses = ''  
    pcp_port = 9898  
    pcp_socket_dir = '/tmp'  
    # - Backend Connection Settings -  
    backend_hostname0 = '127.0.0.1'  
                                       # Host name or IP address to connect to for backend 0  
    backend_port0 = 3389   
                                       # Port number for backend 0  
    backend_weight0 = 1  
                                       # Weight for backend 0 (only in load balancing mode)  
    backend_data_directory0 = '/data01/pg12_3389/pg_data'  
                                       # Data directory for backend 0  
    backend_flag0 = 'ALWAYS_MASTER'  
                                       # Controls various backend behavior  
                                       # ALLOW_TO_FAILOVER, DISALLOW_TO_FAILOVER  
                                       # or ALWAYS_MASTER  
    backend_application_name0 = 'server0'  
                                       # walsender's application_name, used for "show pool_nodes" command  
    backend_hostname1 = '127.0.0.1'  
    backend_port1 = 8002  
    backend_weight1 = 1  
    backend_data_directory1 = '/data01/pg12_8002/pg_data'  
    backend_flag1 = 'DISALLOW_TO_FAILOVER'  
    backend_application_name1 = 'server1'  
    # - Authentication -  
    enable_pool_hba = on   
                                       # Use pool_hba.conf for client authentication  
    pool_passwd = 'pool_passwd'  
                                       # File name of pool_passwd for md5 authentication.  
                                       # "" disables pool_passwd.  
                                       # (change requires restart)  
    allow_clear_text_frontend_auth = off  
                                       # Allow Pgpool-II to use clear text password authentication  
                                       # with clients, when pool_passwd does not  
                                       # contain the user password  
    # - Concurrent session and pool size -  
    num_init_children = 128   
                                       # Number of concurrent sessions allowed  
                                       # (change requires restart)  
    max_pool = 4  
                                       # Number of connection pool caches per connection  
                                       # (change requires restart)  
    # - Life time -  
    child_life_time = 300  
                                       # Pool exits after being idle for this many seconds  
    child_max_connections = 0  
                                       # Pool exits after receiving that many connections  
                                       # 0 means no exit  
    connection_life_time = 0  
                                       # Connection to backend closes after being idle for this many seconds  
                                       # 0 means no close  
    client_idle_limit = 0  
                                       # Client is disconnected after being idle for that many seconds  
                                       # (even inside an explicit transactions!)  
                                       # 0 means no disconnection  
    #------------------------------------------------------------------------------  
    # LOGS  
    #------------------------------------------------------------------------------  
    # - Where to log -  
    log_destination = 'syslog'  
                                       # Where to log  
                                       # Valid values are combinations of stderr,  
                                       # and syslog. Default to stderr.  
    log_connections = on  
                                       # Log connections  
    log_standby_delay = 'if_over_threshold'  
                                       # Log standby delay  
                                       # Valid values are combinations of always,  
                                       # if_over_threshold, none  
    #------------------------------------------------------------------------------  
    # FILE LOCATIONS  
    #------------------------------------------------------------------------------  
    pid_file_name = '/var/run/pgpool-II-12/pgpool.pid'  
                                       # PID file name  
                                       # Can be specified as a path relative to the  
                                       # pgpool.conf file or  
                                       # as an absolute path  
                                       # (change requires restart)  
    logdir = '/tmp'  
                                       # Directory of pgPool status file  
                                       # (change requires restart)  
    #------------------------------------------------------------------------------  
    # CONNECTION POOLING  
    #------------------------------------------------------------------------------  
    connection_cache = on  
                                       # Activate connection pools  
                                       # (change requires restart)  
                                       # Semicolon-separated list of queries  
                                       # to be issued at the end of a session  
                                       # The default is for 8.3 and later  
    reset_query_list = 'ABORT; DISCARD ALL'  
    #------------------------------------------------------------------------------  
    # LOAD BALANCING MODE  
    #------------------------------------------------------------------------------  
    load_balance_mode = on  
                                       # Activate load balancing mode  
                                       # (change requires restart)  
    ignore_leading_white_space = on  
                                       # Ignore leading white spaces of each query  
    white_function_list = ''  
                                       # Comma-separated list of function names  
                                       # that don't write to database  
                                       # Regexp are accepted  
    black_function_list = 'currval,lastval,nextval,setval'  
                                       # Comma-separated list of function names  
                                       # that write to the database  
                                       # Regexp are accepted  
    black_query_pattern_list = ''  
                                       # Semicolon-separated list of query patterns  
                                       # that should be sent to primary node  
                                       # Regexp are accepted  
                                       # valid for streaming replication mode only.  
    database_redirect_preference_list = ''  
                                       # comma-separated list of pairs of database and node id.  
                                       # example: postgres:primary,mydb[0-4]:1,mydb[5-9]:2'  
                                       # valid for streaming replication mode only.  
    app_name_redirect_preference_list = ''  
                                       # comma-separated list of pairs of app name and node id.  
                                       # example: 'psql:primary,myapp[0-4]:1,myapp[5-9]:standby'  
                                       # valid for streaming replication mode only.  
    allow_sql_comments = off  
                                       # if on, ignore SQL comments when judging if load balance or  
                                       # query cache is possible.  
                                       # If off, SQL comments effectively prevent the judgment  
                                       # (pre 3.4 behavior).  
    disable_load_balance_on_write = 'transaction'  
                                       # Load balance behavior when write query is issued  
                                       # in an explicit transaction.  
                                       # Note that any query not in an explicit transaction  
                                       # is not affected by the parameter.  
                                       # 'transaction' (the default): if a write query is issued,  
                                       # subsequent read queries will not be load balanced  
                                       # until the transaction ends.  
                                       # 'trans_transaction': if a write query is issued,  
                                       # subsequent read queries in an explicit transaction  
                                       # will not be load balanced until the session ends.  
                                       # 'always': if a write query is issued, read queries will  
                                       # not be load balanced until the session ends.  
    statement_level_load_balance = off  
                                       # Enables statement level load balancing  
    #------------------------------------------------------------------------------  
    # MASTER/SLAVE MODE  
    #------------------------------------------------------------------------------  
    master_slave_mode = on  
                                       # Activate master/slave mode  
                                       # (change requires restart)  
    master_slave_sub_mode = 'stream'  
                                       # Master/slave sub mode  
                                       # Valid values are stream, slony,  
                                       # or logical. Default is stream.  
                                       # (change requires restart)  
    # - Streaming -  
    sr_check_period = 3   
                                       # Streaming replication check period  
                                       # Disabled (0) by default  
    sr_check_user = 'nobody'  
                                       # Streaming replication check user  
                                       # This is necessary even if you disable streaming  
                                       # replication delay check by sr_check_period = 0  
    sr_check_password = ''  
                                       # Password for the streaming replication check user  
                                       # If empty, Pgpool-II first looks for the  
                                       # password in the pool_passwd file before using an empty password  
    sr_check_database = 'postgres'  
                                       # Database name for the streaming replication check  
    delay_threshold = 512000  
                                       # Threshold before not dispatching query to standby node  
                                       # Unit is in bytes  
                                       # Disabled (0) by default  
    #------------------------------------------------------------------------------  
    # HEALTH CHECK GLOBAL PARAMETERS  
    #------------------------------------------------------------------------------  
    health_check_period = 5  
                                       # Health check period  
                                       # Disabled (0) by default  
    health_check_timeout = 10  
                                       # Health check timeout  
                                       # 0 means no timeout  
    health_check_user = 'nobody'  
                                       # Health check user  
    health_check_password = ''  
                                       # Password for the health check user  
                                       # If empty, Pgpool-II first looks for the  
                                       # password in the pool_passwd file before using an empty password  
    health_check_database = ''  
                                       # Database name for the health check. If '', tries 'postgres' first.   
    health_check_max_retries = 60   
                                       # Maximum number of retries for a failed health check.  
    health_check_retry_delay = 1  
                                       # The delay in seconds between health check retries.  
    connect_timeout = 10000  
                                       # Timeout value in milliseconds before giving up on connecting to a backend.  
                                       # Default is 10000 ms (10 seconds). Users on unreliable networks may want to increase  
                                       # this value. 0 means no timeout.  
                                       # Note that this value is not only used for health checks,  
                                       # but also for ordinary connections to a backend.  
    #------------------------------------------------------------------------------  
    # FAILOVER AND FAILBACK  
    #------------------------------------------------------------------------------  
    failover_on_backend_error = off  
                                       # Initiates failover when reading/writing to the  
                                       # backend communication socket fails.  
                                       # If set to off, pgpool will report an  
                                       # error and disconnect the session.  
    relcache_expire = 0  # After a schema change, it is recommended to set this to 1, reload, and then revert the change. Alternatively, set a specific cache lifetime.      
                                       # Lifetime of the relation cache in seconds.  
                                       # 0 means no cache expiration (the default).  
                                       # The relation cache is used to cache the  
                                       # query result against the PostgreSQL system  
                                       # catalog to obtain various information,  
                                       # including table structures or whether a table  
                                       # is temporary. The cache is  
                                       # maintained in a pgpool child's local memory  
                                       # and is kept for the lifetime of the child process.  
                                       # If a user modifies the table by using  
                                       # ALTER TABLE or a similar command, the cache can  
                                       # become inconsistent.  
                                       # This parameter (relcache_expire)  
                                       # controls the cache lifetime.  
    relcache_size = 8192  
                                       # Number of relation cache entries.  
                                       # If the message "pool_search_relcache: cache replacement happened"  
                                       # appears frequently in the pgpool log, you may want to increase this number.  
  3. Configure the pool_passwd password file. Run the following commands:

    Note

    The password file is required for database connections established through pgpool. It enables pgpool to support the PostgreSQL authentication protocol.

    cd /etc/pgpool-II-12  
    # Usage  
    # pg_md5 --md5auth --username=username password  
    # Generate passwords for the digoal and nobody users and write them to the pool_passwd file.
    pg_md5 --md5auth --username=digoal "xxxxxxx"  
    pg_md5 --md5auth --username=nobody "xxxxxxx"  
  4. View the auto-generated pool_passwd file:

    cd /etc/pgpool-II-12  
    cat pool_passwd   
    digoal:md54dd55116da69d3d03bf2e3a1470564f9  
    nobody:md54240e76623e2511d607f431043a5d1c1 
  5. Configure the pgpool_hba file.

    cd /etc/pgpool-II-12  
    cp pool_hba.conf.sample pool_hba.conf  
    vi pool_hba.conf  
    host all all 0.0.0.0/0 md5  
  6. To configure the pcp management password file, run the following commands:

    Note

    This user and password are for pgpool management, not for database access.

    cd /etc/pgpool-II-12  
    pg_md5 abc  # For example, the password is 'abc'.  
    900150983cd24fb0d6963f7d28e17f72  
    cp pcp.conf.sample pcp.conf  
    vi pcp.conf  
    USERID:MD5PASSWD  
    manage:900150983cd24fb0d6963f7d28e17f72  # Sets 'manage' as the pcp management user.
  7. To start pgpool:

    cd /etc/pgpool-II-12  
    pgpool -f ./pgpool.conf -a ./pool_hba.conf -F ./pcp.conf  
    Note

    To view the pgpool log, run the following command:

    less /var/log/messages   
  8. Connect to a database through pgpool:

    psql -h 127.0.0.1 -p 8001 -U digoal postgres  
    [root@iZbxxxZ pgpool-II-12]# psql -h pgm-bpxxx.pg.rds.aliyuncs.com -p 3433 -U digoal postgres
    Password for user digoal:
    psql (12.2, server 10.10)
    Type "help" for help.
    postgres=>

FAQ

  • How do I test if read-write separation is working?

    Connect to the database and run the pg_is_in_recovery() function. Then, disconnect, reconnect, and run the function again. If the return values alternate between false and true, it means requests are being routed alternately to the primary and standby instances, confirming that read-write separation is working.

  • Does pgpool add latency?

    Yes, it introduces a small amount of latency. In the test environment for this guide, the added latency is approximately 0.12 ms.

  • How do the replay lag check and health check mechanisms in pgpool work?

    • pgpool does not route SQL requests to a read-only instance if its replay lag exceeds the configured threshold. Routing to the instance resumes after its lag falls below the threshold.

      Note

      Check the lag by connecting to the primary instance to query the current WAL write location (LSN 1) and then connecting to the read-only instance to query the WAL replay location (LSN 2). The difference in bytes between LSN 1 and LSN 2 is the replay lag.

    • pgpool performs health checks on backend nodes. If a node is found to be unhealthy, pgpool stops routing SQL requests to it.

  • How do I stop pgpool or reload its configuration?

    Use the pgpool --help command to see all available options. For example, to stop pgpool:

    cd /etc/pgpool-II-12  
    pgpool -f ./pgpool.conf -m fast stop   
  • How do I configure pgpool if I have multiple read-only instances?

    Modify the pgpool.conf file by adding a configuration block for each additional read-only instance. For example:

    backend_hostname1 = 'xx.xx.xxx.xx'  
    backend_port1 = 8002  
    backend_weight1 = 1  
    backend_data_directory1 = '/data01/pg12_8002/pg_data'  
    backend_flag1 = 'DISALLOW_TO_FAILOVER'  
    backend_application_name1 = 'server1'  
    backend_hostname2 = 'xx.xx.xx.xx'  
    backend_port2 = 8002  
    backend_weight2 = 1  
    backend_data_directory2 = '/data01/pg12_8002/pg_data'  
    backend_flag2 = 'DISALLOW_TO_FAILOVER'  
    backend_application_name2 = 'server2'  
  • How do I use pcp to query the backend status?

    Example command:

    # pcp_node_info -U manage -h /tmp -p 9898 -n 1 -v  
    Password: Enter your password  
    Hostname               : 127.0.0.1  
    Port                   : 8002  
    Status                 : 2  
    Weight                 : 0.500000  
    Status Name            : up  
    Role                   : standby  
    Replication Delay      : 0  
    Replication State      :   
    Replication Sync State :   
    Last Status Change     : 2020-02-29 00:20:29  
  • Which ports are used in this configuration?

    This configuration uses the following ports:

    • Primary instance: 3389

    • Standby instance: 8002

    • pgpool: 8001

    • pcp management: 9898