Do nested async/await calls give any performance boost? - entity-framework

This is somewhat related to Why use Async/await all the way down . I have some legacy EF Core I need to maintain with lots of nested calls that are not async. I am wondering if it is just ok to make the top level controller only async and wrap the service call with a Task.Run().
What is the difference between these 2 controller actions.
In the first one, EF core is called synchronously. But the call
is wrapped in a Task.Run so I assume it will be executed asynchronously
In the second, the EF core calls is async
But is it even necessary since the controller action is async?
Will async within async within async... give any performance boost over just one async call at the top?
case 1
//controller, async call
public async Task<ActionList<string>> GetNames()
{
var names = await Task.Run(() => peopleService.GetNames());
return Ok(names);
}
//people service layer, no async
public List<string> GetNames()
{
return _context.People.Where(...).Select(x => x.Name).ToList();
}
case 2
//controller, async
public async Task<ActionList<string>> GetNames()
{
var names = await peopleService.GetNames();
return Ok(names);
}
//people service layer, async
public async Task<List<string>> GetNamesAsync()
{
return await _context.People.Where(...).Select(x => x.Name).ToListAsync();
}

Task.Run only serves to run your code on the thread pool. This can be useful for WPF or WinForms applications, since it frees up the UI thread to continue processing other UI events. However, it does not bring any performance benefits in ASP.NET, since you're just switching execution from one thread-pool thread to another. When it encounters the EF ToList call, this thread-pool thread will get blocked. If you have a lot of concurrent requests, the thread pool will get exhausted. .NET has a thread injection policy, but this is limited, so some requests will time out. If your load gets very high, you will need so many threads that you will deplete your system resources (CPU and memory).
By contrast, async-all-the-way means that you are never blocking any threads while the database I/O operations are in progress. Each asynchronous operation is handled by the underlying system, and execution only resumes on the thread pool once the operation has completed. This allows your system to scale to a much larger number of concurrent requests.

Related

What priority will as Task run as when the operation has the #MainActor decorator?

I'm curious what priority the async block() in the code snippet below will run as when the Task's operation is marked #MainActor?
According to the DispatchQueue docs the main queue always runs with a qos of .userInteractive. So I assume the Task's #MainActor operation will also run as .userInteractive - there's only 1 main thread after all.
But since we requested a custom priority for the Task, what priority will the async block run with?
Task<Void, Never>(priority: priority) { #MainActor in
doStuffOnMainQueue() // .userInteractive
await block() // priority?
}
When you add #MainActor qualifier to the closure, that obviously runs on the main actor. But when it reaches await block(), that translates to “suspend the current actor (i.e., the main actor in this case) so it’s free to do other stuff and await the results of the block().”
Regarding the question of what priority the block() uses, that depends on how it was declared. Assuming block() is just a closure that you passed to this method, then in my experience it runs with the priority that you supplied to Task, not the main actor. But there are lots of variables (e.g., you could specify #MainActor for that closure parameter, too; you could be calling some method running on some other actor; etc.), so we cannot get more specific without seeing a reproducible example.
But, bottom line, when you await something, the actor/priority of the awaited code is dictated by how you defined that, rather than the context of the code from which you called it.

How can I avoid that my Swift async method runs on the main thread in SwiftUI?

I watched all the videos on async/await (and actors), but I am still confused a bit.
So assume I have an async method: func postMessage(_ message: String) async throws and I have a simple SwiftUI view.
#MainActor
struct ContentView: View {
#StateObject private var api = API()
var body: some View {
Button("Post Message") {
Task {
do {
try await api.postMessage("First post!")
} catch {
print(error)
}
}
}
}
}
Here I explicitly tell SwiftUI to use the #MainActor although I know it would have inferred from #StateObject.
To my understanding, since we use the #MainActor the work is done on the main thread. Meaning the work on Task would also be done on the main thread. Which is not what I want as the uploading process might take some while. In this case I could use Task.detached to use a different thread. Which would solve it. If my understanding is correct.
Now to make it a little more complicated. What if... postMessage would return a post identifier as an integer and I like to present that in the view?
struct ContentView: View {
#StateObject private var api = API()
#State private var identifier: Int = 0
var body: some View {
Text("Post Identifier: \(String(describing: identifier))")
Button("Post Message") {
Task {
do {
identifier = try await api.postMessage("First post!")
} catch {
identifier = 0
print(error)
}
}
}
}
}
This would work as (again to my understanding) Task is run on the main thread. If I would change it now to Task.detached we will get an error "Property 'identifier' isolated to global actor 'MainActor' can not be mutated from a non-isolated context".
Which makes sense, but how can we return the value to the main actor so the view can be updated?
Perhaps my assumptions are wrong. Let's look at my API class.
actor API {
func postMessage(_ message: String) async throws -> Int {
// Some complex internet code
return 0
}
}
Since the API runs in its own actor. Would the internet work also run on a different thread?
The question is very good, and answer is complex. I have spent significant time on this topic, for detailed information please follow the link to Apple developer forum in comments.
All tasks mentioned below are unstructured tasks, eg. created by Task ...
This is key: "The main actor is an actor that represents the main thread. The main actor performs all of its synchronization through the main dispatch queue.
Unstructured tasks created by Task.init eg. Task { ... } inherit the actor async context.
Detached tasks Task.detached, async let = tasks, group tasks do NOT inherit actor async context.
Example 1: let task1 = Task { () -> Void in ... } creates and starts new task that inherits priority and async context from the point, where it is called. When created on the main thread, the task will run on the main thread.
Example 2: let task1 = Task.detached { () -> Void in ... } creates and starts new task that does NOT inherit priority nor async context. The task will run on some thread, very likely on other thread than is the current thread. The executor decides that.
Example 3: let task1 = Task.detached { #MainActor () -> Void in ... } creates and starts new task that does NOT inherit priority nor async context, but the task will run on the main thread, because it is annotated so.
Very likely, the task will contain at least one await or async let = command. These commands are part of structured concurrency, and you cannot influence, on what thread are the implicitly created tasks (not discussed here at all) executed. The Swift executor decides that.
The inherited actor async context has nothing to do with threads, after each await the thread may change, however the actor async context is kept throughout all the unstructured task (yes, may be on various threads, but this is important just for the executor).
If the inherited actor async context is MainActor, then the task runs on the main thread, from beginning till its end, because the actor context is MainActor. This is important if you plan to run some really parallel computation - make sure all the unstructured tasks do not run on the same thread.
ContentView is on #MainActor in both the cases: in the first case is it #MainActor explicitely, the second case uses #StateObject property wrapper that is #MainActor, so the whole ContentView structure is #MainActor inferred. https://www.hackingwithswift.com/quick-start/concurrency/understanding-how-global-actor-inference-works
async let = is a structured concurrency, it does not inherit actor async context, and runs in parallel immediately, as scheduled by the executor (on some other thread, if available)
The example on top has one system flaw: #StateObject private var api = API() is #MainActor. This is forced by the #StateObject. So, I would recommend to inject other actor with other actor async context as a dependency without #StateObject. The async/await will really work, keeping await calls with the proper actor contexts.
You said:
To my understanding, since we use the #MainActor the work is done on the main thread. Meaning the work on Task would also be done on the main thread. Which is not what I want as the uploading process might take some while.
Just because the uploading process (a) might take some time; and (b) was invoked from the main actor, that does not mean that the main thread will be blocked while the request is performed.
In fact, this is the whole point of Swift concurrency’s await. It frees the current thread to go do other things while we await results from the called routine. Do not conflate the await of Swift concurrency (which will not block the calling thread) with the various wait constructs of traditional GCD patterns (which will).
E.g., consider the following code launched from the main actor:
Task {
do {
identifier = try await api.postMessage("First post!")
} catch {
identifier = 0
print(error)
}
}
Yes, because you used Task and invoked it from the main actor, that code will also run on the main actor. Thus, identifier will be updated on the main actor. But it says nothing about which actor/queue/thread that postMessage is using. The await keyword means “I'll suspend this path of execution, and let the main thread and go do other things while we await the postMessage initiating its network request and its eventual response.”
You asked:
Let's look at my API class.
actor API {
func postMessage(_ message: String) async throws -> Int {
// Some complex internet code
return 0
}
}
Since the API runs in its own actor. Would the internet work also run
on a different thread?
As a general rule, networking libraries perform their requests asynchronously, not blocking the threads from which they are called. Assuming the “complex internet code” follows this standard convention, you then you simply do not have to worry about what thread it is running on. The actor’s thread will not be blocked while the request runs.
Taking this a step further, the fact that the API is its own actor is immaterial to the question. Even if API was on the main actor, because the network request runs asynchronously, whatever the thread used by API will not be blocked.
(NB: The above assumes that the “complex internet code” has followed conventional asynchronous network programming patterns. We obviously cannot comment further without seeing a representative example of this code.)
See WWDC 2021 video Swift concurrency: Behind the scenes if you are interested in how Swift concurrency manages threads for us.
The Task is the bridge between the Async and the Synchronous world. The completion block of Button being the synchronous world and the postMessage method as being the asynchronous world.
The Task is run on the #MainActor however since we await the result of api.postMessage it may suspend and the main thread is not blocked.
Thanks to Quang Hà and Lorem Ipsum.
Useful information regarding this question from WWDC.
From Discover concurrency in SwiftUI (Timecode - 7:24)
To update correctly, SwiftUI needs these events to happen in order: objectWillChange, the ObservableObject’s state is updated, and then the run loop reaches its next tick. If I can ensure that these all happen on the main actor, I can guarantee this ordering. Prior to Swift 5.5, I might have dispatched back to the main queue to update my state, but now it’s much easier. Just use await! By using await to make an async call from the main actor, I let other work continue on the main actor while the async work happens. This is called “yielding” the main actor.
From Meet async/await in Swift (Timecode - 22:13):
The solution is to use the async task function. An async task packages up the work in the closure and sends it to the system for immediate execution on the next available thread, like the async function on a global dispatch queue.
From Protect mutable state with Swift actors (Timecode - 7:25)
If the actor is busy, then your code will suspend so that the CPU you're running on can do other useful work. When the actor becomes free again, it will wake up your code -- resuming execution -- so the call can run on the actor. The await keyword in this example indicates that the asynchronous call to the actor might involve such a suspension.
From Protect mutable state with Swift actors (Timecode - 23:47)
When you are building an app, you need to think about the main thread. It is where the core user interface rendering happens, as well as where user interaction events are processed. Operations that work with the UI generally need to be performed from the main thread. However, you don't want to do all of your work on the main thread. If you do too much work on the main thread, say, because you have some slow input/output operation or blocking interaction with a server, your UI will freeze. So, you need to be careful to do work on the main thread when it interacts with the UI but get off the main thread quickly for computationally expensive or long-waiting operations. So, we do work off the main thread when we can and then call DispatchQueue.main.async in your code whenever you have a particular operation that must be executed on the main thread.
If you know you're already running on the main thread, you can safely access and update your UI state. If you aren't running on the main thread, you need to interact with it asynchronously. This is exactly how actors work.
The main actor is an actor that represents the main thread. It differs from a normal actor. The main actor performs all of its synchronization through the main dispatch queue. This means that, from a runtime perspective, the main actor is interchangeable with using DispatchQueue.main.

GCD serial queue like approach using swift async/await api?

I am adopting the new async/await Swift API. Things work great.
Currently in my apps I am using GCD serial queues as a pipeline to enforce tasks to happen serially.
For manipulating data of some critical models in the app I use a serial queue accessible from anywhere in the app:
let modelQueue = DispatchQueue(label: "com.myapp.updatemodelqueue")
Anytime a model needs to modify some model data I dispatch to that queue:
modelQueue.async {
// ... model updates
}
With the new async/await changes I am making I still want to force model actual updates to happen serially. So, for example, when I import new model data from the server I want to make sure they happen serially.
For example, I may have a call in my model that looks like this:
func updateCarModel() async {
let data = await getModelFromServer()
modelQueue.async {
// update model
}
}
Writing that function using that pattern however would not wait for the model update changes because of the modelQueue.async. I do not want to use modelQueue.sync to avoid deadlocks.
So then after watching WWDC videos and looking at documentation I implemented this way, leveraging withCheckedContinuation:
func updateCarModel() async {
let data = await getModelFromServer()
await withCheckedContinuation({ continuation in
modelQueue.async {
// update model
continuation.resume()
}
})
}
However, to my understanding withCheckedContinuation is really meant to allow us to incrementally transition to fully adopt the new async/await Swift API. So, it does not seem to be what I should use as a final approach.
I then looked into actor, but I am not sure how that would allow me to serialize any model work I want to serialize around the app like I did with a static queue like shown above.
So, how can I enforce my model around the app to do model updates serially like I used to while also fully adopting the new await/async swift API without using withCheckedContinuation?
By making the model an actor, Swift synchronizes access to it' shared mutable state. If the model is written like this:
actor Model {
var data: Data
func updateModel(newData: Data) {
data = newData
}
}
The updateModel function here is synchronous, it's execution is uninterrupted after it's invoked. Because Model is an actor, Swift restricts you to treat it as if you are calling an asynchronous funtion from the outside. You'd have to await, which results in suspension of your active thread.
If in case you'd want to make updateModel async, the code within will always be synchronous unless if you explicitly suspend it by calling await. The order of execution of multiple updateModel calls is not very deterministic. As far as you don't suspend within the updateModel block, it is sure that they execute serially. In such case, there is no use making the updateModel async.
If your update model code is synchronous you can make your model actor type to synchronize access. Actors in swift behave similar to serial DispatchQueue, they perform only one task at a time in the order of submission. However, current swift actors are re-entrant, which means if you are calling any async method actor suspends the current task until the async function completes and proceeds to process other submitted tasks.
If your update code is asynchronous, using an actor might introduce data race. To avoid this, you can wait for non-reentrant actor support in swift. Or you can try this workaround TaskQueue I have created to synchronize between asynchronous tasks or use other synchronization mechanisms that are also created by me.

Async Issue for DbContext used in constructor of objects created via DI

I wonder if someone can clarify when to await and when not to. Consider this code
public Task<List<User>> GetUsersForParent(int someParentId)
{
var qry = Context.Users.Where(u=>u.parent = someParentId)
.OrderBy(u=>u.Surname)
return FilterActive(qry);
}
//Actually in a generic base class, but not important (I don't think)
protected Task<List<T>> FilterActive(IQueryable<T> query) where T: BaseEntity
{
return query.Where( q=>q.Active == true ).ToListAsync();
}
Then it is used like this
var users = await DbHandler.GetUsersForParent(1);
So the calling method is awaited, but the others are not. Is this correct?
Should the method calling the ToListAsync() be awaited? (this I assume is now doing the work)
My reason for this is I am getting the DbContext is being used by a second thread dreaded exception. I am running out of places to look. My understanding is the methods are building up the whole task which is executed, but could this be messing with the dbContext?
Edit re DbContext error
Having narrowed down the potential locations for the issue, via Debug.Print and SQL Query profiling (just in case that helps anyone else) I can see one statement being profiled (the next in profile is logging the exception) and I can see two methods being run via the debug print.
One of these methods is a PermissionsManager which, when constructed, initialises itself and loads the user data. This is constructed when requested via the DI framework.
The other method is the single query on the OnGet() method for the page. It is running a single query to get an entity by ID, it is awaited correctly.
My working theory at the moment is that the Thread running the DI construction and another thread running the Page initialise are colliding.
When I made the PermissionManager just _person = new Person() // await db.users.get(userid) the issue goes away. I could replicate the issue 1 in 2 or 3 times of refresh, and with that commented I could not replicate, despite refreshing the page 30+ times.
So my real question with async / await is probably more about DI injection and is that construction running on a different thread? if so, any best practice to avoid?
So the calling method is awaited, but the others are not. Is this correct?
I generally recommend using the async and await keywords, and only return the tasks directly if the method is extremely simple.
My reason for this is I am getting the DbContext is being used by a second thread dreaded exception. I am running out of places to look. My understanding is the methods are building up the whole task which is executed, but could this be messing with the dbContext?
No. At least, the code you posted cannot cause that exception. Whether the async/await keywords are used, or whether the tasks are returned directly, the methods are asynchronous and they do not attempt to do more than one thing on the dbcontext at once.
It's possible that your problem is further up the stack. Task.WhenAll is a good thing to search for when tracking this down.
Should the method calling the ToListAsync() be awaited? (this I assume is now doing the work)
If you await the contents of either method you will be returning the result type, not Task of result type which means the execution cannot be deferred.
Your error will be coming up because you either have multiple threads interacting with the same instance of DbContext, awaited or no this would cause problems, that or you have some code calling the ToListAsync()-containing method, or another async DbContext operation without awaiting.
Writing an EF data access layer returning Task is fairly dangerous which can shoot you in the foot very easily.
Given your code structure I would recommend a couple small changes:
public async Task<List<User>> GetUsersForParent(int someParentId)
{
var qry = Context.Users.Where(u=>u.parent = someParentId)
.OrderBy(u=>u.Surname);
qry = FilterActive(qry);
return await qry.ToListAsync();
}
protected IQueryable<T> FilterActive(IQueryable<T> query) where T: BaseEntity
{
return query.Where( q=> q.Active == true );
}
Notably here I would avoid returning Task to reduce risks of improper use and potentially intermittent bugs. The base-class method for FilterActive can return IQueryable<T> to apply the filter without triggering the execution of the operation. This way FilterActive can be applied whether you want a List, a Count, or simply do an Exists check.
Overall I would recommend exploring patterns that return IQueryable<TEntity> rather than List<TEntity> etc. as the later results in either a lot of limitations for performance and flexibility, or requires a lot of boiler-plate code to handle things like:
Sorting,
Pagination,
Getting just a Count,
Performing an Exists check,
Configurable filtering,
Selectively eager loading related data, or
Projection to generate efficient queries
Doing this with methods that return List<TEntity> either results in very complex code to support some of the above considerations, has these operations applied post-execution leading to heavier queries than would otherwise be needed, or requires a lot of near-duplicate code to handle each scenario.
So the constructor thing was a red herring. It was a missing await after all, just not where expected and in code that was unchanged.
I tracked down the culprit. There was a method in the basePage which hooked into the Filter of MVC pages. It took the user and loaded their permissions, however, since this loading of user permissions was made async, this method did not get awaited (it didn't need it before as was synchronous). I moved it to one of the async events on the page life cycle and all seems happy now (with a suitable await!). So it was a missing await, but the moral of the story is any time you make a sync method async, check what the heck is actually using it!

Get Context instance from DbContextPool (EF Core 2.0) to use it in Task

Entity framework core 2.0 introduce DbContext Pooling.
In my code I do a lot of jobs in Tasks because I do some independent heavy operations on database.
My old approach was:
Task.Run(() =>
{
AppDbContext c = new AppDbContext(this.config);
How can I get instance from EF Core 2.0 DbContext Pooling?
Edited:
I am using DI: public CategoryController(AppDbContext context, ...
Reason for doing this is quicker execute Rest API method.
For example, I think this should complete quicker
List<AppUser> users;
List<DbGroup> groups;
Task task1 = Task.Run(async() => {
users = await ContextFromConnectionPool.Users.Where(t => t.Id == 1).ToListAsync();
});
Task task2 = Task.Run(async () => {
groups = await ContextFromConnectionPool.Groups.Where(t => t.Id == 1).ToListAsync();
});
var tags = await this.context.Tags.ToListAsync();
Task.WaitAll(task1, task2);
//process all 3 results
then this:
List<AppUser> users = await this.context.Users.Where(t => t.Id == 1).ToListAsync();
List<DbGroup> groups = await this.context.Groups.Where(t => t.Id == 1).ToListAsync();
var tags = await this.context.Tags.ToListAsync();
//process all 3 results
In second example second query executes after first is completed.
If every query takes 150ms in first example method execute in approx 150ms, but second in approx 450ms. Am I right?
Only problem is how to get context from connection pool in first approach.
The feature of ASP.NET Core 2.0 and Entity Framework Core 2.0, to support connection pooling, is not — in any way — preventing you from doing the time consuming queries at once. The entire concept of pooling is to allow the connection to be reused in multiple requests, instead of having to recreate an instance each time a new request comes in. Sometimes, it can have benefits and sometimes it might have downfalls. Now, for your question, there are two pathways,
Allow the framework to pool the connection in Startup class and then reuse those objects everywhere you need. You can capture them inside the actions, and any other private or local functions that you have.
Do not use DI and database context pooling and instead keep doing what you were doing. Note that, you were never using DI and thus there is no need to register your database context in the Startup class. But you must take care of creation of instance, manually disposing the instance as well.
Second approach is not suitable, and not a good approach as well, for many reasons. If you want to consider the first approach you can then change your controller to accept a property of the type database context, such as,
public class YourController : Controller {
public AppDbContext c { get; set; }
public YourController (AppDbContext c) {
this.c = c;
}
}
Now if you have got that, you can then use this c variable inside your tasks, and run the time consuming queries inside that function — which in any way would be too useless. You can do this,
Task.Run(() =>
{
// Use c here.
});
Just remember a few points:
It is good to build your query, and then call ToListAsync() — ToList() may not be suitable, consider using ToListAsync() and apply await keyword for asynchronously capturing the data.
Your query only gets executed on the database server, when you call ToList or any similar function.
While running tasks in parallel, you must also handle any cases where your query might break the policies, such as data integrity or similar cases in database. It is always a best practice to catch the exceptions.
In your case, for just better practicing you might want to consider wrapping your code inside using block,
Task.Run(() => {
using (var context = new AppDbContext) {
// use context here.
}
}
This is the best that I can state to help you, since you have not shared 1) purpose of not using DI, 2) the sample of your query (why not using LINQ to build query and then executing on server?) 3) any sample code to be used. I hope this would give you an idea of, why you should consider using DI and using the instances returned from there.