You are viewing a plain text version of this content. The canonical link for it is here.
Posted to modperl@perl.apache.org by Jeremy Nixon <je...@exit109.com> on 2005/11/15 08:06:47 UTC

Re: mod_perl: How pass variables (objects) from page to page?

Peter1 Alvin  <te...@awebabove.com> wrote:

> Please tell me I can do this!
>  
> Using mod_perl, how do you keep Perl objects in RAM from page to page? 
> I don't want to re-instantiate my objects on every page request, and I
> don't want the overhead of serializing my objects to a persistent store
> from page to page (I use A LOT of objects).
>  
> I've done extensive reseach and I've not found any "application space"
> to store objects indefinitly.
>  
> I can't believe Perl doesn't support keeping objects in RAM.  This would
> de-qualify Perl for half of the projects I need to develop.

If you stick something into a global variable it will stay around from one
request to another, as long as the server stays running.  But since you
can't guarantee that the next request from a particular browser will come
to the same server, if you're hoping to keep "session" information this
way, don't bother.

But if you just want to cache the result of some expensive operation, and
the result won't be changing from one request to the next, you can either
use a global variable, or (better) a lexical variable that never goes out
of scope.

As an example, I have a handler that needs to read a directory listing.
Most of the time it will be looking at the same directory over and over,
so I decide that it's okay to have to restart the server if the listing
will change (the listing won't change much) and do something like:

{ # start a lexical scope
my %d_cache;
sub handler {
    # stuff
    if (not defined($d_cache{$foo})) {
        # pull in directory listing and store it in $d_cache{$foo}
    }
    # proceed to use $d_cache{$foo} information
}
} end lexical scope

This way, a directory is only actually read once (at most) per server
child process.

Note: I'm no expert in threaded programming, but the above doesn't look
thread-safe to me.  I use the pre-fork MPM.

-- 
Jeremy  |  jeremy@exit109.com


Re: mod_perl: How pass variables (objects) from page to page?

Posted by John Doe <se...@tele2.ch>.
John Doe am Dienstag, 15. November 2005 12.35:
> David Baird am Dienstag, 15. November 2005 11.07:
> > On 11/15/05, John Doe <se...@tele2.ch> wrote:
> > > Jeremy Nixon am Dienstag, 15. November 2005 08.06:
> > > > Peter1 Alvin  <te...@awebabove.com> wrote:
>
> [...]
>
> > > > { # start a lexical scope
> > > > my %d_cache;
> > > > sub handler {
> > > >     # stuff
> > > >     if (not defined($d_cache{$foo})) {
> > > >         # pull in directory listing and store it in $d_cache{$foo}
> > > >     }
> > > >     # proceed to use $d_cache{$foo} information
> > > > }
> > > > } end lexical scope
> > > >
> > > > This way, a directory is only actually read once (at most) per server
> > > > child process.
> > >
> > > Hi Jeremy
> > >
> > > Hope it's not a stupid question, but are you sure %d_cache survives a
> > > request? Maybe I'm totally misunderstanding something but I thought
> > > after the point
> > >
> > > } end lexical scope
> > >
> > > %d_cache gets destroyed (if not still referenced from somewhere else).
> > >
> > > I would have left out the scope-{} to keep %d_cache at file level.
> > > Would that be wrong? And why?
> >
> > The handler() sub stays in scope - it's basically a global variable,
> > and it holds a reference to %d_cache. So %d_cache goes out of scope,
> > but doesn't get destroyed. It hangs around until the next time
> > handler() is called, at which point you're back in the same scope and
> > can access %d_cache again.
>
> Hi David
>
> thanks a lot for your answer! I have still a last question to get it 100%:
>
> The (lexical scoped) code as shown above does _not_ show the required
> referencing of %d_cache, right? Something like
>
>    if (not defined($d_cache{$foo}))
>
> is not sufficient, right?


> Whereas something like
>
>    $keep_ref=\%d_cache;
>
> in the handler() would, right?

Hm, I think even this would not keep %d_cache, because $keep_ref goes out of 
scope at the end of handler()...

Confused,

joe

Re: mod_perl: How pass variables (objects) from page to page?

Posted by John Doe <se...@tele2.ch>.
Jeremy Nixon am Dienstag, 15. November 2005 23.16:
> John Doe  <se...@tele2.ch> wrote:
> > Hope it's not a stupid question, but are you sure %d_cache survives a
> > request?  Maybe I'm totally misunderstanding something but I thought
> > after the point
> >
> > } end lexical scope
> >
> > %d_cache gets destroyed (if not still referenced from somewhere else).

Jeremy, all, thanks very much for the replies; I really appreciate it, 
especially since the questions _were_ stupid. 

But nonetheless, it cleared some of my confusion (I think).

It seems that I use closure features from time to time, and from time to time 
I'm totally confused about it. 

> Lexical scope means the scope in which the "thing" is defined in the
> source. So, as far as the handler subroutine is concerned, the %d_cache
> variable never goes out of scope, and is never destroyed.  It makes the
> subroutine into a closure, as someone else pointed out, though it's a
> different usage than you normally see for closures so I didn't think of
> that term to describe it.

Yes :-)
After all the replies I visualize the issue with 3 snippets:

my %d;
sub a { $d{a}++} }

{  
   my %d;
   sub a { $d{a}++} }
}

sub b {
   my %d;
   return sub a { $d{a}++} }
}

> > I would have left out the scope-{} to keep %d_cache at file level.
> > Would that be wrong? And why?
>
> If you put it at the file level it is visible to the entire file.  This
> probably won't hurt, but it is best in general to keep variable scope
> as limited as possible; in this case, it is visible where it is used,
> but not elsewhere.
>
> > Whereas something like
> >
> >    $keep_ref=\%d_cache;
> >
> > in the handler() would, right?
>
> No, "reference" doesn't mean a literal reference, it's used in the English
> sense of simply referring to the variable in the source code.

This hint to the different meanings of 'reference' was very helpful!

Again, thanks a lot to all...
(And: modperl is great! As are all people working on it!)

Re: mod_perl: How pass variables (objects) from page to page?

Posted by Perrin Harkins <pe...@elem.com>.
David Baird wrote:
> No, the 'reference' to the lexical variable doesn't need to be a 'Perl
> reference', just some mention of the variable in the code. That will
> make it stick around, but only within the lexical scope specified.

David is correct.  This is called a closure.  Any use of a lexical 
variable declared OUTSIDE of the scope of the sub where you use it will 
make it permanent for that sub.  Read up on closures for more.

Here's an example:

my $counter = 1;

sub print_counter {
     print $counter;
     $counter++;
}

The $counter variable will never go out of scope for this sub.

- Perrin

Re: mod_perl: How pass variables (objects) from page to page?

Posted by David Baird <li...@gmail.com>.
On 11/15/05, John Doe <se...@tele2.ch> wrote:
> David Baird am Dienstag, 15. November 2005 11.07:
> > On 11/15/05, John Doe <se...@tele2.ch> wrote:
> > > Jeremy Nixon am Dienstag, 15. November 2005 08.06:
> > > > Peter1 Alvin  <te...@awebabove.com> wrote:
> [...]
> > > > { # start a lexical scope
> > > > my %d_cache;
> > > > sub handler {
> > > >     # stuff
> > > >     if (not defined($d_cache{$foo})) {
> > > >         # pull in directory listing and store it in $d_cache{$foo}
> > > >     }
> > > >     # proceed to use $d_cache{$foo} information
> > > > }
> > > > } end lexical scope
> > > >
> > > > This way, a directory is only actually read once (at most) per server
> > > > child process.
> > >
> > > Hi Jeremy
> > >
> > > Hope it's not a stupid question, but are you sure %d_cache survives a
> > > request? Maybe I'm totally misunderstanding something but I thought after
> > > the point
> > >
> > > } end lexical scope
> > >
> > > %d_cache gets destroyed (if not still referenced from somewhere else).
> > >
> > > I would have left out the scope-{} to keep %d_cache at file level.
> > > Would that be wrong? And why?
> >
> > The handler() sub stays in scope - it's basically a global variable,
> > and it holds a reference to %d_cache. So %d_cache goes out of scope,
> > but doesn't get destroyed. It hangs around until the next time
> > handler() is called, at which point you're back in the same scope and
> > can access %d_cache again.
>
> Hi David
>
> thanks a lot for your answer! I have still a last question to get it 100%:
>
> The (lexical scoped) code as shown above does _not_ show the required
> referencing of %d_cache, right? Something like
>
>    if (not defined($d_cache{$foo}))
>
> is not sufficient, right?
>
> Whereas something like
>
>    $keep_ref=\%d_cache;
>
> in the handler() would, right?
>
> (If I'm not right, I didn't get something basic right, although I read a lot
> of documentation about this issue... I would have to review/change a lot of
> code I've written)
>
> Thanks a lot in advance
>

No, the 'reference' to the lexical variable doesn't need to be a 'Perl
reference', just some mention of the variable in the code. That will
make it stick around, but only within the lexical scope specified.

d.

Re: mod_perl: How pass variables (objects) from page to page?

Posted by Jeremy Nixon <je...@exit109.com>.
John Doe  <se...@tele2.ch> wrote:

> Hope it's not a stupid question, but are you sure %d_cache survives a
> request?  Maybe I'm totally misunderstanding something but I thought
> after the point 
> 
> } end lexical scope
> 
> %d_cache gets destroyed (if not still referenced from somewhere else).

