team-rf
team-rf

Reputation: 241

Mutable application context

I'm building a REST backend that requires a large collection of values to compute the answers. This collection is downloaded at boot time and then on demand but needs to be updated a few times per day. I can't figure out how to cope with this mutable state. I've tried both ref and mutable variables in the main function but the old collection never gets released and memory usage grows without control.

What is the correct way of dealing with this mutable state?

EDIT

While preparing a sample of what I'm doing I found the first bug. I was doing:

let sqlfeed() = use cmd = new SqlFeedType(connectionString) Some (cmd.Execute() |> Seq.toList)

Now I'm doing I'm doing something like this:

let sqlfeed() = Some (using (new SqlFeedType(connectionString)) (fun cmd -> cmd.Execute() |> Seq.toList))

Then

[<EntryPoint>]
let main argv =
    let optionSqlFeed = ref None
    let app =
        choose [
            Filters.path "/sqlfeed" >=> warbler (fun ctx -> 
                match !optionSqlFeed with
                | None ->
                    optionSqlFeed := sqlfeed()
                | Some a ->
                    optionSqlFeed := None
                Successful.OK "done" ) 
         ]
    startWebServer defaultConfig app

Before, when I called sqlfeed and the data was downloaded I saw the memory go up. On successive calls with alternating assignments to None, memory wouldn't be released and the total usage just climbed. Now, when I call sqlfeed and assign None to the mutable variable, the memory still does not get released, but upon the next call to sqlfeed the memory is released.

Why won't the memory be released when I assign None, but will be released when I call sqlfeed again?

Upvotes: 2

Views: 135

Answers (1)

Maslow
Maslow

Reputation: 18746

The GC is not deterministic with regard to when it runs. When you call sqlfeed() again, it exerts memory pressure on the app, I'd bet this is sometimes triggering a garbage collection, whereas setting a variable to no value doesn't take extra memory.

The amount of physical memory present influences when garbage collection happens, and it is lazy if possible - CLR Garbage Collector frequency and system memory available.

You can trigger GC on your own when you set the variable to None but that may not be a good idea - When is it acceptable to call GC.Collect?

from: Understanding garbage collection in .NET

GC.Collect() will only run the tracing portion of garbage collection and add items to finalizer queue but not call finalizers for the types, that is handled by another thread.

If you'd like the chance of the current value being released as needed instead of being held indefinitely use a WeakReference Does WeakReference make a good cache?

Upvotes: 0

Related Questions