question

Kmcnet-3080 avatar image
0 Votes"
Kmcnet-3080 asked AgaveJoe answered

Troubleshooting 11205 Errors

Hello everyone and thanks for the help in advance. I have a web application that is an endpoint for Twilio phone api to make calls to. The endpoint makes three SQL database calls using EntityFramework Core. The server is running Windows Server 2022. The database calls are really not terribly intensive and I will be happy to post those, but am trying to start off simply. The calling application to my api has a timeout of 10 seconds which to me is inconceivable this is happening. But I need to start troubleshooting this problem and really don't know how to get started (that includes understanding IIS logs). Any help would be appreciated.

dotnet-aspnet-core-mvc
· 2
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Hi,@Kmcnet-3080,does the timeout error occur in development enviroment?

0 Votes 0 ·

According to Twilio support docs the timeout error (11205 ) is due to your application not responding.

https://www.twilio.com/docs/api/errors/11205

Where is your application located and can a server reach your application? Is the domain registered?

0 Votes 0 ·
Kmcnet-3080 avatar image
0 Votes"
Kmcnet-3080 answered AgaveJoe edited

Yes, the domain is registered and is reachable. In fact, the endpoint in question actually logs the incoming call to the database as expected, and triggers the expected business logic. The last portion of the code in the endpoint that returns a TwiML (basically xml response) is where the failure appears to be occurring.

· 3
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

This is probably a question for Twilio.

Have you tried hitting the end point yourself? Is the endpoint actually returning the expected results?

0 Votes 0 ·

Stupid question, but not exactly sure how to do that given its data from an outside source. I guess try to emulate a PSOT using Postman?

0 Votes 0 ·

Usually when dealing with services the INs and OUTs are well known. If you do not know what to expect then that's probably an issue. But yes, PostMan is a good way to test an HTTP response.

Again twilio is a better support option.

https://www.twilio.com/docs/usage/troubleshooting/debugging-your-application

0 Votes 0 ·
Kmcnet-3080 avatar image
0 Votes"
Kmcnet-3080 answered AgaveJoe edited

So I think I solved the problem but wasn't expecting the problem. It all came down to a simple database look performed in the controller. The lookup searched a database of approximately 30000 records for a match of the posted fields. This apparently was causing the controller to not respond quickly enough. I inadvertently wrote the lookup as synchronous rather than async. Once I changed this to async, the problem resolved. But I am now wondering why the lookup is causing such a bottleneck. The search uses EF Core 6.0.

· 1
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

But I am now wondering why the lookup is causing such a bottleneck.

We cannot see your database schema or LINQ query. My first guess is the tables not optimized for search being performed. Perhaps you need to add an index.



0 Votes 0 ·
Kmcnet-3080 avatar image
0 Votes"
Kmcnet-3080 answered Kmcnet-3080 commented

The search fields are definitely not indexed. I guess this now migrates to questions about index. This is currently the only application searching this table in this way. I did not implement indexing because the phone number being searched could appear in more than one record, i.e. not unique. Am I misunderstanding indexing concepts?

· 4
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

OK. Since my latest post, the application has begun to fail again. Not sure why this would cause a delayed response given I now have it running asynchronously. The controller looks like this:

         [HttpPost]
         public TwiMLResult InboundVoice(IncomingVoice model)
         {
             var response = new VoiceResponse();
    
             GetForwardingNumber getForwardingNumber = new GetForwardingNumber();
             string forwardingPhone = getForwardingNumber.forwardingNumber.ToString();
    
             GetClientssByPhoneNumberAsync getClientsByPhoneNumberAsync = new GetClientsByPhoneNumberAsync();
             var task = getClientsByPhoneNumberAsync.GetClientsByPhoneNumber(model.From);
    
             LoginboundVoiceAsync loginboundVoiceAsync = new LoginboundVoiceAsync();
             var voicetask = loginboundVoiceAsync.LoginboundVoice(model);
    
             response.Say("Calls may be recorded for quality assurance.");
    
             response.Dial(forwardingPhone);
    
             return TwiML(response);
    
         }