Lexical scope means the scope in which the "thing" is defined in the source.
So, as far as the handler subroutine is concerned, the %d_cache variable
never goes out of scope, and is never destroyed.  It makes the subroutine
into a closure, as someone else pointed out, though it's a different usage
than you normally see for closures so I didn't think of that term to
describe it.

> I would have left out the scope-{} to keep %d_cache at file level.
> Would that be wrong? And why?

If you put it at the file level it is visible to the entire file.  This
probably won't hurt, but it is best in general to keep variable scope
as limited as possible; in this case, it is visible where it is used,
but not elsewhere.

> Whereas something like
> 
>    $keep_ref=\%d_cache;
> 
> in the handler() would, right?

No, "reference" doesn't mean a literal reference, it's used in the English
sense of simply referring to the variable in the source code.

-- 
Jeremy  |  jeremy@exit109.com


Re: Detecting request cancellation behind proxy servers

Posted by Perrin Harkins <pe...@elem.com>.
On Thu, 2005-11-17 at 09:02 +1000, Badai Aqrandista wrote:
> Actually, the locking I meant is the Apache::Session's lock. I am guessing 
> that A::S lock blocks the second page to be loaded if the first page hasn't 
> finished loading.

I know, but you don't have to use that if you are storing the sessions
in a database.  There is a risk of lost updates (i.e. last save wins)
but no risk of data corruption.  So, if what you're storing in the
session can handle the possibility of a later save from the interrupted
page overwriting session data, you don't need the lock.  Look at the
NullLocker class in A::S.

- Perrin


Re: Detecting request cancellation behind proxy servers

Posted by Badai Aqrandista <ba...@hotmail.com>.
>On Wed, 2005-11-16 at 10:11 +1000, Badai Aqrandista wrote:
> > But I need to know if a request has been cancelled. The problem is that 
>I
> > need to release the session lock when someone click submit button before 
>the
> > page is fully loaded, otherwise the next page won't load because it is
> > waiting for the session lock to be released by the previous process that 
>has
> > been cancelled before releasing it.
>
>Why are you using locking with your sessions?  Are you doing something
>that truly requires it?  You already have atomic updates if you use
>database-backed session storage.

Actually, the locking I meant is the Apache::Session's lock. I am guessing 
that A::S lock blocks the second page to be loaded if the first page hasn't 
finished loading.

>In other words, look for a way to avoid the root of the problem: doing a
>slow operation and keeping the session locked the whole time.

I'll look into that. Thanks...

---
Badai Aqrandista
Cheepy (?)

_________________________________________________________________
REALESTATE: biggest buy/rent/share listings   
http://ninemsn.realestate.com.au


Re: Detecting request cancellation behind proxy servers

Posted by Perrin Harkins <pe...@elem.com>.
On Wed, 2005-11-16 at 10:11 +1000, Badai Aqrandista wrote:
> But I need to know if a request has been cancelled. The problem is that I 
> need to release the session lock when someone click submit button before the 
> page is fully loaded, otherwise the next page won't load because it is 
> waiting for the session lock to be released by the previous process that has 
> been cancelled before releasing it.

Why are you using locking with your sessions?  Are you doing something
that truly requires it?  You already have atomic updates if you use
database-backed session storage.

Consider moving this data out of your session and passing it in URLs
instead.  Consider separating out the slow part of handling this request
into a forked process, so that the session is not locked during the
whole calculation.

In other words, look for a way to avoid the root of the problem: doing a
slow operation and keeping the session locked the whole time.

- Perrin


Detecting request cancellation behind proxy servers

Posted by Badai Aqrandista <ba...@hotmail.com>.
Hi All,

I have backend apache-mod_perl processes running behind frontend 
apache-proxy servers. I am aware that it is impossible for the backend 
processes to know if a request has been cancelled, according to:
http://perl.apache.org/docs/1.0/guide/debug.html#Handling_the__User_pressed_Stop_button__case

But I need to know if a request has been cancelled. The problem is that I 
need to release the session lock when someone click submit button before the 
page is fully loaded, otherwise the next page won't load because it is 
waiting for the session lock to be released by the previous process that has 
been cancelled before releasing it.

Does anyone know a workaround or a way to combat this?

Thanks...

---
Badai Aqrandista
Cheepy (?)

_________________________________________________________________
Start something musical - 15 free ninemsn Music downloads! 
http://ninemsn.com.au/share/redir/adTrack.asp?mode=click&clientID=667&referral=HotmailTaglineNov&URL=http://www.ninemsn.com.au/startsomething


Re: mod_perl: How pass variables (objects) from page to page?

Posted by Jeremy Nixon <je...@exit109.com>.
Leo Lapworth  <ra...@gmail.com> wrote:

> I don't think this would work...
> 
> You can set up a global variable within your startup.pl (before apache
> spawns child processed) and all children will get it, but if you
> edit it in anyway the changes are only reflected in that specific child
> process and you have no way of knowing if the user will hit the
> same child process on their next page request.
> 
> or have I missed something ?

You are correct -- you cannot know that the same user will end up with
the same server, and thus the same copy of the variable, at any given
time.  This can be used to store information between requests, but not
for something like session information or in any way that expects that
you can return to the same data in any particular order, or for any
particular user.  The same user's very next request may hit a different
server and thus require the data to be re-generated, and then some other
user can hit the same server and get the cached data.

-- 
Jeremy  |  jeremy@exit109.com


Re: mod_perl: How pass variables (objects) from page to page?

Posted by David Baird <li...@gmail.com>.
On 11/15/05, Leo Lapworth <ra...@gmail.com> wrote:
>
> On 15 Nov 2005, at 11:35, John Doe wrote:
> >>> Hope it's not a stupid question, but are you sure %d_cache
> >>> survives a
> >>> request? Maybe I'm totally misunderstanding something but I
> >>> thought after
> >>> the point
> >>>
> >>> } end lexical scope
> >>>
> >>> %d_cache gets destroyed (if not still referenced from somewhere
> >>> else).
> >>>
> >>> I would have left out the scope-{} to keep %d_cache at file level.
> >>> Would that be wrong? And why?
> >>
> >> The handler() sub stays in scope - it's basically a global variable,
> >> and it holds a reference to %d_cache. So %d_cache goes out of scope,
> >> but doesn't get destroyed. It hangs around until the next time
> >> handler() is called, at which point you're back in the same scope and
> >> can access %d_cache again.
>
> I don't think this would work...
>
> You can set up a global variable within your startup.pl (before apache
> spawns child processed) and all children will get it, but if you
> edit it in anyway the changes are only reflected in that specific child
> process and you have no way of knowing if the user will hit the
> same child process on their next page request.
>
> or have I missed something ?

Yes, but the fact that we're in a multi-process system is a slightly
different topic. The lexical will stick around between requests. But
each process will be accessing its own copy of the lexical variable.
So you should populate the lexical in the parent before forking, and
then never modify it again.

d.

Re: mod_perl: How pass variables (objects) from page to page?

Posted by Leo Lapworth <ra...@gmail.com>.
On 15 Nov 2005, at 11:35, John Doe wrote:
>>> Hope it's not a stupid question, but are you sure %d_cache  
>>> survives a
>>> request? Maybe I'm totally misunderstanding something but I  
>>> thought after
>>> the point
>>>
>>> } end lexical scope
>>>
>>> %d_cache gets destroyed (if not still referenced from somewhere  
>>> else).
>>>
>>> I would have left out the scope-{} to keep %d_cache at file level.
>>> Would that be wrong? And why?
>>
>> The handler() sub stays in scope - it's basically a global variable,
>> and it holds a reference to %d_cache. So %d_cache goes out of scope,
>> but doesn't get destroyed. It hangs around until the next time
>> handler() is called, at which point you're back in the same scope and
>> can access %d_cache again.

I don't think this would work...

You can set up a global variable within your startup.pl (before apache
spawns child processed) and all children will get it, but if you
edit it in anyway the changes are only reflected in that specific child
process and you have no way of knowing if the user will hit the
same child process on their next page request.

or have I missed something ?

Leo


Re: mod_perl: How pass variables (objects) from page to page?

