psycopg2连接GaussDB(DWS)不支持CN Retry特性的问题分析

举报
hhx1003 发表于 2022/01/25 09:51:19 2022/01/25
【摘要】 近年来,随着Python版本的不断更新和快速发展,快速占领流行编程语言排行榜,开始用于大型项目的开发。多个数据仓库使用场景中也使用Python开发,使用Python驱动连接数据库。本博文结合故障模式下psycopg2驱动不支持CN Retry特性的问题分析过程介绍psycopg2驱动的使用和问题定位方法。

概述

近年来,随着Python版本的不断更新和快速发展,快速占领流行编程语言排行榜,开始用于大型项目的开发。多个数据仓库使用场景中也使用Python开发,使用Python驱动连接数据库。

本博文结合故障模式下psycopg2驱动不支持CN Retry特性的问题分析过程介绍psycopg2驱动的使用和问题定位方法

故障模式下psycopg2连接不支持CN Retry问题的分析

使用psycopg2默认连接方式创建的查询不支持CN Retry特性,在故障模式下直接报错退出。下面以故障模式下psycopg2连接不支持CN Retry问题的分析为例介绍psycopg2问题的分析方式。

1 故障环境搭建

1) 数据准

drop table if exists student;
create table student (id int, name varchar(32)) distribute by hash(id);
insert into student values (1, 'Jack');

只插入一条数据,方便找出数据存储的Datanode,构造故障场景。

2) 查看数据分布DN

通过上述SQL可查询到数据存储的Datanode。

select node_name from pgxc_node where node_id in (select xc_node_id from student group by xc_node_id);
node_name
--------------
dn_6001_6002

(1 row)

3) 构造dn故障

通过cm_ctl query -Cvid可查询到dn_6001_6002的数据目录以及主机IP,对dn_6001_6002数据目录重命名可构造单点Datanode故障,触发HA机制。

mv /home/omm/cluster/data/bigcluster/master2 /home/omm/cluster/data/bigcluster/master2_bak

4) 故障恢复

如果需要恢复故障,先将重命名的dn_6001_6002数据目录恢复。等dn_6001_6002实例状态正常后,执行gs_om -t switch --reset命令重置实例状态。

mv /home/omm/cluster/data/bigcluster/master2_bak /home/omm/cluster/data/bigcluster/master2
gs_om -t switch --reset

2 根因定位

1) 问题描述

在上述步骤构造的故障场景下,执行下python代码执行报错退出:

#!/usr/bin/env python3
# _*_ encoding=utf-8 _*_

import psycopg2

def psycopg2_cnretry_sync():
    # 创建连接
    conn = psycopg2.connect(dbname="testdb",
                            user="jack",
                            password="Abcde@123",
                            host="192.168.233.189",
                            port="8109",
                            application_name="psycopg2")

    # 执行查询
    cursor = conn.cursor()
    cursor.execute("select id, name from student;")
    rows = cursor.fetchall()
    for row in rows:
        print(row[0], row[1])
    cursor.close()

    # 关闭连接
    conn.close()

if __name__ == '__main__':
    psycopg2_cnretry_sync()

报错信息:

pooler: failed to create 1 connections, Error Message: remote node dn_6001_6002 

同样场景下,使用gsql查询不会报错,返回结果时间会长一些,这证明gsql查询时CN Retry功能是起作用的。

postgres=# \c testdb
Non-SSL connection (SSL connection is recommended when requiring high-security)
You are now connected to database "testdb" as user "omm".
testdb=# select id, name from student;
 id | name
----+------
  1 | Jack
(1 row)

testdb=# \q

2) 问题分析

gsql执行查询时不对SQL语句做任何处理,直接发给数据库服务器执行。

一般情况,驱动都会对SQL语句做处理,比如开启游标、事务、设置默认的连接参数等。

所以需要先搞清楚驱动发给服务器的SQL语句做了什么处理,方式有两种:查看数据库服务端日志和抓包分析。

数据库服务端日志

通过gs_guc命令设置log_statement参数,可看到报错SQL前后执行的SQL语句 。

gs_guc reload -Z coordinator -I all -N all -c 'log_statement=all'

重新构造故障环境,执行Python代码,查看数据库服务端日志:

