记一次Django Model删除不生效的坑

问题描述

最近使用Django 的ORM框架操作PostgreSQL数据库总是出现删除不生效(尤其是在并发的时候)。业务代码中也没有任何报错。

  • 通过在Python日志中打印ORM框架返回的操作结果, 发现delete操作返回的记录数是1
  • 通过在数据库中增加触发器,对相关数据表的写操作进行记录, 发现没有删除操作

    结合以上2点,猜测是事务没有commit导致。Django默认的事务模式是autocommit,每一次数据库操作执行后都会自动提交。项目使用的SQLAlchemy库的StaticPool连接池,配合gevent使用,一个进程中的所有协程 串行复用一个数据库连接
    (这里解释一下为什么要一个进程中的所有协程复用一个连接,因为Python的PostgreSQL驱动pyscopg2是有c语言编写,协程在与数据库交互时,并不会因为io操作而切走,所以即使使用多个连接,也无法带来并发能力的提升,反而会增加维护多个连接的消耗)

    查看delete操作的源码,delete操作是在一个事务中执行了pre_delete signal、删除表记录、post_delete signal等操作,执行完成后自动commit或者rollback。

    image.png
    这里的pre_delete signal跟post_delete signal类似于数据库的触发器,不过是在Python代码层面实现的。问题就出在这个 post_delete signal 上面,出错的数据表注册了post_delete signal,并在其中调用了REST接口,而 调用REST接口会导致协程发生切换,如果切换后的协程也操作了数据库,会将现有的事务回滚 。(因为从连接池新拿到的连接,应该保证是没有事务在执行的)

    将post_delete相关逻辑注掉后,问题消失

    解决方法有如下几种: