Loading ...
Sorry, an error occurred while loading the content.

Re: [PBML] Perl Phantom Variables in FastCGI

Expand Messages
  • Joe Pepersack
    In FastCGI, your program does not exit after it runs; the perl interpreter and your script stay in memory until the fastcgi server kills it. This is why
    Message 1 of 4 , Sep 26, 2012
    • 0 Attachment
      In FastCGI, your program does not exit after it runs; the perl
      interpreter and your script stay in memory until the fastcgi server
      kills it. This is why FastCGI is so much faster than regular CGI. It's
      doing what it's supposed to do.

      Try running ps on your webserver while it's running and observe the
      lifecycle of the perl process.

      If you're running FastCGI, I suggest using the CGI::Fast module.

      The basic syntax is:

      use CGI::Fast;
      use CGI::Carp;
      while ( my $cgi = CGI::Fast->new ) {
      print_your_page();
      }
      cleanup();

      This code sits idle until the page is hit, then calls
      print_your_page(). cleanup() is actually never called, because the
      while loop is effectively endless - the process never exits normally.
      If you want cleanup() to be called you have to use signal handling to
      catch SIGKILL.

      I wrote an article on using FastCGI to run HTML::Mason
      <http://joe.pepersack.net/blog/programming/mason_fastcgi.html> sites.
      The script attached to the article is well-commented and should serve as
      a non-trivial example of FastCGI in action.

      The CGI::Fast documentation is well worth reading.


      On 9/26/12 7:46 PM, johnlikeglass wrote:
      >
      > Take a look at the simple script below. $c should never show up, right?
      > On FastCGI servers it often does! Try loading it, calling it, and
      > hitting F5 enough, and the text in $c gets displayed. Call them
      > Phantom, or Zombie, but they live on after the script exits and may show
      > up in the next script called. So, what's going on? Well....
      >
      > On a server configured for FastCGI, the perl operating system stays
      > alive so that it does not need to be reloaded every time a script is
      > called, improving performance. Unfortunately, if you are running your
      > script on such a server, you have to take some extra precautions. Using
      > reset 'a-z'; will clear your lower case variables, unfortunately, the
      > upper case version will also clear the environmental variables which you
      > may be using. One workaround on this is to kill the variables as the
      > script exits, for example: reset 'a-z'; reset 'A-Z'; exit;
      >
      > There are other ways to do this for sure, but clearing on exit is an
      > easy way to upgrade scripts you may already have written and recently
      > began to misbehave.
      >
      > #!/usr/local/bin/perl
      >
      > $a = localtime();
      >
      > $test_file = 'test.txt';
      > open DATA, ">>$test_file" or die "can't open $test_file $!";
      > print DATA $a.'<br>';
      > print DATA $c.'<br>';
      > close (DATA);
      >
      > open DATA, "<$test_file" or die "can't open $test_file $!";
      > @new_data = <DATA>;
      > close (DATA);
      >
      > #output page
      > print <<EOF ;
      > Content-type: text/html
      >
      > <html><head>
      > <META http-equiv="EXPIRES" CONTENT="0">
      > <meta http-equiv="pragma" content="no-cache">
      > </head><body>
      > <p>@new_data</p>
      > </body></html>
      > EOF
      >
      > $c = 'This line should never show up';
      > exit;
      >
      > [Non-text portions of this message have been removed]
      >
      >



      [Non-text portions of this message have been removed]
    • Joe Pepersack
      My example below may not be entirely clear. Let s modify it to be: use CGI::Fast; use CGI::Carp; $SIG{PIPE} = sub { carp caught SIGPIPE }; $SIG{USR1} = sub {
      Message 2 of 4 , Sep 26, 2012
      • 0 Attachment
        My example below may not be entirely clear.

        Let's modify it to be:

        use CGI::Fast;
        use CGI::Carp;

        $SIG{PIPE} = sub { carp "caught SIGPIPE" };
        $SIG{USR1} = sub { carp "cleanup1"; cleanup(1) };
        $SIG{TERM} = sub { carp "cleanup2"; cleanup(2) };

        carp 'starting';
        while (my $cgi = CGI::Fast->new) {
        carp "printing page";
        print_your_page();
        }
        carp "cleanup3";
        cleanup(3);

        IF we run this as regular CGI and hit the page 3 times, the Apache
        error.log would look like:
        starting
        printing page
        cleanup3
        starting
        printing page
        cleanup3
        starting
        printing page
        cleanup3

        If we do the exact same exercise under FastCGI error.log would look like:
        starting
        printing page
        printing page
        printing page
        cleanup1


        Note that in the FastCGI run we don't see cleanup1 turn up for quite
        some time (potentially hours), and we never see cleanup3 because the
        while loop never terminates normally. Conversely in regular CGI we
        should never see the cleanup1 or cleanup2 messages unless there was
        something seriously wrong with the Apache daemon.

        See: http://www.fastcgi.com/docs/faq.html for more info on how the
        FastCGI works and how it differs from regular CGI. See perldoc perlipc
        to understand signal handling in Perl.

        If we don't use CGI::Fast (or the less-capable but largely equivalent
        FCGI module), I suspect the FastCGI server is effectively going to put
        an implicit loop around your script, which is why you're seeing the
        value of $c from the previous run showing up in subsequent runs.

        There's nothing "phantom" about it. FastCGI is doing exactly what it's
        supposed to be doing - persisting the Perl process between page hits.
        Your code has to be written properly to take this behavior into
        account. Get CGI::Fast or FCGI from CPAN. Reading the docs for CGI
        module is also highly recommended, since CGI::Fast inherits most of it's
        capabilities.

        On 9/26/12 8:02 PM, Joe Pepersack wrote:
        >
        > In FastCGI, your program does not exit after it runs; the perl
        > interpreter and your script stay in memory until the fastcgi server
        > kills it. This is why FastCGI is so much faster than regular CGI. It's
        > doing what it's supposed to do.
        >
        > Try running ps on your webserver while it's running and observe the
        > lifecycle of the perl process.
        >
        > If you're running FastCGI, I suggest using the CGI::Fast module.
        >
        > The basic syntax is:
        >
        > use CGI::Fast;
        > use CGI::Carp;
        > while ( my $cgi = CGI::Fast->new ) {
        > print_your_page();
        > }
        > cleanup();
        >
        > This code sits idle until the page is hit, then calls
        > print_your_page(). cleanup() is actually never called, because the
        > while loop is effectively endless - the process never exits normally.
        > If you want cleanup() to be called you have to use signal handling to
        > catch SIGKILL.
        >
        > I wrote an article on using FastCGI to run HTML::Mason
        > <http://joe.pepersack.net/blog/programming/mason_fastcgi.html> sites.
        > The script attached to the article is well-commented and should serve as
        > a non-trivial example of FastCGI in action.
        >
        > The CGI::Fast documentation is well worth reading.
        >
        > On 9/26/12 7:46 PM, johnlikeglass wrote:
        > >
        > > Take a look at the simple script below. $c should never show up, right?
        > > On FastCGI servers it often does! Try loading it, calling it, and
        > > hitting F5 enough, and the text in $c gets displayed. Call them
        > > Phantom, or Zombie, but they live on after the script exits and may show
        > > up in the next script called. So, what's going on? Well....
        > >
        > > On a server configured for FastCGI, the perl operating system stays
        > > alive so that it does not need to be reloaded every time a script is
        > > called, improving performance. Unfortunately, if you are running your
        > > script on such a server, you have to take some extra precautions. Using
        > > reset 'a-z'; will clear your lower case variables, unfortunately, the
        > > upper case version will also clear the environmental variables which you
        > > may be using. One workaround on this is to kill the variables as the
        > > script exits, for example: reset 'a-z'; reset 'A-Z'; exit;
        > >
        > > There are other ways to do this for sure, but clearing on exit is an
        > > easy way to upgrade scripts you may already have written and recently
        > > began to misbehave.
        > >
        > > #!/usr/local/bin/perl
        > >
        > > $a = localtime();
        > >
        > > $test_file = 'test.txt';
        > > open DATA, ">>$test_file" or die "can't open $test_file $!";
        > > print DATA $a.'<br>';
        > > print DATA $c.'<br>';
        > > close (DATA);
        > >
        > > open DATA, "<$test_file" or die "can't open $test_file $!";
        > > @new_data = <DATA>;
        > > close (DATA);
        > >
        > > #output page
        > > print <<EOF ;
        > > Content-type: text/html
        > >
        > > <html><head>
        > > <META http-equiv="EXPIRES" CONTENT="0">
        > > <meta http-equiv="pragma" content="no-cache">
        > > </head><body>
        > > <p>@new_data</p>
        > > </body></html>
        > > EOF
        > >
        > > $c = 'This line should never show up';
        > > exit;
        > >
        > > [Non-text portions of this message have been removed]
        > >
        > >
        >
        > [Non-text portions of this message have been removed]
        >
        >



        [Non-text portions of this message have been removed]
      • Shlomi Fish
        Hi John, a few comments on your code: On Thu, 27 Sep 2012 00:46:13 -0000 ... Always add use strict; and use warnings; :
        Message 3 of 4 , Sep 27, 2012
        • 0 Attachment
          Hi John,

          a few comments on your code:

          On Thu, 27 Sep 2012 00:46:13 -0000
          "johnlikeglass" <js_dziel@...> wrote:

          > Take a look at the simple script below. $c should never show up,
          > right? On FastCGI servers it often does! Try loading it, calling it,
          > and hitting F5 enough, and the text in $c gets displayed. Call them
          > Phantom, or Zombie, but they live on after the script exits and may
          > show up in the next script called. So, what's going on? Well....
          >
          > On a server configured for FastCGI, the perl operating system stays
          > alive so that it does not need to be reloaded every time a script is
          > called, improving performance. Unfortunately, if you are running your
          > script on such a server, you have to take some extra precautions.
          > Using reset 'a-z'; will clear your lower case variables,
          > unfortunately, the upper case version will also clear the
          > environmental variables which you may be using. One workaround on
          > this is to kill the variables as the script exits, for example:
          > reset 'a-z'; reset 'A-Z'; exit;
          >
          > There are other ways to do this for sure, but clearing on exit is an
          > easy way to upgrade scripts you may already have written and recently
          > began to misbehave.
          >
          >
          >
          > #!/usr/local/bin/perl
          >

          Always add "use strict;" and "use warnings;":

          http://perl-begin.org/tutorials/bad-elements/#no-strict-and-warnings

          > $a = localtime();
          >

          Declare the variable using "my" and don't call it "$a":

          http://perl-begin.org/tutorials/bad-elements/#vars-a-and-b

          > $test_file = 'test.txt';
          > open DATA, ">>$test_file" or die "can't open $test_file $!";

          1. Use three-args open.

          2. Use lexical file handles

          See:

          http://perl-begin.org/tutorials/bad-elements/#open-function-style

          3. The *DATA file handle has a special meaning in Perl, and should not
          be over-rided.

          > print DATA $a.'<br>';
          > print DATA $c.'<br>';
          > close (DATA);
          >
          > open DATA, "<$test_file" or die "can't open $test_file $!";
          > @new_data = <DATA>;
          > close (DATA);
          >
          > #output page
          > print <<EOF ;

          Always use <<'EOF' or <<"EOF" for avoiding ambiguity:

          http://perl-begin.org/tutorials/bad-elements/#string-notation

          > Content-type: text/html
          >
          > <html><head>
          > <META http-equiv="EXPIRES" CONTENT="0">
          > <meta http-equiv="pragma" content="no-cache">
          > </head><body>
          > <p>@new_data</p>
          > </body></html>
          > EOF
          >

          If you're going to interpolate @new_data into a string, you should slurp it
          as a string using File::Slurp or whatever.

          > $c = 'This line should never show up';
          > exit;

          Well, with package-scope variables, no wonder why FastCGI preserves them.

          Regards,

          Shlomi Fish

          --
          -----------------------------------------------------------------
          Shlomi Fish http://www.shlomifish.org/
          List of Portability Libraries - http://shlom.in/port-libs

          Knuth is not God! Typing “God” into Google and pressing “I’m Feeling Lucky”
          will not lead you to his homepage.

          Please reply to list if it's a mailing list post - http://shlom.in/reply .
        Your message has been successfully submitted and would be delivered to recipients shortly.