Koha/Koha/Database.pm
Julian Maurice 4b65d099d7 Bug 28276: Do not fetch config ($KOHA_CONF) from memcached
memcached address and namespace are in $KOHA_CONF, so it is required to
read it before being able to access the cache. And after that,
configuration is kept in memory forever. Storing this in memcached is
useless and even counter-productive, since Koha reads both the file and
the cache

This patch addresses this issue by removing the cache-related code from
C4::Context->new.
It means that C4::Context->new will always read the configuration file,
so this patch also replaces inappropriate calls to
C4::Context->new->config by appropriate calls to C4::Context->config

It also fixes a bug where C4::Context->new would ignore the filepath
given in parameters if there was something in cache.

It also removes a problematic call to Koha::Caches->get_instance.
Because this call was outside of any subroutine, it would have happened
before the initialization of $C4::Context::context (which happen in
C4::Context::import)

Test plan:
1. Do not apply the patch yet
2. Add the following line at the beginning of Koha::Config::read_from_file
    warn "read_from_file($file)";
   This will allow you to check how many times the file is read.
3. Flush memcached and restart starman
4. Check the logs, you should see "read_from_file" a bunch of times
5. Apply the patch
6. Re-add the line from step 2
7. Flush memcached and restart starman
8. Check the logs, you should see "read_from_file" only once
9. Make sure the memcached config from $KOHA_CONF (memcached_servers,
   memcached_namespace) is taken into account by checking the About page

Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com>

Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
2021-05-10 10:28:04 +02:00

259 lines
6.6 KiB
Perl

package Koha::Database;
# Copyright 2013 Catalyst IT
# chrisc@catalyst.net.nz
#
# This file is part of Koha.
#
# Koha is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Koha is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Koha; if not, see <http://www.gnu.org/licenses>.
=head1 NAME
Koha::Database
=head1 SYNOPSIS
use Koha::Database;
my $database = Koha::Database->new();
my $schema = $database->schema();
=head1 FUNCTIONS
=cut
use Modern::Perl;
use Carp;
use C4::Context;
use base qw(Class::Accessor);
use vars qw($database);
__PACKAGE__->mk_accessors(qw( ));
# _new_schema
# Internal helper function (not a method!). This creates a new
# database connection from the data given in the current context, and
# returns it.
sub _new_schema {
require Koha::Schema;
my $db_driver = C4::Context::db_scheme2dbi(C4::Context->config('db_scheme'));;
my $db_name = C4::Context->config("database");
my $db_host = C4::Context->config("hostname");
my $db_port = C4::Context->config("port") || '';
my $db_user = C4::Context->config("user");
my $db_passwd = C4::Context->config("pass");
my $tls = C4::Context->config("tls");
my $tls_options;
if( $tls && $tls eq 'yes' ) {
my $ca = C4::Context->config('ca');
my $cert = C4::Context->config('cert');
my $key = C4::Context->config('key');
$tls_options = ";mysql_ssl=1;mysql_ssl_client_key=".$key.";mysql_ssl_client_cert=".$cert.";mysql_ssl_ca_file=".$ca;
}
my ( %encoding_attr, $encoding_query, $tz_query, $sql_mode_query );
my $tz = C4::Context->timezone;
$tz = q{} if ( $tz eq 'local' );
if ( $db_driver eq 'mysql' ) {
%encoding_attr = ( mysql_enable_utf8 => 1 );
$encoding_query = "set NAMES 'utf8mb4'";
$tz_query = qq(SET time_zone = "$tz") if $tz;
if ( C4::Context->config('strict_sql_modes')
|| ( exists $ENV{_} && $ENV{_} =~ m|prove| )
|| $ENV{KOHA_TESTING}
) {
$sql_mode_query = q{SET sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'};
} else {
$sql_mode_query = q{SET sql_mode = 'IGNORE_SPACE,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'};
}
}
elsif ( $db_driver eq 'Pg' ) {
$encoding_query = "set client_encoding = 'UTF8';";
$tz_query = qq(SET TIME ZONE = "$tz") if $tz;
}
my $schema = Koha::Schema->connect(
{
dsn => "dbi:$db_driver:database=$db_name;host=$db_host;port=$db_port".($tls_options? $tls_options : ""),
user => $db_user,
password => $db_passwd,
%encoding_attr,
RaiseError => 1,
PrintError => 1,
quote_names => 1,
auto_savepoint => 1,
on_connect_do => [
$encoding_query || (),
$tz_query || (),
$sql_mode_query || (),
]
}
);
my $dbh = $schema->storage->dbh;
eval {
my $HandleError = $dbh->{HandleError};
if ( $ENV{KOHA_DB_DO_NOT_RAISE_OR_PRINT_ERROR} ) {
$dbh->{HandleError} = sub { return 1 };
}
$dbh->do(q|
SELECT * FROM systempreferences WHERE 1 = 0 |
);
$dbh->{HandleError} = $HandleError;
};
if ( $@ ) {
$dbh->{HandleError} = sub { warn $_[0]; return 1 };
}
return $schema;
}
=head2 schema
$schema = $database->schema;
Returns a database handle connected to the Koha database for the
current context. If no connection has yet been made, this method
creates one, and connects to the database.
This database handle is cached for future use: if you call
C<$database-E<gt>schema> twice, you will get the same handle both
times. If you need a second database handle, use C<&new_schema> and
possibly C<&set_schema>.
=cut
sub schema {
my $self = shift;
my $params = shift;
unless ( $params->{new} ) {
return $database->{schema} if defined $database->{schema};
}
$database->{schema} = &_new_schema();
return $database->{schema};
}
=head2 new_schema
$schema = $database->new_schema;
Creates a new connection to the Koha database for the current context,
and returns the database handle (a C<DBI::db> object).
The handle is not saved anywhere: this method is strictly a
convenience function; the point is that it knows which database to
connect to so that the caller doesn't have to know.
=cut
#'
sub new_schema {
my $self = shift;
return &_new_schema();
}
=head2 set_schema
$my_schema = $database->new_schema;
$database->set_schema($my_schema);
...
$database->restore_schema;
C<&set_schema> and C<&restore_schema> work in a manner analogous to
C<&set_context> and C<&restore_context>.
C<&set_schema> saves the current database handle on a stack, then sets
the current database handle to C<$my_schema>.
C<$my_schema> is assumed to be a good database handle.
=cut
sub set_schema {
my $self = shift;
my $new_schema = shift;
# Save the current database handle on the handle stack.
# We assume that $new_schema is all good: if the caller wants to
# screw himself by passing an invalid handle, that's fine by
# us.
push @{ $database->{schema_stack} }, $database->{schema};
$database->{schema} = $new_schema;
}
=head2 restore_schema
$database->restore_schema;
Restores the database handle saved by an earlier call to
C<$database-E<gt>set_schema>.
=cut
sub restore_schema {
my $self = shift;
if ( $#{ $database->{schema_stack} } < 0 ) {
# Stack underflow
die "SCHEMA stack underflow";
}
# Pop the old database handle and set it.
$database->{schema} = pop @{ $database->{schema_stack} };
# FIXME - If it is determined that restore_context should
# return something, then this function should, too.
}
=head2 get_schema_cached
=cut
sub get_schema_cached {
return $database->{schema};
}
=head2 flush_schema_cache
=cut
sub flush_schema_cache {
delete $database->{schema};
return 1;
}
=head2 EXPORT
None by default.
=head1 AUTHOR
Chris Cormack, E<lt>chrisc@catalyst.net.nzE<gt>
=cut
1;
__END__