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
8 8
use List::MoreUtils qw(uniq);
9 9
use YAML;
10 10

  
11
use SL::Auth::ColumnInformation;
11 12
use SL::Auth::Constants qw(:all);
12 13
use SL::Auth::DB;
13 14
use SL::Auth::LDAP;
14 15
use SL::Auth::Password;
16
use SL::Auth::SessionValue;
15 17

  
16 18
use SL::SessionFile;
17 19
use SL::User;
......
29 31

  
30 32
  bless $self, $type;
31 33

  
32
  $self->{SESSION} = { };
33

  
34 34
  $self->_read_auth_config();
35
  $self->reset;
35 36

  
36 37
  $main::lxdebug->leave_sub();
37 38

  
......
41 42
sub reset {
42 43
  my ($self, %params) = @_;
43 44

  
44
  $self->{SESSION}          = { };
45
  $self->{FULL_RIGHTS}      = { };
46
  $self->{RIGHTS}           = { };
47
  $self->{unique_counter}   = 0;
45
  $self->{SESSION}            = { };
46
  $self->{FULL_RIGHTS}        = { };
47
  $self->{RIGHTS}             = { };
48
  $self->{unique_counter}     = 0;
49
  $self->{column_information} = SL::Auth::ColumnInformation->new(auth => $self);
48 50
}
49 51

  
50 52
sub get_user_dbh {
......
537 539
    return $cookie ? SESSION_EXPIRED : SESSION_NONE;
538 540
  }
539 541

  
540
  $query = qq|SELECT sess_key, sess_value FROM auth.session_content WHERE session_id = ?|;
541
  $sth   = prepare_execute_query($form, $dbh, $query, $session_id);
