APGen Documentation Previous Topic: Static Page Generation Next Topic: Page Fragment Caching Parent Topic: Web Site Optimization Techniques    Web Site Optimization Techniques
Two-Phase Content Generation
See Also:

Two-phase content generation offers high-performance pages, while keeping some page content dynamic. 

Definition

Two-phase content generation is an optimization technique where a web page contains code that is executed in two phases.  The first phase is executed by APGen, and the second phase is executed by ASP or another dynamic web page product.

Figure 1 compares a standard ASP page to a two-phase APG script.  In the two-phase model, the first phase is when an APG script is executed that generates an ASP file.  In the second phase, the ASP file is executed and an HTTP stream is returned to a browser.

Figure 1: Two Phase Content Generation vs. Standard ASP

First phase content generation occurs during a programmable event, such as when an administrator runs a "build" web page, a SQL trigger fires, or a scheduled rebuild occurs.  To invoke the first phase, use the APGen COM object to execute an APG script.  It is up to the web site architect to determine what sorts of events will cause execution of the first phase.

Advantages

Two-phase content generation is a compromise between static pages and dynamic pages: Two-phase offers superior performance to dynamic pages, but does not perform as well as static pages; it offers some dynamic content, but the page becomes faster as more dynamic content is eliminated from the second phase.  The more code that can be run in the first phase, the faster a page will be.  User specific functionality (such as shopping cart contents, serving ads, or determining whether the user is logged on) must be performed in the second phase, and shared, less volatile content (such as product information) can be rendered in the first phase.

Two-phase content generation typically performs 3 to 100 times better than dynamic pages.  Results depend upon how much code can be moved into the first phase.  Two-phase content generation generally performs much better than output caching or data caching.  Caching always involves storage and lookup, and two-phase skips these steps.  Two-phase content generation also avoids cache validity checks, which slows caching and makes caching more difficult to implement.

Disadvantages

Two-phase content generation requires some additional development work compared to standard ASP only pages.  The developer must decide which code is run in the first phase, and which code is run in the second phase (this is discussed below).  The additional complexity is comparable to writing ASPs with server-side code and client-side code.  With client-side code, web page code is moved from the server to the client to improve the user interface, reduce web server round-trips, and reduce load on the web server.  Similarly, web page code can be moved from the web server to the first phase to reduce load on the web server.

For pages with many output combinations, two-phase content generation is not an optimal solution.  The reason is that each generated ASP must be individually compiled and cached by the ASP engine (typically several instances of each page are cached on the web server), and each cached ASP instance occupies 3 to 5 MB of RAM.  To illustrate, consider an e-commerce site with 100 products.  If two-phase content generation is used on the product page, 100 different ASPs are generated.  Each ASP must be individually compiled and cached, which wastes CPU and memory resources.  A better choice for non-static pages with many output combinations is page fragment caching.

Guidelines for Writing Two-Phase APG Scripts

To write a two-phase web page, create an APG script that generates an ASP file.  The APG script can contain HTML content, APG script blocks, and ASP script blocks.  Then, determine what will cause the APG script to be executed, and write code to execute the APG script when the event(s) occur.

When writing a two-phase APG script, you will need to consider whether individual blocks within a page should be made first or second phase blocks.  Here are some pointers to help you make this decision:

Example

This example is from an e-commerce site that uses Microsoft Site Server Commerce Edition 3.0.  Due to the size and complexity of the page, only part of the page is shown, and the include files' code is not displayed.  This example is intended to show how a two-phase page is executed in two separate phases.  This example is not intended to explain every detail of this particular page.

The example APG script is named basket.apg:

<!-- #include apg="include/shop_new.apg" -->
<!-- #include apg="include/header.apg" -->
<!-- #include apg="include/footer.apg" -->
<%#
'############################################################################
' FILE:          basket.apg
'
' Copyright (C) 2000 WebGecko Software.  All Rights Reserved.
'############################################################################

Output.Filename = "basket.asp"

#%>
<!-- #include file="include/shop.asp" -->
<!-- #include file="include/ado.asp" -->
<!-- #include file="include/util.asp" -->
<% Response.ExpiresAbsolute=#Jan 01, 1980 00:00:00# %>
<%
REM Run the basic plan
Dim nItemCount
Dim mscsOrderForm

Set mscsOrderForm = UtilRunBasket(mscsShopperId)
nItemCount = mscsOrderForm.Items.Count

%><HTML>
<HEAD>
    <TITLE>Shopping Basket</TITLE>
    <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=ISO-8859-1">
