Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision 9b294bec

Von Sven Schöling vor mehr als 5 Jahren hinzugefügt

  • ID 9b294beca7043c0fa5ae9c57d5766ce4e0d5aa65
  • Vorgänger 7749e0e6
  • Nachfolger 109b7f30

Session Content: Race condition gehoben

Der ursprüngliche Mechanismus hat einfach nur alle Session Variablen
gespeichert und beim Session restore wieder geladen. Es hat sich aber
gezeigt, dass große Daten in der Session Requests deutlich langsamer
machen, also wurde das Flag auto_restore eingeführt. Session Werte, die
nicht automatisch benötigt werden, sollten dann nur bei Bedarf geladen
werden.

Um zu wissen welche Werte existieren wurden aber zum Start des
Requests einmal alle Werte aus der Sessiontabelle geholt, und am Ende
dieser Stand auch wieder hergestellt.

Unter ajax load kann es aber passieren, dass in der Zeit andere Requests
schon Werte eingepflegt haben die dabei gelöscht werden. Das führt dann
zu zufälligen Sessionabbrüchen oder Requestfehlern.

Jetzt werden am Anfang nur und ausschließlich die Daten geladen die auch
auto_restore sind, die dann auch gleich gelöscht werden. nur die Daten
die modifiziert werden, werden am Ende des Requests zurückgespeichert.

Es wäre toll gewesen dafür ein UPSERT zu nehmen, aber das scheitert
daran, dass das ein DB Upgrade auf auth braucht.

Unterschiede anzeigen:

SL/Auth.pm
20 20
use SL::User;
21 21
use SL::DBConnect;
22 22
use SL::DBUpgrade2;
23
use SL::DBUtils qw(do_query do_statement prepare_execute_query prepare_query selectall_array_query selectrow_query);
23
use SL::DBUtils qw(do_query do_statement prepare_execute_query prepare_query selectall_array_query selectrow_query selectall_ids);
24 24

  
25 25
use strict;
26 26

  
......
635 635
  my $query = <<SQL;
636 636
    SELECT sess_key, sess_value, auto_restore
637 637
    FROM auth.session_content
638
    WHERE (session_id = ?)
638
    WHERE (auto_restore AND session_id = ? OR sess_key IN (@{[ join ',', ("?") x keys %auto_restore_keys ]}))
639 639
SQL
640
  my $sth = prepare_execute_query($::form, $dbh, $query, $session_id);
640
  my $sth = prepare_execute_query($::form, $dbh, $query, $session_id, keys %auto_restore_keys);
641 641

  
642
  my $need_delete;
642 643
  while (my $ref = $sth->fetchrow_hashref) {
643
    if ($ref->{auto_restore} || $auto_restore_keys{$ref->{sess_key}}) {
644
      my $value = SL::Auth::SessionValue->new(auth         => $self,
645
                                              key          => $ref->{sess_key},
646
                                              value        => $ref->{sess_value},
647
                                              auto_restore => $ref->{auto_restore},
648
                                              raw          => 1);
649
      $self->{SESSION}->{ $ref->{sess_key} } = $value;
650

  
651
      next if defined $::form->{$ref->{sess_key}};
652

  
653
      my $data                    = $value->get;
654
      $::form->{$ref->{sess_key}} = $data if $value->{auto_restore} || !ref $data;
655
    } else {
656
      my $value = SL::Auth::SessionValue->new(auth => $self,
657
                                              key  => $ref->{sess_key});
658
      $self->{SESSION}->{ $ref->{sess_key} } = $value;
659
    }
644
    $need_delete = 1 if $ref->{auto_restore};
645
    my $value = SL::Auth::SessionValue->new(auth         => $self,
646
                                            key          => $ref->{sess_key},
647
                                            value        => $ref->{sess_value},
648
                                            auto_restore => $ref->{auto_restore},
649
                                            raw          => 1);
650
    $self->{SESSION}->{ $ref->{sess_key} } = $value;
651

  
652
    next if defined $::form->{$ref->{sess_key}};
653

  
654
    my $data                    = $value->get;
655
    $::form->{$ref->{sess_key}} = $data if $value->{auto_restore} || !ref $data;
660 656
  }
661 657

  
662 658
  $sth->finish;
659

  
660
  if ($need_delete) {
661
    do_query($::form, $dbh, 'DELETE FROM auth.session_content WHERE auto_restore AND session_id = ?', $session_id);
662
  }
663 663
}
664 664

  
665 665
sub destroy_session {
......
753 753
    return;
754 754
  }
755 755

  
756
  my @unfetched_keys = map     { $_->{key}        }
757
                       grep    { ! $_->{fetched}  }
758
                       values %{ $self->{SESSION} };
759
  # $::lxdebug->dump(0, "unfetched_keys", [ sort @unfetched_keys ]);
760
  # $::lxdebug->dump(0, "all keys", [ sort map { $_->{key} } values %{ $self->{SESSION} } ]);
761
  my $query          = qq|DELETE FROM auth.session_content WHERE (session_id = ?)|;
