访问Django模型时PDF库出现非确定性异常,如何定位原因?
问题排查:PDF处理偶发损坏与数据库访问的关联
基于Django 4.2和Python 3.10的应用,在本该无状态的处理环节偶发异常:当使用pypdf 3.17.4处理PDF时,如果同时通过psycopg 3.1.18访问PostgreSQL 14数据库,输出的PDF文档会损坏,大约每8次尝试就会出现1次。损坏的PDF存在内容缺失,因此可以通过对比输出文件大小来判断是否触发了问题。
复现代码
# 调用方式: python3 manage.py minrepro input.pdf import argparse, io from django.core.management.base import BaseCommand from pypdf import PdfReader, PdfWriter from djangoapp.models import DjangoModel def main(fin): pdfout = PdfWriter() pageout = pdfout.add_blank_page(width=200, height=200) for i in range(8): # 注意:PDF合并过程中访问数据库是关键! # 注释掉下面这行就无法复现问题 for c in range(31): a = DjangoModel.objects.first() fin.seek(0) for pagein in PdfReader(fin, strict=True).pages: pageout.merge_page(pagein) with io.BytesIO() as fout: pdfout.write(fout) return fout.tell() class Command(BaseCommand): def add_arguments(self, parser): parser.add_argument(dest="pdf", type=argparse.FileType("rb") ) def handle(self, *args, **options): for i in range(30): if i == 0: first_size = main(options["pdf"]) current_size = main(options["pdf"]) if not first_size == current_size: print(f"无状态调用出现异常 {i=}, {first_size=} != {current_size=}")
初步排查情况
- 最初猜测pypdf未能为合并后的重复标识符生成真正唯一的名称,数据库访问只是让问题显现的触发条件,但在相关库中未找到time或random模块的导入,用
time.sleep(0.02)替代数据库访问也无法复现问题,只有数据库访问能触发异常; - 使用的是支持插入顺序字典的CPython,相同输入下代码逻辑应完全一致,开启
-W module::ResourceWarning等警告也未发现相关问题; - 已在单线程的Django管理命令中复现该问题,因此可以排除HTTP/Web服务器的影响。
内容的提问来源于stack exchange,提问作者user
相关产品推荐
相关产品推荐

