Использование памяти Haskell Snap для больших тел ответов HTTP
Я пытаюсь доставить большой HTTP-ответ, используя платформу Haskell Snap, но использование памяти растет пропорционально размеру ответа. Вот несколько урезанных тестовых случаев, которые используют большой ленивый ByteString:
import Snap.Core (Snap, writeLBS, readRequestBody)
import Snap.Http.Server (quickHttpServe)
import Control.Monad.IO.Class (MonadIO(liftIO))
import qualified Data.ByteString.Lazy.Char8 as LBS (ByteString, length, replicate)
main :: IO ()
main = quickHttpServe $ site test1 where
test1, test2 :: LBS.ByteString -> Snap ()
-- Send ss to client
test1 = writeLBS
-- Print ss to stdout upon receiving request
test2 = liftIO . print
site write = do
body <- readRequestBody 1000
-- Making ss dependant on the request stops GHC from keeping a
-- reference to ss as pointed out by Reid Barton.
let bodyLength = fromIntegral $ LBS.length body
write $ ss bodyLength
ss c = LBS.replicate (1000000000000 * (c + 1)) 'S'
- test1 доставляет клиенту ss. Использование памяти растет пропорционально размеру ByteString.
- test2 печатает ss на стандартный вывод в стеке монад Snap при получении запроса. Это выполняется в небольшом постоянном объеме памяти.
Ответы доставляются с использованием чанкованного кодирования, поэтому я подумал, что Snap также сможет доставлять ответ в постоянной памяти. Есть ли способ добиться этого? Также стоит отметить, что ответ начинает доставляться немедленно. Я пытался использовать transformRequestBody, но столкнулся с той же проблемой.
Память измерялась с помощью "top" в linux, и наблюдалось постоянное увеличение до 15 ГБ резидента, и в этот момент она только начала меняться. Запрос выполнен, поэтому использование памяти на пару порядков меньше размера ByteString. Как только запрос был выполнен, он сел на 15Gb резидента. Когда я отправил еще один запрос, он все еще оставался стабильным на 15Gb и завершил запрос, как и раньше. Виртуальный размер остался в пределах 5% от размера резидента.
Выполнение 2 одновременных запросов привело к падению виртуальной и резидентной памяти примерно до 5 ГБ, а затем к увеличению примерно до 17 ГБ, после чего машина стала непригодной для использования, поэтому я остановил процесс.
GHC версия 7.8.3
Snap версия 0.14.0.5