-
Notifications
You must be signed in to change notification settings - Fork 193
Support for TPL & async await #33
Comments
Do you mean make the rest of the calls async (e.g. I'd probably make a separate namespace like The major reason I re-wrote the client to use HttpClient is someone wanted DNXCore50 support and WebRequest/WebResponse doesn't exist in .NET Core. I'm very much open to discussion about this, so please let me know what exactly you mean by "async support". |
Also FYI, creating an async client should be trivial. It's literally just copying the normal client interfaces, adding the |
Thanks @highlyunavailable that's great news. To clarify I did mean using the *Async methods on HttpClient, adding the async/await keywords and returning Thanks again. |
Yeah, okay. I already sort of do that: Each other call ( It feels cleaner to me to make a Unless you have any strong objections to this method, the above is what I'm going to do. |
Well, I found one strong objection: it's a huge pain to do it that way. I see why we have 5 billion |
"It probably does make more sense to make an interface to just hang these off of and then implement it on the existing classes." I agree. |
I'm just trying to figure out a good way to do this - the problem is such: https://github.com/PlayFab/consuldotnet/blob/master/Consul/ACL.cs#L336-L360 Note how in that chunk of code, ACL is of type Unless I want to expand the existing interface This is kind of why I wanted a class specifically for async, since I've sort of painted myself into a corner with the interfaces - either I change the interface, or I have to expose this functionality some other way. |
I may have misunderstood the problem, but it doesn't look like that code is making any IO bound calls so it doesn't need an Async version? Also it is best where possible to use the await keyword rather than GetAwaiter().GetResult(). There is a good explanation of why here: http://blog.stephencleary.com/2014/12/a-tour-of-task-part-6-results.html Apologies but I will be AFK from now for the evening. |
Yeah, I'll figure it out. I'll have something ready for you to take a look at to see if it's something you'd like to use by the time you come back. |
Why not go more radical - only expose async APIs, and document how to call them synchronously? |
@sergeybykov because this library was originally created as a sync library, so either I make an Async version or I break compatibility for everyone that was using it before I rewrote it to be all-Async. It's not a bad idea though, and it would let me clean up some of the uglier interfaces, and since old versions are still in nuget, it won't really break people's code unless they want to update their copy of the library to use new Consul functionality. What is the best way to call an async API synchronously anyway? I always hear "don't use |
@highlyunavailable, I see that you need some advice on |
@shayhatsor thanks for the advice. The whole @sergeybykov suggested a way where I provide both sync and async versions (e.g. there will be I'd like to rewrite the whole thing to be async but I know most of the current users consume the Sync APIs. Maybe the right answer is just to say "Hey we changed everything by making it async, if you need sync just add |
I know... it's annoying. the best part of it is that if you forget it even once, your sync users will get a nice deadlock.
you can do that, it's ugly but will work.
Even if you decide to currently provide both sync and async APIs you still must:
|
Another path to consider is add async methods now, mark sync methods as deprecated (now or later), remove sync methods in a few releases. |
I'm going to just make it fully async. The reason is I'm already introducing breaking changes (Client renamed to ConsulClient) in the next release and I'm converting to use the HttpClient classes instead of WebRequest/WebResponse and XUnit from MSTest, so there are already plenty of changes that will cause havoc in other small ways, plus I'm not confident that I won't cause deadlocks with my half-baked use of HttpClient so I may as well do this the right way and just make a clean break between the releases. |
Thanks for your help, everyone. I've got a fully async client here: Nuget packages: Could you take a quick look to see if it's what you need @PaulNorth ? Note: Somethings are still not going to be "truly" async - for example, Locks and Semaphores use If it's what you're looking for then I'll update the readme with big warning signs and roll out a release. |
@highlyunavailable, there are async versions of those:
about awaiting in finally blocks, you should use C# 6. AFAIK this is orthogonal to the .NET version. |
Great work everybody! I will pick this up today and include it in the improvements I am making to the Consul-Orleans Membership Provider. |
@shayhatsor I mean the Consul kind of Lock/Semaphore - the distributed ones that use Consul's consistent state to allow multiple machines to contend for a K/V in Consul, not the C# kind. I noticed that @PaulNorth please note that this isn't released to nuget, I'd just like you to test it and make sure it's what you need and that there are no surprises before I do make a real release. There was plenty of redesign so it's entirely possible that I introduced a new bug somewhere. I'm fairly confident I didn't since all the tests pass, but I'd like to have someone try it at least. |
ok, but you noted:
Your code can still be fully async. as an example I've looked at your lock implementation.
|
Having switched to the new async build the methods we are using all appear to be working just fine. Our usage is by no means an exhaustive test; we are only using KW.Put, KW.Get, KW.CAS, Agent.ServiceRegister, Agent.ServiceDeregister & Catalog.Service. The migration was pretty painless and I can only imagine your existing clients will be grateful to have a purely async interface. Cheers! |
@shayhatsor Okay, fair enough, I can replace much of the Is this saying it's fine if Acquire(), Release() etc are not async and use One final thing I'm struggling with is AutoSemaphore (and AutoLock) - they're a pair of classes that are supposed to let you do Bad practice due to long constructor/disposer calls, I suppose, but it's in there now, and I don't see a good way of doing async acquire/release in the constructor and disposer without @PaulNorth Sounds good - I'll cut a release and any further modifications (e.g. the discussion above) will be new releases. Thanks for putting in this issue! |
ok, i'll explain again, remember I wrote this:
it is weird, but it's what you want. note that you can't await in a normal lock because it is owned by the running thread, which you actually release upon await... so it doesn't really make sense. Instead, you need something that isn't thread related that provides mutual exclusion asynchronously. this is what AsyncLock gives you. Keep in mind that AsyncLock isn't reentant and normal lock is. but I don't see it as a requirement here.
what you do is this:
|
just noticed I haven't explained this:
I'll explain by example. let's say you had your sync class: public class fooAPI
{
int Foo1()
{
//some blocking code goes here
}
} and now you're going async, but wish to also keep the sync methods. you'd do this: public class fooAPI
{
int Foo1()
{
//this is OK because it's a public surface method that only calls the async code.
//it isn't part of any async flow, it's here just to keep the sync interface.
return Foo1Async.GetAwaiter().GetResult();
}
Task<int> Foo1Async()
{
//there's no blocking code here or in any method called from this method.
//every async method is called with await and ConfigureAwait(false)
}
} |
Awesome, the example is what I was thinking and we're both thinking the same thing. Okay, then I'm not going to change anything further this release because that's exactly what I'm doing. Only the Lock/Semaphore stuff is sync, EVERYTHING else (the primitives in KV storage, sessions, etc.) are async. I am not putting any syncs in any async flows. Thanks for all your help and for explaining this, everyone. I'm going to close this issue now by saying that there's a new release that's all Async except for lock/semaphore at https://www.nuget.org/packages/Consul tagged I'll open a new issue for explore converting the distributed locks to fully async. |
👍 |
Great cross project collaboration! 4 different teams, and the async support added to Consul.Net in 2 days! |
I would like to use your library to add support for Consul to Microsoft Orleans (https://github.com/dotnet/orleans) but the lack of async support is a big issue for that project.
I see there is a PR which has switched the client code to use HttpClient, do you intend to build on this and move to supporting async requests?
Thanks for the work so far!
The text was updated successfully, but these errors were encountered: