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.
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: