Solved

Response.Flush in Classic ASP causing TIME_WAIT ports

Posted on 2016-08-16
9
72 Views
Last Modified: 2016-08-19
I have recently become aware of this issue: https://blogs.msdn.microsoft.com/spike/2008/09/17/nested-recordset-and-the-portsocket-in-time_wait-problem-by-example/

Where if you open a recordset, then use the connection for something else with the recordset still open, the web server opens a new port to talk to the DB, then immediately puts that port into the TIME_WAIT state.

I have been going through the site to fix this (by closing recordsets before the connection is re-used - it works) but noticed something odd.

When the page has a "Response.Flush" on it, no matter WHAT you do, it causes an extra port to be used and then be put into the useless TIME_WAIT state. This can lead to a serious case of port exhaustion.

SAMPLE CODE:

<%@ Language=VBScript %>

 <html>

<head>

</head>

<body>
<%
response.flush 

set cnn = server.CreateObject("adodb.connection")

CNN.cursorlocation=3
cnn.open [YOUR CONNECTION STRING]

set rs=server.createobject("adodb.recordset")
set rs=cnn.execute("select top 1 * from [YOUR TABLE]")





cnn.close
set cnn=nothing
%>


</body>
</html>

Open in new window

To check for time waits you can run:

netstat -nao | find /i "[YOUR DB IP]" 

Open in new window

via the command prompt on the web server. Assuming this is a test system, you should immediately see time_wait connections popup from your web server to your DB server. Remove the flush, and this stops.

Googling has not helped - open to any suggestions.
0
Comment
Question by:AlizaN
  • 5
  • 4
9 Comments
 
LVL 52

Expert Comment

by:Scott Fell, EE MVE
Comment Utility
How many concurrent users do you have in a 3 to 5 minute time span?  In other words, is this a real issue that you are actually experiencing where you have too many connections? I have had a very old server from way back in the late 90's handle 100,000 page views inside of an hour or about 20 to 30 calls per second before being bottlenecked.  That is a lot of traffic. .

If you are going to reuse data from a recordset, pull all the data to an array via getrows.  Then you can use the same data over and over again without going back to the db.   This will be a lot faster when you are grabbing a lot of data.

Response.Flush gets used a lot what you have large amounts of data to send to the browser.  Sometimes  using an ajax call when you need to get beyond x records may be a better option.

If you are nesting recordsets, I would look into creating a view or stored procedure that would allow only one call to the db.

In short, there are ways you can get your application to be more lean.  Real world details will help you a lot more here.
0
 
LVL 1

Author Comment

by:AlizaN
Comment Utility
Yes it's a real issue, the server is busier than 100k/hour.

We use the array logic elsewhere but as you can see in the example here there is just one simple DB call, I don't even <i>use</i> the recordset.

WIth the flush on, a TIME_WAIT connection spawns, without it, the DB uses the already established port.

We have been using things such as arrays, views, disposing of objects, etc, and what it left us was with this response.flush issue where any communication to the DB ends up using a new port and then dumping it in TIME_WAIT.
0
 
LVL 1

Author Comment

by:AlizaN
Comment Utility
EDIT 2
Turns out its got nothing to do with database. If you have JUST a response.flush, nothing else, that already causes an extra connection, from IIS to the client, so I've been looking at the wrong of things.

The question now becomes can you get away with not having response.flush spawn an extra connection from IIS to the client.
0
 
LVL 52

Expert Comment

by:Scott Fell, EE MVE
Comment Utility
The real question  is why you need it at all if you only are using static content.

If you want to stick to the code you provided, you answered your own question and you simply need to remove response.flush because it is not needed.

If you want us to help make your actual app more efficient, then please provide the current code and sample data you are using.
0
Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

 
LVL 1

Author Comment

by:AlizaN
Comment Utility
It's a massive app with thousands of pages, the most important reason we use flush is to flush out a loading message/overlay before doing the long work. Any other use of flush we can and likely will remove.

So something like (pseudo code):

<body>
<nice loading message div>
<%
Response.Flush

Some long running database call, i.e. a stored procedure that takes 5 seconds to load
.
.
Write out the results of the SP
.
%>
</body>

Open in new window


Is there some way of accomplishing that otherwise? Someone suggested having an ajax call do the actual work so that the report page JUST says "loading" and then uses an ajax call to get the data. That's one potential but the reports are usually large so the ajax call would be returning quite a bit of data - bit nervous with that.
0
 
LVL 52

Expert Comment

by:Scott Fell, EE MVE
Comment Utility
It's a massive app with thousands of pages,
But we are only concentrating on just on page for now.  You should be able to create a working example void of anything not necessary for this.  Data of course is important http://sscce.org/

the most important reason we use flush is to flush out a loading message/overlay before doing the long work
I wonder if you are using response.flush for something not needed.  You can easily clear out a "loading message" with javascript on the client.

You probably should be using an ajax call to start with and I did suggest that earlier.
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>Testing 1...2..3...</title>
</head>
<body>
  <div id="loading">...loading message or gif/png... <div>
    <div id="results"></div>  
<script src="https://code.jquery.com/jquery-3.0.0.js"></script>
<script>
  $(function(){ // ANYTHING INSIDE HERE LOADS WHEN THE PAGE LOADS
  // START PROCESSING AJAX AND SEND 2 VARIABLES TO OUR PROCESSING PAGE
  $.ajax({
  method: "GET",
  url: "verylongrunning.asp",
  data: { var1: "1", var2: "50" }
})
  .done(function( data ) {
    // GET RID OF LOADING MESSAGE
    $('#loading').hide();
    
    // PROCCESSIG PAGE SENDS JSON DATA
     // PARSE JSON VIA JS TO POPULATE COLUMNS OR TABLE
    // PUSH DATA TO #RESULTS
    $('#results').html(data);
  });
  
  
});
</script>
</body>
</html>

Open in new window

Your processing page should simply except your get/post variables if any, start a recordset, push to array via getrows(), close recordset, then use aspjson to generate json data to display https://github.com/rcdmk/aspJSON or you can build your table on the fly here but it is cleaner to send json to the page and let javascript format the data

I wouldn't send more than a hundred or so rows of data at one time.  Instead of your 1000 rows of data,send 100.  When the user is ready to page, then send a hundred more and append to the current page instead of a refresh.  Then after 10 "pages" you now have your 1000 rows of data.

Of course your other option on your processing pages is to
for x = 1 to 1000
  if x mod 100 = 0 then
      response.flush  'push to browser
  end if
  response.write x
next

Open in new window


If you have not discovered datatables yet, have a look https://datatables.net/ and specifically the ajax data https://datatables.net/examples/data_sources/ajax.html.  I have a lot of examples of using this with classic asp on this site. It will only access the data it requires. If you have not used it before, there will be small learning curve but in the end if you are working with a lot of data, it make your life easier.
0
 
LVL 1

Author Comment

by:AlizaN
Comment Utility
Ok so basically it seems the overall methodology is what I was thinking, to have the processing happen in an ajax page (i.e. verylongrunning.asp) and then efficiently pass the data back to the main page. Outside of that there isn't too much of a solution, correct?
0
 
LVL 52

Accepted Solution

by:
Scott Fell,  EE MVE earned 500 total points
Comment Utility
Correct, the solution is to change your methodology.

There is a developer saying something similar to when you find that your application feels big and complex, it is.  Time to sit back take a rest and simplify.

How many times do we go back to our old code and face palm and say to ourself... what was I thinking.  

The main points are
  • No need to respons.flush at start.
  • Use Ajax on the main page.
  • Send small amounts of data at a time.
  • On the processing page, put data in an array via get.rows instead of looping through a recordset.
  • Proper index on the database.
  • Use efficient queries.

Obviously this is over simplified based on what has been provided.

If you want more detailed and private help, use Gigs to put together some goals and budget and have one of our experts assist you privately.  Just click on the waffle at the top, then gigs.
0
 
LVL 1

Author Closing Comment

by:AlizaN
Comment Utility
End issue ended up being the response.flush which can be avoided using suggestions provided.
0

Featured Post

What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

Join & Write a Comment

This article explains how to reset the password of the sa account on a Microsoft SQL Server.  The steps in this article work in SQL 2005, 2008, 2008 R2, 2012, 2014 and 2016.
Load balancing is the method of dividing the total amount of work performed by one computer between two or more computers. Its aim is to get more work done in the same amount of time, ensuring that all the users get served faster.
This videos aims to give the viewer a basic demonstration of how a user can query current session information by using the SYS_CONTEXT function
This tutorial will walk an individual through setting the global and backup job media overwrite and protection periods in Backup Exec 2012. Log onto the Backup Exec Central Administration Server. Examine the services. If all or most of them are stop…

743 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

16 Experts available now in Live!

Get 1:1 Help Now