Posted by John Doe <se...@tele2.ch>.
David Baird am Dienstag, 15. November 2005 11.07:
> On 11/15/05, John Doe <se...@tele2.ch> wrote:
> > Jeremy Nixon am Dienstag, 15. November 2005 08.06:
> > > Peter1 Alvin  <te...@awebabove.com> wrote:
[...]
> > > { # start a lexical scope
> > > my %d_cache;
> > > sub handler {
> > >     # stuff
> > >     if (not defined($d_cache{$foo})) {
> > >         # pull in directory listing and store it in $d_cache{$foo}
> > >     }
> > >     # proceed to use $d_cache{$foo} information
> > > }
> > > } end lexical scope
> > >
> > > This way, a directory is only actually read once (at most) per server
> > > child process.
> >
> > Hi Jeremy
> >
> > Hope it's not a stupid question, but are you sure %d_cache survives a
> > request? Maybe I'm totally misunderstanding something but I thought after
> > the point
> >
> > } end lexical scope
> >
> > %d_cache gets destroyed (if not still referenced from somewhere else).
> >
> > I would have left out the scope-{} to keep %d_cache at file level.
> > Would that be wrong? And why?
>
> The handler() sub stays in scope - it's basically a global variable,
> and it holds a reference to %d_cache. So %d_cache goes out of scope,
> but doesn't get destroyed. It hangs around until the next time
> handler() is called, at which point you're back in the same scope and
> can access %d_cache again.

Hi David

thanks a lot for your answer! I have still a last question to get it 100%:

The (lexical scoped) code as shown above does _not_ show the required 
referencing of %d_cache, right? Something like 

   if (not defined($d_cache{$foo}))

is not sufficient, right?

Whereas something like

   $keep_ref=\%d_cache;

in the handler() would, right?

(If I'm not right, I didn't get something basic right, although I read a lot 
of documentation about this issue... I would have to review/change a lot of 
code I've written)

Thanks a lot in advance

Re: mod_perl: How pass variables (objects) from page to page?

Posted by David Baird <li...@gmail.com>.
On 11/15/05, John Doe <se...@tele2.ch> wrote:
> Jeremy Nixon am Dienstag, 15. November 2005 08.06:
> > Peter1 Alvin  <te...@awebabove.com> wrote:
> > > Please tell me I can do this!
> > >
> > > Using mod_perl, how do you keep Perl objects in RAM from page to page?
> [...]
> > As an example, I have a handler that needs to read a directory listing.
> > Most of the time it will be looking at the same directory over and over,
> > so I decide that it's okay to have to restart the server if the listing
> > will change (the listing won't change much) and do something like:
> >
> > { # start a lexical scope
> > my %d_cache;
> > sub handler {
> >     # stuff
> >     if (not defined($d_cache{$foo})) {
> >         # pull in directory listing and store it in $d_cache{$foo}
> >     }
> >     # proceed to use $d_cache{$foo} information
> > }
> > } end lexical scope
> >
> > This way, a directory is only actually read once (at most) per server
> > child process.
>
> Hi Jeremy
>
> Hope it's not a stupid question, but are you sure %d_cache survives a request?
> Maybe I'm totally misunderstanding something but I thought after the point
>
> } end lexical scope
>
> %d_cache gets destroyed (if not still referenced from somewhere else).
>
> I would have left out the scope-{} to keep %d_cache at file level.
> Would that be wrong? And why?

The handler() sub stays in scope - it's basically a global variable,
and it holds a reference to %d_cache. So %d_cache goes out of scope,
but doesn't get destroyed. It hangs around until the next time
handler() is called, at which point you're back in the same scope and
can access %d_cache again.

d.

Re: mod_perl: How pass variables (objects) from page to page?

Posted by John Doe <se...@tele2.ch>.
Jeremy Nixon am Dienstag, 15. November 2005 08.06:
> Peter1 Alvin  <te...@awebabove.com> wrote:
> > Please tell me I can do this!
> >
> > Using mod_perl, how do you keep Perl objects in RAM from page to page?
[...]
> As an example, I have a handler that needs to read a directory listing.
> Most of the time it will be looking at the same directory over and over,
> so I decide that it's okay to have to restart the server if the listing
> will change (the listing won't change much) and do something like:
>
> { # start a lexical scope
> my %d_cache;
> sub handler {
>     # stuff
>     if (not defined($d_cache{$foo})) {
>         # pull in directory listing and store it in $d_cache{$foo}
>     }
>     # proceed to use $d_cache{$foo} information
> }
> } end lexical scope
>
> This way, a directory is only actually read once (at most) per server
> child process.

Hi Jeremy

Hope it's not a stupid question, but are you sure %d_cache survives a request? 
Maybe I'm totally misunderstanding something but I thought after the point 

} end lexical scope

%d_cache gets destroyed (if not still referenced from somewhere else).

I would have left out the scope-{} to keep %d_cache at file level.
Would that be wrong? And why?

I'd appreciate every hint.

joe