Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision e0ba33ff

Von Moritz Bunkus vor mehr als 13 Jahren hinzugefügt

  • ID e0ba33ff38a40207472185d639567966e136da6b
  • Vorgänger 874a3b3d
  • Nachfolger be3db7d1

Nicht immer alle Session-Werte automatisch laden und parsen

Durch das Speichern ganzer Forms in der Session wurde das Laden sehr
langsam, weil bei jedem Request alle Session-Werte geladen und mit
YAML geparst wurden. Rief man z.B. ein Erzeugnis mit 50 Einzelteilen
auf, so erhöhte sich dadurch die Requestzeit um eine Sekunde -- bis
die Session gelöscht wird (Logout, Timeout oder bei nicht-FastCGI).

Lösung ist, dass nur diejenigen Werte bei jedem Request automatisch
geladen werden, die entweder 'auto_restore' gesetzt haben, oder die
immer benötigt werden (Login, Passwort, Root-Passwort). Alle anderen
Werte werden nur auf Existenz geprüft und erst bei
$::auth->get_session_value() wirklich geladen und per YAML geparst.

Unterschiede anzeigen:

SL/Auth.pm
use List::MoreUtils qw(uniq);
use YAML;
use SL::Auth::ColumnInformation;
use SL::Auth::Constants qw(:all);
use SL::Auth::DB;
use SL::Auth::LDAP;
use SL::Auth::Password;
use SL::Auth::SessionValue;
use SL::SessionFile;
use SL::User;
......
bless $self, $type;
$self->{SESSION} = { };
$self->_read_auth_config();
$self->reset;
$main::lxdebug->leave_sub();
......
sub reset {
my ($self, %params) = @_;
$self->{SESSION} = { };
$self->{FULL_RIGHTS} = { };
$self->{RIGHTS} = { };
$self->{unique_counter} = 0;
$self->{SESSION} = { };
$self->{FULL_RIGHTS} = { };
$self->{RIGHTS} = { };
$self->{unique_counter} = 0;
$self->{column_information} = SL::Auth::ColumnInformation->new(auth => $self);
}
sub get_user_dbh {
......
return $cookie ? SESSION_EXPIRED : SESSION_NONE;
}
$query = qq|SELECT sess_key, sess_value FROM auth.session_content WHERE session_id = ?|;
$sth = prepare_execute_query($form, $dbh, $query, $session_id);
while (my $ref = $sth->fetchrow_hashref()) {
$self->{SESSION}->{$ref->{sess_key}} = $ref->{sess_value};
next if defined $form->{$ref->{sess_key}};
my $params = $self->_load_value($ref->{sess_value});
$form->{$ref->{sess_key}} = $params->{data} if $params->{auto_restore} || $params->{simple};
if ($self->{column_information}->has('auto_restore')) {
$self->_load_with_auto_restore_column($dbh, $session_id);
} else {
$self->_load_without_auto_restore_column($dbh, $session_id);
}
$sth->finish();
$main::lxdebug->leave_sub();
return SESSION_OK;
}
sub _load_value {
my ($self, $value) = @_;
sub _load_without_auto_restore_column {
my ($self, $dbh, $session_id) = @_;
return { simple => 1, data => $value } if $value !~ m/^---/;
my $query = <<SQL;
SELECT sess_key, sess_value
FROM auth.session_content
WHERE (session_id = ?)
SQL
my $sth = prepare_execute_query($::form, $dbh, $query, $session_id);
my %params = ( simple => 1 );
eval {
my $data = YAML::Load($value);
while (my $ref = $sth->fetchrow_hashref) {
my $value = SL::Auth::SessionValue->new(auth => $self,
key => $ref->{sess_key},
value => $ref->{sess_value},
raw => 1);
$self->{SESSION}->{ $ref->{sess_key} } = $value;
if (ref $data eq 'HASH') {
map { $params{$_} = $data->{$_} } keys %{ $data };
$params{simple} = 0;
next if defined $::form->{$ref->{sess_key}};
} else {
$params{data} = $data;
}
my $data = $value->get;
$::form->{$ref->{sess_key}} = $data if $value->{auto_restore} || !ref $data;
}
}
sub _load_with_auto_restore_column {
my ($self, $dbh, $session_id) = @_;
my $auto_restore_keys = join ', ', map { "'${_}'" } qw(login password rpw);
my $query = <<SQL;
SELECT sess_key, sess_value, auto_restore
FROM auth.session_content
WHERE (session_id = ?)
AND ( auto_restore
OR sess_key IN (${auto_restore_keys}))
SQL
my $sth = prepare_execute_query($::form, $dbh, $query, $session_id);
while (my $ref = $sth->fetchrow_hashref) {
my $value = SL::Auth::SessionValue->new(auth => $self,
key => $ref->{sess_key},
value => $ref->{sess_value},
auto_restore => $ref->{auto_restore},
raw => 1);
$self->{SESSION}->{ $ref->{sess_key} } = $value;
1;
} or $params{data} = $value;
next if defined $::form->{$ref->{sess_key}};
return \%params;
my $data = $value->get;
$::form->{$ref->{sess_key}} = $data if $value->{auto_restore} || !ref $data;
}
$sth->finish;
$query = <<SQL;
SELECT sess_key
FROM auth.session_content
WHERE (session_id = ?)
AND NOT COALESCE(auto_restore, FALSE)
AND (sess_key NOT IN (${auto_restore_keys}))
SQL
$sth = prepare_execute_query($::form, $dbh, $query, $session_id);
while (my $ref = $sth->fetchrow_hashref) {
my $value = SL::Auth::SessionValue->new(auth => $self,
key => $ref->{sess_key});
$self->{SESSION}->{ $ref->{sess_key} } = $value;
}
}
sub destroy_session {
......
$dbh->begin_work unless $provided_dbh;
do_query($::form, $dbh, qq|LOCK auth.session_content|);
do_query($::form, $dbh, qq|DELETE FROM auth.session_content WHERE session_id = ?|, $session_id);
my $query = qq|SELECT id FROM auth.session WHERE id = ?|;
my @unfetched_keys = map { $_->{key} }
grep { ! $_->{fetched} }
values %{ $self->{SESSION} };
# $::lxdebug->dump(0, "unfetched_keys", [ sort @unfetched_keys ]);
# $::lxdebug->dump(0, "all keys", [ sort map { $_->{key} } values %{ $self->{SESSION} } ]);
my $query = qq|DELETE FROM auth.session_content WHERE (session_id = ?)|;
$query .= qq| AND (sess_key NOT IN (| . join(', ', ('?') x scalar @unfetched_keys) . qq|))| if @unfetched_keys;
my ($id) = selectrow_query($::form, $dbh, $query, $session_id);
do_query($::form, $dbh, $query, $session_id, @unfetched_keys);
my ($id) = selectrow_query($::form, $dbh, qq|SELECT id FROM auth.session WHERE id = ?|, $session_id);
if ($id) {
do_query($::form, $dbh, qq|UPDATE auth.session SET mtime = now() WHERE id = ?|, $session_id);
......
do_query($::form, $dbh, qq|INSERT INTO auth.session (id, ip_address, mtime) VALUES (?, ?, now())|, $session_id, $ENV{REMOTE_ADDR});
}
if (%{ $self->{SESSION} }) {
my $query = qq|INSERT INTO auth.session_content (session_id, sess_key, sess_value) VALUES (?, ?, ?)|;
my $sth = prepare_query($::form, $dbh, $query);
my @values_to_save = grep { $_->{fetched} }
values %{ $self->{SESSION} };
if (@values_to_save) {
my ($columns, $placeholders) = ('', '');
my $auto_restore = $self->{column_information}->has('auto_restore');
foreach my $key (sort keys %{ $self->{SESSION} }) {
do_statement($::form, $sth, $query, $session_id, $key, $self->{SESSION}->{$key});
if ($auto_restore) {
$columns .= ', auto_restore';
$placeholders .= ', ?';
}
$query = qq|INSERT INTO auth.session_content (session_id, sess_key, sess_value ${columns}) VALUES (?, ?, ? ${placeholders})|;
my $sth = prepare_query($::form, $dbh, $query);
foreach my $value (@values_to_save) {
my @values = ($value->{key}, $value->get_dumped);
push @values, $value->{auto_restore} if $auto_restore;
do_statement($::form, $sth, $query, $session_id, @values);
}
$sth->finish();
......
my $key = shift @params;
if (ref $key eq 'HASH') {
my $value = { data => $key->{value},
auto_restore => $key->{auto_restore},
};
$self->{SESSION}->{ $key->{key} } = YAML::Dump($value);
$self->{SESSION}->{ $key->{key} } = SL::Auth::SessionValue->new(key => $key->{key},
value => $key->{value},
auto_restore => $key->{auto_restore});
} else {
my $value = shift @params;
$self->{SESSION}->{ $key } = YAML::Dump(ref($value) eq 'HASH' ? { data => $value } : $value);
$self->{SESSION}->{ $key } = SL::Auth::SessionValue->new(key => $key,
value => $value);
}
}
......
sub get_session_value {
$main::lxdebug->enter_sub();
my $self = shift;
my $params = $self->{SESSION} ? $self->_load_value($self->{SESSION}->{ $_[0] }) : {};
my $self = shift;
my $data = $self->{SESSION} && $self->{SESSION}->{ $_[0] } ? $self->{SESSION}->{ $_[0] }->get : undef;
$main::lxdebug->leave_sub();
return $params->{data};
return $data;
}
sub create_unique_sesion_value {
......
$self->{unique_counter}++ while exists $self->{SESSION}->{$key . ($self->{unique_counter} + 1)};
$self->{unique_counter}++;
$value = { expiration => $params{expiration} ? ($now[0] + $params{expiration}) * 1000000 + $now[1] : undef,
data => $value,
};
$self->{SESSION}->{$key . $self->{unique_counter}} = YAML::Dump($value);
$self->set_session_value($key . $self->{unique_counter} => $value);
return $key . $self->{unique_counter};
}
......
return $self;
}
sub expire_session_keys {
my ($self) = @_;
$self->{SESSION} ||= { };
my @now = gettimeofday();
my $now = $now[0] * 1000000 + $now[1];
$self->delete_session_value(map { $_->[0] }
grep { $_->[1]->{expiration} && ($now > $_->[1]->{expiration}) }
map { [ $_, $self->_load_value($self->{SESSION}->{$_}) ] }
keys %{ $self->{SESSION} });
return $self;
}
sub _has_expiration {
my ($value) = @_;
return (ref $value eq 'HASH') && exists($value->{expiration}) && $value->{data};
}
sub set_cookie_environment_variable {
my $self = shift;
$ENV{HTTP_COOKIE} = $self->get_session_cookie_name() . "=${session_id}";
......
Create a unique key in the session and store C<$value>
there.
If C<$params{expiration}> is set then it is interpreted as a number of
seconds after which the value is removed from the session. It will
never expire if that parameter is falsish.
Returns the key created in the session.
=item C<expire_session_keys>
Removes all keys from the session that have an expiration time set and
whose expiration time is in the past.
=item C<save_session>
Stores the session values in the database. This is the only function
SL/Auth/ColumnInformation.pm
package SL::Auth::ColumnInformation;
use strict;
use Carp;
use Scalar::Util qw(weaken);
use SL::DBUtils;
sub new {
my ($class, %params) = @_;
my $self = bless {}, $class;
$self->{auth} = $params{auth} || croak "Missing 'auth'";
weaken $self->{auth};
return $self;
}
sub _fetch {
my ($self) = @_;
return $self if $self->{info};
my $query = <<SQL;
SELECT a.attname, format_type(a.atttypid, a.atttypmod) AS format_type, d.adsrc, a.attnotnull
FROM pg_attribute a
LEFT JOIN pg_attrdef d ON (a.attrelid = d.adrelid) AND (a.attnum = d.adnum)
WHERE (a.attrelid = 'auth.session_content'::regclass)
AND (a.attnum > 0)
AND NOT a.attisdropped
ORDER BY a.attnum
SQL
$self->{info} = { selectall_as_map($::form, $self->{auth}->dbconnect, $query, 'attname', [ qw(format_type adsrc attnotnull) ]) };
return $self;
}
sub info {
my ($self) = @_;
return $self->_fetch->{info};
}
sub has {
my ($self, $column) = @_;
return $self->info->{$column};
}
1;
SL/Auth/SessionValue.pm
package SL::Auth::SessionValue;
use strict;
use Scalar::Util qw(weaken);
use YAML;
use SL::DBUtils;
sub new {
my ($class, %params) = @_;
my $self = bless {}, $class;
map { $self->{$_} = $params{$_} } qw(auth key value auto_restore);
$self->{fetched} = exists $params{value};
$self->{parsed} = !$params{raw} && exists $params{value};
# delete $self->{auth};
# $::lxdebug->dump(0, "NEW", $self);
# $self->{auth} = $params{auth};
weaken $self->{auth};
return $self;
}
sub get {
my ($self) = @_;
return $self->_fetch->_parse->{value};
}
sub get_dumped {
my ($self) = @_;
return YAML::Dump($self->get);
}
sub _fetch {
my ($self) = @_;
return $self if $self->{fetched};
my $dbh = $self->{auth}->dbconnect;
my $query = qq|SELECT sess_value FROM auth.session_content WHERE (session_id = ?) AND (sess_key = ?)|;
($self->{value}) = selectfirst_array_query($::form, $dbh, $query, $self->{auth}->get_session_id, $self->{key});
$self->{fetched} = 1;
return $self;
}
sub _parse {
my ($self) = @_;
$self->{value} = YAML::Load($self->{value}) unless $self->{parsed};
$self->{parsed} = 1;
return $self;
}
sub _load_value {
my ($self, $value) = @_;
return { simple => 1, data => $value } if $value !~ m/^---/;
my %params = ( simple => 1 );
eval {
my $data = YAML::Load($value);
if (ref $data eq 'HASH') {
map { $params{$_} = $data->{$_} } keys %{ $data };
$params{simple} = 0;
} else {
$params{data} = $data;
}
1;
} or $params{data} = $value;
return \%params;
}
1;
SL/Dispatcher.pm
};
# cleanup
$::auth->expire_session_keys->save_session;
$::auth->save_session;
$::auth->expire_sessions;
$::auth->reset;
sql/Pg-upgrade2-auth/session_content_auto_restore.sql
-- @tag: session_content_auto_restore
-- @description: Spalte "auto_restore" in auth.session_content
-- @depends:
-- @charset: utf-8
ALTER TABLE auth.session_content ADD COLUMN auto_restore boolean;
UPDATE auth.session_content SET auto_restore = FALSE;

Auch abrufbar als: Unified diff