762
  $query            .= qq| AND (sess_key NOT IN (| . join(', ', ('?') x scalar @unfetched_keys) . qq|))| if @unfetched_keys;
763

  
764
  do_query($::form, $dbh, $query, $session_id, @unfetched_keys);
765

  
766 756
  my ($id) = selectrow_query($::form, $dbh, qq|SELECT id FROM auth.session WHERE id = ?|, $session_id);
767 757

  
768 758
  if ($id) {
......
776 766
    do_query($::form, $dbh, qq|UPDATE auth.session SET api_token = ? WHERE id = ?|, $self->_create_session_id, $session_id) unless $stored_api_token;
777 767
  }
778 768

  
779
  my @values_to_save = grep    { $_->{fetched} }
769
  my @values_to_save = grep    { $_->{modified} }
780 770
                       values %{ $self->{SESSION} };
781 771
  if (@values_to_save) {
782
    my ($columns, $placeholders) = ('', '');
772
    my %known_keys = map { $_ => 1 }
773
      selectall_ids($::form, $dbh, qq|SELECT sess_key FROM auth.session_content WHERE session_id = ?|, 'sess_key', $session_id);
783 774
    my $auto_restore             = $self->{column_information}->has('auto_restore');
784 775

  
785
    if ($auto_restore) {
786
      $columns      .= ', auto_restore';
787
      $placeholders .= ', ?';
788
    }
776
    my $insert_query  = $auto_restore
777
      ? "INSERT INTO auth.session_content (session_id, sess_key, sess_value, auto_restore) VALUES (?, ?, ?, ?)"
778
      : "INSERT INTO auth.session_content (session_id, sess_key, sess_value) VALUES (?, ?, ?)";
779
    my $insert_sth = prepare_query($::form, $dbh, $insert_query);
789 780

  
790
    $query  = qq|INSERT INTO auth.session_content (session_id, sess_key, sess_value ${columns}) VALUES (?, ?, ? ${placeholders})|;
791
    my $sth = prepare_query($::form, $dbh, $query);
781
    my $update_query  = $auto_restore
782
      ? "UPDATE auth.session_content SET sess_value = ?, auto_restore = ? WHERE session_id = ? AND sess_key = ?"
783
      : "UPDATE auth.session_content SET sess_value = ? WHERE session_id = ? AND sess_key = ?";
784
    my $update_sth = prepare_query($::form, $dbh, $update_query);
792 785

  
793 786
    foreach my $value (@values_to_save) {
794 787
      my @values = ($value->{key}, $value->get_dumped);
795 788
      push @values, $value->{auto_restore} if $auto_restore;
796 789

  
797
      do_statement($::form, $sth, $query, $session_id, @values);
790
      if ($known_keys{$value->{key}}) {
791
        do_statement($::form, $update_sth, $update_query,
792
          $value->get_dumped, ( $value->{auto_restore} )x!!$auto_restore, $session_id, $value->{key}
793
        );
794
      } else {
795
        do_statement($::form, $insert_sth, $insert_query,
796
          $session_id, $value->{key}, $value->get_dumped, ( $value->{auto_restore} )x!!$auto_restore
797
        );
798
      }
798 799
    }
799 800

  
800
    $sth->finish();
801
    $insert_sth->finish;
802
    $update_sth->finish;
801 803
  }
802 804

  
803 805
  $dbh->commit() unless $provided_dbh;
......
815 817
    if (ref $key eq 'HASH') {
816 818
      $self->{SESSION}->{ $key->{key} } = SL::Auth::SessionValue->new(key          => $key->{key},
817 819
                                                                      value        => $key->{value},
820
                                                                      modified     => 1,
818 821
                                                                      auto_restore => $key->{auto_restore});
819 822

  
820 823
    } else {
821 824
      my $value = shift @params;
822 825
      $self->{SESSION}->{ $key } = SL::Auth::SessionValue->new(key   => $key,
823
                                                               value => $value);
826
                                                               value => $value,
827
                                                               modified => 1);
824 828
    }
825 829
  }
826 830

  
......
837 841
}
838 842

  
839 843
sub get_session_value {
840
  my $self = shift;
841
  my $data = $self->{SESSION} && $self->{SESSION}->{ $_[0] } ? $self->{SESSION}->{ $_[0] }->get : undef;
844
  my ($self, $key) = @_;
845

  
846
  return if !$self->{SESSION};
842 847

  
843
  return $data;
848
  ($self->{SESSION}{$key} //= SL::Auth::SessionValue->new(auth => $self, key => $key))->get
844 849
}
845 850

  
846 851
sub create_unique_sesion_value {
SL/Auth/SessionValue.pm
16 16

  
17 17
  my $self = bless {}, $class;
18 18

  
19
  map { $self->{$_} = $params{$_} } qw(auth key value auto_restore);
19
  map { $self->{$_} = $params{$_} } qw(auth key value auto_restore modified);
20 20

  
21 21
  $self->{fetched} =                  exists $params{value};
22 22
  $self->{parsed}  = !$params{raw} && exists $params{value};

Auch abrufbar als: Unified diff