lihao has asked for the wisdom of the Perl Monks concerning the following question:

Dear Monks:

Under FastCGI processes, is there a good way to update a preloaded Perl Module just like Apache::Reload for mod_perl users. I am currently using kill -9 to restart the fcgi process, so each time I get some changes on my modules, I have to restart(kill) fcgi process to update these changes.. Is there any better way to do this?? I am using Apache2.0.59, mod_fastcgi/2.4.2, CGI::Fast with HTML::Mason.

A couple of days ago, I asked this question in FCGI's mailing group, some suggestions include:

  1. Add code that checks module timestamps when you get a call and does a do on the changed files before processing.
  2. Add a signal handler that processes the files when it is received

For suggestion-(1), I think I can do things like

if ( -M "/path/to/lib/MyModule.pm" < 0 ) { require MyModule; MyModule->import(qw[ :mytag ]); }

But my questions keep:

  1. the '-M' test looks expensive if I put it in the FCGI loop and do it on every request
  2. what are the differences between 'require' and the suggested "do" here?, given that I do need to import some functions from that modules
  3. What should be considered differently between an OO-modules and non-OO-modules in this context..

For the suggestion-(2), I am not sure which SIG should I trap to handle the file change

Thanks for your further advices..

Best regrads,

Lihao(XC)

Replies are listed 'Best First'.
Re: FastCGI and mod_perl's Apache::Reload equivalent???
by shmem (Chancellor) on Dec 13, 2007 at 22:06 UTC
    I am currently using kill -9 to restart the fcgi process

    Don't do that! Signal 9 is SIGKILL, and a process which receives this signal has no chance to

    • flush buffers
    • close filehandles properly
    • clean up resources
    • execute anything compiled into END blocks

    since it is not trappable, and AFAIK not propagated at all to the signalled process, but an instruction to the kernel to reap the process immediately (although the parent of the reaped process gets noified).

    Use signals 1, 2, 3 or 15, not 9. Signal 9 is last resort only. Try

    perl -le '$SIG{KILL} = sub {print "boom!"}; print $$; sleep;'

    and kill that process from another shell with SIGKILL: no output.

    But my questions keep:
    1. the '-M' test looks expensive if I put it in the FCGI loop and do it on every request
    2. what are the differences between 'require' and the suggested "do" here?, given that I do need to import some functions from that modules
    3. What should be considered differently between an OO-modules and non-OO-modules in this context..

    Instead of using the rather obscure -M file test (which calls stat under the hood), I'd go with (stat $module)[9] i.e. mtime. Set up a $hash {$modulepath} = $timestamp at script start, preferrably using the key in %INC for easy comparison. Expensive? That's relative to what else your fcgi application is doing.

    Also, you don't have to check the module files on every request. Set up a signal handler for e.g. SIGUSR1 (on the shell command line, kill -l will show you the available signals and their numbers) which sets a package global variable to some true value, and check that variable at every request. If it's true, reload the module (take care to undef $INC{'Some/Module.pm'} first) and clear the variable.

    Differences between require and do:

    • require propagates $@ from inside modules at compilation, whereas do does not.
    • do evals a file's content regardless of the contents of %INC, whereas require just returns 1 if the module's path (relative to any directory in @INC if found in one of those, or an absolute path if not) exists as key in %INC.
    • require dies on compilation errors whilst evaling the required file - or if the required file doesn't return a true value -, whereas do does not.

    See perlfaq8 for any bits that I missed here (or perldoc -q require).

    For the suggestion-(2), I am not sure which SIG should I trap to handle the file change

    Any but ALRM, CONT, ILL, ABORT, KILL, SEGV, ... I'd go with USR1 or USR2. But as stated above, I'd just set a variable in that signal handler, but not process the file immediately from somewhere inside your request processing where you could be using that module; instead delay that to the next request cycle.

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
Re: FastCGI and mod_perl's Apache::Reload equivalent???
by CountZero (Bishop) on Dec 13, 2007 at 21:13 UTC
    if ( -M "/path/to/lib/MyModule.pm" < 0 ) { require MyModule; MyModule->import(qw[ :mytag ]); }
    That will not work as require will refuse to reload your module if it is already loaded.

    You have to delete the corresponding entry in the %INC hash before you do a new require MyModule.

    But do have a look at the source code of Apache::Reload. Other than the way to bind it into the webserver's request cycle, I think you can almost fully re-use Apache::Reload's code.

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

Re: FastCGI and mod_perl's Apache::Reload equivalent???
by Errto (Vicar) on Dec 13, 2007 at 21:56 UTC
    the '-M' test looks expensive if I put it in the FCGI loop and do it on every request
    It is a little expensive, but hopefully not compared to the overall execution time of your script. And you should only use it on a development system anyway - for a production system you generally shouldn't hot-deploy individual modules.
Re: FastCGI and mod_perl's Apache::Reload equivalent???
by suaveant (Parson) on Dec 13, 2007 at 20:46 UTC
    You can put something into your code to do a 'do' on the file when you receive a signal or when the file updates... bout the only way I know, and you have to make sure all children do it :)

                    - Ant
                    - Some of my best work - (1 2 3)

Re: FastCGI and mod_perl's Apache::Reload equivalent???
by lihao (Monk) on Dec 14, 2007 at 05:31 UTC

    Hi, guys:

    really appreciate all your helpful suggestions and informative links. The following is what I'm gonna try in the next step, please correct me out if I got something wrong:
    1. I will write a separate HTML file which contains lines like
      { local $SIG{USR1} = \&myapp_reload_module($modulename); kill("USR1", -$$); }
      where $modulename comes from a select-options box in this HTML file(or from a form submission). myapp_reload_module is a customized module function which reloads a module by following the ways in Apache::Reload(I will read that source though).
    2. Each time I modified a module, I only need to open this webpage, select that module from a dropdown list(select-option box), and then submit the form, the above Perl code with kill(USR1,-$$) will trigger the reloading of the selected module, Right?

    I will not use kill -9 for sure, thanks shmem for this nice tip.

    Best regards,

    Lihao(XC)

    Updated:missed out using kill(USR1, -$$) to send the signal:)

      Just a follow-up, I followed the above idea and have made it work for my FCGI process, pretty happy.. thanks again. :- )