Response.Write / Flush hangs after many calls

1

DISCLAIMER: I asked the same question in StackOverflow in English, I'll carry the valid answer from here to there, if necessary, but I know that not all users here access the StackOverflow in English, so I intended to extend the radius of response:)

Problem

I have been trying to resolve this problem for days. We have a process of importing records from a csv file into the database , through an administrative page that resides in a project ASP.NET Web Forms (.NET 4.0) . The process was slow and I was responsible for making it more agile. I started by altering the internal logic, which already gave a significant gain in performance.

However, if I upload large files (well, relatively large of max. 3 MB), I have to wait until the upload process finishes until I start importing, and I do not return any progress information to the client while I do this. The process itself is not so time-consuming, it takes 5 to 10 seconds to complete, and yes, I've thought about creating a separate Task and using polling to ping the server, but I found this a cannon to kill mosquito.

What have I done so far?

So, to fix this problem, I decided to read the stream from the request and import the values while doing so. I created a generic handler (.ashx), and put the following code inside the void ProcessRequest(HttpContext context) :

using (var stream = context.Request.GetBufferlessInputStream())
{
}

First I remove the headers (request headers), and then I read the stream (through StreamReader ) until I find a CRLF r \ n), I convert the line to my model , and continue reading the CSV. When I get 200 files (or to the end of the file), I update all at once in the database. So I continue the process by getting more records to the end of the file.

This seems to work, but then I decided to respond via stream as well. First, I disabled BufferOutput :

context.Response.BufferOutput = false;

And so, I've added these headers to my response )

context.Response.AddHeader("Keep-Alive", "true");
context.Response.AddHeader("Cache-Control", "no-cache");
context.Response.ContentType = "application/X-MyUpdate";
Then, after sending the 200 records to the database, I write in response :

response.Write(s);
response.Flush();

s is a fixed-length string of 256 chars. I know that 256 chars are not always 256 bytes long, but I just wanted to be sure not to write text walls at once and ruin something.

Here's the format:

| pipeline (delimitador)
1 or 0 sucesso ou falha
; delimitador
mensagem de erro (se houver)
| pipeline (próximo delimitador)

Example:

|0;"Preço inválido na linha 123"|1;|1;|0;"Id inválido na linha 127"|

In the client-side , I have this here (just the snippet that does the request):

function import(){
    var formData = new FormData();
    var file = $('[data-id=file]')[0].files[0];
    formData.append('file', file);

    var xhr = new XMLHttpRequest();
    var url = "/Site/Update.ashx";

    xhr.onprogress = updateProgress;

    xhr.open('POST', url, true);
    xhr.setRequestHeader("Content-Type", "multipart/form-data");
    xhr.setRequestHeader("X-File-Name", file.name);
    xhr.setRequestHeader("X-File-Type", file.type);
    xhr.send(formData);
}

function updateProgress(evt){
    debugger;
}

What happened: (

  • The data is not sent immediately to the client when I call response.Flush . I understand that there is buffering on the client side, but it still does not seem to work even when I send a bunch of junk together to get past that buffer.
  • After a while, after typing a lot in%%, the method will progressively slow down until it stops. The same thing can happen in Response.Write . I think I'm letting something go through here ...
  • I've created a simple webform project to test what I'm trying to do. It has a generic handler that will return one number per second for 10 seconds. It actually updates (not always at the rate of 1 second per update) and I can see progress.
  • When I write a few lines in the response , the progress is displayed (the event is called), but ALWAYS after the process is almost finished. The problem is that when getting errors, I write these errors in the response. That is, the message gets bigger than when everything returns success, because they contain the error messages.

I'm assuming that if I write Response.Flush it's not a 100% guarantee that what I wrote goes immediately to the client , right? Or is the problem client itself? If it is the client , why does the server hang when I call Response.Flush many times?

I'll be happy to provide more information if you feel it is necessary.

    
asked by anonymous 01.07.2014 / 19:54

1 answer

2

Conrad,

I think you have already solved the problem but it's worth thinking.

I found your solution very clever and I do not understand why the flush is giving trouble. I do not think a 3MB file is great but I see that you need to re-evaluate the purpose of the feature. The solution you are using now is genuinely complex: you are establishing a connection to the server, transmitting the file, inserting it into the database, and responding to the progress all in the same operation at the same time. It's a lot at once. The risks of mistakes and obscurity situations are remarkable.

Pooling solutions are interesting, at least those based on creating temporary sessions on the server for the client are asking the status of the processing. In the image below is my view on a standard pooling architecture for asynchronous processes.

It'sworthreflectingthatifwe'retalkingaboutamultipurposepublicutilityapp,it'sbesttousesomethingresponsiveandcareful.Solutionsthatmakeagoodimpressionfortheuserareexpensivetodevelopandhavenoway.

Butifyouconsiderit"a mosquito-killing cannon," then I encourage you to use an even simpler solution. For example: A process where your server receives the file and processes the information asynchronously with the detailed response right at the end. This log could be sent by email or appear in any specific area.

    
08.07.2014 / 06:35