</HEAD>
<BODY TEXT="
<%# = color_text #%>" LINK="<%# = color_link #%>" VLINK="<%# = color_vlink #%>" ALINK="<%# = color_alink #%>"BACKGROUND="<%# = background #%>">
<OBJECT RUNAT=Server ID=errorList PROGID="Commerce.SimpleList"></OBJECT>
<%#
     Call OutputHeader( "heading_basket.gif", "Shopping Basket" )
#%>

<% if nItemCount = 0 then %>
    <BLOCKQUOTE><STRONG>Your basket is empty.</STRONG></BLOCKQUOTE>
    <P>
<% else %>
    You have
<% = nItemCount %> item<% if nItemCount > 1 then %>s<% end if %> in your basket.
    <P>
    To change or remove an item in the basket, click on its description.
    <P>
    <TABLE WIDTH=600>
     . . .

After the APG script is written, it must be executed - in this case an administrator ASP page executes basket.apg.  (For more information, see Executing APG Scripts.)  When basket.apg is executed, the APG script blocks are executed and basket.asp is generated:

<!-- #include file="include/shop.asp" -->
<!-- #include file="include/ado.asp" -->
<!-- #include file="include/util.asp" -->
<% Response.ExpiresAbsolute=#Jan 01, 1980 00:00:00# %>
<%
REM Run the basic plan
Dim nItemCount
Dim mscsOrderForm

Set mscsOrderForm = UtilRunBasket(mscsShopperId)
nItemCount = mscsOrderForm.Items.Count

%><HTML>
<HEAD>
    <TITLE>Shopping Basket</TITLE>
    <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=ISO-8859-1">
</HEAD>
<BODY TEXT="#51392B" LINK="#CC9966" VLINK= "#FF0000"ALINK="#00FF00"BACKGROUND="assets/images/main.gif">
<OBJECT RUNAT=Server ID=errorList PROGID="Commerce.SimpleList"></OBJECT>

<TABLE CELLPADDING="2" CELLSPACING="2" WIDTH="600" BORDER="1" RULES="none" background="http://server/vcapgen/assets/images/navbar.gif?">
<TR>
<TD ALIGN=LEFT WIDTH=115><A HREF="http://server/vcapgen/home.asp"><IMG SRC="http://server/vcapgen/assets/images/logo.gif?" WIDTH=98 HEIGHT=76 BORDER=0 ALT="Click here to return Home" ALIGN=TOP></A></TD>

     
<% If Not IsEmpty(mscsShopperId) Then %>

<TD ALIGN=CENTER>
<A HREF="http://server/vcapgen/listing.asp"><IMG SRC="http://server/vcapgen/assets/images/button_catalog_up.gif?" BORDER=0 ALT="Store Directory" ALIGN=CENTER></A>
<A HREF="http://server/vcapgen/search.asp"><IMG SRC="http://server/vcapgen/assets/images/button_search_up.gif?" BORDER=0 ALT="Search" ALIGN=CENTER></A>
<A HREF="http://server/vcapgen/basket.asp"><IMG SRC="http://server/vcapgen/assets/images/button_basket_up.gif?" BORDER=0 ALT="Basket" ALIGN=CENTER></A>
<A HREF="http://server/vcapgen/checkout-ship.asp"><IMG SRC="http://server/vcapgen/assets/images/button_ready2pay_up.gif?" BORDER=0 ALT="Pay" ALIGN=CENTER></A>
</TD>        

     
<% End If %>


</TR>
</TABLE>
<TABLE WIDTH=600>
<TR ALIGN=CENTER><TD><BR><IMG SRC="http://server/vcapgen/assets/images/heading_basket.gif?" BORDER="0" ALT="Shopping Basket" ALIGN=CENTER><BR></TD></TR>
</TABLE>

<% if mscsOrderForm.[_Basket_Errors].Count > 0 then  %>
    <UL>
        
<% for each errorStr in mscsOrderForm.[_Basket_Errors] %>
            <li>
<% = errorStr %></li>
        
<% next %>
    </UL>
    <P>
<% end if %>

<% if nItemCount = 0 then %>
    <BLOCKQUOTE><STRONG>Your basket is empty.</STRONG></BLOCKQUOTE>
    <P>
<% else %>
    You have
<% = nItemCount %> item<% if nItemCount > 1 then %>s<% end if %> in your basket.
    <P>
    To change or remove an item in the basket, click on its description.
    <P>
    <TABLE WIDTH=600>
     . . .

When basket.asp is requested by a user, the ASP code is executed and a web page response like this is returned:

