ChilliCream / graphql-platform

Welcome to the home of the Hot Chocolate GraphQL server for .NET, the Strawberry Shake GraphQL client for .NET and Banana Cake Pop the awesome Monaco based GraphQL IDE.
https://chillicream.com
MIT License
5.23k stars 745 forks source link

.net classic middleware #322

Closed namnm closed 5 years ago

namnm commented 5 years ago

Hi, I'm running into some memory leak issues now. I wonder if you guys have any document or suggestion about debugging and best practice when using HotChocolate? May be passing a function to the schema to call and clean up after executed? Thanks in advance.

michaelstaib commented 5 years ago

Hmm, are you running our preview version? If yes you will have access to our new diagnostic source that can be used to track performance data on request and field resolver base.

If you could give us more detail and access to code we would be willing to review and help you.

Are the memory leaks occurring when there are lots of requests or just over time? It would be great if you could provide more detail.

We have a slack channel and could discuss details there in more detail If you want to: https://join.slack.com/t/hotchocolategraphql/shared_invite/enQtNDUyMjIxOTc1MTU3LWNlN2ViMGM5YTVhMWI3MzcwNDdjOTVkYWQ5ODZkMjJiODIwZjY5MzYzODc1Zjg5NDE5ZDQ4Zjk0MGExZWMzZGY

namnm commented 5 years ago

I have tested again with the simplest version of a working endpoint, and after 5-6 times of querying, I got 1.2GB memory in use for the whole process, it didnt drop down after 10 minutes too. Edit: After about 20 minutes, it has dropped down to 250MB Here is the code that I used:

    public class Query {
        public string Hello() => "world";
    }
    public class GraphQLController : Controller {
        [HttpGet]
        public ActionResult Index() {
            return View("GraphiQL");
        }
        [HttpPost]
        public ActionResult Index(GraphQLRequest req) {
            var schema = Schema.Create(c => {
                        c.RegisterType(new ObjectType<Query>());
                        c.Options.ExecutionTimeout = TimeSpan.FromSeconds(120);
                        c.Options.DeveloperMode = true;
            });
            var res = schema.Execute(req.Query, req.Variables) as IQueryExecutionResult;
            return Content(res.ToJson(), "application/json");
        }
    }
michaelstaib commented 5 years ago

Oh, I see the problem. Why are you not using our middleware instead of putting the schema in your controller? There are several issues with your approach.

First, you need to know that each time you create a schema we compile all the resolvers in the background. We basically create an in-memory assembly that contains all the generated resolvers. This makes the resolvers much faster than with reflection.

So, by creating a schema each time a call comes in you create lots of in-memory assemblies. A schema should be a singleton and registered with your dependency injection.

Look at this example it shows how you should setup a server with our middleware: https://github.com/ChilliCream/hotchocolate-templates/tree/master/Server/content

The middleware will also provide support for all request types (GET, POST and WebSocket). Moreover we have integrated GraphiQL as middleware.

The schema will be much faster with the middleware.

namnm commented 5 years ago

Yes but right now I'm working on a legacy project, and it use mvc5 instead of asp.net core, I'm not sure how to setup middleware and configure it in mvc5?

namnm commented 5 years ago

Even if I use it as my way, it should not keep inscreasing memory and not drop down like that, it should release all the memory after the request completed?

michaelstaib commented 5 years ago

Ok, we are introducing the classic middlewares with 0.7.0.

But to get your thing running you could just add schema to your dependence injection. Then also add QueryExecuter to your dependency injection. Both have to be singletons.

Inject the QueryExecuter into your controller:

public class GraphQLController : Controller {
       public QueryExecuter _executer;

       public GraphQLController(QueryExecuter executer)
       {
            _executer = executer;
       }

        [HttpGet]
        public ActionResult Index() {
            return View("GraphiQL");
        }

        [HttpPost]
        public ActionResult Index(GraphQLRequest req) {
             IExecutionResult result = await _executer
                .ExecuteAsync(request, context.RequestAborted)
                .ConfigureAwait(false);

            return Content(result.ToJson(), "application/json");
        }
    }

This should solve the issue.

michaelstaib commented 5 years ago

One issue is that schema is not disposed. The other issue is that assemblies cannot be unloaded. So, in order to do that the schema would have to be moved into an appdomain. But all of this is now problem if you register it as singleton with your dependency injection provider.

michaelstaib commented 5 years ago

I have created a repo for the classic middleware and we will add a rudimentary middleware for you today. I think that will be a much better experience for you.

https://github.com/ChilliCream/hotchocolate-aspnetclassic

It is empty at the moment and the code that we will add today will be preview code. I hope that will help you.

rstaib commented 5 years ago

I have recently published the first version 0.6.2 of the ASP.Net classic OWIN middleware released. Although there is currently no subscription support. I suppose we will bring it in the next minor version 0.7.0.

rstaib commented 5 years ago

Issue #348 brings support for subscriptions to ASP.Net classic.