DDL_PARSER_TIMEOUT_FIX.md 5.0 KB

DDL Parser 超时问题修复说明

问题描述

在调用 /api/resource/ddl/parse 接口时出现超时错误:

message: "API请求失败: HTTPSConnectionPool(host='dashscope.aliyuncs.com', port=443): Read timed out. (read timeout=30)"

问题原因

  1. 超时时间过短:原始代码使用固定的 30 秒超时时间,对于复杂的 DDL 解析任务可能不够
  2. 无重试机制:网络波动或 API 临时不可用时,请求直接失败,没有自动重试
  3. 错误处理不够健壮:没有区分超时错误和其他类型的错误

解决方案

1. 增加超时时间

将默认超时时间从 30 秒增加到 60 秒,并支持自定义配置:

def __init__(self, api_key=None, timeout=60, max_retries=3):
    self.timeout = timeout  # 默认60秒
    self.max_retries = max_retries  # 默认重试3次

2. 实现自动重试机制

新增 _make_llm_request 方法,支持:

  • 指数退避策略:重试等待时间逐渐增加(2秒、4秒、8秒)
  • 区分错误类型
    • requests.Timeout:超时错误,可重试
    • requests.RequestException:网络错误,可重试
    • 其他异常:不重试
  • 详细日志记录:记录每次尝试的状态
def _make_llm_request(self, payload, operation_name="LLM请求"):
    """发送LLM请求,支持自动重试"""
    for attempt in range(self.max_retries):
        try:
            if attempt > 0:
                wait_time = 2 ** attempt  # 指数退避
                time.sleep(wait_time)
            
            response = requests.post(
                f"{self.base_url}/chat/completions",
                headers=self.headers,
                json=payload,
                timeout=self.timeout
            )
            response.raise_for_status()
            return response.json()
            
        except requests.Timeout as e:
            logger.warning(f"{operation_name} 超时: {str(e)}")
        except requests.RequestException as e:
            logger.warning(f"{operation_name} 失败: {str(e)}")

3. 统一错误处理

所有 LLM 调用方法(parse_ddlparse_db_conn_strvalid_db_conn_str)都使用统一的重试机制:

def parse_ddl(self, sql_content):
    result = self._make_llm_request(payload, "DDL解析")
    
    if not result:
        return {
            "code": 500,
            "message": f"API请求失败: 在{self.max_retries}次尝试后仍然失败"
        }
    # ... 处理成功结果

改进效果

1. 可靠性提升

  • ✅ 自动重试:网络波动时自动重试,成功率显著提高
  • ✅ 超时容忍:更长的超时时间适应复杂查询
  • ✅ 指数退避:避免对 API 造成压力

2. 可观测性提升

  • ✅ 详细日志:记录每次尝试的状态和结果
  • ✅ 操作区分:不同操作有明确的名称标识
  • ✅ 错误追踪:清晰记录失败原因

3. 可配置性提升

  • ✅ 自定义超时:可根据需要调整超时时间
  • ✅ 自定义重试:可根据网络环境调整重试次数
  • ✅ 向后兼容:默认参数保证现有代码无需修改

使用示例

默认配置(推荐)

from app.core.llm.ddl_parser import DDLParser

# 使用默认配置:60秒超时,最多重试3次
parser = DDLParser()
result = parser.parse_ddl(sql_content)

自定义配置

# 针对复杂任务:120秒超时,最多重试5次
parser = DDLParser(timeout=120, max_retries=5)
result = parser.parse_ddl(complex_sql_content)

# 快速失败模式:30秒超时,不重试
parser = DDLParser(timeout=30, max_retries=1)
result = parser.parse_ddl(simple_sql_content)

测试验证

运行测试脚本验证修复效果:

# 快速测试
python quick_test_ddl.py

# 完整测试(包含超时处理验证)
python test_ddl_timeout_fix.py

相关文件

修改的文件

  • app/core/llm/ddl_parser.py:添加超时和重试机制

新增的文件

  • test_ddl_timeout_fix.py:超时和重试测试脚本
  • DDL_PARSER_TIMEOUT_FIX.md:本文档

注意事项

  1. API 配额:重试机制会增加 API 调用次数,注意监控配额使用
  2. 响应时间:最坏情况下(3次重试都超时),总耗时可能达到 60s × 3 + 2s + 4s = 186秒
  3. 日志监控:建议监控日志中的重试频率,如果重试过于频繁,可能需要检查网络或 API 服务状态

未来优化建议

  1. 可配置的退避策略:支持线性退避、固定间隔等多种策略
  2. 断路器模式:当 API 持续失败时,快速失败避免长时间等待
  3. 缓存机制:对相同的 DDL 语句缓存解析结果,减少 API 调用
  4. 异步处理:对于大批量 DDL 解析,考虑使用异步任务队列

总结

通过增加超时时间、实现自动重试机制和改进错误处理,DDL Parser 的稳定性和可靠性得到显著提升。现在即使在网络不稳定的情况下,也能更好地完成 DDL 解析任务。