3
0
mirror of https://github.com/pragma-/pbot.git synced 2024-11-22 20:09:43 +01:00
pbot/PBot/LagChecker.pm
2020-01-03 21:26:49 -08:00

159 lines
5.2 KiB
Perl

# File: LagChecker.pm
# Author: pragma_
#
# Purpose: sends PING command to IRC server and times duration for PONG reply in
# order to maintain lag history and average.
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
package PBot::LagChecker;
use warnings;
use strict;
use feature 'unicode_strings';
use feature 'switch';
use Time::HiRes qw(gettimeofday tv_interval);
use Time::Duration;
use Carp ();
sub new {
Carp::croak("Options to LagChecker should be key/value pairs, not hash reference") if ref($_[1]) eq 'HASH';
my ($class, %conf) = @_;
my $self = bless {}, $class;
$self->initialize(%conf);
return $self;
}
sub initialize {
my ($self, %conf) = @_;
$self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference to LagChecker");
$self->{lag_average} = undef; # average of entries in lag history, in seconds
$self->{lag_string} = undef; # string representation of lag history and lag average
$self->{lag_history} = []; # history of previous PING/PONG timings
$self->{pong_received} = undef; # tracks pong replies; undef if no ping sent; 0 if ping sent but no pong reply yet; 1 if ping/pong completed
$self->{ping_send_time} = undef; # when last ping was sent
# maximum number of lag history entries to retain
$self->{pbot}->{registry}->add_default('text', 'lagchecker', 'lag_history_max', $conf{lag_history_max} // 3);
# lagging is true if lag_average reaches or exceeds this threshold, in milliseconds
$self->{pbot}->{registry}->add_default('text', 'lagchecker', 'lag_threshold', $conf{lag_threshhold} // 2000);
# how often to send PING, in seconds
$self->{pbot}->{registry}->add_default('text', 'lagchecker', 'lag_history_interval', $conf{lag_history_interval} // 10);
$self->{pbot}->{registry}->add_trigger('lagchecker', 'lag_history_interval', sub { $self->lag_history_interval_trigger(@_) });
$self->{pbot}->{timer}->register(
sub { $self->send_ping },
$self->{pbot}->{registry}->get_value('lagchecker', 'lag_history_interval'),
'lag_history_interval'
);
$self->{pbot}->{commands}->register(sub { return $self->lagcheck(@_) }, "lagcheck", 0);
$self->{pbot}->{event_dispatcher}->register_handler('irc.pong', sub { $self->on_pong(@_) });
}
sub lag_history_interval_trigger {
my ($self, $section, $item, $newvalue) = @_;
$self->{pbot}->{timer}->update_interval('lag_history_interval', $newvalue);
}
sub send_ping {
my $self = shift;
return unless defined $self->{pbot}->{conn};
$self->{ping_send_time} = [gettimeofday];
$self->{pong_received} = 0;
$self->{pbot}->{conn}->sl("PING :lagcheck");
}
sub on_pong {
my $self = shift;
$self->{pong_received} = 1;
my $elapsed = tv_interval($self->{ping_send_time});
push @{$self->{lag_history}}, [ $self->{ping_send_time}[0], $elapsed * 1000];
my $len = @{$self->{lag_history}};
my $lag_history_max = $self->{pbot}->{registry}->get_value('lagchecker', 'lag_history_max');
while ($len > $lag_history_max) {
shift @{$self->{lag_history}};
$len--;
}
$self->{lag_string} = "";
my $comma = "";
my $lag_total = 0;
foreach my $entry (@{$self->{lag_history}}) {
my ($send_time, $lag_result) = @$entry;
$lag_total += $lag_result;
my $ago = concise ago(gettimeofday - $send_time);
$self->{lag_string} .= $comma . "[$ago] " . sprintf "%.1f ms", $lag_result;
$comma = "; ";
}
$self->{lag_average} = $lag_total / $len;
$self->{lag_string} .= "; average: " . sprintf "%.1f ms", $self->{lag_average};
return 0;
}
sub lagging {
my $self = shift;
if (defined $self->{pong_received} and $self->{pong_received} == 0) {
# a ping has been sent (pong_received is not undef) and no pong has been received yet
my $elapsed = tv_interval($self->{ping_send_time});
return $elapsed >= $self->{pbot}->{registry}->get_value('lagchecker', 'lag_threshold');
}
return 0 if not defined $self->{lag_average};
return $self->{lag_average} >= $self->{pbot}->{registry}->get_value('lagchecker', 'lag_threshold');
}
sub lagstring {
my $self = shift;
my $lag = $self->{lag_string} || "initializing";
return $lag;
}
sub lagcheck {
my ($self, $from, $nick, $user, $host, $arguments) = @_;
if (defined $self->{pong_received} and $self->{pong_received} == 0) {
# a ping has been sent (pong_received is not undef) and no pong has been received yet
my $elapsed = tv_interval($self->{ping_send_time});
my $lag_total = $elapsed;
my $len = @{$self->{lag_history}};
my $lagstring = "";
my $comma = "";
foreach my $entry (@{$self->{lag_history}}) {
my ($send_time, $lag_result) = @$entry;
$lag_total += $lag_result;
my $ago = concise ago(gettimeofday - $send_time);
$lagstring .= $comma . "[$ago] " . sprintf "%.1f ms", $lag_result;
$comma = "; ";
}
$lagstring .= $comma . "[waiting for pong] $elapsed";
my $average = $lag_total / ($len + 1);
$lagstring .= "; average: " . sprintf "%.1f ms", $average;
return $lagstring;
}
return "My lag: " . $self->lagstring;
}
1;