[Haskell-cafe] Space leak in WAI 3.0 application

Thomas Koster tkoster at gmail.com
Thu Nov 20 04:40:06 UTC 2014


On Tue, Nov 18, 2014 at 4:59 PM, Thomas Koster <tkoster at gmail.com> wrote:
> -- | This version has a space leak.
> zerosAppA :: Application
> zerosAppA _req respond =
>   withZeros 100000000 $ \ largeLBS ->
>     respond $ responseStream status200 [] $ \ write _flush ->
>       write $ fromLazyByteString largeLBS
>
> -- | This version does not have a space leak.
> zerosAppB :: Application
> zerosAppB _req respond =
>  respond $ responseStream status200 [] $ \ write _flush ->
>    withZeros 100000000 $ \ largeLBS ->
>      write $ fromLazyByteString largeLBS

On 20 November 2014 05:30, Gregory Collins <greg at gregorycollins.net> wrote:
>> Why does version A not process the LBS in constant space?
>
> The lazy bytestring is let-lifted out of the function so that subsequent
> calls reuse the same heap value.
>
>> What in version A is preventing the GC from collecting the LBS chunks
>> after they have been fed to Warp?
>
> The value is re-used (and the closure holds a reference) so the GC can't
> collect it.
>
>> What is it about version B that permits the LBS chunks to be collected?
>
> The allocation is performed underneath the lambda in version B and so you
> get a fresh copy every time.

Thanks for your reply.

My coding style makes heavy use of let to keep lines succinct so am I
going to have to be more careful about the order of lets in case I
accidentally introduce unwanted sharing?

I was able to re-arrange the leaky version A into version B in my
example, but I may not be able to do this in my real application. What
if I need the first few bytes of "largeLBS" to determine the response
status code or headers? This is only possible in version A.

Or will replacing the lazy bytestring with a streaming abstraction
like pipes or conduit make the problem go away in both versions?

Thanks,
--
Thomas Koster


More information about the Haskell-Cafe mailing list