Async/Await in .NET

edthehead

New member
Joined
Jun 21, 2021
Messages
1
Programming Experience
5-10
We are trying to build a simple API to retrieve some documents from a Cosmos container and send it back as a JSON response. We initially write the code in Python. This worked fine for a single requests(avg response time 500ms) but when we did a load test(300 concurrency) we found that the average response time reached nearly 50s.

After a lot of research we decided to rewrite the code in .NET using async and await async programming. The avg response time improved drastically to 8s for 300 concurrency. However, I still noticed that the avg response rises gradually with concurrency. We wrote this code in Azure functions and we also noticed that the response time didn't really improve even though functions was scaling to additional instances. I also checked the metrics on the Cosmos and found plenty of through normalized throughput available.

I still think there are some improvements to be made in the code as far as async programming goes. The increase in response time tells me the problem may have something to do with thread exhaustion. I'm no expert in .NET which is why I'm reaching out to the community but I've put the code below and would appreciate if someone can suggest some improvements which I can try. I have already used a static cosmos client and am using the v3 of the cosmos .NET SDK.
C#:
#r "Newtonsoft.Json"
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Azure.Cosmos;
using System.Text;
using Microsoft.Azure.WebJobs.Extensions;

private const string EndpointUrl = "https://xxxxxxxxxxxxxxxxxxx.documents.azure.com:443/";
private const string AuthorizationKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
private static CosmosClient client = new CosmosClient(EndpointUrl, AuthorizationKey,new
CosmosClientOptions
{
    PortReuseMode = PortReuseMode.PrivatePortPool
});
private static string DatabaseId = "sfsbi-cfgw-rt-cosmos-sqldb";
private static string ContainerId = "cfgw_rt";

int maxItemCount = -1;
int maxDegreeOfParallelism = 10000;
int maxBufferedItemCount = 10000;

