Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
Red Mike
Jul 11, 2011
Instance count won't be correct unless you: never remove instances, maybe have it be an IDisposable and use Dispose to remove the instance, need the instance count relating to the garbage collector (this is not what the majority of people will be expecting).

If you actually need a GC-wise count, then you should know enough about how GC works to be comfortable setting it up with a finaliser without help.

Adbot
ADBOT LOVES YOU

Red Mike
Jul 11, 2011
So I've run into a bit of an issue. I've got an ASP.NET Web API that's using HttpClient to request some data from another API, then returns it as a response. It's been working well for quite a while. However, earlier today, this third party API had some issues and the responses took longer than the timeout to reply. This wouldn't normally be an issue, however instead of raising a sane WebException, the timeout was raising a TaskCancelledException. I've found lots of info online about how it does that (apparently by design?) and about how it doesn't or didn't play nice with async/await, so that if you're awaiting the response from the response stream and it's in a using block, then it'll actually go out of scope before the stream get is done so the task gets cancelled somehow.

Now, if it were just that it'd be fine. However the behaviour I'm seeing is that a single request timing out is making all the pending requests using that HttpClient instance fail. In an ASP.NET app that's serving thousands of requests per second. While serving more than one request type with the same HttpClient. The majority of the random task cancellations were mostly showing up in entirely unrelated request handlers. By which I mean, it was showing up in a request handler that's calling an entirely different API and in a request handler that's calling a part of the same API that wasn't having timeouts. This was hell to diagnose as you can imagine.

What am I actually supposed to do?

1. Use one HttpClient instance per API?
2. Use one HttpClient instance per request type?
3. Use one HttpClient instance per request (with or without a using block?)
4. Don't use HttpClient? If so, any recommendations which aren't WebClient or basic HttpWebRequest?

One of our other services is actually using number 3 on the list due to different people implementing it, and it's had no timeout-related issues, but it has been the first server to actually run into socket/resource exhaustion against expectations, so I'm assuming 3 is a really bad idea.

e: nothing against WebClient, but we've had a number of issues with it on a different system so it's not my first choice.

Red Mike
Jul 11, 2011

ljw1004 posted:

Could you go into more detail about that? TaskCancelledException (and the related OperationCancelledException) are fine in general, and they're what I expect to see if a CPU-busy-loop is listening to a CancellationToken, and on their own they do play fine with async/await and always have.

But if a web-related operation is ending in this way then it suggests to me the third party has implemented cancellation in a weird way. At best, I can imagine the third party API is leaving the web request open (i.e. consuming resources) even after it should have cancelled. At worst, well I have no idea what it's doing.

This isn't a CPU-busy-loop, this is the timeout being reached cancelling the continuation task instead of throwing a TimeoutException or WhateverException. Reference: https://stackoverflow.com/questions/10547895/how-can-i-tell-when-httpclient-has-timed-out along with a large number of other threads you can find with 'httpclient timeout exception'. They're all consistent with what I'm seeing.

ljw1004 posted:

Please go into more detail about that, hopefully with a code snippet. I don't think what you're describing is even possible :(

Reference: https://stackoverflow.com/questions/33095737/httpclient-in-using-statement-causes-task-cancelled

This isn't something I've run into myself, it's just something I found while searching for my own problem.


ljw1004 posted:

I'd love to see a code sample. The general advice from the team behind HttpClient is to reuse the heck out of it since that's more efficient -- go ahead make a singleton global instance. And the only time you can't do that is when you're doing the per-instance customization of it (I forget which customizations are per instance). I wonder if the third party API is doing such customizations under your feed?

This third party API is a web API. I'm literally just doing web requests to some random URLs, and there's no effective difference between them besides that one may be timing out while others might not be.

I don't have a code sample at the moment, but it boils down to: set up an HttpClient instance. Concurrently (or constantly) fire off a large number of requests (enough to make a lot of them be queued up instead of instantly fired). Make sure one of them in the middle times out (by making it fire off to your own server that will ensure a timeout) while the rest should succeed within the timeout. When that timeout is hit, all pending requests at the time fail with a thrown TaskCancellationException instead of just the single request you ensured would time out. I'll write up a code sample tomorrow when I have more time.

But what I said above is exactly what was happening to me:

95% of requests from the server were going to URL http://this.works.fine/api/request
5% of requests from the server were going to URL http://this.is.timing.out/api/request

My error logs were absolutely full of apparent timeouts to 'this.works.fine/api/request' (once I figured out that 'task was canceled' meant a timeout), so I immediately began running the request myself, and it wasn't timing out at all. I eventually filtered out the URLs that were actually working, and was left with a single URL that I was timing out to from all available PCs. The company in charge of 'this.is.timing.out' then sent out a message saying they were having technical issues, which is different from the company in charge of 'this.works.fine'.

Red Mike
Jul 11, 2011

ljw1004 posted:

If you simultaneously fire off a large enough number of requests that they have to queue up, and one of them times out, then BY DEFINITION all the ones after it in the queue will also time out. (assuming that timeout is measured from the timepoint that the request was fired off until the timepoint that the request was delivered).

Are you sure you're observing that all pending requests are failing? (i.e. including the requests that were fired off prior to the one that timed out?)

Or are you merely observing that all requests issued after the timeout one and hence pending are failing?

Uh, what?

I am assuming that the timeout is from when the request has actually started sending. This means a request failing says absolutely nothing about future requests from the same instance. Timeout really shouldn't be from when the request has been queued, in which case what you're saying would be true.

Red Mike
Jul 11, 2011
I'm not an MS person, but most of those read like they were written by terrible coders who couldn't understand a system, refused to follow a team's guidelines (however silly they might be), or refused to learn how stuff worked. It doesn't take a Ph.D to figure out what ASP.NET needs to run and how to get it to run. I'm sure some part of those are true or have some kernel of truth to them, though. Technical debt, skipping full QA coverage for certain features in favour of limited opt-in deployment, developers not knowing how to work with databases are all par for the course for software development no matter where you work.

Red Mike
Jul 11, 2011
In case I wasn't clear with my reply, what I mean is that most of those complaints don't reflect badly on the company in my opinion. Management is hard: bad choices will be made, technical debt will happen, documentation will be lacking. If you're complaining about these things as a coder and are saying you can't do your job because of it, then either: it's bad beyond belief (which I don't think is the case from what I've heard) or you're not experienced enough yet. If you are experienced enough to work through them, and you're still complaining, I'd recommend becoming a manager (however bad you are at management) and trying to put a stop to it yourself.

As a coder, they're all things you work around as best as you can or bring to the attention of managers when it gets too much to work around. If they won't do anything about it, then you do the job anyway and contribute to the technical debt, bad documentation, and bad choices in doing so.
As a manager, they're all things you try and minimise to the best of your ability. When you're blocked by other managers, by tasks, by deadlines, etc, you do the job anyway and let it sit there and ferment while trying to keep the project on-schedule, and bad decisions will make that more and more impossible as time goes on.

e: Also what the previous poster said. Worse, I just mentally substitute 'refactor an entire codebase' with 'introduce a million bugs'. That poo poo should be done in discrete parts that are launched independently so you can at least find the bugs gradually and without interactions that might make it harder to debug.


In .NET news, I've got a project at work that's using the latest pre-Core ASP.NET, and I've been asked to consider switching to ASP.NET Core in order to switch our hosting from Windows to Linux (EC2 Windows support is woefully bad), how bad of an idea is this considering we depend on a fairly large number of libraries like Entity Framework, various NoSQL libraries, etc? And more importantly, how much has the architecture changes and are all the other developers have to learn an entirely new set of gotchas to work with it successfully?

Red Mike
Jul 11, 2011

B-Nasty posted:

Bad, at least how you described it. It sounds like you're trying to develop your way out of a cost/infrastructure challenge, and underestimating how much time and frustration that will involve for your dev team. Unless your app is fairly trivial, of course.

At this point in time, I don't see a ton of compelling reasons to switch a large, working ASP.NET 4.X application to Core, but I would consider anything green-field to be work taking a look.

Well nothing's final right now, but the original reasoning was 'this is obviously just a new version of ASP.NET'. I..may have failed explaining to them how that's not true, so I was asked to investigate anyway to see if there's any major roadblocks, since running on Linux would be a huge benefit for us (for reasons I can't get into). We've already got a couple cons on the board, which are the huge amount of time we'd have to allocate to switch, and the doubling of work for developers during the transition.