<HTML>
<HEAD>
    <TITLE>Shopping Basket</TITLE>
    <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=ISO-8859-1">
</HEAD>
<BODY TEXT="#51392B" LINK="#CC9966" VLINK= "#FF0000"ALINK="#00FF00"BACKGROUND="assets/images/main.gif">

<TABLE CELLPADDING="2" CELLSPACING="2" WIDTH="600" BORDER="1" RULES="none" background="http://server/vcapgen/assets/images/navbar.gif?">
<TR>
<TD ALIGN=LEFT WIDTH=115><A HREF="http://server/vcapgen/home.asp"><IMG SRC="http://server/vcapgen/assets/images/logo.gif?" WIDTH=98 HEIGHT=76 BORDER=0 ALT="Click here to return Home" ALIGN=TOP></A></TD>

<TD ALIGN=CENTER>
<A HREF="http://server/vcapgen/listing.asp"><IMG SRC="http://server/vcapgen/assets/images/button_catalog_up.gif?" BORDER=0 ALT="Store Directory" ALIGN=CENTER></A>
<A HREF="http://server/vcapgen/search.asp"><IMG SRC="http://server/vcapgen/assets/images/button_search_up.gif?" BORDER=0 ALT="Search" ALIGN=CENTER></A>
<A HREF="http://server/vcapgen/basket.asp"><IMG SRC="http://server/vcapgen/assets/images/button_basket_up.gif?" BORDER=0 ALT="Basket" ALIGN=CENTER></A>
<A HREF="http://server/vcapgen/checkout-ship.asp"><IMG SRC="http://server/vcapgen/assets/images/button_ready2pay_up.gif?" BORDER=0 ALT="Pay" ALIGN=CENTER></A>
</TD>        

</TR>
</TABLE>
<TABLE WIDTH=600>
<TR ALIGN=CENTER><TD><BR><IMG SRC="http://server/vcapgen/assets/images/heading_basket.gif?" BORDER="0" ALT="Shopping Basket" ALIGN=CENTER><BR></TD></TR>
</TABLE>


    You have 2 items in your basket.
    <P>
    To change or remove an item in the basket, click on its description.
    <P>
    <TABLE WIDTH=600>
     . . .

How to Optimize ASPs Using Two-Phase Content Generation

Often an existing ASP web page will need to be optimized using two-phase content generation.  The following steps describe the tasks involved in optimizing ASP pages.

Keep in mind that the goal of two-phase content generation is to move as much code as possible from the second phase into the first phase.  This provides maximal performance and reliability benefits.

Optimization using two-phase content generation typically involves these steps:

  1. Determine if a page contains any ASP code that does not have to be executed for every user.  If a page does not have to be 100% dynamic, follow the remaining steps to optimize the page.  Pages with no user interface (such as pages that log the shopper on and redirect to another page) are 100% dynamic and cannot be optimized with APGen.
  2. If the page will be optimized with two-phase content generation, copy the file to a /apg directory and rename the file to use the .apg extension.
  3. Change any code that can be run in the first phase to use <%# .. #%> brackets instead of ASP brackets.  Insert code at the beginning of the page to specify the name of the ASP file to be generated:

    <%#
    Output.Filename = "default.asp"
    . . .
    #%>. . .

    If optimizing an include file (included by other APG scripts), do not specify the Output.Filename - the root APG script should specify the Output.Filename.
  4. Change any ASP object references within APG script to their APGen equivalent. For example,

    <%# Set cn = Server.CreateObject("ADODB.Recordset")

    should be changed to

    <%# Set cn = Script.CreateObject("ADODB.Recordset")

    For a list of ASP object references and their APG equivalents, see the ASP To APG Syntax Conversion Table.

    Because not all ASP object methods have an APGen object equivalent, conversion of ASP blocks to APG script blocks requires creativity, familiarity with ASP and APGen programming, and an understanding of how the page works.
  5. Use the previous steps to optimize any #include files.  #include files should not specify an Output.Filename.
  6. Include code to execute the APG script in a build script or web page.  The APG script must be executed before the generated ASP file can be requested.  See APGen Examples and Executing APG Scripts for different ways that APG scripts can be executed.

The ASPToAPG Example can automate steps 3 and 4 for you, but it converts all ASP code to APG code.  This is useful for generating static pages, but does not automatically provide the best two-phase page code.

For best results, we recommend manual conversion of two-phase pages and minor re-engineering.  Most pages will contain some code that should be left as ASP code, and some code that should be converted to APG script.  The decision as to what should be converted is best made by a developer familiar with the page.