public static async Task<IActionResult> Run(HttpRequest req, ILogger log)
{
    log.LogInformation("C# HTTP trigger function processed a request.");
    string Payer = req.Query["Payer"];
    string offset = req.Query["pageSize"];
    int pageSize = Convert.ToInt32(offset);
    var database = client.GetDatabase(DatabaseId);
    var container = database.GetContainer(ContainerId);
    string responseMessage = await QueryItems(container, Payer, log, pageSize);
    return new JsonStringResult(responseMessage);
}
public static async Task<String> QueryItems(Container container, string Payer, ILogger log, int pageSize)
{
    var sqlQueryText = $"SELECT * FROM c WHERE c.Legal_Entity_Customer_Number = '{Payer}' order by c.Legal_Entity_Customer_Number, c.transaction_date_time desc OFFSET 0 LIMIT {pageSize}";
QueryDefinition queryDefinition = new QueryDefinition(sqlQueryText);
Output output = new Output();

List<Output> outputs = new List<Output>();
using (FeedIterator<ToDoActivity> feedIterator = container.GetItemQueryIterator<ToDoActivity>(
    queryDefinition,
    requestOptions: new QueryRequestOptions()
    {
        PartitionKey = new PartitionKey(Payer),
        MaxItemCount = -1,
        MaxBufferedItemCount = 100,
        MaxConcurrency = 100
    }))
    {
        while (feedIterator.HasMoreResults)
        {
        foreach(var item in await feedIterator.ReadNextAsync())
            {
            output.iccdatatermCountryCode=item.iccdata_term_Country_Code;
            output.globalproductcode=item.global_product_code;
            output.custdatadriverid=item.cust_data_driver_id;
            output.Forwardstandardproductcode=item.Forward_standard_product_code;
            output.LegalEntityCustomerNumber=item.Legal_Entity_Customer_Number;
            output.Latitudeno=item.Latitude_no;
            output.hostingcollectingcompanynumber=item.hosting_collecting_company_number;
            output.hostingcollectingcompanyname=item.hosting_collecting_company_name;
            output.Sourcecardid=item.Source_card_id;
            output.transactiondatetime=item.transaction_date_time;
            output.panmasked=item.pan_masked;
            output.transactiondate=item.transaction_date;
            output.transactionid=item.transaction_id;
            output.custdatacustomerentered=item.cust_data_customer_entered;
            output.delcoretailtotalgross=item.delco_retail_total_gross;
            output.custdatafleetid=item.cust_data_fleet_id;
            output.collectingcompanycurrencyisocode=item.collecting_company_currency_iso_code;
            output.Issueractioncode=item.Issuer_action_code;
            output.h3siteid=item.h3_site_id;
            output.AuthorisedFlag=item.AuthorisedFlag;
            output.LegalEntityTradingName=item.Legal_Entity_Trading_Name;
            output.ProductGrouplevel2=item.Product_Group_level_2;
            output.accountcustomernumber=item.account_customer_number;
            output.issuingcollectingcompanynumber=item.issuing_collecting_company_number;
            output.euroshellsitenumber=item.euroshell_site_number;
            output.merchantcountry=item.merchant_country;
            output.poireceiptnumber=item.poi_receipt_number;
            output.ProductGrouplevel4=item.Product_Group_level_4;
            output.ProductGrouplevel3=item.Product_Group_level_3;
            output.TransactionTimeGMT=item.TransactionTimeGMT;
            output.merchantcategorydescription=item.merchant_category_description;
            output.networkdeliverycompanyname=item.network_delivery_company_name;
            output.transactionstatus=item.transaction_status;
            output.merchantnamelocation=item.merchant_name_location;
            output.merchantnetwork=item.merchant_network;
            output.odometerreadingmiles=item.odometer_reading_miles;
            output.productscodeAdditional=item.products_codeAdditional;
            output.merchantid=item.merchant_id;
            output.accountcustomername=item.account_customer_name;
            output.TransactionCurrencySymbol=item.TransactionCurrencySymbol;
            output.merchantcategory=item.merchant_category;
            output.custdataFleetDescription=item.cust_data_Fleet_Description;
            output.cardissuenumber=item.card_issue_number;
            output.odometerreading=item.odometer_reading;
            output.vehicleregistrationnumber=item.vehicle_registration_number;
            output.iccdatatranType=item.iccdata_tran_Type;
            output.unitofmeasure=item.unit_of_measure;
            output.sitecurrencyisocode=item.site_currency_iso_code;
            output.authorisationresponsedescription=item.authorisation_response_description;
            output.quantity=item.quantity;
            output.productstaxcode=item.products_tax_code;
            output.authorisationresponsecode=item.authorisation_response_code;
            output.TransactionTime=item.TransactionTime;
            output.Longitudeno=item.Longitude_no;
            output.UnitPrice=item.UnitPrice;
            output.networkdeliverycompanynumber=item.network_delivery_company_number;
            output.pinusedindicator=item.pin_used_indicator;
            output.PAN=item.PAN;
            output.globalproductdescription=item.global_product_description;
            output.sfgwcarddateofexpiry=item.sfgw_card_date_of_expiry;
            outputs.Add(output);
            output = new Output();
            }
        }
        string JSONString = JsonConvert.SerializeObject(outputs);
        return JSONString;
    }
}

public class ToDoActivity{
public string   iccdata_term_Country_Code   {get;set;}
public string   global_product_code {get;set;}
public string   cust_data_driver_id {get;set;}
public string   Forward_standard_product_code   {get;set;}
public string   Legal_Entity_Customer_Number    {get;set;}
public string   Latitude_no {get;set;}
public string   hosting_collecting_company_number   {get;set;}
public string   hosting_collecting_company_name {get;set;}
public string   Source_card_id  {get;set;}
public string   transaction_date_time   {get;set;}
public string   pan_masked  {get;set;}
public string   transaction_date    {get;set;}
public string   transaction_id  {get;set;}
public string   cust_data_customer_entered  {get;set;}
public string   delco_retail_total_gross    {get;set;}
public string   cust_data_fleet_id  {get;set;}
public string   collecting_company_currency_iso_code    {get;set;}
public string   Issuer_action_code  {get;set;}
public string   h3_site_id  {get;set;}
public string   AuthorisedFlag  {get;set;}
public string   Legal_Entity_Trading_Name   {get;set;}
public string   Product_Group_level_2   {get;set;}
public string   account_customer_number {get;set;}
public string   issuing_collecting_company_number   {get;set;}
public string   euroshell_site_number   {get;set;}
public string   merchant_country    {get;set;}
public string   poi_receipt_number  {get;set;}
public string   Product_Group_level_4   {get;set;}
public string   Product_Group_level_3   {get;set;}
public string   TransactionTimeGMT  {get;set;}
public string   merchant_category_description   {get;set;}
public string   network_delivery_company_name   {get;set;}
public string   transaction_status  {get;set;}
public string   merchant_name_location  {get;set;}
public string   merchant_network    {get;set;}
public string   odometer_reading_miles  {get;set;}
public string   products_codeAdditional {get;set;}
public string   merchant_id {get;set;}
public string   account_customer_name   {get;set;}
public string   TransactionCurrencySymbol   {get;set;}
public string   merchant_category   {get;set;}
public string   cust_data_Fleet_Description {get;set;}
public string   card_issue_number   {get;set;}
public string   odometer_reading    {get;set;}
public string   vehicle_registration_number {get;set;}
public string   iccdata_tran_Type   {get;set;}
public string   unit_of_measure {get;set;}
public string   site_currency_iso_code  {get;set;}
public string   authorisation_response_description  {get;set;}
public string   quantity    {get;set;}
public string   products_tax_code   {get;set;}
public string   authorisation_response_code {get;set;}
public string   TransactionTime {get;set;}
public string   Longitude_no    {get;set;}
public string   UnitPrice   {get;set;}
public string   network_delivery_company_number {get;set;}
public string   pin_used_indicator  {get;set;}
public string   PAN {get;set;}
public string   global_product_description  {get;set;}
public string   sfgw_card_date_of_expiry    {get;set;}

}

public class Output{
public string   iccdatatermCountryCode  {get;set;}
public string   globalproductcode   {get;set;}
public string   custdatadriverid    {get;set;}
public string   Forwardstandardproductcode  {get;set;}
public string   LegalEntityCustomerNumber   {get;set;}
public string   Latitudeno  {get;set;}
public string   hostingcollectingcompanynumber  {get;set;}
public string   hostingcollectingcompanyname    {get;set;}
public string   Sourcecardid    {get;set;}
public string   transactiondatetime {get;set;}
public string   panmasked   {get;set;}
public string   transactiondate {get;set;}
public string   transactionid   {get;set;}
public string   custdatacustomerentered {get;set;}
public string   delcoretailtotalgross   {get;set;}
public string   custdatafleetid {get;set;}
public string   collectingcompanycurrencyisocode    {get;set;}
public string   Issueractioncode    {get;set;}
public string   h3siteid    {get;set;}
public string   AuthorisedFlag  {get;set;}
public string   LegalEntityTradingName  {get;set;}
public string   ProductGrouplevel2  {get;set;}
public string   accountcustomernumber   {get;set;}
public string   issuingcollectingcompanynumber  {get;set;}
public string   euroshellsitenumber {get;set;}
public string   merchantcountry {get;set;}
public string   poireceiptnumber    {get;set;}
public string   ProductGrouplevel4  {get;set;}
public string   ProductGrouplevel3  {get;set;}
public string   TransactionTimeGMT  {get;set;}
public string   merchantcategorydescription {get;set;}
public string   networkdeliverycompanyname  {get;set;}
public string   transactionstatus   {get;set;}
public string   merchantnamelocation    {get;set;}
public string   merchantnetwork {get;set;}
public string   odometerreadingmiles    {get;set;}
public string   productscodeAdditional  {get;set;}
public string   merchantid  {get;set;}
public string   accountcustomername {get;set;}
public string   TransactionCurrencySymbol   {get;set;}
public string   merchantcategory    {get;set;}
public string   custdataFleetDescription    {get;set;}
public string   cardissuenumber {get;set;}
public string   odometerreading {get;set;}
public string   vehicleregistrationnumber   {get;set;}
public string   iccdatatranType {get;set;}
public string   unitofmeasure   {get;set;}
public string   sitecurrencyisocode {get;set;}
public string   authorisationresponsedescription    {get;set;}
public string   quantity    {get;set;}
public string   productstaxcode {get;set;}
public string   authorisationresponsecode   {get;set;}
public string   TransactionTime {get;set;}
public string   Longitudeno {get;set;}
public string   UnitPrice   {get;set;}
public string   networkdeliverycompanynumber    {get;set;}
public string   pinusedindicator    {get;set;}
public string   PAN {get;set;}
public string   globalproductdescription    {get;set;}
public string   sfgwcarddateofexpiry    {get;set;} 
public string   index1  {get;set;}  
}

QueryRequestOptions options = new QueryRequestOptions
{
MaxItemCount = maxItemCount,
MaxBufferedItemCount = maxBufferedItemCount,
MaxConcurrency = maxDegreeOfParallelism
};

public class JsonStringResult : ContentResult
{
    public JsonStringResult(string json)
    {
        Content = json;
        ContentType = "application/json";
    }
}
 
Last edited by a moderator:
Back
Top Bottom