Hi!

I have a working, minimal intrusive and documented patch to add preloading of 
compiled XSP pages. It appends one line to the XSP perl code, a subroutine 
call to tell XSP.pm the true mtime of the page, and it adds that sub to 
XSP.pm, which simply enters the given value as $cache->{$key}{mtime}. 
Everything else is encapsulated in a separate module. See attached diff and 
module. It doesn't change any behaviour, not even when preloading is used - 
modified files are still reparsed.
It greatly improves XSP response time on more complex pages. Some of my test 
pages take 10 seconds on a mildly loaded 300MHz K6 to eval(), plus 5 seconds 
to parse XML->perl. (This is my devel server, don't laugh :-) Afterwards, 
there is just 1sec execution time, the other two are cut to zero.

The diff also fixes (or rather, unifies) caching behaviour for 
XSP-after-$something. Before, an XSP page generated out of 
pnotes('xml_string') was cached in-memory unconditionally, while those from 
pnotes('dom_tree') were never cached in-memory. With this patch, both types 
are now left in-memory as long as the source XML file doesn't change. This is 
in line with XSP-as-first-step, as we have recently seen on axkit-users 
(XIncludes are not consulted for XSP cache expiry). This does change axkit 
behaviour, yet makes it more consistent and predictable.

What do you think of both patches?

-- 
CU
   Joerg
Index: XSP.pm
===================================================================
RCS file: /home/cvspublic/xml-axkit/lib/Apache/AxKit/Language/XSP.pm,v
retrieving revision 1.50
diff -u -r1.50 XSP.pm
--- XSP.pm	17 Sep 2003 19:49:52 -0000	1.50
+++ XSP.pm	22 Sep 2003 10:46:07 -0000
@@ -24,6 +24,11 @@
 
 my $cache;
 
+sub cache_key {
+	$cache->{$_[0]}{mtime} = $_[1] if $_[1];
+	return 1;
+}
+
 # useful for debugging - not actually used by AxKit:
 # sub get_code {
 #     my $filename = shift;
@@ -63,17 +68,28 @@
 
     my $to_eval;
 
+    my $mtime = $xml->mtime();
     eval {
         if (my $dom_tree = $r->pnotes('dom_tree')) {
-            AxKit::Debug(5, 'XSP: parsing dom_tree');
-            $to_eval = $parser->parse($dom_tree);
+            if ($r->no_cache() || !exists($cache->{$key})
+                    || !defined &{"${package}::xml_generator"}
+                    || $xml->has_changed($cache->{$key}{mtime})) {
+                AxKit::Debug(5, "XSP: parsing dom_tree $mtime > ".$cache->{$key}{mtime});
+                $to_eval = $parser->parse($dom_tree);
+                $cache->{$key}{mtime} = $mtime;
+            }
+            else {
+                AxKit::Debug(5, 'XSP: not reparsing dom_tree (cached)');
+            }
             delete $r->pnotes()->{'dom_tree'};
         }
         elsif (my $xmlstr = $r->pnotes('xml_string')) {
-            if ($r->no_cache()
-                    || !defined &{"${package}::xml_generator"}) {
+            if ($r->no_cache() || !exists($cache->{$key})
+                    || !defined &{"${package}::xml_generator"}
+                    || $xml->has_changed($cache->{$key}{mtime})) {
                 AxKit::Debug(5, 'XSP: parsing xml_string');
                 $to_eval = $parser->parse($xmlstr);
+                $cache->{$key}{mtime} = $mtime;
             }
             else {
                 AxKit::Debug(5, 'XSP: not reparsing xml_string (cached)');
@@ -82,7 +98,6 @@
         else {
             my $xcache = Apache::AxKit::Cache->new($r, $package, 'compiled XSP');
             # check mtime.
-            my $mtime = $xml->mtime();
             no strict 'refs';
             if (exists($cache->{$key})
                     && !$xml->has_changed($cache->{$key}{mtime})
@@ -94,6 +109,7 @@
             } elsif (!$xml->has_changed($xcache->mtime())) {
                 AxKit::Debug(5, 'XSP: xsp script cached on disk');
                 $to_eval = $xcache->read();
+                $cache->{$key}{mtime} = $mtime;
             } else {
                 AxKit::Debug(5, 'XSP: parsing fh');
                 $to_eval = eval {
@@ -112,6 +128,7 @@
     }
 
     if ($to_eval) {
+    	$to_eval .= 'Apache::AxKit::Language::XSP::cache_key('.makeSingleQuoted($key).','.$cache->{$key}{mtime}.");\n";
         eval {
             require Perl::Tidy;
             AxKit::Debug(5,'Running PerlTidy...');
# $Id$

use strict;
no warnings;

BEGIN {

use AxKit;
use Apache::AxKit::Language::XSP;

my $r = Apache->server();
my $dir = $r->dir_config('AxXSPPreloadDir');
my $dirh;
opendir $dirh, $dir;
while (my $file = readdir $dirh) {
	if (-f "$dir/$file") {
		my $rc = eval { do "$dir/$file" };
		warn("Could not load $file: $@") if $@;
		warn("Loading of $file failed") if !$rc;
	}
}

}
1;
__END__

=head1 NAME

Apache::AxKit::Language::XSP::Preload - preload compiled XSP scripts

=head1 SYNOPSIS

Use something similar to this in your httpd.conf:

  PerlModule AxKit
  PerlSetVar AxXSPPreloadDir /etc/apache/xsp-preload
  PerlModule Apache::AxKit::Language::XSP::Preload

=head1 DESCRIPTION

This module will preload all compiled XSP files that reside in a
configurable directory. This is good for live sites, as the memory
for this XSP code will be shared among child processes and XSP.pm
doesn't need to reparse or re-eval your code at request time.

You must put compiled XSP files into the given directory. Use
AxTraceIntermediate to have them saved to disk, and copy any *.XSP
file you want into the preload dir.

B<Warning:> Do not, never, ever set AxTraceIntermediate to the same value as
AxXSPPreloadDir. First of all, this module tries to load all files in the
preload dir, and most files generated by AxTraceIntermediate are not
preloadable, only those ending in .XSP. More importantly, regenerating
preloaded files automatically opens a whole lot of security risks.

B<The preloaded code is loaded while apache is still running as root.> So make
absolutely sure you trust these files. You should only load files you generated
yourself. On a sidenote, similar systems like HTML::Mason have this problem, too,
so don't be afraid - but be careful.

=head1 AUTHOR

Jörg Walter E<lt>[EMAIL PROTECTED]<gt>

=head1 LICENSE

This module is available under the same terms as AxKit itself.

=cut

Reply via email to