The app is definitely not trivial, so yeah this would be at least a couple months just to get the basic functionality working again and devs even half-competent, even ignoring library issues.


Drastic Actions posted:

Have you tried running it on Mono? ASP.NET compatibility with Mono can be iffy but it might be worth trying that to see if what you have right now just works on Linux, rather than rewriting it at all.

ASP.NET Core is a not a simple upgrade from 4.6. It's going to probably require a bit of work to move it over, depending on how complex it is. If you want to run it on .NET Core, that would mean having to upgrade to EF Core (I think EF 6 can be run on ASP.NET Core, but only if you run with the full .NET Framework or Mono), and that would require a bit of refactoring too, and it's also not exactly production safe just yet IMO.

We had a test of running it on Mono, and just constantly ran into issues. The amount of requests a single instance could handle dropped down to less than half, and it was quite unstable after running for a while. The memory usage also shot up a lot, which is down to problems we have in the code (and problems in Entity Framework and MySQL Connector) but that shoots up on our current version anyway. :v: In the end, running on Mono basically meant we needed over twice the number of instances, which ended up being more expensive, and it was more unstable, which meant nearly three times the number of instances for better failover.

Having to switch to a different EF version pretty much blocks a transition, since we're already planning to move off of EF to a less popular library which is probably going to support Core even less. Ah well, I just need to compile all of the reasons into a handy list so I can try and convince everyone that it's a bad idea now. Thanks.

Red Mike
Jul 11, 2011

ljw1004 posted:

Red Mike, my team at Microsoft is the one dealing with adoption and migration of .NET and .NET Core. If you'd like to run over your handy list with us, or discuss in more depth, send me an email at lwischik@microsoft.com. Same goes for anyone else of course.

Thanks! I'll be sure to run it by you once I've got time to actually finish it. The way it's looking, it seems to be a pretty definite no-go for the near future even ignoring library issues, but some discussion with people who properly know the system would definitely help.

Slightly relating to this, I've once again had the fun chance to diagnose bugs with Entity Framework interactions with Mysql/Connector. That's now one definite bug and a potential bug we've had to investigate down to source code for both libraries, each causing us to have twice the amount of code needed everywhere in order to make a server actually be remotely stable. :v:

Red Mike
Jul 11, 2011

GameCube posted:

Looking at the code you suggested made me realize one more thing I don't understand yet - what actually happens when you call an async function without awaiting it? Does it get executed on a background thread? I'm coming from very multithreaded C++ projects where concurrency was a nightmare and I'm just now realizing that I might not have to worry about it at all now.

I may be misunderstanding this, however this is how I understand all this to work:

When you call an async function, a new task gets created (as if you'd called Task.Run, or similar). If the async function is an async void, then that's that, the task will run and any exceptions are lost to the ether (unless you're running a WinForms projects). If the async function is an async Task or async Task<T>, then you get a reference to the created task, which has already been started. This doesn't guarantee that it's already running, just that it's been told to start, which ties in to the next bit.

You have available a thread pool on which your code is running. This may vary depending on your project and your settings (an ASP.NET project has different behaviour to a console project, etc) but the general idea is that you have a pool of a lot of threads. You don't tend to personally tell a task where the task can run, except for one single case where you force the task to run on your current thread and nowhere else (I believe this is also default behaviour in async/await in a WinForms project?). The thread it chooses can be literally any thread that is currently available. In normal circumstances, this will be on a different thread from the caller I believe.

Now, when you don't actually await a task (an async Task that you store the reference to, but never actually await), the task will try and find any available thread to run on. Depending on the specific situation, if you do end up yielding soon enough after the task start (which includes if the calling function awaits something else, or if you return out of the function and that yields) then there's a chance the task will run on the same thread that it was created from. Mostly, you can treat it as if it's another thread entirely. I'm fairly certain you don't have to worry about garbage collection cleaning up the task you just started because it went out of scope, because the scheduler holds a reference to the task itself, so you can be sure it'll run, just not when it'll actually run, when it'll actually finish, or on what thread it'll run.

Something to make clear however, since there seems like there might be a misunderstanding, a normal (no configuration) await will yield, but won't, or rather won't necessarily, run the task on the thread that's yielded.

e: vvv nevermind, looks like I'm mostly wrong :v:

Red Mike fucked around with this message at 18:51 on Jul 22, 2016

Red Mike
Jul 11, 2011

ljw1004 posted:


When you're writing library code which might be called from any synchronization context, you have to watch out for this. When you're just writing a UI or ASP.NET app, you can really take full advantage of the single-threaded timesharing nature where it only ever yields at "await" points. That liberates you to no longer have to use thread-safe datastructures :)

This entire post was illuminating, and I have a question about this bit. I was under the impression that ASP.NET doesn't work in that way and that independent requests started running concurrently on independent threads, so thread-safe data structures would still be needed. Is this the 'slightly fudging the truth' bit? If so, can you go into a bit of detail on this? A lot of my work is on ASP.NET apps, and we take the multi-threaded nature of it as one of the first principles we use to design our code.

Red Mike
Jul 11, 2011
I see, so it would be single-threaded (sort of) in the context of a single request, which isn't unexpected. I'm..fairly certain that's not exactly true, but it shouldn't come up as a problem too often. However this is basically irrelevant at the point at which you start using anything like a database context though, since that's generally shared across multiple requests (and thus multiple threads).

But yeah, that's mostly in line with what I thought, except for maybe individual requests behaving mostly like UI behaviour.

Red Mike
Jul 11, 2011

chippy posted:

I miss working with Dapper actually. Unfortunately the project I'm working on currently already used EF so I had to roll with it.

OK, another async question for me.

Why can't I do this?
[/code]

On the bolded call, I get the compiler error "Since this is an async method, the return expression must be of type 'Foo' rather than Task<Foo>'. I have to add an await to the return to make it 'return await DoARelatedThing(aBar, baz)'. I don't understand why. Both methods return the same type, so why can't DoAThing just return the result of DoARelatedThing?

When you mark a function as async, you are essentially telling the function that your return type is X, when your defined return type is Task<X>. The correct way to do what you want is this:

code:
public Task<Foo> DoAThing(Bar aBar)
{
	// some async work with awaits and stuff that makes a Baz called baz eventually

	[b]return DoARelatedThing(aBar, baz);[/b]
}

public async Task<Foo> DoARelatedThing(Bar aBar, Baz aBaz)
{
	// some async work with awaits and stuff that creates a Foo called foo eventually

	return foo;
}
This will do what you expect, however you will be unable to use awaits in DoAThing. It does however mean that DoAThing is an async wrapper of an async method, that doesn't have to await at any point, and the compiler won't complain about it running synchronously since it does.

This doesn't strictly answer your question since I don't know exactly why you can't just do this, but it took me a while to realise you can combine async/non-async like this and potentially you might not have realised either.

Red Mike
Jul 11, 2011
It's not made anywhere near clear enough that HttpClient is meant to be shared across as many of your requests as you can because creating and disposing it constantly is expensive.

It gets better, because it queues internally when too many requests are being made. It gets even better, because the timeout on the request includes the time spent in the queue. It gets even better because by default a timeout will not only time out the currently running requests, but also all the queued ones (unless you provide a CancellationToken yourself). And let's not mention that the timeout shows up as a TaskCanceledException.

Red Mike
Jul 11, 2011

Mr Shiny Pants posted:

So we are back to using plain old webrequests? There was someone in this thread who also had a weird problem with HttpClient killing all the outstanding requests on an Exception.

Fairly sure that was me.

Using HttpClient is fine, but you have to:

  • Always use SendAsync, and always pass in a new CancellationToken for every request. This prevents one cancellation from cancelling all pending requests.
  • Always use one shared instance instead of creating one for every request. This prevents slowdown due to cleanup/startup of HttpClient.
  • Preferably, use one shared instance for every type of request. There's some amount of caching of paths, dns lookups and such going on, keeping independent instances for entirely different things is a good idea either way.
  • Be aware that the time counted for timeout includes everything from as soon as you call that send method. If you aren't keeping this in mind, requests queueing will bite you in the arse.

Except for that, the only outstanding issue I still have is how DNS name resolution actually works internally, because I keep running into issues like: I use HttpClient to contact something behind a DNS load balancer (Route 53 weighting), it keeps using the same IP it resolved to initially on all requests through the same HttpClient instance. Or more recently, the datacentre's DNS server screws up for a second while I'm requesting something and sends me a completely wrong IP address for the name I requested, that wrong IP address is cached until the process is restarted, causing constant errors until someone goes online to restart it.

e: ^^^ I cannot recommend getting someone starting out to use EntityFramework, especially if they're new to the whole database thing in general. Use something like Dapper.NET, build queries yourself, run everything yourself. Only then should you toy around with EntityFramework, so you can get nice and mad about all the insane things it does when you accidentally tell it to do insane things.

Red Mike
Jul 11, 2011
Or just use something like Dapper.NET and skip the constant trouble with the correct way of opening connections, reading into the appropriate variables, turning stuff into lists, etc. You're still writing your own SQL, just skipping the boilerplate.

Red Mike
Jul 11, 2011

Furism posted:

How do I do that in a WebAPI project (the ASP.NET Core version, I guess that's technically ASP.NET5)? Won't the instance be destroyed as soon as the Action is done? Would it work is Dependency Injection?

The same way you would call services from your controllers. Either have an instance be created with your controller (which means it's not technically one shared instance, but it's close enough) or with something like Simple Injector, and you can define a lifetime there.

Red Mike
Jul 11, 2011

Chamook posted:

If you create a class that implements IHttpControllerActivator you can use that instead of the default for creating your controllers on each request, you can then create a HttpClient on app start and pass that into the constructor of your ControllerActivator so you use a single shared HttpClient for the lifetime of your application.

With the proviso that "app start" might mean (depending on hosting method) something other than "the application start". What it'll actually mean is "when the app pool gets initialised, which will be on the first received request without an active app pool; also app pool can get torn down if no requests are received for a relatively short amount of time".

e: oh also if you do long-running pre-caching of data in that setup step, don't. Because then this data pre-fetch will take ages and delay a request apparently randomly if the user is unlucky to have done the first request since the app pool was torn down.

Red Mike
Jul 11, 2011

chippy posted:

It's pretty great. You might want to consider adding DapperExtensions. It adds basic CRUD, async/await support and some other useful things, notably it makes mapping one to many queries to objects easier which, trust me, is a loving ballache in vanilla Dapper.

Async/await is in Dapper.NET from the start now. One to many query mapping to objects is also dead simple now. Check out the latest versions maybe.

Red Mike
Jul 11, 2011

Bognar posted:

Does it still deserialize an object for every single row, then throw it away if it's a duplicate?

I wasn't even aware this was a thing. I've had good results with all kinds of queries so far, but I expect I'll run into that soon if it's still a thing.


EssOEss posted:

When you say one to many query mapping in this context, what exactly do you mean?

What I took it to mean was, for example, a Users table that has a one-to-many relation to a Flags table. It's fairly simple to set up a query with a join and turn that into a set of User class instances that contain IEnumerable<Flag>.

e: ^^^ huh, I must be using some dapper extension then that provides this support? I know I'm not using DapperExtensions though.

e2: Ah no, I'm being dumb, it's too late for coding. One to one multi-querying, or whatever it's called, is what's natively supported. Doing one to many relations like that is being done manually throughout the codebase. But it's still dead simple. At the point at which you're grabbing a single object, you also grab the flags and use multi-mapping to grab the list of flags in the same query call. If you're grabbing a list of objects it depends on what your use-case is, but this is a good thing because it means you have to consider where you want the trade-off (memory usage, initial query time, lazy loading, etc) to be.

Red Mike fucked around with this message at 22:49 on Jan 25, 2017

Red Mike
Jul 11, 2011

KoRMaK posted:

I'm woring in C# (for a Unity game) and I have a bunch of boiler plate code that multiple objects need. Inheritence doesn't really seem to be a great solution, and I'd rather use a mixin (pulling from my Ruby programming day job here)

Well, those don't exist in C#, ok fine I'll do a macro. gently caress, those don't exist either. And Interfaces don't seem like they allow for the method to be defined on the interface - defeating the purpose because I'll have to redefine the same friggen method in every object.


Anyway, how do I do mixins-ish programming in C# so that I don't have to keep copy/pasting the same 3 line method over and over?

This sort of design is a really bad fit for C#. Instead of trying to make the design work by force (it's possible, from partial classes to some weird inheritance with tons of complicated abstract classes and templates) I recommend just ignoring what you'd do in Ruby and trying to learn the C# design, which is fundamentally composition-based. Using interfaces and templates can help make that composition a lot simpler and quicker to write.

I've seen way too many people come in from JS, Ruby, Python, and then make a hash of all their code because they just want to do <simple thing in JS/Ruby/Python> and don't want to spend a few hours learning C# first.

Red Mike
Jul 11, 2011
I've had a seemingly related bug, that apparently was to do with how we stored packages in source control and the fact that it was VS2012. (or at least upgrading to 2015 fixed it?)

Every so often when cloning from scratch and opening the project, the references would come from the first place it could find them in the system path even if they were the wrong version (and it would then complain about the wrong version). This included random other projects that I'd put in my path folder for other reasons, or unrelated folders for other developers. This ended up causing half an hour's work to undo every time someone cloned from scratch.

edit: these weren't ASP.NET projects though.

edit2: also hilariously but non-NuGet, sometimes when it wasn't a fresh workspace the references would freak out and the project ended up thinking that its references were in its own bin/ folder. Which meant that as we don't add bin/ to source control, you had to re-add everything when you cloned a fresh copy or the version wouldn't change even if you updated the dll.

Red Mike fucked around with this message at 22:59 on Mar 8, 2017

Red Mike
Jul 11, 2011

Duct Tape posted:

Yeah, this is my stance as well. I was just worried that I was unintentionally breaking the state machine for more-complex methods by not wrapping trivial results in Task.FromResult. Doesn't sound like that's the case, though.

It will make it throw warnings though, which is mildly annoying. In most cases I've run into (return null because method shouldn't do anything, or because conditions aren't met, etc) since it's always the same value, I've used Task.FromResult. The created Task is reused so there's no downside other than readability. However, if you're not using await and are getting a Task from somewhere (including Task.FromResult), you can just remove the async modifier and return the Task directly without awaiting it.

So:

code:
Task<Thing> GetThingAsync()
{
  return GetThingAsync_Internal();
    or
  return Task.FromResult<object>(null);
}
Supposedly it has some effect on where exceptions would be thrown, but I've yet to run into a case where it actually matters myself.

Red Mike
Jul 11, 2011

Night Shade posted:

It makes exceptions throw immediately, instead of faulting the returned Task. This normally isn't a huge deal if all you're doing is
code:
try {
  await thing();
} catch (...) {}
but if you're doing something like using .Select() to turn a list of urls into a list of parallel downloads, having exceptions propagate immediately will interrupt iterating the list. This prevents assigning the result of .Select(), which will leave you with orphaned tasks that iirc will crash your app if they fault and then get collected.

Yeah, except in all the practical cases I've seen during reviews, it wouldn't have mattered anyway because the code itself was badly designed and had major flaws besides this. The situation you mention would have been replaced at code review stage in all the codebases I've worked with, because you don't just turn a list of URLs into a list of parallel downloads like that, you call an async method of your own that does its own try/catching (and probably other functionality).

I'm sure there must be cases where it is a concern, which is why I mentioned it in the first place.

Also, in the 'orphaned tasks' situation, you normally just hit the default uncaught exception handler. Wouldn't crash your app, same as throwing in an async void wouldn't.

Red Mike
Jul 11, 2011

Night Shade posted:

Setting the contrivance of .Select(DownloadParallel) aside, if you're kicking off parallel Tasks at some point in the call chain you need to not be awaiting them immediately regardless of how smart the Task-returning method is. This makes the difference in exception propagation significant. e: because i can trust that an async method won't throw until the Task is observed and is therefore safe to pass directly to .Select / Seq.map / whatever. I can't say the same about a non-async method that happens to return Task.

That's fair enough, I guess. I've still yet to run into this case in the wild, so you can just mentally remove 'Supposedly' from my first post.

quote:

:colbert:

Huh, I stand corrected. I'm not sure what ASP.NET and the two other server techs we've used do then, because we've got those events for safety/logging, but uncaught exceptions never cause issues (unless they're actual hard faults).

e: And similarly in Unity on the client-side, actually, but that doesn't have async/await anyway.

Red Mike
Jul 11, 2011

ljw1004 posted:

I don't think that's right...

Up to VS2012 (.NET4), a task that ended in the "failed" state and which was never observed (i.e. no one ever did .ContinueWith on it) would raise the global unhandled exception handler.

But as of VS2012 (.NET4.5), once async/await was introduced, that behavior didn't make sense. So we removed it. We specifically wanted to support patterns like this, which never signs up anything for after JonesAsync.
code:
try {
  var task1 = FredAsync();
  var task2 = JonesAsync(); // kick them both off at the same time
  await task1; // but imagine if this throws!!!
  await task2;
} catch (Exception e) {
  Console.WriteLine("oops"); // at this stage JonesAsync has never been observed
}
That's entirely unrelated to the question of exceptions thrown inside async void....

code:
async void Button1Handler() {
  await Task.Delay(1);
  throw new Exception("oops");
}
An exception here will cause the exception to be rethrown inside the current SynchronizationContext: https://referencesource.microsoft.com/#mscorlib/system/runtime/compilerservices/AsyncMethodBuilder.cs,223b5f320d791728

What does that do? It varies. I remember trying it inside WPF, Winforms and UWP (each have their own slightly different synchronization context). I remember that there were different behaviors between the three -- ignored? pop up an error dialog? terminate the program? -- but I can't remember which. I think they all involve the unhandled exception filter first.

What does it do when there's no synchronization context, i.e. it hits the threadpool? I think that goes down the unhandled exception filter route and then terminates the program, but can't remember.

What does it do in the ASP.NET synchronization context? I don't know.

This is really good info, thanks. I'll forward it to my team and check with the server software we use so we can figure out what they do (if anything) so we're at least aware of what could happen. If nothing else, this entire exchange has convinced me that I should pay more attention to this and maybe add specifics to the guidelines instead of relying just on the code reviewer catching it.

Re: the initial bit though, does that mean that unobserved faulted tasks have no ill effects, so the original situation with the Select wouldn't have any issues (other than the exception being thrown where you might not expect)?

Red Mike
Jul 11, 2011

Night Shade posted:

e: actually if Select blows up you may well be left with work happening inside Tasks that you no longer have any way of dealing with unless you've got a CancellationTokenSource you can use to abort them.

Honestly, edge cases like this are part of the reason why I said code like this wouldn't make it past review at my workplace. If you just wrap whatever you're calling into a task of your own with proper error handling then suddenly it's a null point because you've fixed both the exception case and the fact that the exception isn't thrown in the place you'd expect. And one of the guidelines already is "expect any async method to later turn into a sync one with no work done other than removing the await", so yeah.

I do now feel like this is yet another gotcha with regards to async/await that isn't highlighted enough even as an edge case.

Red Mike
Jul 11, 2011
I've been struggling with a problem in a live environment (doesn't reproduce in test environments) for the past couple years, on and off, and I haven't been able to find any solutions online. It's to do with async/await performance tracking, ie. actual Tasks.

