Cache Frequently-Used Data in the Application or Session Objects

Choose the loading strategy that best suits your app's needs and data usage patterns

ASP Application and Session objects are like handy storage lockers in your app's memory. You can stash data in either one, and it'll stay put even when users hop between pages. But remember:

  • Session data gets a separate locker for each user. It's perfect for things like shopping cart contents or user preferences.

  • Application data has just one shared locker for everyone. Use it for info that's the same across the board, like a list of product categories or company contact details.

When to load data into these lockers? You've got options:

  • Load it right when the Application or Session starts: Add your code to the Application_OnStart() or Session_OnStart() functions in the Global.asa file.

  • Load it only when you actually need it: This is like only unpacking your suitcase when you find the outfit you want. It's called "lazy evaluation" and can give your app a performance boost. Just add a check to your ASP pages to see if the data's already there, and load it if it's not.

Choose the loading strategy that best suits your app's needs and data usage patterns.

An example:

<%
Function GetEmploymentStatusList
 Dim d
 d = Application("EmploymentStatusList")
 If d = "" Then
    ' FetchEmploymentStatusList function (not shown)
    ' fetches data from DB, returns an Array
    d = FetchEmploymentStatusList()
    Application("EmploymentStatusList") = d
 End If
 GetEmploymentStatusList = d
End Function
%>

Similar functions could be written for each chunk of data needed.
When it comes to storing data in Application or Session objects, you've got freedom of choice! They happily house any kind of data you throw at them, from simple numbers and text to complex arrays.

One common scenario is storing ADO recordsets. While you could manually copy each field into separate variables, there's a faster and more elegant way:

Use ADO's handy recordset persistence functions to package the data up neatly:

  • GetRows() transforms the recordset into a handy array, perfect for quick access and manipulation.

  • GetString() condenses it into a single string, ideal for compact storage or file operations.

  • Save() (in ADO 2.5) lets you create a disk-based snapshot, offering persistence beyond memory.

While I can't dive into the details of each function here, I'll gladly demonstrate how to use GetRows() to fetch an array of recordset data. Check out this code example:

' Get Recordset, return as an Array
Function FetchEmploymentStatusList
 Dim rs
 Set rs = CreateObject("ADODB.Recordset")
 rs.Open "select StatusName, StatusID from EmployeeStatus", adoCon
 FetchEmploymentStatusList = rs.GetRows() 'Return data as an Array
 rs.Close
 Set rs = Nothing
End Function

A further refinement of the above might be to cache the HTML for the list, rather than the array. Here's a simple sample:

' Get Recordset, return as HTML Option list
Function FetchEmploymentStatusList
 Dim rs, fldName, s
 Set rs = CreateObject("ADODB.Recordset")
 rs.Open "select StatusName, StatusID from EmployeeStatus", adoCon
 s = "<select name='EmploymentStatus'>" & vbCrLf
 Set fldName = rs.Fields("StatusName") ' ADO Field Binding
 Do Until rs.EOF
   ' Next line violates Don't Do String Concats,
   ' but it's OK because we are building a cache
   s = s & " <option>" & fldName & "</option>" & vbCrLf
   rs.MoveNext
 Loop
 s = s & "</select>" & vbCrLf
 rs.Close
 Set rs = Nothing ' See Release Early
 FetchEmploymentStatusList = s ' Return data as a String
End Function

Caching ADO recordsets directly in Application or Session scope is possible, but it comes with two strict rules:

  1. ADO must be free-threaded: This ensures it plays nicely with multiple users accessing the data simultaneously.

  2. The recordset must be disconnected: It should be independent of the database connection to avoid resource conflicts.

If you can't guarantee these conditions, don't cache the recordset. Storing COM objects like ADO recordsets can lead to trouble if not handled carefully (more on that in the Non-Agile Components and Don't Cache Connections tips).

Once data is in Application or Session scope, it stays there until:

  • You change it programmatically

  • The session ends (for Session data)

  • The web app restarts (for Application data)

Need to update cached data? You have options:

  • Manual refresh: Create an admin-only ASP page to trigger updates when needed.

  • Automatic refresh: Use a function to periodically check timestamps and refresh data after a set time interval. Here's an example of how to do this:

<%
' error handing not shown...
Const UPDATE_INTERVAL = 300 ' Refresh interval, in seconds
' Function to return the employment status list
Function GetEmploymentStatusList
 UpdateEmploymentStatus
 GetEmploymentStatusList = Application("EmploymentStatusList")
End Function

' Periodically update the cached data
Sub UpdateEmploymentStatusList
 Dim d, strLastUpdate
 strLastUpdate = Application("LastUpdate")
 If (strLastUpdate = "") Or _
       (UPDATE_INTERVAL < DateDiff("s", strLastUpdate, Now)) Then

    ' Note: two or more calls might get in here. This is okay and will simply
    ' result in a few unnecessary fetches (there is a workaround for this)

    ' FetchEmploymentStatusList function (not shown)
    ' fetches data from DB, returns an Array
    d = FetchEmploymentStatusList()

    ' Update the Application object. Use Application.Lock()
    ' to ensure consistent data
    Application.Lock
    Application("EmploymentStatusList") = d
    Application("LastUpdate") = CStr(Now)
    Application.Unlock
 End If
End Sub

For another example, see World's Fastest ListBox with Application Data.

Hold on before stuffing those huge arrays into Session or Application objects! It might sound convenient, but it can backfire in a big way. Here's why:

Every time you need to access even a single element, the entire array gets duplicated. Imagine a 100,000-item array of zip codes and weather stations. Just to grab one string, ASP would have to create a whole temporary copy of all those stations. Talk about a waste of resources!

For large datasets like this, explore better alternatives:

  • Custom components: Build a tailored solution with efficient methods for storing and retrieving data.

  • Dictionaries: These handy structures offer faster lookups than arrays for non-contiguous key-value pairs.

But remember:

  • Arrays still reign supreme for lightning-fast access to contiguous data. Don't discard them completely!

  • Choose wisely based on your specific needs and performance goals. The right data structure can make a world of difference.

Original Source: https://axcs.blogspot.com/2006/07/asp-tips-tip-2-cache-frequently-used.html