How do I make a PSGI program do costly initialisation only once per process, not per thread?
Asked Answered
M

2

18

cross-post: http://perlmonks.org/?node_id=1191821

Consider app.psgi:

#!perl
use 5.024;
use strictures;
use Time::HiRes qw(sleep);

sub mock_connect {
    my $how_long_it_takes = 3 + rand;
    sleep $how_long_it_takes;
    return $how_long_it_takes;
}
sub main {
    state $db_handle = mock_connect($dsn);
    return sub { [200, [], ["connect took $db_handle seconds\n"]] };
}
my $dsn = 'dbi:blahblah'; # from config file
my $app = main($dsn);

Measuring plackup (HTTP::Server::PSGI: Accepting connections at http://0:5000/):

› perl -MBenchmark=timeit,timestr,:hireswallclock -E"say timestr timeit 10, sub { system q(curl http://localhost:5000) }"
connect took 3.0299610154043 seconds
connect took 3.0299610154043 seconds
connect took 3.0299610154043 seconds
connect took 3.0299610154043 seconds
connect took 3.0299610154043 seconds
connect took 3.0299610154043 seconds
connect took 3.0299610154043 seconds
connect took 3.0299610154043 seconds
connect took 3.0299610154043 seconds
connect took 3.0299610154043 seconds
2.93921 wallclock secs ( 0.03 usr +  0.06 sys =  0.09 CPU) @ 107.53/s (n=10)

Measuring thrall (Starting Thrall/0.0305 (MSWin32) http server listening at port 5000):

› perl -MBenchmark=timeit,timestr,:hireswallclock -E"say timestr timeit 10, sub { system q(curl http://localhost:5000) }"
connect took 3.77111188120125 seconds
connect took 3.15455510265111 seconds
connect took 3.77111188120125 seconds
connect took 3.15455510265111 seconds
connect took 3.77111188120125 seconds
connect took 3.64333342488772 seconds
connect took 3.15455510265111 seconds
connect took 3.77111188120125 seconds
connect took 3.85268922343767 seconds
connect took 3.64333342488772 seconds
17.4764 wallclock secs ( 0.02 usr +  0.09 sys =  0.11 CPU) @ 90.91/s (n=10)

This performance is not acceptable because the initialisation happens several times, despite the state variable. How do you make it so it happens only once?

Murr answered 30/5, 2017 at 8:44 Comment(5)
Do you just call them with thrall app.psgi and plackup app.psgi and default settings?Crippling
Yes, I'm executing just plackup and thrall without any arguments.Murr
My guess is something with your environment. I tried your code (perl 5.22.1) and it works perfectly. I have the 3 seconds wait when launching the app, then identical values for $dsn, if I use thrall or not.Watercool
Sorry, but why do you use use 5.024;? state was only available since 5.10.Diffluent
LSerni, you're confusing decimal and dotted decimal version types. perldoc version ; perldoc -f useMurr
L
1

For whatever reason, the program thrall hard-coded a "loader" parameter in its configuration section:

my $runner = Plack::Runner->new(
    server     => 'Thrall',
    env        => 'deployment',
    loader     => 'Delayed',
    version_cb => \&version,
);

$runner->parse_options(@ARGV);

That string "Delayed" refers to the module Plack::Loader::Delayed, which delays the loading of .psgi files until first request comes. That would match your benchmarking result. (If you re-run the benchmark again without killing thrall, you'll see identical output).

You may try running thrall -L +Plack::Loader app.psgi, which reverts the "loader" parameter to the default value hard-coded in Plack::Runner.

Livia answered 19/8, 2019 at 14:41 Comment(0)
A
-1

Isn't this what the --preload-app option to Starman does?

Avirulent answered 22/7, 2017 at 6:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.