psycopg2 0 cn_5001 00000 72902019032825364 [BACKEND] LOG:  statement: BEGIN
psycopg2 0 cn_5001 00000 72902019032825365 [BACKEND] LOG:  statement: select id, name from student;
psycopg2 0 cn_5001 08006 72902019032825365 [BACKEND] LOG:  pooler: Local 192.168.233.189 failed to connect dn_6001_6002[16384]

从上述日志可以看出psycopg2在发送SQL语句前先发送了BEGIN语句开启事务。

本地抓包分析

为了方便本地使用wireshark抓包分析,需要在psycopg2连接中关闭ssl加密,连接信息变更如下:

conn = psycopg2.connect(dbname="testdb",
                        user="jack",
                        password="Abcde@123",
                        host="192.168.233.189",
                        port="8109",
                        application_name="psycopg2",
                        sslmode="disable")  # 禁止使用ssl加密,方便抓包分析

抓包结果如下,从TCP包中可看出psycopg2在发送SQL语句前先发送了BEGIN语句开启事务。

3) CN Retry特性约束

CN Retry不支持事务块中的语句是特性约束,可以通过gsql连接数据库进行验证。

postgres=# \c testdb
Non-SSL connection (SSL connection is recommended when requiring high-security)
You are now connected to database "testdb" as user "omm".
testdb=# begin;
BEGIN
testdb=# select id, name from student order by id;
ERROR:  pooler: failed to create 1 connections, Error Message: remote node dn_6001_6002, detail: could not connect to server: Operation now in progress
        Is the server running on host "192.168.233.189" and accepting
        TCP/IP connections on port 64600?, remote datanode dn_6001_6002

testdb=# commit;
ROLLBACK
testdb=# \q

该特性约束在GaussDB(DWS)产品文档中也有详细描述。


故障模式下psycopg2连接不支持CN Retry的解决方法

通过分析psycopg2源码与官方文档发现psycopg2.connect接口默认是通过同步连接方式,在发送SQL之前,会将SQL放在事务内执行。

psycopg2.connect接口还提供了异步连接方式,会直接将SQL发送到数据库服务端执行。

下面分别从同步连接异步连接方式提供两种可参考的解决方案。

1 同步方式连接

发送语句之前增加end语句主动结束驱动开启的事务。

    cursor = conn.cursor()
    cursor.execute("end;select id, name from student;") # 增加end语句主动结束驱动开启的事务
    rows = cursor.fetchall()

2 异步方式连接

关于异步连接的介绍见psycopg2官网:https://www.psycopg.org/docs/advanced.html?highlight=async

码修改如下:

#!/usr/bin/env python3
# _*_ encoding=utf-8 _*_

import psycopg2
import select

# psycopg2官方提供的异步连接方式时的wait函数
# 详见https://www.psycopg.org/docs/advanced.html?highlight=async
def wait(conn):
    while True:
        state = conn.poll()
        if state == psycopg2.extensions.POLL_OK:
            break
        elif state == psycopg2.extensions.POLL_WRITE:
            select.select([], [conn.fileno()], [])
        elif state == psycopg2.extensions.POLL_READ:
            select.select([conn.fileno()], [], [])
        else:
            raise psycopg2.OperationalError("poll() returned %s" % state)

def psycopg2_cnretry_sync():
    # 创建连接
    conn = psycopg2.connect(dbname="testdb",
                            user="jack",
                            password="Abcde@123",
                            host="192.168.233.189",
                            port="8109",
                            application_name="psycopg2",
                            async=1) # 使用异步方式连接
    wait(conn)

    # 执行查询
    cursor = conn.cursor()
    cursor.execute("select id, name from student order by id;")
    wait(conn)
    rows = cursor.fetchall()
    for row in rows:
        print(row[0], row[1])

    # 关闭连接
    conn.close()

if __name__ == '__main__':
    psycopg2_cnretry_async()

至此,故障模式下psycopg2连接不支持CN Retry问题的分析结束。


想了解GuassDB(DWS)更多信息,欢迎微信搜索“GaussDB DWS”关注微信公众号,和您分享最新最全的PB级数仓黑科技,后台还可获取众多学习资料哦~


【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。