0

So I have a stateless Asp.Net Core REST Web API that seems to have a memory leak... which seems strange to me, but the evidence is pretty compelling.

http://prntscr.com/ge9k2l

This is a screenshot of my Azure portal showing the RAM slowly going up.

Now my API is pretty standard... CRUD, lots of data retrieval... not a lot of crazy logic or anything, but it gets a good amount of traffic (about 17 requests per second on average). Eventually the server gets to the point where I have to restart it so that the memory is released.

Below is a typical Save method...

Again, the API is stateless so I don't really cache or hold onto any data... just do what I gotta do and give back the data.

Thoughts?

    [HttpPost]
    public IActionResult Save([FromBody]QuizViewModel quiz)
    {
        if (ValidateAdmin() == null)
            return StatusCode(401);

        try
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            Quiz dbQuiz = null;

            if (quiz.ID == new Guid())
            {
                LogInfo("Saving new quiz: Name [" + quiz.Name + "]");

                // new 
                dbQuiz = new Quiz();
                dbQuiz.ID = Guid.NewGuid();
                dbQuiz.CreatorID = quiz.CreatorID;
                dbQuiz.CreateDate = DateTime.UtcNow;
                _quizRepository.Add(dbQuiz);
            }
            else
            {
                LogInfo("Updating quiz: ID [" + quiz.ID + "], Name: [" + quiz.Name + "]");
                // update
                dbQuiz = _quizRepository.GetSingle(x => x.ID == quiz.ID, y => y.Questions);
                dbQuiz.UpdateDate = DateTime.UtcNow;
                dbQuiz.UpdaterID = quiz.CreatorID;
            }

            _quizRepository.ManageQuestions(dbQuiz, quiz.Questions);

            dbQuiz.Name = quiz.Name;
            dbQuiz.LessonID = quiz.LessonID;
            dbQuiz.Instructions = quiz.Instructions;
            dbQuiz.ImagePath = quiz.ImagePath;
            dbQuiz.VideoPath = quiz.VideoPath;

            _quizRepository.Commit();

            quiz = Mapper.Map<Quiz, QuizViewModel>(dbQuiz);

            CreatedAtRouteResult result = CreatedAtRoute("Save", new { controller = "Quiz", ID = quiz.ID }, quiz);

            LogInfo("Quiz saved: ID [" + dbQuiz.ID + "]");
            return new OkObjectResult(result);
        }
        catch (Exception ex)
        {
            HandleError(ex);
            return BadRequest(ex);
        }
    }
David
  • 2,173
  • 3
  • 25
  • 36
  • 1
    You can't be sure that's a memory leak, the garbage collector can decide to not to delete the objects if it has enough resources. Ad a `GC.Collect()` before each return and do the test again. – Gusman Aug 29 '17 at 01:28
  • Also, unless you post your full api is impossible to determine if there's a memory leak as you are showing only one of the functions from the api. – Gusman Aug 29 '17 at 01:29
  • 1
    Does the program eventually crash or throw an exception? Can you create a dump file and check what is using all the memory? From your screenshot it doesn't look like a significant increase. – Xela Aug 29 '17 at 01:30
  • Stateless refers to requests & responses between client/server, not "the the server doesn't retain anything ever". if you have DI (which you do due to it being webapi), and the DI is holding a reference to an object returned from a request, it could build up in memory, even though the application is still "stateless". As @Gusman said, it's not easy to determine location of a leak from a small snippet of code. – AndrewP Aug 29 '17 at 01:43
  • I didn't think y'all would want my full API :) I included the snippet just to give an idea of the type of calls we have. @xela yes, eventually the server will run out of memory and crash... takes about a day. The data dump is a good idea. Will do that and report back. Thanks. – David Aug 29 '17 at 13:45

1 Answers1

1

Ok... so, in case anyone runs into this in the future... the memory link turned out to be a configuration issue. I was instantiating the config objects too often and they weren't being let go...

The eventual solution was to create a singleton for the config. You can see the solve here:

ASP.Net Core 2 configuration taking up a lot of memory. How do I get config information differently?

David
  • 2,173
  • 3
  • 25
  • 36