Bug 42664: Reload SIP2 configuration in every worker process
SIP2 accounts/institutions are stored in the database and the SIP server is supposed to reload its configuration when they change. In practice, after adding or editing a SIP2 account, most SIP worker processes keep serving the old configuration (e.g. new accounts get "SIP access not authorized") until the SIP server is restarted ( I tested multiple times over 30 minutes ). There seem to be two issues: * L1 cache flushed too late. process_request() calls _config_up_to_date() before Koha::Caches->flush_L1_caches(). The SIP parent reads sip2_resource_last_modified into its own L1 cache at startup and never flushes it. Every worker inherits that stale L1, so a worker's first connection compares against the startup-era value instead of the current shared value. * sip2_config_read_timestamp is stored in the shared (memcached) cache, but the configuration it tracks is per-process. When one worker reloads the config from the database, it bumps the shared timestamp, so every other worker then evaluates as "up to date" and never reloads its own stale in-memory config. Test Plan: 1) Configure the SIP server with several worker processes (e.g. set min_servers and max_servers to 5) and confirm an existing SIP2 account works 2) Add a new SIP2 account in the staff interface 3) Without restarting the SIP server, test the new account with misc/sip_cli_emulator.pl 4) Note the login fails 5) Apply this patch 6) Restart all the things! 7) Add another new SIP2 account 8) Without restarting the SIP server test this new account with sip_cli_emulator.pl 9) Note that the new account works! 10) prove t/db_dependent/SIP/SIPServer.t Signed-off-by: Brendan Lawlor <blawlor@clamsnet.org> Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
This commit is contained in:
parent
fa92634932
commit
f899761666
|
|
@ -55,6 +55,10 @@ my %transports = (
|
|||
# Read configuration
|
||||
#
|
||||
my $config = C4::SIP::Sip::Configuration->new( $ARGV[0] );
|
||||
|
||||
# Per-process record of when this worker loaded $config. Not shared since $config is per-process.
|
||||
our $sip2_config_read_timestamp = Koha::Caches->get_instance->get_from_cache('sip2_resource_last_modified');
|
||||
|
||||
my @params;
|
||||
|
||||
#
|
||||
|
|
@ -234,13 +238,15 @@ sub _config_up_to_date {
|
|||
|
||||
my $cache = Koha::Caches->get_instance();
|
||||
my $sip2_resource_last_modified = $cache->get_from_cache("sip2_resource_last_modified");
|
||||
my $sip2_config_read_timestamp = $cache->get_from_cache("sip2_config_read_timestamp");
|
||||
|
||||
unless ($sip2_resource_last_modified) {
|
||||
siplog( "LOG_WARNING", "Couldn't find sip2_resource_last_modified, considering configuration not up to date" );
|
||||
siplog( "LOG_INFO", "Couldn't find sip2_resource_last_modified, considering configuration not up to date" );
|
||||
return 0;
|
||||
}
|
||||
return $sip2_config_read_timestamp >= $sip2_resource_last_modified;
|
||||
|
||||
return 0 unless defined $sip2_config_read_timestamp;
|
||||
|
||||
return $sip2_config_read_timestamp >= $sip2_resource_last_modified ? 1 : 0;
|
||||
}
|
||||
|
||||
#
|
||||
|
|
@ -260,14 +266,15 @@ sub process_request {
|
|||
my ( $sockaddr, $port, $proto );
|
||||
my $transport;
|
||||
|
||||
# Flush L1 before the freshness check so it sees the current shared value, not a stale one inherited at fork.
|
||||
Koha::Caches->flush_L1_caches();
|
||||
|
||||
$self->{config} = $config;
|
||||
unless ( $self->_config_up_to_date() ) {
|
||||
$self->{config} = C4::SIP::Sip::Configuration->get_configuration( undef, $self->{config} );
|
||||
$sip2_config_read_timestamp = Koha::Caches->get_instance->get_from_cache('sip2_resource_last_modified');
|
||||
}
|
||||
|
||||
# Flushing L1 to make sure the request will be processed using the correct data
|
||||
Koha::Caches->flush_L1_caches();
|
||||
|
||||
$self->{account} = undef; # Clear out the account from the last request, it may be different
|
||||
$self->{logger} = set_logger( Koha::Logger->get( { interface => 'sip' } ) );
|
||||
|
||||
|
|
|
|||
|
|
@ -41,17 +41,13 @@ my $parser = XML::Simple->new(
|
|||
sub get_configuration {
|
||||
my ( $class, $config_file, $current_config ) = @_;
|
||||
|
||||
my $cache = Koha::Caches->get_instance();
|
||||
my $sip2_resource_last_modified = $cache->get_from_cache("sip2_resource_last_modified");
|
||||
my $cache = Koha::Caches->get_instance();
|
||||
|
||||
if ($sip2_resource_last_modified) {
|
||||
$cache->set_in_cache( 'sip2_config_read_timestamp', $sip2_resource_last_modified );
|
||||
} else {
|
||||
unless ( $cache->get_from_cache("sip2_resource_last_modified") ) {
|
||||
|
||||
# Set sip2_resource_last_modified so config won't be read from db on each request
|
||||
my $now = dt_from_string()->epoch;
|
||||
$cache->set_in_cache( 'sip2_resource_last_modified', $now );
|
||||
$cache->set_in_cache( 'sip2_config_read_timestamp', $now );
|
||||
# Seed sip2_resource_last_modified so the configuration is not re-read
|
||||
# from the database on every request while the value is missing.
|
||||
$cache->set_in_cache( 'sip2_resource_last_modified', dt_from_string()->epoch );
|
||||
}
|
||||
|
||||
my $cfg;
|
||||
|
|
|
|||
|
|
@ -183,28 +183,19 @@ subtest 'config_timestamp is updated when database configuration changes' => sub
|
|||
|
||||
subtest 'get_configuration() reseeds sip2_resource_last_modified when missing from cache' => sub {
|
||||
|
||||
plan tests => 4;
|
||||
plan tests => 2;
|
||||
|
||||
my $schema = Koha::Database->new->schema;
|
||||
$schema->storage->txn_begin;
|
||||
|
||||
my $cache = Koha::Caches->get_instance();
|
||||
$cache->clear_from_cache('sip2_resource_last_modified');
|
||||
$cache->clear_from_cache('sip2_config_read_timestamp');
|
||||
|
||||
is( $cache->get_from_cache('sip2_resource_last_modified'), undef, 'sip2_resource_last_modified cleared' );
|
||||
|
||||
C4::SIP::Sip::Configuration->get_configuration( undef, { listeners => {} } );
|
||||
|
||||
my $resource_last_modified = $cache->get_from_cache('sip2_resource_last_modified');
|
||||
my $config_read_timestamp = $cache->get_from_cache('sip2_config_read_timestamp');
|
||||
|
||||
ok( $resource_last_modified, 'sip2_resource_last_modified is repopulated' );
|
||||
ok( $config_read_timestamp, 'sip2_config_read_timestamp is set' );
|
||||
is(
|
||||
$config_read_timestamp, $resource_last_modified,
|
||||
'sip2_config_read_timestamp matches sip2_resource_last_modified so subsequent requests skip a full reload'
|
||||
);
|
||||
ok( $cache->get_from_cache('sip2_resource_last_modified'), 'sip2_resource_last_modified is repopulated' );
|
||||
|
||||
$schema->storage->txn_rollback;
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue