psycopg2连接GaussDB(DWS)不支持CN Retry特性的问题分析
概述
近年来,随着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级数仓黑科技,后台还可获取众多学习资料哦~
- 点赞
- 收藏
- 关注作者
评论(0)