Hedis是Haskelk目前最完善的redis绑定,而且有个独特的特性:自动流水线,号称是最优的。
conn <- connect defaultConnectInfo
runRedis conn $ do
foo <- get "foo"
bar <- get "bar"
liftIO $ print (foo,bar)
在这个简单的例子里,据作者说这两个get命令被pipeline,完全不需用户干预。我用Wireshark查看发现两个get请求会在一个连接上分两次发送,没有达到号称的最优。更严重的问题是自动流水线是通过Lazy IO实现的,而且跨过多个runRedis块,然后问题就来了:
conn <- connect defaultConnectInfo
let fast = runRedis conn (get "nextid")
blocked = runRedis conn (blpop ["queue"] 10)
forkIO $ blocked >>= print
threadDelay (10^6)
forkIO $ fast >>= print
我们分别在两个线程里执行runRedis,理想情况下blpop会阻塞一下,而get会马上返回。但是Hedis会把这两个请求放在同一个连接上分两次发送,结果get必须等blpop返回之后才能返回。
如果你没有在runRedis里面对结果求值,请求就可能和其他runRedis里的请求共用一个连接。
我的临时应对方案就是在runRedis的最后对结果进行求值,强制流水线结束,防止对后续请求产生影响(实际上会让后续请求使用连接池里其他空闲连接):
runRedis' conn cmd = runRedis conn $ do
result <- cmd
result `seq` return result
也就是说,有大量blpop这些可能阻塞的请求时,就要用很大的连接池来防止非阻塞请求被阻塞。
Lazy IO真是坑,Hedis的自动流水线反而让这库变得难用了。