The phone lookup must be done synchronously as it is needed to determine the forwarding number.




0 Votes 0 ·

OK. Since my latest post, the application has begun to fail again. Not sure why this would cause a delayed response given I now have it running asynchronously.

I think you misunderstand what asynchronous means. An asynchronous method does not magically speed up processing. Asynchronous means without timing or no reason to wait for a response. The system will let you know when the response returns. This is similar to ordering pizza delivery. After ordering pizza delivery you can watch your favorite sports on the TV. When the door bell rings you know the pizza is waiting at the door. Ordering pizza delivery does not speed up the time it takes to get pizza delivered but you can do something else, like watch sports, at the same time.

The code you've shared has the word "Async" but there are no awaits and the action does not define an async Task<T> response so I doubt the code is async. But, I can't see the actual methods so who knows what's going on.

0 Votes 0 ·

Here is the method:

     public class GetClientsByPhoneNumberAsync
     {
         public async Task<int> GetClientsByPhoneNumber(string phoneNumber)
         {
             try
             {
    
                 List<tblPtmstr1> Clients = new List<tblPtmstr1>();
    
                 using (var ctx = new myDbContext())
                 {                    
                     Clients = await(ctx.tblClients.FromSqlRaw("Exec sp_GetCleintByPhoneNumber @PhoneNumber", new SqlParameter("@PhoneNumber", phoneNumber)).ToListAsync());
    
                 }
                 string ClientList = "";
                 foreach (var client in Clients)
                 {
                     ClientList += client.ClientID + "-" + client.ClientLastName + ", " + client.ClientFirstName + "; ";
                 }
                 if (!(PatientList == ""))
                 {
                     ClientList += phoneNumber;
                 }
    
                 SendIncomingCallAlert sendIncomingCallAlert = new SendIncomingCallAlert(ClientList);
    
             }
             catch (Exception ex)
             {
    
             }
             return 0;
         }
0 Votes 0 ·

Corrected method. Disregard previous:

     public class GetClientsByPhoneNumberAsync
     {
         public async Task<int> GetClientsByPhoneNumber(string phoneNumber)
         {
             try
             {
    
                 List<tblClients> Clients = new List<tblPtmstr1>();
    
                 using (var ctx = new myDbContext())
                 {                    
                     Clients = await(ctx.tblClients.FromSqlRaw("Exec sp_GetCleintByPhoneNumber @PhoneNumber", new SqlParameter("@PhoneNumber", phoneNumber)).ToListAsync());
    
                 }
                 string ClientList = "";
                 foreach (var client in Clients)
                 {
                     ClientList += client.ClientID + "-" + client.ClientLastName + ", " + client.ClientFirstName + "; ";
                 }
                 if (!(PatientList == ""))
                 {
                     ClientList += phoneNumber;
                 }
    
                 SendIncomingCallAlert sendIncomingCallAlert = new SendIncomingCallAlert(ClientList);
    
             }
             catch (Exception ex)
             {
    
             }
             return 0;
         }
0 Votes 0 ·
AgaveJoe avatar image
0 Votes"
AgaveJoe answered Kmcnet-3080 commented

There no way to say it other than the design is really bad. The GetClientsByPhoneNumber can fail silently. If this method takes a long time to execute it could cause a race condition with other parts of the code. Is GetClientsByPhoneNumber a send and forget method?

The LoginboundVoice() method could have the same issue as above. If there is any dependency between GetClientsByPhoneNumber() and LoginboundVoice() then you do have a race condition.

The GetForwardingNumber() logic is unknown so I can't comment on the code. Nor can I comment on the Twilio specific logic.

Lastly, you should be able to optimize the sp_GetCleintByPhoneNumber if the procedure is the actual bottleneck.

Am I misunderstanding indexing concepts?

I don't know what you know about indexes. Execute the stored procedure from SSMS to see how long it take to execute.



· 1
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Let me bite off small pieces of this. First, do I have the async set up correctly that will allow both loginboundVoiceAsync and GetClientsByPhoneNumberAsync to run asynchronously and not block the execution of the next line of code?

The GetForwardingNumber() returns a query from a one row one column table that returns a default value in the event of query failure. I have tested this code in live use and that has never pointed to be the problem. I will unit test to get an idea of execution time. Code is:

         public string forwardingNumber { get; set; }
         public GetForwardingNumber()
         {

             using (var ctx = new myDbContext())
             {
                 var entity = ctx.tbl_Log_ForwardingNumber.FirstOrDefault(item => item.ID == 1);
                 forwardingNumber = entity.PhoneNumber.ToString();
             }
    
         }

There is absolutely no dependence between GetClientsByPhoneNumber() and LoginboundVoice(). LogInboundVoice() is purely a database add.

GetClientsByPhoneNumber is as you say, a send and forget method that simply sends a text message to the forwarding phone.

0 Votes 0 ·
AgaveJoe avatar image
0 Votes"
AgaveJoe answered Kmcnet-3080 commented

Let me bite off small pieces of this. First, do I have the async set up correctly that will allow both loginboundVoiceAsync and GetClientsByPhoneNumberAsync to run asynchronously and not block the execution of the next line of code?

You code is like order pizza delivery put moving to a new location before the pizza guy arrives. There's no one home answer the door. Even worse it is unknown if the pizza was made or if the pizza guy showed up.

The GetForwardingNumber() returns a query from a one row one column table that returns a default value in the event of query failure.

There's no logical reason to execute the query since the the ID filter is hardcoded. Just store the number in configuration.

GetClientsByPhoneNumber is as you say, a send and forget method that simply sends a text message to the forwarding phone.

The way the code is designed, the original request context could be disposed while the GetClientsByPhoneNumber is executing. I believe you've created a race to complete before the object is torn down.

I'm pretty sure you need to create a back ground task to make sure the logic completes.

· 3
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

I do love pizza! Sadly, this is not the topic. As I stated earlier, the GetForwardingNumber() returns a single stored column from a single stored row. The hardcode ID 0f 1 is superfluous since the field itself is updated regularly from another application that updates the data field with the forwarding phone number. So I'm not really sure I can do much more than that and it is a mission critical component for the application to work. So if I understand what you are saying, having multiple context open might be causing the block. So I suppose I could create one context and perform each lookup within the single context, the proceed with the processing. My thinking was making the GetForwardingNumbers and LodIncomingVoice async was to allow them to go off and complete their tasks when they can as they are not time sensitive within the application. The mission critical parts are the forwarding phone lookup that allows the call to be forwarded to the correct recipient. So I'm not sure its as messed up as you think, but I am open to solutions because it is obviously causing problems. And I will always be home for pizza. Thanks for the help

0 Votes 0 ·

So if I understand what you are saying, having multiple context open might be causing the block.

No. I'm warning you about the general design. The classes instantiated within the InboundVoice action are torn down when the action returns. Classes like GetClientsByPhoneNumberAsync are in jeopardy of being garbage collected before completing their tasks.

However, changing to async logic did not fix the issue.

I can't troubleshot your logic. You need to find the code that is taking over 10 seconds to execute. Add logging by writing the DateTime at each step within the InboundVoice action. This will tell you which step or steps cause the bottleneck. Then focus on fixing the bottleneck.

You can certainly test the GetForwardingNumber class by itself.

0 Votes 0 ·

Please explain what exactly you mean by logging. IIS logging or some other third party, or something else?

0 Votes 0 ·
AgaveJoe avatar image
0 Votes"
AgaveJoe answered Kmcnet-3080 commented

I read the twilio documentation. Are you sure you are using Dial correctly?

 response.Dial(forwardingPhone);

https://www.twilio.com/docs/voice/twiml/dial

· 1
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Yes. This I am positive this is correct.

0 Votes 0 ·
AgaveJoe avatar image
0 Votes"
AgaveJoe answered Kmcnet-3080 commented

I recommend fixing your C# code so it follow standards. I can't test your code becasue I do not have all the source but your action should have the following async/await pattern.

Asynchronous programming

  [HttpPost]
  public async Task<TwiMLResult> InboundVoice(IncomingVoice model)
  {
      var response = new VoiceResponse();
    
      GetForwardingNumber getForwardingNumber = new GetForwardingNumber();
      string forwardingPhone = getForwardingNumber.forwardingNumber.ToString();
         
      var tasks = new List<Task<int>>();
      GetClientssByPhoneNumberAsync getClientsByPhoneNumberAsync = new GetClientsByPhoneNumberAsync();
      tasks.Add(getClientsByPhoneNumberAsync.GetClientsByPhoneNumber(model.From));
    
      LoginboundVoiceAsync loginboundVoiceAsync = new LoginboundVoiceAsync();
      tasks.Add(loginboundVoiceAsync.LoginboundVoice(model));
         
      response.Say("Calls may be recorded for quality assurance.");
    
      response.Dial(forwardingPhone);
         
      await Task.WhenAll(tasks);
      return TwiML(response);
  }

You tagged this thread in asp.net Core. You should use dependency injection for the DbContext rather than a using block. I would also catch the exception.

Dependency injection in ASP.NET Core

  public class GetClientsByPhoneNumberAsync
  {
      private readonly myDbContext _ctx;
      public GetClientsByPhoneNumberAsync(myDbContext ctx) 
      {
          _ctx = ctx
      }
      public async Task<int> GetClientsByPhoneNumber(string phoneNumber)
      {
          try
          {
              List<tblPtmstr1> Clients = await(_ctx.tblClients.FromSqlRaw("Exec sp_GetCleintByPhoneNumber @PhoneNumber", new SqlParameter("@PhoneNumber", phoneNumber)).ToListAsync());
    
              string ClientList = "";
              foreach (var client in Clients)
              {
                  ClientList += client.ClientID + "-" + client.ClientLastName + ", " + client.ClientFirstName + "; ";
              }
              if (!(PatientList == ""))
              {
                  ClientList += phoneNumber;
              }
    
              SendIncomingCallAlert sendIncomingCallAlert = new SendIncomingCallAlert(ClientList);
    
          }
          catch (Exception ex)
          {
    
          }
          return 0;
      }
  }

Other than refactoring the code, I would create a test with the only the twilio bits.


· 4
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Once again, thank you for the help. I really do want to learn this. But I do have a few questions. First, what is gained by using dependency injection as opposed to a using? Does it increase performance? Second, and more importantly, if you await the tasks prior to returning the response, am I not back to creating the possible bottleneck rather than spinning off tasks that can be executed without locking the thread? AS far as testing the code, everything works fine without any of the database call, so the Twilio bits work fine.

0 Votes 0 ·

The problems are absolutely coming from the database calls and I'm starting to wonder if I am recreating the DbContext, which I think is what you are telling me. I read the article https://docs.microsoft.com/en-us/ef/core/dbcontext-configuration and since I have a myDbContext class, I assume it is initialized using the "New" method as the article states. In your sample, te GetClientsByPhoneNumberAsync method appears to receive instantiate its own myDbContext and then receives the current context from the calling controller. However, the example you showed GetClientssByPhoneNumberAsync getClientsByPhoneNumberAsync = new GetClientsByPhoneNumberAsync(); in the IncomingVoice controller require a myDbContext be passed to it, so I am confused where this get instantiated.

0 Votes 0 ·

I would follow standard async/await patterns and practices and take full advantage of dependency injection in ASP.NET Core.

Asynchronous programming with async and await

You have 3 database calls. Each should be an async call. Use the pattern in my previous post to await all three DB calls. The processing time is the time it takes to process the longest DB call. See the previous link.

The following reference document explains how the async/await pattern flows. This should give some insight to how your code is functioning.
Task asynchronous programming model

The problems are absolutely coming from the database calls

How long does each DB call take?

However, the example you showed GetClientssByPhoneNumberAsync getClientsByPhoneNumberAsync = new GetClientsByPhoneNumberAsync(); in the IncomingVoice controller require a myDbContext be passed to it, so I am confused where this get instantiated.

The DbContext is registered with the IOC container when the application starts up. Constructor injection is used to instantiate the DbContext. This is a fundamental programming pattern in ASP.NET Core.

Dependency injection in ASP.NET Core


0 Votes 0 ·

Thanks for the response. I read the article. However your code example:

       GetClientssByPhoneNumberAsync getClientsByPhoneNumberAsync = new GetClientsByPhoneNumberAsync();
       tasks.Add(getClientsByPhoneNumberAsync.GetClientsByPhoneNumber(model.From));


Does not work when the signature for the method requires a myDbContext:

       private readonly myDbContext _ctx;
       public GetClientsByPhoneNumberAsync(myDbContext ctx) 
       {
           _ctx = ctx
       }

Unless I am completely misunderstanding things.

SSMS shows each of the queries run 00:00:00




0 Votes 0 ·
AgaveJoe avatar image
0 Votes"
AgaveJoe answered

I really do want to learn this. But I do have a few questions. First, what is gained by using dependency injection as opposed to a using? Does it increase performance?

It breaks dependency on your classes and makes testing much easier. Plus you get to configure service scope in one location. For example, the DbContext can be scoped to the request. One DbContext is created per request. Since the DbContext can be large, this can increase performance.


Second, and more importantly, if you await the tasks prior to returning the response, am I not back to creating the possible bottleneck rather than spinning off tasks that can be executed without locking the thread?

Your design does not work how you think. Your InboundVoice() method is synchronous.

It would be better to make all three processes async then wait all. The total processing time is the time it takes the longest process to run. This type of information is covered in the documentation in my last post.

AS far as testing the code, everything works fine without any of the database call, so the Twilio bits work fine.

Sound like a bottleneck with the database. But, after reading the Twilio docs I'm not sure.









5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

AgaveJoe avatar image
0 Votes"
AgaveJoe answered

Does not work when the signature for the method requires a myDbContext:

It sounds like you did not register the DbContext as a service when the application starts. The following line of code registers a DbContext in a .NET 6 program.cs file.

 builder.Services.AddDbContext<MvcSqlLiteContext>(options =>
        options.UseSqlite(builder.Configuration.GetConnectionString("MvcSqlLiteConnectionString")));


Once registered the DbContext can be injected into any constructor. Honestly, your GetClientsByPhoneNumberAsync should follow a standard service pattern and be injected into the MVC controller. It seems like you've skipped over the ASP.NET Core fundamentals documentation and just started coding. That's fine but you miss out on some pretty nifty features.

The following is a standard service pattern. This is the type of pattern commonly found in repositories, domain driven design, or API development. Notice that the DbContext is injected into the constructor.

     public interface ICategoryService
     {
         Task<List<Category>> GetAllCategoriesAsync();
     }
    
     public class CategoryService : ICategoryService
     {
         private readonly MvcSqlLiteContext _context;
         public CategoryService(MvcSqlLiteContext context)
         {
             _context = context;
         }
    
         public async Task<List<Category>> GetAllCategoriesAsync()
         {
             return await _context.Category.ToListAsync();
         }
    
     }

For the dependency injection to work properly, the CategoryService service must be registered. Make sure CategoryService is register after the Dbcontext registration.

 builder.Services.AddDbContext<MvcSqlLiteContext>(options =>
        options.UseSqlite(builder.Configuration.GetConnectionString("MvcSqlLiteConnectionString")));
    
 builder.Services.AddScoped<ICategoryService, CategoryService>();


Now, you can inject the CategoryService into an MVC controller. The DbContext is automatically injected into the CategoryService constructor.

     public class ServiceTestController : Controller
     {
         private readonly ICategoryService _service;
         public ServiceTestController(ICategoryService service)
         {
             _service = service;
         }
         public async Task<IActionResult> Index()
         {
             var data = await _service.GetAllCategoriesAsync();
             return Ok(data);
         }
     }

This pattern is covered in the asp.net fundamentals documentation. So it is not clear what you're missing exactly.

Dependency injection in ASP.NET Core
Service registration methods


5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.