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
type State = { Data: Data; Updating: System.Threading.Tasks.Task<Data> option }
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
let! msg = inbox.Receive ()
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 }
)
Data
that also takes care of updates? \$\endgroup\$