[rt-devel] Apache::Session and persistent data

Ruslan U. Zakirov cubic at acronis.ru
Mon Aug 11 13:26:02 EDT 2003


        Hello.
I've understand your position about session data and user preferences.
But what about this? (see patch)

It's implementing transaction support like in Postgres Apache::Session 
implementation, but slightly better.
We don't use Postgres, but I think that RT users with PG as backend 
don't have persistency at all bacause when 
Apache::Session::Store::Postgres get connected dbh as argument it's 
don't check for AutoCommit :( Even if it would be check, it's wrong to 
force AutoCommit to 0, because this may broke other parts of system. 
With DBI 1.33 and later we now could change this behavior and use 
"clone" function of dbh if we got not transactional handle.

All this only words. But it's really help me to use more then one window 
and do something else then search during search.
And it also helps with bad search queries that need long time and/or 
started by mistake like this:
Requestor = qwerty at qwe.ru [delete] 
<http://192.168.1.165:8000/Search/Listing.html?DeleteRestriction=13>
Requestor = qwe at qwerty.ru [delete] 
<http://192.168.1.165:8000/Search/Listing.html?DeleteRestriction=12>
This search uses ORs and really takes long time.

I've tested only search aspects of this patch, but I can't really 
guarant that there is no other places in data flow where it could broke 
something. I really think that you can comment on this.

And you really can't use Set($WebSessionClass , 
'Apache::Session::MySQLInnoDB'); to run this patch, because of current 
implementation of this option in RT :( I think you know this issue :)

Jesse Vincent wrote:

[snip]

>  
>
>>      5) something else
>>It would be really good two work in more then one browser window. Our 
>>support team would be work better.
>>    
>>
>
>The only place I can think of where you're going to run into deadlocks
>is when a single user is trying to do multiple searches at once.
>
>  
>
Really? Could be there something else that store info except search in 
the session data?
I can believe in this because I don't see places where RT uses data from 
previouse request.

>>      Any ideas on it? What happens on Postgres with this?
>>
>>I think that it's bad system design. IMHO, SessionData really useful for 
>>users specific preferences, but not for dinamic data.
>>    
>>
>
>I strongly disagree. We want something that's actually really persistent
>for user preferences. And that's something that we're working on. But
>shoving it into a session data hash for long-term use is just plain wrong.
>That hash is about session-specific data.
>
I agree that this hash is for session specific data, but I think that in 
case of web programming 'last search conditions' - it's not an atribute 
of user session.
When we breaking the abbility to do concurent requests - it's a bad web 
sistem design. I'm absolutely wrong again?
I've got an idea: If search conditions is only stumbling-block then why 
don't use hidden form fields to save conditions of previouse request?

>
>Switching to a url-based session identifier may get you your
>multi-sessions stuff. But it's going to lead to annoying things like 
>"oddness" with what happens when you click a bookmark after a session
>has been deleted. Should it open in your current session. Should it
>require a new login?
>  
>
Not right question, in URL we could store id of persistent object, but 
sessions can be tracked by cookies how RT doing now.
We could give user interface for something like history of this objects 
searches or something else if needed.
When he click "new search", RT creates new object and starts to fill it. 
If user want to store it, he could bookmark it in web browser, no - then 
after sometime we could remove very old objects. Something like this?
I just think that IMO it's very bad to see system where I can't do two 
searches at one time, but anyway we continue use your product. %)
                                Ruslan.
