You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Granite ORM结合Fiber操作MySQL遇Unexpected EOF异常求助

Granite + Fiber 使用问题:MySQL连接EOF异常解决思路

首先直接给结论:Granite是支持与Fiber配合使用的,你遇到的异常本质是MySQL数据库连接在Fiber环境下的复用冲突问题,而非Granite本身不兼容Fiber。

异常原因分析

从你提供的栈追踪来看,Unexpected EOF发生在MySQL连接读取数据包时,这通常是因为多个Fiber共享了同一个数据库连接导致的。Crystal的DB连接池默认是基于操作系统线程设计的,而Fiber是用户态轻量级线程,旧版本的MySQL驱动或不正确的连接池配置会导致连接被多个Fiber抢占,进而出现连接中断、EOF错误。

具体解决步骤

  • 升级MySQL驱动版本
    确保你使用的mysql.cr驱动是较新版本(建议>=0.17.0),旧版本对Fiber的兼容性较差,新版本已经针对Fiber做了线程安全/Fiber安全的优化。

  • 配置Fiber安全的数据库连接池
    在Granite的数据库配置文件(比如config/database.yml)中,显式开启fiber_safe选项,并调整连接池大小适配你的Fiber数量:

    development:
      adapter: mysql
      host: localhost
      database: your_db_name
      user: your_user
      password: your_password
      max_pool: 200  # 匹配你要启动的Fiber数量
      max_idle: 50
      fiber_safe: true  # 关键:开启Fiber安全模式
    
  • 修正代码中的语法与逻辑问题
    你的代码存在语法错误,同时要确保每个Fiber独立处理数据库操作,避免变量传递错误:

    lines = File.read_lines(INVENTORY_FILE)
    channel = Channel(Int32).new(199)
    row_number = 0
    
    lines[0..200].each do |line|
      row_number += 1
      # 修正Proc的参数定义,确保类型匹配
      process_line = ->(item_number : String) do
        spawn do
          # 每个Fiber独立执行查询,Granite会自动从安全的连接池获取连接
          sku = Sku.find_by :item_number, item_number
          channel.send(1)
        end
      end
      # 假设line本身是item_number,若不是请自行解析(比如split取对应字段)
      process_line.call(line.strip)
    end
    
    199.times { channel.receive }
    
  • 避免连接泄漏
    不要手动持有数据库连接实例并在Fiber之间传递,Granite的查询方法(如find_by)会自动从连接池获取并释放连接,确保每个操作完成后连接归还给池。

额外验证

如果调整后仍有问题,可以先减少Fiber数量(比如从199降到10)测试,确认是连接池配置问题还是代码逻辑问题;同时检查MySQL的wait_timeout参数,避免连接被MySQL主动断开。

内容的提问来源于stack exchange,提问作者VRana

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:21:58