MailboxProcessor and prioritized messages

I'm trying to create an agent that holds on to Data and does further work with it upon receiving the Work message. Update is sent to the agent several times a minute, may potentially run longer than 10 seconds and signals the need to update Data.

Because update takes a lot of time, I've decided to do it on the background so as not to block the processing of Work messages. One option is to do the updating elsewhere and receive the new version as, say, NewData of Data message. However, I'd like to use new data as soon as possible, skipping the backlog of Work messages (MailboxProcessor.Scan would surely work, but I want to avoid it), so I keep the computation (actually, I had to resort to using a Task, because I could not find a way to check the status of an Async) as part of State and check it on every cycle.

Is there a better way to achieve the same behavior? I appreciate any comments and suggestions.

type Data = unit

type Message =
| Work
| Update

let update data = async {
do! Async.Sleep 5000
return data }

let work data = async {
do! Async.Sleep 10 }

let mb = MailboxProcessor.Start (fun inbox ->
let rec loop state = async {
let state =
match state.Updating with
| Some t when t.IsFaulted ->
{ state with Updating = update state.Data |> Async.StartAsTask |> Some } // retry
| Some t when t.IsCompleted ->
{ Data = t.Result; Updating = None }
| _ -> state

match msg with
| Work ->
work state.Data |> Async.Start
return! loop state
| Update ->
match state.Updating with
| Some _ -> return! loop state // old update still in progress, ignore
| None -> return! loop { state with Updating = update state.Data |> Async.StartAsTask |> Some }
}

loop { Data = (); Updating = None }
)

• Why not have two MailboxProcessors? – Mark Seemann Oct 29 '16 at 5:31
• How would you use the other one? As a repository of Data that also takes care of updates? – nphx Oct 30 '16 at 8:33