@@ -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
465794This module is free software; you can redistribute it and/or
466795modify 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