artg at cs
Mar 16, 2010, 12:01 PM
Post #5 of 22
You're welcome. You are correct about the limitations of
Apache2::SizeLimit. Processes cannot be 'scrubbed'; rather they should
be killed and restarted.
Rapid memory growth should be prevented by prohibiting processes from
ever growing large than a preset limit. On Unix systems, the system
call setrlimit sets process resource limits. These limits are
inherited by children of the process. These limits can view and set
with the bash command rlimit. Many resources can be limited, but I'm
focusing on process size, which is controlled by resource RLIMIT_AS,
the maximum size of a process's virtual memory (address space) in
bytes. (Some operating systems control RLIMIT_DATA, The maximum size
of the process's data segment, but Linux doesn't.)
When a process tries to exceeds a resource limit, the system call that
requested the resource fails and returns an error. The type of error
depends on which resource's limit is violated (see man page for
setrlimit). In the case of virtual memory, the RLIMIT_AS can be
exceeded by any call that asks for additional virtual memory, such as
brk(2), which sets the end of the data segment. Perl manages memory
via either the system's malloc or its own malloc. If asking for
virtual memory fails, then malloc will fail, which will typically
cause the Perl process to write "Out of Memory!" to STDERR and die.
RLIMIT_AS can be set in many ways. One direct way an Apache/mod_perl
process can set it is via Apache2::Resource. For example, these
commands can be added to httpd.conf:
# set child memory limit to 100 megabytes
# RLIMIT_AS (address space) will work to limit the size of a process
PerlSetEnv PERL_RLIMIT_AS 100
The PerlSetEnv line sets the Perl environment variable PERL_RLIMIT_AS.
The PerlChildInitHandler line directs Apache to load Apache2::Resource
each time it creates an httpd process. Apache2::Resource then reads
PERL_RLIMIT_AS and sets the RLIMIT_AS limit to 100 (megabytes). Any
httpd that tries to grow bigger than 100 MB will fail. (Also,
PERL_RLIMIT_AS can be set to soft_limit:hard_limit, where soft_limit
is the limit at which the resource request will fail. At any time the
soft_limit can be adjusted up to the hard_limit.)
I recommend against setting this limit for a threaded process, because
if one Request handler gets the process killed then all threads
handling requests will fail.
When the process has failed it is difficult to output an error message
to the web user, because Perl calls die and the process exits.
As I wrote yesterday, failure of a mod_perl process with "Out of
Memory!", as occurs when the softlimit of RLIMIT_AS is exceeded, does
not trigger an Apache ErrorDocument 500. A mod_perl process that exits
(actually CORE::exit() must be called) doesn't trigger an
ErrorDocument 500 either.
Second, if Apache detects a server error it can redirect to a script
as discussed in Custom Error Response. It can access the REDIRECT
environment variables but doesn't know anything else about the HTTP
At this point I think that the best thing to do is use
MaxRequestsPerChild and Apache2::SizeLimit to handle most memory
problems, and simply let processes that blow up die without feedback
to users. Not ideal, but they should be extremely rare events.
On Mar 16, 2010, at 2:31 PM, Pavel Georgiev wrote:
> Thank you both for the quick replies!
> Apache2::SizeLimit is no solution for my problem as I'm looking for
> a way to limit the size each requests take, the fact that I can
> scrub the process after the request is done (or drop the requests if
> the process reaches some limit, although my understanding is that
> Apache2::SizeLimit does its job after the requests is done) does not
> help me.
> Let me make I'm understanding this right - I'm not using any buffers
> myself, all I do is sysread() from a unix socked and print(), its
> just that I need to print a large amount of data for each request.
> Are you saying that there is no way to free the memory after I've
> done print() and rflush()?
> BTW thanks for the other suggestions, switching to cgi seems like
> the only reasonable thing for me, I just want to make sure that this
> is how mod_perl operates and it is not me who is doing something
> On Mar 16, 2010, at 11:18 AM, ARTHUR GOLDBERG wrote:
>> You could use Apache2::SizeLimit ("because size does matter") which
>> evaluates the size of Apache httpd processes when they complete
>> HTTP Requests, and kills those that grow too large. (Note that
>> Apache2::SizeLimit can only be used for non-threaded MPMs, such as
>> prefork.) Since it operates at the end of a Request, SizeLimit has
>> the advantage that it doesn't interrupt Request processing and the
>> disadvantage that it won't prevent a process from becoming
>> oversized while processing a Request. To reduce the regular load of
>> Apache2::SizeLimit it can be configured to check the size
>> intermittently by setting the parameter CHECK_EVERY_N_REQUESTS.
>> These parameters can be configured in a <Perl> section in
>> httpd.conf, or a Perl start-up file.
>> That way, if your script allocates too much memory the process will
>> be killed when it finishes handling the request. The MPM will
>> eventually start another process if necessary.
>> On Mar 16, 2010, at 9:30 AM, William T wrote:
>>> On Mon, Mar 15, 2010 at 11:26 PM, Pavel Georgiev <pavel [at] 3tera>
>>>> I have a perl script running in mod_perl that needs to write a
>>>> large amount of data to the client, possibly over a long period.
>>>> The behavior that I observe is that once I print and flush
>>>> something, the buffer memory is not reclaimed even though I
>>>> rflush (I know this cant be reclaimed back by the OS).
>>>> Is that how mod_perl operates and is there a way that I can force
>>>> it to periodically free the buffer memory, so that I can use that
>>>> for new buffers instead of taking more from the OS?
>>> That is how Perl operates. Mod_Perl is just Perl embedded in the
>>> Apache Process.
>>> You have a few options:
>>> * Buy more memory. :)
>>> * Delegate resource intensive work to a different process (I would
>>> NOT suggest a forking a child in Apache).
>>> * Tie the buffer to a file on disk, or db object, that can be
>>> explicitly reclaimed
>>> * Create a buffer object of a fixed size and loop.
>>> * Use compression on the data stream that you read into a buffer.
>>> You could also architect your system to mitigate resource usage if
>>> large data serve is not a common operation:
>>> * Proxy those requests to a different server which is optimized to
>>> handle large data serves.
>>> * Execute the large data serves with CGI rather than Mod_Perl.
>>> I'm sure there are probably other options as well.
>> Arthur P. Goldberg, PhD
>> Research Scientist in Bioinformatics
>> Plant Systems Biology Laboratory
>> Visiting Academic
>> Computer Science Department
>> Courant Institute of Mathematical Sciences
>> artg [at] cs
>> New York University
>> 212 995-4918
>> Coruzzi Lab
>> 8th Floor Silver Building
>> 1009 Silver Center
>> 100 Washington Sq East
>> New York NY 10003-6688