Is there a way to get performance counters (or similar) on, for example, how many pending tasks are waiting for a spot on the threadpool to run? Or the number of available executors/similar?

For context, this is basically a massive parallel socket server that starts each request it receives on its own Task (ordering the requests amongst each other via a 'fiber'). These Tasks are async and allows us to use async/await all the way down, which works great. Sometimes though, we end up with a single request that sometimes (with certain parameters) either takes ages in sync code (that we can't avoid, ie. DLLs and similar) or that spawns many more tasks which then spawn more tasks, etc. We've tried to avoid anything like loops that Task.Run internally (and then Task.WhenAll await them at the end) but sometimes they're strictly required.

When that situation appears, what tends to happen is that all the running threads get stuck on one of these tasks eventually, which lock up all available threads and no tasks can finish running, which maxes out CPU and effectively locks the server up.

Being more careful when writing the code hasn't helped as the team grew, so I'm looking for any sort of useful diagnostics that can tell us when this is the actual issue, and maybe something to let us find out which task it is (besides a dotTrace profile which isn't easy to run on production servers).

e: I should mention, this isn't 'the task locks up one core', it's 'the tasks are locking up all cores'. I'm confident it's properly parallelised.

Red Mike fucked around with this message at 13:25 on May 14, 2018

Red Mike
Jul 11, 2011

Shy posted:

Is there a way to create publishing options for plain .net framework console applications that would copy build output to a predetermined ftp location or a local directory, like web apps? The only option VS offers me is ClickOnce which doesn't look good. I could simply copy it myself but the task is to simplify the process so that me and other developers could deploy different configurations without loving up configs and locations over and over again.

Post-build task using robocopy or a simple script you write and build configurations. Simplest to set up.

If you don't want it tied to a build, you can write the script then add a "Custom Tool" that shows up in the tools menu and you can run manually.

Red Mike
Jul 11, 2011
If you need to offer some customisation to the constructor, but need to keep most of it the same (and/or private so not accessible to subclasses). Example:


ClassName constructor calls method A, then calls method B. You want method A to be override-able, but method B is sealed and marked as private since you shouldn't be able to mess with it when subclassing.

Mark method A as virtual, method B is sealed by default and private.

If someone implements SubClassName : ClassName, they can't try to make their own constructor that calls their new method C instead of A, then calls method B, because method B is not accessible in the first place. If method B uses private members, they can't even reimplement method B as method D.

Literally the only thing they can do short of reimplementing 90% of the base functionality with new members is have their constructor call base() and override method A. The base class then calls their implementation instead.

Red Mike
Jul 11, 2011
There is a page with terminology in the documentation. "Open" isn't explicitly used in there, but it's what the rest of the documentation tends to use. I think the same terminology is used for method definitions, however that isn't really explicitly stated.

Couple more quirks to be aware of from just that page:

quote:

It is important to note that a method is not generic just because it belongs to a generic type, or even because it has formal parameters whose types are the generic parameters of the enclosing type.

quote:

The common language runtime considers nested types to be generic, even if they do not have generic type parameters of their own.

I recommend you read this: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/generics-and-reflection
Followed by this (linked from the previous one), this one has the breakdown of terminology despite it being on an API ref article: https://docs.microsoft.com/en-us/dotnet/api/system.type.isgenerictype?view=netframework-4.8#remarks

Red Mike
Jul 11, 2011
The two things you're trying to do have different causes and different ways of resolving them (which may not go together):

1. Instance static member: it's effectively telling you that this class itself can't be the one creating the instance, unless it does it via some sort of factory/factory method.
It makes sense if you just look at this abstract, because the question is "how does this abstract know which of its non-abstract subclasses to create?". Generally the answer is going to be "it doesn't, your singleton pattern doesn't belong in this class but in one/all of the subclasses".

That depends on what you want to happen though, how do you expect to be calling this?
If you want to do BullshitClass<Whatever>.Instance.DoThing(), then what subclass of BullshitClass<Whatever> do you expect to get used?
If you want to do MySubclass<Whatever>.Instance.DoThing() where MySubclass inherits BullshitClass, then the same pattern you have with .Instance should be in MySubclass. There's no way to reduce the boilerplate there since Instance is a static field (so no inheriting).

Generally, I think this kind of thing is a signal that you don't really want a singleton, so maybe see if you actually want one (and consider that static fields and generics don't work the way you expect; you will have one __instance value for every value of <TInst> that gets used. So not a singleton, but a "singleton" per TInst type.)

2. Using a non-static method in a static context (base constructor): there's no general solution, but this specific situation is very common. You're trying to make it so the subclasses define their own "name", or "description", or "whatever piece of data", even though you know that bit of data should be static and stateless, but there's no way to inherit statics. Your best bet is to pass the data directly at compile time. Because the base constructor is there, a subclass is forced to implement it anyway (as you were just forced to implement it) so there's no chance of the developer forgetting.

code:
public abstract class MyBaseClass
{
  public MyBaseClass(string name) 
  {
     Console.WriteLine("Called from " + name);
  }
}

public class MySubClass : MyBaseClass
{
  public MySubClass() : base("a special class") { }
}
Overall: If the reason you're trying to write this class is to reduce boilerplate, I don't think it'll help in the way you're writing it now.
I can't be sure because I don't know what the rest of your code is like, but it sounds like what you're looking for is something like a container/wrapper for Python objects that just handles the boilerplate of creation/init?
If so:

1. Don't inherit from PyClass, instead store a private field of type PyClass and make the class non-generic (just use PyObject). That inheritance is what's enforcing your constructor passing a static type name in.
2. Don't use weird singletons because it doesn't sound like you want a singleton, you just want to not have duplicates. If that's the case then you want whatever's creating this object to be a factory that stores and reuses existing instances, mixing static and generics to try to do a factory like this won't work the way you're hoping.
Not having a singleton means the class boils down to a wrapper for PyClass that handles the creation of __new__/__init__ and type name, which is the 'boilerplate' you want to reduce duplication of.
Having a factory ahead of it that handles the creation of this wrapper means you're no longer handling anything static, so no more issues with static fields or abstracts.

Red Mike
Jul 11, 2011
You're using 'static' as an analogue for 'single instance' which is incorrect. You're also using inheritance to reduce the duplication of that code when the correct tool is composition.

If I'm following correctly, what you're looking for is something like this:

code:
//this class is a true static that's only meant to serve as a single
//place to put lookup/conversion logic or something like that
//even then static isn't the best choice if it can be avoided
public static class PythonTypeMappings
{
    public static string GetName(Type type)
    {
        //use whatever makes sense to generate the type name here
        //a useful pattern would be some sort of visitor/handler setup
    }
}

//this class is responsible for making sure you only have one instance of whatever type you want
//it can be static if you want a static factory, but using generics will defeat the point there (among other issues)
public class ClassFactory
{
    //this doesn't store MyClass<TInst> because that's pointless, you have the base class you really care about
    private readonly Dictionary<Type, PyClass> _types = new Dictionary<Type, PyClass>();

    //this could also just return PyClass, but still take TInst as a generic argument
    //depends if whoever calls it needs to know that it's a MyClass<TInst> or not
    public TInst GetClass<TInst>() where TInst : PyObject
    {
        var type = typeof(TInst);
        if (_types.ContainsKey(type))
        {
            return (TInst)_types[type]; //may want to run checks here
        }

        //generally this kind of mapping should come from outside ClassFactory
        //a better option than the singleton would be some sort of PythonTypeMapper that 
        //you pass into ClassFactory as you create it, as a service
        var pythonTypeName = PythonTypeMappings.GetName(type);

        //no clue where __init__ is meant to come from here, but you can pass it into GetClass<TInst>()
        var classInstance = new MyClass<TInst>(pythonTypeName, __init__);
        _types[type] = classInstance;
        return classInstance;
    }

    //this class makes no assumptions about what PyObject it's really using
    //it just cares about being a factory of MyClass/PyClass objects
}

// this class doesn't need to care about if it's the only instance of its type or not, it assumes that's the case
public class MyClass<TInst> : PyClass where TInst : PyObject
{
    public MyClass(string pythonTypeName, CodeObject __init__) : base(pythonTypeName, __init__, new PyClass[0])
    {
        //copy pasted from your code, although this might be replace-able by something like Action/Func?
        Expression<Action<PyTypeObject>> expr = instance => DefaultNew<TInst>(null);
        var methodInfo = ((MethodCallExpression)expr.Body).Method;
        __new__ = new WrappedCodeObject("__new__", methodInfo, this);
        //whatever other class setup you want

    }

    //whatever extensions on PyClass go here, they know that it's specifically about TInst
}

//ALTERNATIVELY, as a single class
public class MyClassMapper
{
    private readonly Dictionary<Type, PyClass> _mapping = new Dictionary<Type, PyClass>();

    public PyClass GetClass<TClass>(CodeObject __init__) where TClass : PyObject
    {
        if (_mapping.ContainsKey(typeof(TClass)))
        {
            return _mapping[typeof(TClass)];
        }

        var pythonTypeName = PythonTypeMappings.GetName(type);

        var class = new PyClass(pythonTypeName, __init__, new PyClass[0]);
        //copy pasted from your code, although this might be replace-able by something like Action/Func?
        //unclear what the access level is of those fields, so you may need a subclass like MyClass anyway?
        Expression<Action<PyTypeObject>> expr = instance => class.DefaultNew<TInst>(null);
        var methodInfo = ((MethodCallExpression)expr.Body).Method;
        class.__new__ = new WrappedCodeObject("__new__", methodInfo, this);
        
        //whatever other init here

        _mapping[typeof(TClass)] = class;

        return class;
    }
}
This way you have a clean split of:
1. Mapping/parsing/whatever shared logic needs to exist statically (or effectively statically)
2. A class concerned with keeping one instance of a thing per type (or per whatever conditions you set)
3. A class concerned with either subclassing or wrapping a type.

#2 and 3 can be merged together somewhat cleanly, it just depends on access modifiers and what the goal is (do you actually need a subclass/wrapper?).

Basically, I really recommend taking a step back and starting over from what you're really trying to achieve because you're already down the rabbithole of fixing something that's only really needed because of a wrong choice back up the line.

Red Mike
Jul 11, 2011

Rocko Bonaparte posted:

I'm still looking over what I can do to change the entire approach in the larger post. For one, I am seeing how I am currently looking up these static instances since I've lost track of it.

But about this particular mess: I'm trying to remember what made me wind up doing this. I think I was trying to make a compile-time binding to DefaultNew instead of trying to get it dynamically using GetMethod(). There's a few places where I gave up and used GetMethod() because the signatures were async and I couldn't use an expression tree for them. It ultimately hasn't turned out to be so bad; it's a simple one-liner versus this code salad. It's just I get kicked in the dick if I type the method binding wrong and I'm very good at making that particular kind of typo.

Edit: I might as well append on to here about my recall on the whole static Instance thing. The main place where I'm using that Instance is every time I create an instance of the actual object type. That's the TInst generic argument in my example. Every instance of an Pythonlike object needs to have an association with its class. If you are a Python person, you can see that as the __class__ dunder in any instance of an object. That becomes useful for issubclass() and isinstance(). This is looking like premature optimization, but I'd prefer to avoid a dictionary lookup in every constructor if I can avoid it. On the flip side, the alternative is that boilerplate garbage that I'm liable to copy-paste then forget to properly alter for the new class.

I think you'll need to find an alternative yourself, since the problem is quite specific around the Python bits.

Regarding the dictionary lookup specifically: a dictionary lookup is effectively O(1) and constructors can't be async so you don't need locks or blocking, it's way premature optimisation. Worse, what you've come up with (GetMethod/reflection) is many orders of magnitude slower and way more difficult to predict the performance of (e.g. turn one class it ends up needing to look up into a generic class and suddenly your O(n) might become O(n log n), who knows).

Red Mike
Jul 11, 2011

Mr Shiny Pants posted:

And if one of these errors out, it will take all your other outstanding requests with it if I am not mistaken. Or did they fix that?

If you don't pass a different cancellation token in for every request/endpoint, then a timeout (specifically a timeout, which shows up as a TaskCanceledException) will cancel and raise the exception for every pending request. This is just because they use one cancellation token per instance if you don't pass it in, and it's initialised at instantiation. I believe the 'instance' is the underlying handler rather than the HttpClient class, so you'd have one per endpoint which acts as basically a connection pool for that endpoint. So if your HTTP client does requests to two completely hosts, the two shouldn't be interfering with each other.

Either way, HttpClientFactory is the way forwards and it's a lot easier to use. I don't know if named clients are the best way vs just calling the factory to get a generic one, but named clients are the only easy way I'm aware of to set up default headers and things only in one place.

Its only downside is that it's tied to the DI framework and not actually generic, but so long as you're using the standard generic/web host it should be OK. If you're migrating a legacy app across that can't use those hosts, you're basically going to have to reimplement it in whatever DI framework you're using.

Red Mike
Jul 11, 2011

epswing posted:

Lock vs Monitor.TryEnter

I believe the lock statement and Monitor.Enter/TryEnter are equivalent, with the exception: lock will actually call Monitor.Enter (which in practice is equivalent to Monitor.TryEnter with an infinite timeout like you wrote) which means no possible way to fail out/not block.

TryEnter returns void however it also returns a lockTaken boolean value (by ref) so that you can check if the lock was acquired or not. Obviously that only matters if you pass a non-infinite timeout, which generally you should (or else just use Enter/lock statement).

Do note that Monitor.Enter/TryEnter may have some funky behaviour depending on what you pass in because of boxing, if you don't pass in an object of type object.

Red Mike
Jul 11, 2011

NihilCredo posted:

dotnet tool install dotnet-svcutil && dotnet dotnet-svcutil <url to WSDL file>

I invoke SOAP services from .NET Core on Linux by pointing to WSDLs. There are various flags for stuff like generating sync methods, implementing INotifyPropertyChanged, etc.

I'll be a second person to confirm that while yes theoretically that works, it won't always work on the weird Java-based SOAP services you might sometimes have to call.

For example, when doing integration with classic Oracle ERP (ignoring that one half of the services need you to host a SOAP service anyway, but chances are you don't need those) the WSDL they offer contains specific encryption/WSDL extensions that they use (which were never really implemented in .NET). When you generate your .cs file it'll work fine, and when you call the web service it'll reject your request. For this particular case, I couldn't get it working on .NET Framework either.

As a different more recent example, when integrating with a postal order broker service, their WSDL used addressing modes that aren't supported in .NET Core or SoapCore but are supported in .NET Framework. How you generate the C# class/wrappers isn't relevant here I don't think, but running it on a .NET Core application fails because the request that goes out will just ignore the addressing mode. If you tweak the code to force that addressing mode to be used, you'll get a not implemented exception I think (might have been just in SoapCore).

So what the previous poster said: "invalid SOAP schema and/or generating such esoteric weird (but technically correct) WSDL"

Red Mike
Jul 11, 2011
Related to that, that second example I gave of a postal order broker service, we ended up agreeing on the service offering an updated SOAP service that used more standard things which worked in .NET Core. Unfortunately that meant my next task was to set up a mock of the service to be used for testing instead of the live endpoint (for various good reasons). Getting that done in .NET Core with SoapCore proved impossible because of one of the more standard things, and I wanted to avoid .NET Framework wherever possible.

So we just figured out all the potential responses and did text replacement to generate them. That was about a year ago and I've since left the company, so I bet I'm being cursed at by newer devs discovering that particular project.

Red Mike
Jul 11, 2011

Mr Shiny Pants posted:

I don't really like the HttpClient to be honest, and I like that I can just create a HttpWebRequest from scratch using the "obsolete" APIs.

Is there any reason I need to switch? I mean, System.Net is pretty bulletproof by now right?

If you want to build your own wrapper around it, to handle all connection pooling/error and timeout handling/disposal, there's no reason to switch. If you want those things, HttpClient is the only reliable built-in way to get them.

Additionally, if you're working with modern ASP.NET Core then HttpClient itself is well integrated so you can more easily grab instances of it as needed (tied to scope, etc).

Red Mike
Jul 11, 2011

Mr Shiny Pants posted:

I get that, and some things it does well (Forms and the like) but for just getting a reply from an endpoint I really like just being able to create an async method from scratch and know where all the stuff is getting used.

I really like being able to do:

code:
let getAudio text = 
    async {
        let request = System.Net.WebRequest.Create(buildUrl(text)) :?> HttpWebRequest
        request.Method <- "GET"
        let! response = Async.FromBeginEnd(request.BeginGetResponse, request.EndGetResponse)
        return response.GetResponseStream()
    }

So is there a difference between that and this (C# obviously):

code:
var task = Task.Run(async () => {
  var httpClient = new HttpClient(); //or grab it from an IHttpClientFactory
  //the underlying pools are shared so instantiation isn't that expensive
  //however a shared member should still be used for certain quirks (a timeout cancelling subsequent requests on the same URL)
  using (var request = new HttpRequestMessage(HttpMethod.Get, "/my/url"))
  {
    //here you could add request body
    //request.Content = new StringContent(...)
    string responseBody;
    using (var response = await _httpClient.SendAsync(request))
    {
      if (response.StatusCode == HttpStatusCode.OK) {
        responseBody = await response.Content.ReadAsStringAsync();
      } else {
        //do whatever, throw, etc
      }
    }
    return responseBody;
  }
});
//await it, or .GetResult it or whatever
If you mean that this code abstracts away some of the logic/resource usage (e.g. pooling connections, handling timeouts, etc), then yeah that's the trade-off. If you mean something else, I'm not seeing a difference between the two.

e: Also to be clear, I think some of the choices in HttpClient are pretty daft. The timeout handling especially is finnicky and can catch you by surprise in certain situations (hitting the same URL/path with different data for different operations, and some operations being expected to sometimes time out while others don't - this can cause those others to also act as if they timed out if they were pending while the first one times out - the workaround is to always pass in a CancellationToken as appropriate instead of letting it create one against the URL/path/handler)

But I still prefer it to the raw request classes whenever I actually need to do HTTP because it's a low enough level construct that I just care about the HTTP request happening, and any fixes I want to this logic need to apply to all outgoing HTTP requests anyway. Basically, my job is to solve problems, not to write perfect code.

Red Mike fucked around with this message at 11:30 on Jan 7, 2022

Adbot
ADBOT LOVES YOU

Red Mike
Jul 11, 2011

fuf posted:

Does anyone have big thoughts on Rider vs Visual Studio, especially for blazor development?

I keep flipping between the two and can't really settle on one.

Rider seems to be a lot faster and smoother than VS.

But one thing I really like about VS is the code suggestions. Not just normal intellisense but when it will suggest an entire line for you based on the previous lines. Sometimes I'm amazed how accurate and helpful it is. I can't figure out if Rider has something similar?

I use the vim extensions for both which adds some complications. I sort of half-learned vim about 15 years ago and now I'm stuck with it even though I'm not that good at it.

Assuming there isn't licensing/cost issues, just pick one and stick with it. They're similar enough that you can work one if you know the other, but also once you're familiar enough with either of them you tend to be more productive in it than if you keep flipping back and forth and only know how to use the features they have in common.

Using Rider will let you be more productive with features like its DataGrip-like integration, the various tooling integrations, some specific plugins, as well as custom run config/launching/etc configurations, etc etc.
Using VS will let you be more productive with intellisense/roslyn-related integrations, better integration of source generators and some extensions around containers/etc, as well as better integration into certain platform you might be using (like Azure), etc etc.

The only "big" thing in my mind besides that would be if you're having to work with legacy/specific tech where VS works correctly and Rider sometimes breaks. You mentioned Blazor, but even with Razor I've had nothing but issues in bigger projects. For example, right now in Rider I've got a .cshtml without anything fancy except for a local function used for templating; that just breaks the parsing so Rider keeps insisting there's wrong symbols/it won't build, but it builds it fine. Apparently it's RIDER-34875 which has been open for 2 years and isn't any closer to a fix (and has an unsuitable workaround).

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply