Search code examples
c#.netazure-functionsazure-storagestringbuilder

Azure Function StringBuilder object persists across multiple runs and appends to itself when writing new text files


So I have created my first Azure Function (4.0) (.NET 7.0) that takes an input text file from an Azure Storage Blob Container, does some processing on it, then generates additional text files based on the contents of the input file. The function then saves those new files to a newly created temporary Azure Storage Blob Container, zips the container and its contents, and saves that zip file in a separate blob container for access later by another application. The temporary container is then deleted, and the function is complete.

To generate the output text files I'm using a StringBuilder to create the text string, then using that with a MemoryStream to create the text file and finally uploading to Azure Storage. The simplest example of this process from my application is below:

private static void GenerateExceptionFile(BlobContainerClient tempContainer)
{
    var sb = new StringBuilder().AppendLine("Store\tStore\tLat\tLon");

    foreach (CustomSite s in ExceptionSites)
        sb.AppendLine($"{s.Location}\t{s.StoreName}\t{s.Latitude}\t{s.Longitude}");

    using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(sb.ToString())))
        tempContainer.UploadBlob("exceptions.txt", ms);
}

My issue is that the output file is correct the first time it runs after the application is restarted, but any subsequent runs it will keep increasing in size, appending to itself. For example, if the exceptions.txt file should have only 2 lines, the second time it runs it will have 4, the third time it will have 6, and so on.

I'm struggling to see how this is possible, both due to how my code should be clearing itself out after it exits this method, and due to the fact that the text files are being saved to a completely separate location every time they are created due to how it is being stored in a temporary container. Whether necessary or not, I have also taken care to ensure the temporary container name is being "randomized" by appending the current DateTime.Now.Ticks to the container name:

var ticks = DateTime.Now.Ticks.ToString().Substring(DateTime.Now.Ticks.ToString().Length - 5);
string tempDirectoryName = ("tempcustomscore-" + project.Release + project.Name + "prox" + DateTime.Now.ToString("yyyyMMdd") + "t" + ticks).Replace(" ", "").ToLower();

At this point the only possibility I can think of is that the StringBuilder is not getting reset properly each time the Azure Function is run, and whatever is in that StringBuilder is persisting across runs until the application is manually restarted via the Azure Portal. I have even tried a few things like adding sb.Clear(); or sb.Length = 0; sb.Capacity = 0; to the end of the GenerateExceptionFile() code block above but have had no luck.

This also happens when running locally, so it doesn't seem to be directly related to running in the Azure production environment, but it may be directly related to it being an Azure Function. Any advice would be appreciated.


Solution

  • So I discovered the cause of my issue: Global variables. In true noob fashion rather than initializing the list objects within any of the methods in my function, I was initializing them globally. Due to the nature of persistence of Azure Functions, it was simply appending to the lists every time it ran rather than creating new ones. Here's how it was before:

    public class ProcessCustomScore
    {
        public static List<CustomSite> Sites = new List<CustomSite>();
        public static List<CustomSite> ExceptionSites = new List<CustomSite>();
    
        ...
    
        [Function("ProcessCustomScore")]
        public async Task<HttpResponseData> Run([HttpTrigger(AuthorizationLevel.Anonymous, "POST")] HttpRequestData req)
        {
    

    The solution was to initialize those lists inside one of the methods and pass them as parameters to other methods as needed.