Item9425: DBCachePlugin stores web data in global variables and does not work correctly with VirtualHostingContrib

pencil
Priority: Normal
Current State: Closed
Released In: n/a
Target Release: n/a
Applies To: Extension
Component: DBCachePlugin
Branches:
Reported By: AntonioTerceiro
Waiting For: Main.MichaelDaum
Last Change By: AntonioTerceiro
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. wink

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
 

ItemTemplate edit

Summary DBCachePlugin stores web data in global variables and does not work correctly with VirtualHostingContrib
ReportedBy AntonioTerceiro
Codebase
SVN Range
AppliesTo Extension
Component DBCachePlugin
Priority Normal
CurrentState Closed
WaitingFor MichaelDaum
Checkins DBCachePlugin:793113b86839
TargetRelease n/a
ReleasedIn n/a
Topic revision: r8 - 02 Nov 2010, AntonioTerceiro
The copyright of the content on this website is held by the contributing authors, except where stated elsewhere. See Copyright Statement. Creative Commons License    Legal Imprint    Privacy Policy