-------------- next part --------------
diff -Nru Session.backup/MySQLInnoDB.pm Session/MySQLInnoDB.pm
--- Session.backup/MySQLInnoDB.pm	1970-01-01 03:00:00.000000000 +0300
+++ Session/MySQLInnoDB.pm	2003-08-11 20:04:22.000000000 +0400
@@ -0,0 +1,88 @@
+#############################################################################
+#
+# Apache::Session::MySQLInnoDBInnoDB
+# Apache persistent user sessions in a MySQL database 
+# with transaction locking instead of exlusive
+# Copyright(c) 2003 Ruslan U. Zakirov (cubic at acronis.ru)
+# Based on Apache::Session::Postgres
+#
+############################################################################
+
+package Apache::Session::MySQLInnoDBInnoDB;
+
+use strict;
+use vars qw(@ISA $VERSION);
+
+$VERSION = '0.9';
+ at ISA = qw(Apache::Session);
+
+use Apache::Session;
+use Apache::Session::Lock::Null;
+use Apache::Session::Store::MySQLInnoDBInnoDB;
+use Apache::Session::Generate::MD5;
+use Apache::Session::Serialize::Storable;
+
+sub populate {
+    my $self = shift;
+
+    $self->{object_store} = new Apache::Session::Store::MySQLInnoDBInnoDB $self;
+    $self->{lock_manager} = new Apache::Session::Lock::Null $self;
+    $self->{generate}     = \&Apache::Session::Generate::MD5::generate;
+    $self->{validate}     = \&Apache::Session::Generate::MD5::validate;
+    $self->{serialize}    = \&Apache::Session::Serialize::Storable::serialize;
+    $self->{unserialize}  = \&Apache::Session::Serialize::Storable::unserialize;
+
+    return $self;
+}
+
+1;
+
+
+=pod
+
+=head1 NAME
+
+Apache::Session::MySQLInnoDB - An implementation of Apache::Session
+
+=head1 SYNOPSIS
+
+ use Apache::Session::MySQLInnoDB;
+ 
+ #if you want Apache::Session to open new DB handles:
+ 
+ tie %hash, 'Apache::Session::MySQLInnoDB', $id, {
+    DataSource => 'dbi:mysql:sessions',
+    UserName   => $db_user,
+    Password   => $db_pass,
+    LockDataSource => 'dbi:mysql:sessions',
+    LockUserName   => $db_user,
+    LockPassword   => $db_pass,
+    Commit         => 1
+ };
+
+ #or, if your handles are already opened:
+
+ tie %hash, 'Apache::Session::MySQLInnoDB', $id, {
+    Handle     => $dbh,
+    LockHandle => $dbh,
+    Commit     => 1
+ };
+
+=head1 DESCRIPTION
+
+This module is an implementation of Apache::Session.  It uses the MySQL backing
+store and the MySQL transaction scheme.  See the example, and the documentation for
+Apache::Session::Store::MySQLInnoDB for more details.
+
+=head1 NOTE
+If you pass already opened handle and AutoCommit is enable then it's necessary to
+clone database handle. This could increase number of connections.
+
+=head1 AUTHOR
+
+This module was written by Ruslan U. Zakirov <cubic at acronis.ru>.
+
+=head1 SEE ALSO
+
+L<Apache::Session::File>, L<Apache::Session::Flex>,
+L<Apache::Session::DB_File>, L<Apache::Session::Postgres>, L<Apache::Session>
diff -Nru Session.backup/Store/MySQLInnoDB.pm Session/Store/MySQLInnoDB.pm
--- Session.backup/Store/MySQLInnoDB.pm	1970-01-01 03:00:00.000000000 +0300
+++ Session/Store/MySQLInnoDB.pm	2003-08-11 20:04:02.000000000 +0400
@@ -0,0 +1,189 @@
+#############################################################################
+#
+# Apache::Session::Store::MySQLInnoDB
+# Implements session object storage via MySQL InnoDB tables
+# Copyright(c) 2003 Ruslan U. Zakirov <cubic at acronis.ru>
+# Distribute under the Artistic License
+#
+############################################################################
+
+package Apache::Session::Store::MySQLInnoDB;
+
+use strict;
+
+use DBI 1.33;
+use Apache::Session::Store::DBI;
+
+use vars qw(@ISA $VERSION);
+
+ at ISA = qw(Apache::Session::Store::DBI);
+$VERSION = '0.9';
+
+$Apache::Session::Store::MySQLInnoDB::DataSource = undef;
+$Apache::Session::Store::MySQLInnoDB::UserName   = undef;
+$Apache::Session::Store::MySQLInnoDB::Password   = undef;
+
+sub connection {
+    my $self    = shift;
+    my $session = shift;
+    
+    return if (defined $self->{dbh});
+
+    if (exists $session->{args}->{Handle}) {
+        if ($session->{args}->{Handle}->{AutoCommit} == 1) {
+            $self->{dbh} = $session->{args}->{Handle}->clone() || die $DBI::errstr;
+            $self->{dbh}->{AutoCommit} = 0;
+            $self->{disconnect} = 1;
+	} else {
+            $self->{dbh} = $session->{args}->{Handle};
+	}
+        $self->{commit} = $session->{args}->{Commit};
+        return;
+    }
+
+    my $datasource = $session->{args}->{DataSource} || 
+        $Apache::Session::Store::MySQLInnoDB::DataSource;
+    my $username = $session->{args}->{UserName} ||
+        $Apache::Session::Store::MySQLInnoDB::UserName;
+    my $password = $session->{args}->{Password} ||
+        $Apache::Session::Store::MySQLInnoDB::Password;
+        
+    $self->{dbh} = DBI->connect(
+        $datasource,
+        $username,
+        $password,
+        { RaiseError => 1, AutoCommit => 0 }
+    ) || die $DBI::errstr;
+
+    
+    #If we open the connection, we close the connection
+    $self->{disconnect} = 1;
+    
+    #the programmer has to tell us what commit policy to use
+    $self->{commit} = $session->{args}->{Commit};
+}
+
+sub materialize {
+    my $self    = shift;
+    my $session = shift;
+
+    $self->connection($session);
+
+    local $self->{dbh}->{RaiseError} = 1;
+
+warn "AutoCommit: ".$self->{dbh}->{AutoCommit}."\n";
+
+    if (!defined $self->{materialize_sth}) {
+        $self->{materialize_sth} = 
+            $self->{dbh}->prepare_cached(qq{
+                SELECT a_session FROM sessions WHERE id = ? FOR UPDATE });
+    }
+    
+    $self->{materialize_sth}->bind_param(1, $session->{data}->{_session_id});
+    
+    $self->{materialize_sth}->execute;
+    
+    my $results = $self->{materialize_sth}->fetchrow_arrayref;
+
+    if (!(defined $results)) {
+        die "Object does not exist in the data store";
+    }
+
+    $self->{materialize_sth}->finish;
+
+    $session->{serialized} = $results->[0];
+}
+
+sub DESTROY {
+    my $self = shift;
+    # sleep(10);
+    if ($self->{commit}) {
+        $self->{dbh}->commit;
+    }
+    
+    if ($self->{disconnect}) {
+        $self->{dbh}->disconnect;
+    }
+}
+
+1;
+
+=pod
+
+=head1 NAME
+
+Apache::Session::Store::MySQLInnoDB - Store persistent data in a Postgres database
+
+=head1 SYNOPSIS
+
+ use Apache::Session::Store::MySQLInnoDB;
+ 
+ my $store = new Apache::Session::Store::MySQLInnoDB;
+ 
+ $store->insert($ref);
+ $store->update($ref);
+ $store->materialize($ref);
+ $store->remove($ref);
+
+=head1 DESCRIPTION
+
+Apache::Session::Store::MySQLInnoDB fulfills the storage interface of
+Apache::Session. Session data is stored in a Postgres database.
+
+=head1 SCHEMA
+
+To use this module, you will need at least these columns in a table 
+called 'sessions':
+
+ id char(32)     # or however long your session IDs are.
+ a_session text  # This has an ~8 KB limit :(
+
+To create this schema, you can execute this command using the psql program:
+
+ CREATE TABLE sessions (
+    id char(32) not null primary key,
+    a_session text
+ );
+
+If you use some other command, ensure that there is a unique index on the
+table's id column.
+
+=head1 CONFIGURATION
+
+The module must know what datasource, username, and password to use when
+connecting to the database.  These values can be set using the options hash
+(see Apache::Session documentation).  The options are:
+
+=over 4
+
+=item DataSource
+
+=item UserName
+
+=item Password
+
+=back
+
+Example:
+
+ tie %hash, 'Apache::Session::MySQLInnoDB', $id, {
+     DataSource => 'dbi:Pg:dbname=database',
+     UserName   => 'database_user',
+     Password   => 'K00l'
+ };
+
+Instead, you may pass in an already-opened DBI handle to your database.
+
+ tie %hash, 'Apache::Session::MySQLInnoDB', $id, {
+     Handle => $dbh
+ };
+
+=head1 AUTHOR
+
+This modules was written by Ruslan U. Zakirov <cubic at acronis.ru>
+
+A fix for the commit policy was contributed by Michael Schout <mschout at gkg.net>
+
+=head1 SEE ALSO
+
+L<Apache::Session>, L<Apache::Session::Store::DBI>


More information about the Rt-devel mailing list