Scotty/WAI アプリケーションがあり、エンドポイントの 1 つが、Text
要素のリストから構築された大量の出力を送信します。関連するコードは次のとおりです。
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のチャンクを1つずつ送信することになっている関数を作成しました。
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 に制限しようとしましたが、これによりプロセスがクラッシュします。変換プロセス全体で一部のメモリが保持されます...