542

  
543
  while (my $ref = $sth->fetchrow_hashref()) {
544
    $self->{SESSION}->{$ref->{sess_key}} = $ref->{sess_value};
545
    next if defined $form->{$ref->{sess_key}};
546

  
547
    my $params                = $self->_load_value($ref->{sess_value});
548
    $form->{$ref->{sess_key}} = $params->{data} if $params->{auto_restore} || $params->{simple};
542
  if ($self->{column_information}->has('auto_restore')) {
543
    $self->_load_with_auto_restore_column($dbh, $session_id);
544
  } else {
545
    $self->_load_without_auto_restore_column($dbh, $session_id);
549 546
  }
550 547

  
551
  $sth->finish();
552

  
553 548
  $main::lxdebug->leave_sub();
554 549

  
555 550
  return SESSION_OK;
556 551
}
557 552

  
558
sub _load_value {
559
  my ($self, $value) = @_;
553
sub _load_without_auto_restore_column {
554
  my ($self, $dbh, $session_id) = @_;
560 555

  
561
  return { simple => 1, data => $value } if $value !~ m/^---/;
556
  my $query = <<SQL;
557
    SELECT sess_key, sess_value
558
    FROM auth.session_content
559
    WHERE (session_id = ?)
560
SQL
561
  my $sth = prepare_execute_query($::form, $dbh, $query, $session_id);
562 562

  
563
  my %params = ( simple => 1 );
564
  eval {
565
    my $data = YAML::Load($value);
563
  while (my $ref = $sth->fetchrow_hashref) {
564
    my $value = SL::Auth::SessionValue->new(auth  => $self,
565
                                            key   => $ref->{sess_key},
566
                                            value => $ref->{sess_value},
567
                                            raw   => 1);
568
    $self->{SESSION}->{ $ref->{sess_key} } = $value;
566 569

  
567
    if (ref $data eq 'HASH') {
568
      map { $params{$_} = $data->{$_} } keys %{ $data };
569
      $params{simple} = 0;
570
    next if defined $::form->{$ref->{sess_key}};
570 571

  
571
    } else {
572
      $params{data}   = $data;
573
    }
572
    my $data                    = $value->get;
573
    $::form->{$ref->{sess_key}} = $data if $value->{auto_restore} || !ref $data;
574
  }
575
}
576

  
577
sub _load_with_auto_restore_column {
578
  my ($self, $dbh, $session_id) = @_;
579

  
580
  my $auto_restore_keys = join ', ', map { "'${_}'" } qw(login password rpw);
581

  
582
  my $query = <<SQL;
583
    SELECT sess_key, sess_value, auto_restore
584
    FROM auth.session_content
585
    WHERE (session_id = ?)
586
      AND (   auto_restore
587
           OR sess_key IN (${auto_restore_keys}))
588
SQL
589
  my $sth = prepare_execute_query($::form, $dbh, $query, $session_id);
590

  
591
  while (my $ref = $sth->fetchrow_hashref) {
592
    my $value = SL::Auth::SessionValue->new(auth         => $self,
593
                                            key          => $ref->{sess_key},
594
                                            value        => $ref->{sess_value},
595
                                            auto_restore => $ref->{auto_restore},
596
                                            raw          => 1);
597
    $self->{SESSION}->{ $ref->{sess_key} } = $value;
574 598

  
575
    1;
576
  } or $params{data} = $value;
599
    next if defined $::form->{$ref->{sess_key}};
577 600

  
578
  return \%params;
601
    my $data                    = $value->get;
602
    $::form->{$ref->{sess_key}} = $data if $value->{auto_restore} || !ref $data;
603
  }
604

  
605
  $sth->finish;
606

  
607
  $query = <<SQL;
608
    SELECT sess_key
609
    FROM auth.session_content
610
    WHERE (session_id = ?)
611
      AND NOT COALESCE(auto_restore, FALSE)
612
      AND (sess_key NOT IN (${auto_restore_keys}))
613
SQL
614
  $sth = prepare_execute_query($::form, $dbh, $query, $session_id);
615

  
616
  while (my $ref = $sth->fetchrow_hashref) {
617
    my $value = SL::Auth::SessionValue->new(auth => $self,
618
                                            key  => $ref->{sess_key});
619
    $self->{SESSION}->{ $ref->{sess_key} } = $value;
620
  }
579 621
}
580 622

  
581 623
sub destroy_session {
......
665 707
  $dbh->begin_work unless $provided_dbh;
666 708

  
667 709
  do_query($::form, $dbh, qq|LOCK auth.session_content|);
668
  do_query($::form, $dbh, qq|DELETE FROM auth.session_content WHERE session_id = ?|, $session_id);
669 710

  
670
  my $query = qq|SELECT id FROM auth.session WHERE id = ?|;
711
  my @unfetched_keys = map     { $_->{key}        }
712
                       grep    { ! $_->{fetched}  }
713
                       values %{ $self->{SESSION} };
714
  # $::lxdebug->dump(0, "unfetched_keys", [ sort @unfetched_keys ]);
715
  # $::lxdebug->dump(0, "all keys", [ sort map { $_->{key} } values %{ $self->{SESSION} } ]);
716
  my $query          = qq|DELETE FROM auth.session_content WHERE (session_id = ?)|;
717
  $query            .= qq| AND (sess_key NOT IN (| . join(', ', ('?') x scalar @unfetched_keys) . qq|))| if @unfetched_keys;
671 718

  
672
  my ($id)  = selectrow_query($::form, $dbh, $query, $session_id);
719
  do_query($::form, $dbh, $query, $session_id, @unfetched_keys);
720

  
721
  my ($id) = selectrow_query($::form, $dbh, qq|SELECT id FROM auth.session WHERE id = ?|, $session_id);
673 722

  
674 723
  if ($id) {
675 724
    do_query($::form, $dbh, qq|UPDATE auth.session SET mtime = now() WHERE id = ?|, $session_id);
......
677 726
    do_query($::form, $dbh, qq|INSERT INTO auth.session (id, ip_address, mtime) VALUES (?, ?, now())|, $session_id, $ENV{REMOTE_ADDR});
678 727
  }
679 728

  
680
  if (%{ $self->{SESSION} }) {
681
    my $query = qq|INSERT INTO auth.session_content (session_id, sess_key, sess_value) VALUES (?, ?, ?)|;
682
    my $sth   = prepare_query($::form, $dbh, $query);
729
  my @values_to_save = grep    { $_->{fetched} }
730
                       values %{ $self->{SESSION} };
731
  if (@values_to_save) {
732
    my ($columns, $placeholders) = ('', '');
733
    my $auto_restore             = $self->{column_information}->has('auto_restore');
683 734

  
684
    foreach my $key (sort keys %{ $self->{SESSION} }) {
685
      do_statement($::form, $sth, $query, $session_id, $key, $self->{SESSION}->{$key});
735
    if ($auto_restore) {
736
      $columns      .= ', auto_restore';
737
      $placeholders .= ', ?';
738
    }
739

  
740
    $query  = qq|INSERT INTO auth.session_content (session_id, sess_key, sess_value ${columns}) VALUES (?, ?, ? ${placeholders})|;
741
    my $sth = prepare_query($::form, $dbh, $query);
742

  
743
    foreach my $value (@values_to_save) {
744
      my @values = ($value->{key}, $value->get_dumped);
745
      push @values, $value->{auto_restore} if $auto_restore;
746

  
747
      do_statement($::form, $sth, $query, $session_id, @values);
686 748
    }
687 749

  
688 750
    $sth->finish();
......
704 766
    my $key = shift @params;
705 767

  
706 768
    if (ref $key eq 'HASH') {
707
      my $value = { data         => $key->{value},
708
                    auto_restore => $key->{auto_restore},
709
                  };
710
      $self->{SESSION}->{ $key->{key} } = YAML::Dump($value);
769
      $self->{SESSION}->{ $key->{key} } = SL::Auth::SessionValue->new(key          => $key->{key},
770
                                                                      value        => $key->{value},
771
                                                                      auto_restore => $key->{auto_restore});
711 772

  
712 773
    } else {
713 774
      my $value = shift @params;
714
      $self->{SESSION}->{ $key } = YAML::Dump(ref($value) eq 'HASH' ? { data => $value } : $value);
775
      $self->{SESSION}->{ $key } = SL::Auth::SessionValue->new(key   => $key,
776
                                                               value => $value);
715 777
    }
716 778
  }
717 779

  
......
736 798
sub get_session_value {
737 799
  $main::lxdebug->enter_sub();
738 800

  
739
  my $self   = shift;
740
  my $params = $self->{SESSION} ? $self->_load_value($self->{SESSION}->{ $_[0] }) : {};
801
  my $self = shift;
802
  my $data = $self->{SESSION} && $self->{SESSION}->{ $_[0] } ? $self->{SESSION}->{ $_[0] }->get : undef;
741 803

  
742 804
  $main::lxdebug->leave_sub();
743 805

  
744
  return $params->{data};
806
  return $data;
745 807
}
746 808

  
747 809
sub create_unique_sesion_value {
......
756 818
  $self->{unique_counter}++ while exists $self->{SESSION}->{$key . ($self->{unique_counter} + 1)};
757 819
  $self->{unique_counter}++;
758 820

  
759
  $value  = { expiration => $params{expiration} ? ($now[0] + $params{expiration}) * 1000000 + $now[1] : undef,
760
              data       => $value,
761
            };
762

  
763
  $self->{SESSION}->{$key . $self->{unique_counter}} = YAML::Dump($value);
821
  $self->set_session_value($key . $self->{unique_counter} => $value);
764 822

  
765 823
  return $key . $self->{unique_counter};
766 824
}
......
795 853
  return $self;
796 854
}
797 855

  
798
sub expire_session_keys {
799
  my ($self) = @_;
800

  
801
  $self->{SESSION} ||= { };
802

  
803
  my @now = gettimeofday();
804
  my $now = $now[0] * 1000000 + $now[1];
805

  
806
  $self->delete_session_value(map  { $_->[0]                                                 }
807
                              grep { $_->[1]->{expiration} && ($now > $_->[1]->{expiration}) }
808
                              map  { [ $_, $self->_load_value($self->{SESSION}->{$_}) ]      }
809
                              keys %{ $self->{SESSION} });
810

  
811
  return $self;
812
}
813

  
814
sub _has_expiration {
815
  my ($value) = @_;
816
  return (ref $value eq 'HASH') && exists($value->{expiration}) && $value->{data};
817
}
818

  
819 856
sub set_cookie_environment_variable {
820 857
  my $self = shift;
821 858
  $ENV{HTTP_COOKIE} = $self->get_session_cookie_name() . "=${session_id}";
......
1257 1294
Create a unique key in the session and store C<$value>
1258 1295
there.
1259 1296

  
1260
If C<$params{expiration}> is set then it is interpreted as a number of
1261
seconds after which the value is removed from the session. It will
1262
never expire if that parameter is falsish.
1263

  
1264 1297
Returns the key created in the session.
1265 1298

  
1266
=item C<expire_session_keys>
1267

  
1268
Removes all keys from the session that have an expiration time set and
1269
whose expiration time is in the past.
1270

  
1271 1299
=item C<save_session>
1272 1300

  
1273 1301
Stores the session values in the database. This is the only function
SL/Auth/ColumnInformation.pm
1
package SL::Auth::ColumnInformation;
2

  
3
use strict;
4

  
5
use Carp;
6
use Scalar::Util qw(weaken);
7

  
8
use SL::DBUtils;
9

  
10
sub new {
11
  my ($class, %params) = @_;
12

  
13
  my $self = bless {}, $class;
14

  
15
  $self->{auth} = $params{auth} || croak "Missing 'auth'";
16
  weaken $self->{auth};
17

  
18
  return $self;
19
}
20

  
21
sub _fetch {
22
  my ($self) = @_;
23

  
24
  return $self if $self->{info};
25

  
26
  my $query = <<SQL;
27
    SELECT a.attname, format_type(a.atttypid, a.atttypmod) AS format_type, d.adsrc, a.attnotnull
28
    FROM pg_attribute a
29
    LEFT JOIN pg_attrdef d ON (a.attrelid = d.adrelid) AND (a.attnum = d.adnum)
30
    WHERE (a.attrelid = 'auth.session_content'::regclass)
31
      AND (a.attnum > 0)
32
      AND NOT a.attisdropped
33
    ORDER BY a.attnum
34
SQL
35

  
36
  $self->{info} = { selectall_as_map($::form, $self->{auth}->dbconnect, $query, 'attname', [ qw(format_type adsrc attnotnull) ]) };
37

  
38
  return $self;
39
}
40

  
41
sub info {
42
  my ($self) = @_;
43
  return $self->_fetch->{info};
44
}
45

  
46
sub has {
47
  my ($self, $column) = @_;
48
  return $self->info->{$column};
49
}
50

  
51
1;
SL/Auth/SessionValue.pm
1
package SL::Auth::SessionValue;
2

  
3
use strict;
4

  
5
use Scalar::Util qw(weaken);
6
use YAML;
7

  
8
use SL::DBUtils;
9

  
10
sub new {
11
  my ($class, %params) = @_;
12

  
13
  my $self = bless {}, $class;
14

  
15
  map { $self->{$_} = $params{$_} } qw(auth key value auto_restore);
16

  
17
  $self->{fetched} =                  exists $params{value};
18
  $self->{parsed}  = !$params{raw} && exists $params{value};
19

  
20
  # delete $self->{auth};
21
  # $::lxdebug->dump(0, "NEW", $self);
22
  # $self->{auth} = $params{auth};
23

  
24
  weaken $self->{auth};
25

  
26
  return $self;
27
}
28

  
29
sub get {
30
  my ($self) = @_;
31
  return $self->_fetch->_parse->{value};
32
}
33

  
34
sub get_dumped {
35
  my ($self) = @_;
36
  return YAML::Dump($self->get);
37
}
38

  
39
sub _fetch {
40
  my ($self) = @_;
41

  
42
  return $self if $self->{fetched};
43

  
44
  my $dbh          = $self->{auth}->dbconnect;
45
  my $query        = qq|SELECT sess_value FROM auth.session_content WHERE (session_id = ?) AND (sess_key = ?)|;
46
  ($self->{value}) = selectfirst_array_query($::form, $dbh, $query, $self->{auth}->get_session_id, $self->{key});
47
  $self->{fetched} = 1;
48

  
49
  return $self;
50
}
51

  
52
sub _parse {
53
  my ($self) = @_;
54

  
55
  $self->{value}  = YAML::Load($self->{value}) unless $self->{parsed};
56
  $self->{parsed} = 1;
57

  
58
  return $self;
59
}
60

  
61
sub _load_value {
62
  my ($self, $value) = @_;
63

  
64
  return { simple => 1, data => $value } if $value !~ m/^---/;
65

  
66
  my %params = ( simple => 1 );
67
  eval {
68
    my $data = YAML::Load($value);
69

  
70
    if (ref $data eq 'HASH') {
71
      map { $params{$_} = $data->{$_} } keys %{ $data };
72
      $params{simple} = 0;
73

  
74
    } else {
75
      $params{data}   = $data;
76
    }
77

  
78
    1;
79
  } or $params{data} = $value;
80

  
81
  return \%params;
82
}
83

  
84
1;
SL/Dispatcher.pm
234 234
  };
235 235

  
236 236
  # cleanup
237
  $::auth->expire_session_keys->save_session;
237
  $::auth->save_session;
238 238
  $::auth->expire_sessions;
239 239
  $::auth->reset;
240 240

  
sql/Pg-upgrade2-auth/session_content_auto_restore.sql
1
-- @tag: session_content_auto_restore
2
-- @description: Spalte "auto_restore" in auth.session_content
3
-- @depends:
4
-- @charset: utf-8
5
ALTER TABLE auth.session_content ADD COLUMN auto_restore boolean;
6
UPDATE auth.session_content SET auto_restore = FALSE;

Auch abrufbar als: Unified diff