Skip to content

Commit f7a528f

Browse files
committed
Merge POD with PM
1 parent 258c645 commit f7a528f

22 files changed

Lines changed: 2929 additions & 2982 deletions

lib/cPanel/StateFile.pm

Lines changed: 354 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -460,8 +460,361 @@ sub import {
460460

461461
__END__
462462
463-
Copyright (c) 2010, cPanel, Inc. All rights reserved.
463+
464+
=head1 NAME
465+
466+
cPanel::StateFile - Standardize the handling of file-based state data.
467+
468+
=head1 SYNOPSIS
469+
470+
use cPanel::StateFile;
471+
472+
# create some cacheable object $obj.
473+
# ...
474+
475+
my $state = cPanel::StateFile->new(
476+
{ state_file => '/path/to/state/file', data_obj => $obj }
477+
);
478+
$state->synch(); # now memory and disk match.
479+
480+
# Prepare to make a change to object.
481+
482+
{
483+
my $guard = $state->synch();
484+
# Cache is now locked against changes
485+
# make changes to $obj
486+
$obj->modify();
487+
488+
# update state
489+
$guard->update_file();
490+
}
491+
# state matches memory and file is unlocked.
492+
493+
=head1 DESCRIPTION
494+
495+
We have a generic need to be able to safely state data in a disk file. Unfortunately,
496+
we've redeveloped this multiple times in slightly different ways. I needed yet another
497+
implementation, so I decided to do it more generically.
498+
499+
The safety is provided by a pair of cooperating classes: C<cPanel::StateFile> and
500+
C<cPanel::StateFile:Guard>. The C<Guard> object provides safe locking and makes
501+
certain that the lock is always released in the face of exceptions and such.
502+
503+
The C<Guard> object is returned by the C<cPanel::StateFile::synch()> method that
504+
reads the disk data into the memory object. If you don't store this object, the
505+
lock is immediately released. If you hold the C<Guard> object, you can write to
506+
the disk using the C<Guard::update_file()> method.
507+
508+
=head1 INTERFACE
509+
510+
The interface for this system is described in two parts: C<cPanel::StateFile> and
511+
C<cPanel::StateFile::Guard>.
512+
513+
=head2 cPanel::StateFile
514+
515+
The C<cPanel::StateFile> interface creates the state file and provides a way to lock
516+
and read the file. For methods to modify the state on disk, see the next section on
517+
L<cPanel::StateFile::Guard>.
518+
519+
=over 4
520+
521+
=item cPanel::StateFile->new( $hashref )
522+
523+
Create a new cPanel::StateFile object. The only parameter is a hashref that contains
524+
the following parameters:
525+
526+
=over 4
527+
528+
=item I<cache_file>
529+
530+
I<Deprecated> option that is now replaced by I<state_file>.
531+
532+
=item I<state_file>
533+
534+
This parameter is I<required>. The path to the state file that we use for this data.
535+
536+
=item I<data_obj>
537+
538+
This parameter is I<required>. The data object that will be cached in the file.
539+
This object must support two methods for dealing with the state file.
540+
541+
=item I<locker>
542+
543+
This optional parameter supplies a file locking object to replace the default
544+
declared for the StateFile class. See the section on L</"FILE LOCKER OBJECT">
545+
for the interface this class must provide.
546+
547+
If this object is not provided, it will default to the class-level object
548+
supplied on C<import> or an object of the default C<cPanel::StateFile::FileLocker>
549+
class. This class is only C<use>d if no object is provided.
550+
551+
=item I<logger>
552+
553+
This optional parameter supplies a logging object to replace the default
554+
declared for the StateFile class. See the section on L</"LOGGER OBJECT">
555+
for the interface this class must provide.
556+
557+
If this object is not provided, it will default to the class-level object
558+
supplied on C<import> or an object of the C<DefaultLogger> class.
559+
560+
=over 4
561+
562+
=item $obj->load_from_cache( $fh )
563+
564+
Will receive an opened file handle to read the data from.
565+
566+
This method is not responsible for opening or closing the filehandle. This
567+
method will only be called when the C<StateFile> object determines that the
568+
data needs to be re-read, so you do not need to try to figure that out yourself.
569+
570+
The method should throw an exception on failure.
571+
572+
=item $obj->save_to_cache( $fh )
573+
574+
Will receive an opened file handle pointing to the truncated file on which to
575+
write the data.
576+
577+
This method is not responsible for opening or closing the filehandle. This
578+
method will only be called when the C<StateFile> object determines that the
579+
data needs to be saved, so you do not need to figure that out in this method.
580+
581+
The method should throw an exception on failure.
582+
583+
=back
584+
585+
=item I<timeout>
586+
587+
This I<optional> parameter specifies the timeout in seconds for C<flock>ing the
588+
file on open. The default value is 60 seconds.
589+
590+
=back
591+
592+
=item $state->synch()
593+
594+
This method is called to make sure that the memory version of the data matches
595+
the disk version. If the state file doesn't exist, this method makes certain
596+
the C<Guard::update_file()> is called to create the initial version.
597+
598+
If the disk file is newer than the version in memory, the data object is given
599+
a chance to load itself. THe file is locked for the duration on the read and the
600+
lifetime of the returned C<Guard> object. If the C<Guard> object is not stored,
601+
the file is immediately closed and unlocked.
602+
603+
=item $cache->get_logger()
604+
605+
Return the logger object used by the C<StateFile> object. See the section on
606+
L</"LOGGER OBJECT"> for the interface this object provides.
607+
608+
=item $state->throw( $msg )
609+
610+
Log the supplied message and C<die>.
611+
612+
=item $state->warn( $msg )
613+
614+
Log the supplied message as a warning.
615+
616+
=item $state->info( $msg )
617+
618+
Log the supplied message as an informational message.
619+
620+
=back
621+
622+
=head2 cPanel::StateFile::Guard
623+
624+
The C<cPanel::StateFile::Guard> interface provides the writing methods and makes the locking
625+
safe. Once you have a guard, there is no reason to load data from the disk file, the
626+
in-memory copy matches the disk copy.
627+
628+
=over 4
629+
630+
=item $guard->update_file()
631+
632+
Overwrite the state file from the memory object. This destroys whatever was in the
633+
file on disk and replaces it with what is in memory.
634+
635+
This method performs its work using the C<save_to_file> method of the data object
636+
associated with the C<StateFile> object.
637+
638+
=item $guard->call_unlocked( $coderef )
639+
640+
Sometimes, it is necessary to call a long-running process while you have the queue
641+
locked. You don't want to leave the queue locked, so that other processes can access
642+
it. But several blocks of locking and unlocking code can quickly result in race
643+
conditions.
644+
645+
This method solves that problem. It is called with a code reference. The queue lock
646+
is temporarily released, we run the code, and then the queue lock is reacquired.
647+
This allows one lock to protect code that works on the queue, without blocking on
648+
other code forever.
649+
650+
=back
651+
652+
=head1 LOGGER OBJECT
653+
654+
By default, the C<StateFile> uses C<die> and C<warn> for all messages during
655+
runtime. However, it supports a mechanism that allows you to insert a
656+
logging/reporting system in place by providing an object to do the logging for
657+
us.
658+
659+
To provide a different method of logging/reporting, supply an object to do the
660+
logging as follows when C<use>ing the module.
661+
662+
use cPanel::StateFile ( '-logger' => $logger );
663+
664+
The supplied object should supply (at least) 4 methods: C<throw>, C<warn>,
665+
C<info>, and C<notify>. When needed, these methods will be called with the
666+
messages to be logged.
667+
668+
For example, an appropriate class for C<Log::Log4perl> and C<Email::Sender>
669+
might do something like the following:
670+
671+
package Policy::Log4perl;
672+
use strict;
673+
use warnings;
674+
use Log::Log4perl;
675+
use Email::Sender::Simple;
676+
use Email::Simple;
677+
use Email::Simple::Creator;
678+
679+
sub new {
680+
my ($class) = shift;
681+
my $self = {
682+
logger => Log::Log4perl->get_logger( @_ )
683+
};
684+
return bless, $class;
685+
}
686+
687+
sub throw {
688+
my $self = shift;
689+
$self->{logger}->error( @_ );
690+
die @_;
691+
}
692+
693+
sub warn {
694+
my $self = shift;
695+
$self->{logger}->warn( @_ );
696+
}
697+
698+
sub info {
699+
my $self = shift;
700+
$self->{logger}->info( @_ );
701+
}
702+
703+
sub notify {
704+
my $self = shift;
705+
my $subj = shift;
706+
my $email = Email::Simple->create(
707+
header => [
708+
From => 'taskqueue@example.com',
709+
To => 'sysadmin@example.com',
710+
Subject => $subj,
711+
],
712+
body => shift,
713+
);
714+
Email::Sender::Simple::sendmail( $email );
715+
}
716+
717+
This would call the C<Log4perl> code as errors or other messages result in
718+
messages.
719+
720+
This only works once for a given program, so you can't reset the policy in
721+
multiple modules and expect it to work.
722+
723+
In addition to setting a global logger, a new logger object can be supplied
724+
when creating a specific C<StateFile> object.
725+
726+
=head2 throw( $msg )
727+
728+
This method is expected to log or report the critical error condition supplied
729+
as an argument and then use C<die> to exit the method.
730+
731+
=head2 warn( $msg )
732+
733+
This method is expected to log or report a warning condition using the supplied
734+
message and then return.
735+
736+
=head2 info( $msg )
737+
738+
This method is expected to optionally report or log the supplied informational
739+
message and then return.
740+
741+
=head2 notify( $subj, $msg )
742+
743+
This method is expected to perform some form of critical notification of the
744+
triggering condition (such as an email to an appropriate administrator). The
745+
first argument is a summary or title and the second is the body of the supplied message.
746+
747+
The method should return on completion.
748+
749+
=head1 FILE LOCKER OBJECT
750+
751+
The default file locking policy is build around the C<flock> function. However,
752+
this method may not be appropriate in all circumstances. So C<StateFile> supports
753+
the ability to replace the locking policy by supplying a file locking object.
754+
755+
To provide a different method of file locking, use the following syntax when
756+
C<use>ing the module.
757+
758+
use cPanel::StateFile ( '-filelock' => $locker );
759+
760+
The supplied object should supply (at least) 2 methods: C<file_lock>, and
761+
C<file_unlock>.
762+
763+
=over 4
764+
765+
=item file_lock( $file )
766+
767+
To the class name, this method receives the name of the file to protect with
768+
the lock. This is B<not> the name of a lock file. The method should return a
769+
token that is passed to the C<file_unlock> method to tell which lock to unlock.
770+
771+
If the method coannot protect (or lock) the file, the method should throw an exception.
772+
773+
=item file_unlock( $lock )
774+
775+
To the class name, this method receives the return value from the C<file_lock>
776+
method to use when unprotecting the file.
777+
778+
=back
779+
780+
=head1 DEPENDENCIES
781+
782+
L<Fcntl> and L<File::Path>.
783+
784+
=head1 BUGS AND LIMITATIONS
785+
786+
At present, the C<synch> system detects changes in the file by means of the file's
787+
modification time and size. If a file is modified within the resolution of the mtime
788+
and it's size does not change, it may not be detected.
789+
790+
=head1 COPYRIGHT
791+
792+
Copyright (c) 2014, cPanel, Inc. All rights resrved.
464793
465794
This module is free software; you can redistribute it and/or
466795
modify it under the same terms as Perl itself. See L<perlartistic>.
467796
797+
798+
=head1 DISCLAIMER OF WARRANTY
799+
800+
BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
801+
FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
802+
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
803+
PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
804+
EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
805+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
806+
ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
807+
YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
808+
NECESSARY SERVICING, REPAIR, OR CORRECTION.
809+
810+
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
811+
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
812+
REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
813+
LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
814+
OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
815+
THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
816+
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
817+
FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
818+
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
819+
SUCH DAMAGES.
820+

0 commit comments

Comments
 (0)