Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Thread-safe GetModelAsync #306

Closed
wants to merge 3 commits into from
Closed

Thread-safe GetModelAsync #306

wants to merge 3 commits into from

Conversation

rayao
Copy link
Contributor

@rayao rayao commented Mar 7, 2016

This ONLY synchronizes multiple simultaneous calls to model building pipeline, WebApiConfig still could return before a long running GetModelAsync completes.
Partially resolve #304

This ONLY synchronizes multiple simultaneous calls to model building pipeline, WebApiConfig still could return before a long running GetModelAsync completes.
var builder = context.GetApiService<IModelBuilder>();
if (builder == null)
{
return null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should throw if there is no model builder? It is more meaningful than a later null-reference exception.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, thanks.
The message I put into Resources is "IEdmModel cannot be generated since API service IModelBuilder is not registered."
Let me know better description.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds good:-) Thanks.

@lewischeng-ms
Copy link
Contributor

So basically this PR is to make any call to GetModelAsync to wait for the first one to complete? If we need to sync with the later requests, we have to register a model factory to the underlying OData Web API rather than the model instance itself. Is my understanding correct?

source.SetResult(model);
return model;
}
catch (AggregateException e)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we going to do anything special for an AggregateException in the future? If not, we may consider merge the two catch-clauses.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we catch AggregatedException as normal Exception, and SetException to the task, the task will wrap it again and so ends with an AggreatedException, which has an inner AggregatedException, which has the actual exceptions as its InnerExceptions.

@rayao
Copy link
Contributor Author

rayao commented Mar 13, 2016

So basically this PR is to make any call to GetModelAsync to wait for the first one to complete? If we need to sync with the later requests, we have to register a model factory to the underlying OData Web API rather than the model instance itself. Is my understanding correct?

Perhaps I don't get your idea clearly, since GetModelAsync works on ApiConfiguration, so calling it with any model instance will be synchronized. So, if a query needs EdmModel to complete, an await GetModelAsync will guarantee that.
The real problem is the async nature of GetModelAsync, since WebApiConfig.Register is synchronous, it can't wait Restier route to be completely established, for example:

        public static async void RegisterNorthwind(
            HttpConfiguration config, HttpServer server)
        {
            await config.MapRestierRoute<NorthwindApi>(
                "NorthwindApi", "api/Northwind",
                new RestierBatchHandler(server));
        }

RegisterNorthwind could have returned before the route is completely registered. So if the model generation is really costly, RESTier routing will likely get messed up.
You can put an await Task.Delay(5000) before the config.Map call to repro.

Throw on IModelBuilder missing.
@lewischeng-ms
Copy link
Contributor

@rayao Yeah, I understand your concern about requests coming before the route has been completely registered. But in this case, since the route has not been registered yet. The service could safely return 404 for those requests. And what I was thinking about was that you can't process any OData request without getting the model instance (parse URI or serialization/deserialization). Now the model instance is registered staticly into ODataPathRouteConstraint. If we could register a factory method to produce the model into Web API and let the factory method call await GetModelAsync(), the service could probably wait for model build completion then process the first incoming request.

@lewischeng-ms
Copy link
Contributor

I've no other questions, please feel free to merge it.

@rayao rayao closed this Mar 14, 2016
@rayao rayao deleted the model branch March 14, 2016 08:46
@rayao
Copy link
Contributor Author

rayao commented Mar 14, 2016

But in this case, since the route has not been registered yet. The service could safely return 404 for those requests

@lewischeng-ms This is only true for synchronous long running GetModelAsync call (like all of our existing samples, although they return task but they're synchronous in nature). A truly async GetModelAsync call won't have the chance to registered a route before WebApiConfig pipeline ends, and that results in RESTier routing not established, no further requests could be processed.
You can add an await Task.Delay(1000) in model pipeline to see the effect.

If we could register a factory method to produce the model into Web API and let the factory method call await GetModelAsync()

Yeah I initially thought that, but since the route selection pipeline is synchronous, we can't await.
I've checked in this change and working on another PR which could possibly be the resolution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

If GetModelAsync takes too long to complete, requests will fail.
4 participants