Item9425: DBCachePlugin stores web data in global variables and does not work correctly with VirtualHostingContrib
Priority: Normal
Current State: Closed
Released In: n/a
Target Release: n/a
DBCachePlugin stores the per-web databases in global variables and thus does not work correctly with
VirtualHostingContrib. In special, this happens because the
getDB
method in Foswiki::Plugins::DBCachePlugin::Core will look for
$webDB{$theWeb}
; since
$webDB
is a global variable, when it searches for the DB corresponding the web
MyWeb
in virtualhost VH1, it can actually use the DB corresponding to the web
MyWeb
in virtualhost VH2.
--
AntonioTerceiro - 01 Aug 2010
This happens when executing under
FastCGI. The same will happen under mod_perl and friends.
--
AntonioTerceiro - 16 Sep 2010
The point of using
DBCachePlugin is keeping things in memory as long as possible, so I don't think that removing the global variables is a good sollution. Instead, I want to focus on keeping separate entries in the
webDB
hash for webs with the same name but from different virtual hosts.
The solution I propose is to change the getDB() function in
Foswiki::Plugins::DBCachePlugin::Core
by adding the current hostname in the hash key used to store/retrieve the web databases:
Index: lib/Foswiki/Plugins/DBCachePlugin/Core.pm
===================================================================
--- lib/Foswiki/Plugins/DBCachePlugin/Core.pm (revisão 9120)
+++ lib/Foswiki/Plugins/DBCachePlugin/Core.pm (cópia de trabalho)
@@ -1244,20 +1244,22 @@
sub getDB {
my $theWeb = shift;
#writeDebug("called getDB($theWeb)");
# We do not need to reload the cache if we run on mod_perl or fastcgi or
# whatever perl accelerator that keeps our global variables and
# the database wasn't modified!
$theWeb =~ s/\//\./go;
+ my $query = Foswiki::Func::getCgiQuery();
+ $theWeb .= '/' . $query->virtual_host();
my $isModified = 0;
unless (defined $webDB{$theWeb}) {
# never loaded
$isModified = 1;
writeDebug("fresh reload of '$theWeb'");
} else {
unless (defined $webDBIsModified{$theWeb}) {
# never checked
$webDBIsModified{$theWeb} = $webDB{$theWeb}->isModified();
Can I go ahead and commit this?
--
AntonioTerceiro - 16 Sep 2010
Give it a second: as far as I get it, this will add a different key to the hash for every virtual host the server is contacted with. This sometimes is quite regular even without
VirtualHostingContrib, but with aliases to the same which are all equivalents. However in this case it would be totally valid to reuse the same webDB.
Nother question, are there webs among all instances configured in
VirtualHostingContrib that share some webs, i.e. the System web? It would make sense not to load the same webDB redundantly.
--
MichaelDaum - 17 Sep 2010
You are right.
What about this:
Index: lib/Foswiki/Plugins/DBCachePlugin/Core.pm
===================================================================
--- lib/Foswiki/Plugins/DBCachePlugin/Core.pm (revisão 9516)
+++ lib/Foswiki/Plugins/DBCachePlugin/Core.pm (cópia de trabalho)
@@ -38,6 +38,7 @@
use Foswiki::Plugins::DBCachePlugin::WebDB ();
use Foswiki::Sandbox ();
use Foswiki::Time ();
+use Cwd;
###############################################################################
sub writeDebug {
@@ -1251,25 +1252,26 @@
# the database wasn't modified!
$theWeb =~ s/\//\./go;
+ my $webKey = Cwd::abs_path($Foswiki::cfg{DataDir} . '/' . $theWeb);
my $isModified = 0;
- unless (defined $webDB{$theWeb}) {
+ unless (defined $webDB{$webKey}) {
# never loaded
$isModified = 1;
writeDebug("fresh reload of '$theWeb'");
} else {
- unless (defined $webDBIsModified{$theWeb}) {
+ unless (defined $webDBIsModified{$webKey}) {
# never checked
- $webDBIsModified{$theWeb} = $webDB{$theWeb}->isModified();
+ $webDBIsModified{$webKey} = $webDB{$webKey}->isModified();
if (DEBUG) {
- if ($webDBIsModified{$theWeb}) {
+ if ($webDBIsModified{$webKey}) {
writeDebug("reloading modified $theWeb");
} else {
writeDebug("don't need to load webdb for $theWeb");
}
}
}
- $isModified = $webDBIsModified{$theWeb};
+ $isModified = $webDBIsModified{$webKey};
}
if ($isModified) {
@@ -1279,20 +1281,20 @@
$impl =~ s/\s+$//go;
writeDebug("loading new webdb for '$theWeb($isModified) '");
#writeDebug("impl='$impl'");
- $webDB{$theWeb} = new $impl($theWeb);
- $webDB{$theWeb}->load($doRefresh, $baseWeb, $baseTopic);
- $webDBIsModified{$theWeb} = 0;
+ $webDB{$webKey} = new $impl($theWeb);
+ $webDB{$webKey}->load($doRefresh, $baseWeb, $baseTopic);
+ $webDBIsModified{$webKey} = 0;
}
if (DEBUG) {
#require Data::Dumper;
- #my $db = $webDB{$theWeb};
+ #my $db = $webDB{$webKey};
#print STDERR "=====================\n";
#print STDERR Data::Dumper->Dump([$db], [ref $db]);
#print STDERR "=====================\n";
}
- return $webDB{$theWeb};
+ return $webDB{$webKey};
}
###############################################################################
First you obtain the directory where the web is supposed to be, and then apply
Cwd::abs_path
to it in order to generate a web key. Since
Cwd::abs_path
will follow symbolic links, if the web is shared then it will load a single database no matter which virtual host we are in.
This solves your concerns, but there is one problem: it assumes that webs are stored in directories, and ideally
DBCachePlugin should not know about such implementation details.
--
AntonioTerceiro - 06 Oct 2010
This patch looks good.
Yes, it does rely on the fact that webs are stored in data directories. But as soon as this goes away,
DBCachePlugin will too.
No, it won't.
Instead, it will interface the real DB store directly providing the wiki application API we've got, i.e. DBQUERY, DBCALL and DBRECURSE.
--
MichaelDaum - 07 Oct 2010
committed.
--
AntonioTerceiro - 02 Nov 2010