Text
我有一个 Scotty/WAI 应用程序,其中一个端点发送从元素列表构建的大量输出。以下是相关代码:
import Data.Text.Lazy as L
import Data.Text.Lazy.Encoding as E
class (Show csv) => ToCSV csv where
toCSV :: csv -> L.Text
toCSV = pack . show
instance (ToCSV c) => ToCSV [c] where
toCSV [] = empty
toCSV (c:cs) = toCSV c <> "\n" <> toCSV cs
get "/api/transactions" $ accept "text/csv" $ do
purp <- selectPurpose
txs <- allEntries <$> inWeb (listTransactions purp)
setHeader "Content-Type" "text/csv"
raw $ E.encodeUtf8 $ toCSV txs
据我了解Scotty 的文档,输出应该是懒惰地构建并通过网络发送,而不需要在内存中构建整个文本/字节串。然而,这不是我观察到的行为:当我调用这个端点时,服务器开始消耗内存,我推断它正在构建整个字符串,然后一次性发送它。
我错过了什么吗?
编辑 1:
我编写了一个doStream
函数,该函数应该一个接一个地发送生成的 BS 块:
doStream :: Text -> W.StreamingBody
doStream t build flush = do
let bs = E.encodeUtf8 t
mapM_ (\ chunk -> build (B.fromByteString chunk)) (BS.toChunks bs)
flush
但实际上它仍然在内存中构建整个输出......
编辑 2:
实际上,以这种方式流式传输效果很好。但是,服务器进程仍然会占用大量内存,这实际上可能在发送每个块时都可以进行垃圾回收。我将尝试更深入地分析内存使用情况,以了解这种消耗来自何处。
编辑 3:
我试图将堆限制为 2GB,但这会使进程崩溃。在整个转换过程中保留了一些记忆......