Granite ORM结合Fiber操作MySQL遇Unexpected 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

