Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Already on GitHub? Sign in to your account

startup.pl instructions for mod_perl #357

Merged
merged 1 commit into from Jan 31, 2013

Conversation

Projects
None yet
4 participants
Contributor

rkitover commented Dec 12, 2012

The instructions for preloading using the directive does not work
correctly, add instructions for using a startup.pl file.

Contributor

avar commented Dec 15, 2012

This is going in a really nice direction. But you've missed a critical
caveat in Apache 2: That mod perl will load your startup file in
multiple startup phases.

The correct way to pre-load is to stick this in the Apache config:

PerlPostConfigRequire /path/to/preload.pm

Which contains something like this, slightly edited from what I'm
actually using in production:

use strict;
use warnings;
use Apache2::ServerUtil ();

BEGIN {
    unless (Apache2::ServerUtil::restart_count() > 1) {
        print STDERR "Running in Apache/Plack initialization phase #1\n";
        return;
    }

    require lib;
    lib->import('/path/to/your/lib');

    require Plack::Handler::Apache2;
    Plack::Handler::Apache2->preload(
        do {
            # Trick Catalyst, CGI.pm, CGI::Cookie and others that check
            # for $ENV{MOD_PERL}.
            #
            # Note that we delete it instead of just localizing
            # $ENV{MOD_PERL} because some users may check if the key
            # exists, and we do it this way because "delete local" is new
            # in 5.12:
            # http://perldoc.perl.org/5.12.0/perldelta.html#delete-local
            #
            # This got submitted upstream in Plack pull request #333
            # (https://github.com/plack/Plack/pull/333) and was merged
            # in
            # https://github.com/plack/Plack/compare/aae534acf0f1...b8eadec8546f
            # but we need to do it for our app because it won't be compiled
            # at this point.
            local $ENV{MOD_PERL};
            delete $ENV{MOD_PERL};

            require My::App::Config;
            "/path/to/my/app/plack/" . My::App::Config::PLACK_INIT_FILE() . ".psgi"
        }
    );
}

1;

Basically Apache will load the file you give it via
PerlPostConfigRequire twice during startup (see
http://perl.apache.org/docs/2.0/api/Apache2/ServerUtil.html#C_restart_count_),
but only the latter load will actually be the load that constructs the
app you pre-fork, the former is just to do various things in earlier
startup phases (like modifying the internal Apache config or
whatever).

If you don't do this correctly you'll load the app twice, which aside
from doubling your restart time could cause all sorts of really odd
issues.

I think the bit about certain env variables not being available is due
to not correctly checking the restart_count(). PerlSetEnv et al should
be available in the restart_count() phase that matters if it's placed
before the PerlPostConfigRequire in the apache config.

@avar avar commented on an outdated diff Dec 19, 2012

lib/Plack/Handler/Apache2.pm
<Location />
SetHandler perl-script
PerlResponseHandler Plack::Handler::Apache2
PerlSetVar psgi_app /path/to/app.psgi
</Location>
- # Optional, preload the application in the parent like startup.pl
- <Perl>
- use Plack::Handler::Apache2;
- Plack::Handler::Apache2->preload("/path/to/app.psgi");
- </Perl>
+ PerlPostConfigRequire /etc/httpd/startup.pl
+
+ # In your startup.pl
+ use Apache2::ServerUtil ();
+ use Try::Tiny;
@avar

avar Dec 19, 2012

Contributor

I'd add this to the BEGIN block as:

require Try::Tiny;
Try::Tiny->import;

Since otherwise it'll load Try::Tiny in the initial init phase, where it isn't needed.

@avar avar commented on an outdated diff Dec 19, 2012

lib/Plack/Handler/Apache2.pm
+ use Apache2::ServerUtil ();
+ use Try::Tiny;
+
+ BEGIN {
+ return unless Apache2::ServerUtil::restart_count() > 1;
+
+ require Plack::Handler::Apache2;
+
+ my @psgis = ('/path/to/app1.psgi', '/path/to/app2.psgi');
+
+ foreach my $psgi (@psgis) {
+ try {
+ Plack::Handler::Apache2->preload($psgi);
+ }
+ catch {
+ print STDERR "Error loading psgi '$psgi': $_\n";
@avar

avar Dec 19, 2012

Contributor

I'd actually just skip the try/catch here. Better to make the server die than print a bunch of stuff on STDERR but start up when it can't compile anything. I think for an example that's probably the more common case.

@avar avar commented on an outdated diff Dec 19, 2012

lib/Plack/Handler/Apache2.pm
+ PerlSetEnv PERL5LIB /some/path
+
+or
+
+ PerlSwitches -I/some/path
+
+in your C<httpd.conf>, which will also work.
+
+=item * loading errors
+
+Any exceptions thrown in your C<startup.pl> will stop Apache from starting at
+all.
+
+You probably don't want a stray syntax error to bring your whole server down
+(especially in a development environment) so it's a good idea to wrap the
+L</preload> call in an eval or L<Try::Tiny/try>. See example below.
@avar

avar Dec 19, 2012

Contributor

I don't get why not. If you have a syntax error your app won't actually work. So it's a matter of whether you'd like to start some zombie apache anyway.

document how to preload apps in startup.pl
Add instructions for using a startup.pl file, checking the restart_count
to make sure the app doesn't load twice (thanks to avar for that info.)

Also document how to catch errors from app loading so that the server
still starts, how to detect dynamically loaded modules so that they
can be preloaded and add paths to @INC.
Contributor

rkitover commented Jan 9, 2013

@plack/core

Hi guys, please take a look at my doc patch for mod_perl (on how to preload apps correctly) and give me a +1 if you like it!

I worked on this with avar.

Owner

miyagawa commented Jan 31, 2013

I guess pinging might not work if you're not member of the team :/ Sorry about it!

So there you go @plack/core do you have any opinion on this patch?

Owner

doy commented Jan 31, 2013

I have effectively no experience with mod_perl, so I don't have any particular opinion.

@miyagawa miyagawa commented on the diff Jan 31, 2013

lib/Plack/Handler/Apache2.pm
<Location />
SetHandler perl-script
PerlResponseHandler Plack::Handler::Apache2
PerlSetVar psgi_app /path/to/app.psgi
</Location>
- # Optional, preload the application in the parent like startup.pl
- <Perl>
- use Plack::Handler::Apache2;
- Plack::Handler::Apache2->preload("/path/to/app.psgi");
- </Perl>
+ PerlPostConfigRequire /etc/httpd/startup.pl
@miyagawa

miyagawa Jan 31, 2013

Owner

I need some clarification - is these startup.pl handling still optional? If so, keep the comment here to make it less confusing.

I don't want people reading the SYNOPSIS scared nor cargo cult code like this. It might be a better idea to just have a comment like "# Optionally preload apps with startup.pl. See below for STARTUP FILES" or something?

Owner

miyagawa commented Jan 31, 2013

It looks ok, aside from some questions. I will merge it down and work on some cleanups.

miyagawa added a commit that referenced this pull request Jan 31, 2013

Merge pull request #357 from rkitover/master
startup.pl instructions for mod_perl

@miyagawa miyagawa merged commit 4284cce into plack:master Jan 31, 2013

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment