Implementing a Service that caches requests and then executes on an interval.
Any examples I could look at for something like this?
I have some objects that when they are created need to refresh some data from a service endpoint that has strict requests per minute. However I can batch a set of objects and request data for many of them only using one request.
If I have a service
bool IUpdateService.IsObjectRegistered(string objectId)
and I wanted to
await concreteService.IsObjectRegistered("12345")
but have that service queue all requests, waiting 5 seconds, batching those requests to the service endpoint, parsing the return data, and then returning the relevant data to the thread, how would I do that?
I have something working right now but it fails some of the time.
My current implementation is using TimedQueue and a ManualResetEvent.
The TimedQueue enqueues objects and then every 5 seconds raises an event to batch the service request and populate a variable. Meanwhile the consumer is waiting with manualResetEvent.WaitOne(), which is set and then immediately reset after the batched service request is completed.
Anyway, any examples of something like this I could look at?7 Replies
wouldn't it be more useful trying solve this problem? what is exactly failing, and why are you using a ManualResetEvent?
I think the approach is poor and am looking for an existing pattern to solve this I guess.
I'm using ManualResetEvent so once the objects are added to the queue, their background threads are blocked until the TimedQueue fires the batched request and then notifies the threads they may continue with ManualResetEvent.Set.
kind of sounds like a job that channels and background services would work well for
assuming your app uses the generic .net host
your producer code has a reference to a ChannelWriter<Foobar> which it uses to push items onto the channel asynchronously
your background service has a reference to a ChannelReader<Foobar>
and it does a Task.Delay(Timespan.FromSeconds(5)) in a while loop to, every five seconds, read everything from the channel
Stephen Toub - MSFT
.NET Blog
An Introduction to System.Threading.Channels - .NET Blog
“Producer/consumer” problems are everywhere, in all facets of our lives. A line cook at a fast food restaurant, slicing tomatoes that are handed off to another cook to assemble a burger, which is handed off to a register worker to fulfill your order, which you happily gobble down. Postal drivers delivering mail all along their […]
Thanks! I'll take a look.
@Becquerel that was SOOO useful and it's working brilliantly!
I ended up creating a couple request objects that had metadata required to send to the endpoint, but also included a channel w/ the requested response type.
the service requesters write to the request channel and then await readasync from their provided channels.
Once the delay task finishes on the producer side, I lock the request channel writer so we wait for any more requests to come in until we're finished with this bunch. Perform the request to the end point, and then iterate through the request objects and write back to their provided response channels. Not sure if that approach is the best way to broadcast back to the original requesters but it worked a charm, exactly how I hoped 😄 😄 😄 .
:eyy:
very nice
i've never used that strategy of bundling a channel with the request object, but if it works, it works
my intuition instead would be to have a ChannelWriter<ActionCompleted> in your consumer code, where ActionCompleted is some empty marker class which just contains the ID of whatever record got processed - that kind of thing
and then the original producer code can listen to that channel if it cares to
also, i mentioned Task.Delay because i didn't want to overload you with new stuff
but now you have that working look into PeriodicTimer
it is slightly better and cleaner imo
while (await timer.WaitForNextTickAsync()) { ... }
@Becquerel perfect! Thanks! I was just getting into looking at cleaning that up and implementing IDisposable to kill those background threads with cancellation tokens.
Bummer, this project is in .NET 4.8, doesn't look like PeriodicTimer is available.