DRVTiny

Net::LDAP::WithRetries.pm

Jun 15th, 2020
253
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Perl 3.26 KB | None | 0 0
  1. package Net::LDAP::WithRetries;
  2. use 5.20.1;
  3. use warnings;
  4. use constant {
  5.     MAX_RETRIES_COUNT   => 3,
  6.     MAX_RECON_TRIES     => 10,
  7.     RECON_INTERVAL      => 0.01,
  8. };
  9. use IO::Socket;
  10. use Errno;
  11. use Net::LDAP;
  12. use Scalar::Util qw(blessed refaddr);
  13. use Time::HiRes qw(sleep);
  14. use Data::Dumper;
  15.  
  16. sub new {
  17.     my $class = shift;
  18.     # save to avoid any further changes in the parent constructor
  19.     my @args = @_;
  20.     my $ldap_con = __connect(@_);
  21.    
  22.     # $self->SUPER is very slow, so we have to save parent's $ldapc inside the child instance    
  23.     bless {args => \@args, ldapc => $ldap_con}, ref($class) || $class
  24. }
  25.  
  26. our $AUTOLOAD;
  27. sub AUTOLOAD {
  28.     my $self = $_[0];
  29.     my ($method) = $AUTOLOAD =~ /::([^:]+)$/;
  30.     $self->{'bind_args'} = [@_[1..$#_]] if $method eq 'bind';
  31.    
  32.     no strict 'refs';
  33.    
  34.     *{$AUTOLOAD} =
  35.     sub {
  36.         my $self = shift;
  37.  
  38.         my $ldap_res; my $op_count = 0;
  39.         while ($op_count++ < MAX_RETRIES_COUNT) {
  40.             $ldap_res = $self->{'ldapc'}->$method(@_);
  41.             ( blessed($ldap_res) and $ldap_res->isa('Net::LDAP::Message') )
  42.                 or do {
  43.                     printf STDERR "Strange thing: method %s returns this instead of Net::LDAP::Message instance: %s\n", $method, Dumper([$ldap_res]);
  44.                     last
  45.                 };
  46.             if ( $ldap_res->code ) {
  47.                 unless ($ldap_res->error =~ /(?:Broken pipe|Connection)/i ) {
  48.                     printf STDERR qq{Got LDAP error <<[%d] %s>>, but this is not "Broken pipe" or "Connection ...", so we are happy to do NOTHING :)\n}, $ldap_res->code, $ldap_res
  49.                     last
  50.                 }
  51.                 say STDERR 'Ooops. Broken pipe!';
  52.             } else { # all OK: this is very rare/unobvious case
  53.                 last
  54.             }
  55.             printf STDERR
  56.                 qq<LDAP connection is lost, will try to reconnect maximum %d times, with %s s. interval btw retries\n>,
  57.                                                                 MAX_RECON_TRIES,       RECON_INTERVAL;
  58.             $self->{'ldapc'} = __connect( @{$self->{'args'}} );
  59.             if( $self->{'bind_args'} and $method ne 'bind' ) {
  60.                 my $bind_res = $self->{'ldapc'}->bind( @{$self->{'bind_args'}} );
  61.                 $bind_res->code
  62.                     and die sprintf "failed to rebind: [%d] %s\n", $bind_res->code, $bind_res->error;
  63.             }
  64.         }
  65.         return $ldap_res
  66.     };
  67.     goto &{$AUTOLOAD};
  68. }
  69.  
  70. sub __connect {
  71.     my $ldapc;
  72.     my $recon_count = 0;
  73.     do {
  74.         sleep RECON_INTERVAL if $recon_count and ! $!{'ETIMEDOUT'};
  75.         $ldapc = Net::LDAP->new(@_);
  76.         $recon_count and
  77.             printf STDERR "LDAP reconnection try #%d: %s\n",
  78.                                                 $recon_count,
  79.                                                     $ldapc
  80.                                                     ?           'success'
  81.                                                     : sprintf   'failed by the reason: <<%s>>', $@;
  82.     } until $ldapc or $recon_count++ >= MAX_RECON_TRIES;
  83.  
  84.     $ldapc or die sprintf 'LDAP reconnection failed after %d retries', $recon_count;
  85.     $ldapc->socket->sockopt(SO_KEEPALIVE, 1);
  86.     $ldapc
  87. }
  88.  
  89. 1;
Add Comment
Please, Sign In to add comment