Index: anches/neb_distrib_20081210/Nebulous-Server/.cvsignore
===================================================================
--- /branches/neb_distrib_20081210/Nebulous-Server/.cvsignore	(revision 23536)
+++ 	(revision )
@@ -1,6 +1,0 @@
-Build
-META.yml
-Makefile
-Makefile.PL
-_build
-blib
Index: /branches/neb_distrib_20081210/Nebulous-Server/Changes
===================================================================
--- /branches/neb_distrib_20081210/Nebulous-Server/Changes	(revision 23536)
+++ /branches/neb_distrib_20081210/Nebulous-Server/Changes	(revision 23537)
@@ -1,3 +1,6 @@
 Revision history for Nebulous
+
+0.17
+    - retry database transactions when a deadlock is detected
 
 0.16
Index: /branches/neb_distrib_20081210/Nebulous-Server/MANIFEST
===================================================================
--- /branches/neb_distrib_20081210/Nebulous-Server/MANIFEST	(revision 23536)
+++ /branches/neb_distrib_20081210/Nebulous-Server/MANIFEST	(revision 23537)
@@ -10,10 +10,8 @@
 bin/neb-voladd
 bin/nebdiskd
-docs/c_api.h
 docs/database_setup.txt
 docs/design.txt
+docs/install.txt
 docs/requirements.txt
-docs/setup.txt
-docs/tmp.txt
 examples/Makefile
 examples/nebexample.c
Index: /branches/neb_distrib_20081210/Nebulous-Server/bin/neb-admin
===================================================================
--- /branches/neb_distrib_20081210/Nebulous-Server/bin/neb-admin	(revision 23536)
+++ /branches/neb_distrib_20081210/Nebulous-Server/bin/neb-admin	(revision 23537)
@@ -23,4 +23,6 @@
     $dbpass,
     $dbuser,
+    $so_id_start,
+    $so_id_range,
     $limit,
     $pending,
@@ -41,4 +43,6 @@
     'pendingreplicate|r'    => \$pending,
     'pendingremoval'        => \$removal,
+    'so_id_start=i'         => \$so_id_start,
+    'so_id_range=i'         => \$so_id_range,
     'limit|l=i'             => \$limit,
     'verbose|v'             => \$verbose,
@@ -58,4 +62,5 @@
 
 # check to make sure that only one instance of neb-admin is running
+# XXX this implies we should move pending replicate elsewhere
 my $pidfile = '/var/tmp/neb-admin';
 # abort if an instance is already running
@@ -91,4 +96,22 @@
     $dbh->do("INSERT INTO myvolume SELECT * FROM volume");
 
+    if (not defined $so_id_start) { $so_id_start = 0; }
+    if (not defined $so_id_range) { $so_id_range = 100000; }
+    my $so_id_end = $so_id_start + $so_id_range;
+
+    # XXX check if so_id_start is beyond MAX(so_id): if so, exit with exit status 10
+    { 
+	my $query = $dbh->prepare("SELECT MAX(so_id) from storage_object");
+	$query->execute;
+        my $answer = $query->fetchrow_arrayref;
+        my $max_so_id = $$answer[0];
+	$query->finish;
+
+	if ($so_id_start > $max_so_id) { 
+	    print STDERR "at end of so_id range, reset please\n";
+	    exit 10;
+	}
+    }
+	
     my $query = $dbh->prepare(
     "        SELECT
@@ -111,5 +134,7 @@
                 USING(vol_id)
             WHERE mymountedvol.available = 1
-    --        WHERE storage_object_xattr.name = 'user.copies'
+            AND storage_object_xattr.name = 'user.copies'
+            AND storage_object.so_id >= $so_id_start
+            AND storage_object.so_id <  $so_id_end
             GROUP BY so_id
             HAVING available_instances < instances OR instances < copies
Index: /branches/neb_distrib_20081210/Nebulous-Server/bin/neb-voladd
===================================================================
--- /branches/neb_distrib_20081210/Nebulous-Server/bin/neb-voladd	(revision 23536)
+++ /branches/neb_distrib_20081210/Nebulous-Server/bin/neb-voladd	(revision 23537)
@@ -79,5 +79,5 @@
 =head1 SYNOPSIS
 
-    neb-voladd --volume <volume name> --uri <volume uri>
+    neb-voladd --vname <volume name> --vhost <host name> --uri <volume uri>
     [--db <database>] [--user <username>] [--pass <password>] [--host <hostname]
 
@@ -92,7 +92,11 @@
 =over 4
 
-=item * --volume <volume name>
+=item * --vname <volume name>
 
 Symbolic name of volume being added.
+
+=item * --vhost <host name>
+
+Name of the host that containes the C<URI> of the volume being added. 
 
 =item * --uri|-u <volume uri>
Index: anches/neb_distrib_20081210/Nebulous-Server/docs/c_api.h
===================================================================
--- /branches/neb_distrib_20081210/Nebulous-Server/docs/c_api.h	(revision 23536)
+++ 	(revision )
@@ -1,24 +1,0 @@
-/*
- * Copyright (C) 2004 Joshua Hoblitt
- *
- * $Id: c_api.h,v 1.1.1.1 2004-12-22 02:16:23 jhoblitt Exp $
- */
-
-// should there be an argument for "suggested" storage location?
-int add( const char *ext_id, const char *data, int size )
-
-int delete( const char *ext_id )
-
-// should there be an argument for "suggested" storage location?
-int update( const char *ext_id, const char *data, int size )
-
-// should there be an argument for "suggested" storage location?
-int move( const char *old_ext_id, const char *new_ext_id )
-
-// open returns a fd 
-int open( const char *ext_id )
-
-int close( const char *ext_id )
-
-// maybe open and close are a bad idea, perhaps...
-int read( const char *ext_id, char *data )
Index: /branches/neb_distrib_20081210/Nebulous-Server/docs/install.txt
===================================================================
--- /branches/neb_distrib_20081210/Nebulous-Server/docs/install.txt	(revision 23537)
+++ /branches/neb_distrib_20081210/Nebulous-Server/docs/install.txt	(revision 23537)
@@ -0,0 +1,373 @@
+=pod
+
+=head1 Installing a Nebulous Server
+
+=head2 Create a UNIX group for Nebulous
+
+All Nebulous user's need to be in the same UNIX group as the Nebulous server.
+
+    groupadd -g 877 nebulous
+
+This is a silly hack to generate a gid.
+
+    perl -le 'map { $gid += ord } split //, shift; print $gid' nebulous
+
+=head2 Add the users that you want to be able to access Nebulous to that group
+
+    usermod -G nebulous foouser
+
+=head2 Setup directory permissions for the nebulous group
+
+As root:
+
+    mkdir /data/ipp000.0/nebulous
+    mkdir /data/ipp000.1/nebulous
+    mkdir /data/ipp002.0/nebulous
+    mkdir /data/ipp003.0/nebulous
+
+    # note that this is just an example, this won't work over NFS with
+    # root_squash enablded
+    chown nobody:nebulous /data/ipp00?.?/nebulous
+
+    chmod 0770 /data/ipp00?.?/nebulous
+    ls -lad /data/ipp00?.?/nebulous
+
+    mkdir /data/`hostname`.0/nebulous
+    chown nobody:nebulous /data/`hostname`.0/nebulous
+    chmod 0770 /data/`hostname`.0/nebulous
+
+=head2 Build and install the Nebulous Perl modules (see: README)
+
+=head2 Create and initialize the database
+
+    mysql -u root mysql -p
+    (enter your MySQL root password)
+
+    DROP DATABASE IF EXISTS nebulous;
+    CREATE DATABASE nebulous;
+    GRANT ALL PRIVILEGES ON nebulous.* TO 'nebulous'@'localhost'
+        IDENTIFIED BY '@neb@';
+    FLUSH PRIVILEGES;
+    exit
+
+    export NEB_DB=nebulous
+    export NEB_USER=nebulous
+    export NEB_PASS=@neb@
+
+    neb-initdb
+    neb-addvol --name ipp000.0 --uri file:///data/ipp000.0/nebulous
+    neb-addvol --name ipp000.1 --uri file:///data/ipp000.1/nebulous
+    neb-addvol --name ipp002.0 --uri file:///data/ipp002.0/nebulous
+    neb-addvol --name ipp003.0 --uri file:///data/ipp003.0/nebulous
+
+#    perl -e 'for (1..24) { $i = sprintf "po%02d", $_ ; system "neb-addvol --name $i --uri file:/$i/nebulous" }'
+    .
+    .
+
+If you get an error similar to this while initializing the database it means
+that the creation of the Nebulous's stored procedures failed:
+
+    ERROR 1146 (42S02): Table 'mysql.proc' doesn't exist
+
+It probably means that your instance of MySQL has been upgraded for an older
+version that did not support stored precedures.  You can fix this by running
+the C<mysql_fix_privilege_tables> script that you should have received with
+MySQL.
+
+In order for the test suite to pass the "test" account needs have the
+appropriate permissions to create and modify stored procedures.
+
+    update user set Create_routine_priv = 'Y', Alter_routine_priv = 'Y', Execute_priv = 'Y' where user = 'test';
+    update db set Create_routine_priv = 'Y', Alter_routine_priv = 'Y', Execute_priv = 'Y' where db = 'test';
+
+    flush privileges;
+
+Please see this webpage for further details:
+
+http://dev.mysql.com/doc/refman/5.0/en/stored-procedure-privileges.html
+
+
+=head2 Setup nebdiskd daemon
+
+Create the .netdiskrc file in the home directory of the user that nebdiskd
+will run as.  This does not need to be/should not be the root user.  Merely a
+user with permissions to see all of the approparite volumes.
+
+Example F<.netdiskrc>:
+
+    db: nebulous
+    dbpass: '@neb@'
+    dbuser: nebulous
+    mounts:
+      - /data/ipp000.0
+      - /data/ipp000.1
+      - /data/ipp002.0
+      - /data/ipp003.0
+    pidfile: /var/tmp/nebdiskd
+    poll_interval: 5
+
+Start the daemon by running the command C<nebdiskd>.  Use this command to
+verify that the daemon is working correclty.
+
+    neb-volstat
+
+nebdiskd should always been running when nebulous is use.  This command is an
+example of how to startup the daemon running as another user.  This could be
+use to start the daemon from the root account while the system init scripts
+are running.
+
+    sudo -H -u jhoblitt nebdiskd
+
+See the nebdiskd POD/man page for further details
+
+=head2 Install Apache2 with mod_perl
+
+=head3 Minimum software requirements:
+
+Nebulous will work with both Apache 1.3 & 2.x.  However, this guide will only
+cover configuraton with Apache 2.x and mod_perl 2.x.
+
+=over 4
+
+=item * Apache2 >= 2.0.54
+
+=item * mod_perl >= 2.0.0
+
+=item * Apache::DBI >= '0.97' [*]
+
+=back
+
+[*] required for connect_on_init() to work under mod_perl >= 2.0.1
+
+=head3 Apache 1.x 
+
+=item * building mod_perl-1.29 from source:
+
+    perl Makefile.PL USE_APXS=1 INSTALLDIRS=vendor WITH_APXS=/usr/sbin/apxs EVERYTHING=1 PERL_DEBUG=1
+
+    #MP_TRACE = 1 MP_DEBUG = 1 MP_USE_DSO = 1 MP_INST_APACHE2 = 1 MP_APXS = /usr/sbin/apxs2
+
+=head3 Apache 2.x 
+
+=head4 removing Apache on RHEl3
+
+On RHEL3 I had to remove these packages in order to fully remove MySQL.
+
+As C<root>:
+
+    rpm -e \
+    httpd-2.0.46-46.2.ent \
+    httpd-devel-2.0.46-46.2.ent \
+    redhat-config-httpd-1.1.0-4.30.2 \
+    mod_perl-1.99_09-10.ent \
+    mod_authz_ldap-0.22-5 \
+    mod_auth_pgsql-2.0.1-4.ent \
+    mod_python-3.0.3-5.ent \
+    mod_auth_mysql-20030510-2.ent \
+    mod_ssl-2.0.46-46.2.ent \
+    webalizer-2.01_10-15.ent \
+    mod_perl-1.99_09-10.ent \
+    mod_auth_pgsql-2.0.1-4.ent \
+    mod_python-3.0.3-5.ent \
+    mod_auth_mysql-20030510-2.ent \
+    mod_ssl-2.0.46-46.2.ent \
+    squirrelmail-1.4.3a-11.EL3 \
+    php-4.3.2-25.ent \
+    php-imap-4.3.2-25.ent \
+    php-ldap-4.3.2-25.ent \
+    php-mysql-4.3.2-25.ent \
+    php-odbc-4.3.2-25.ent \
+    php-pgsql-4.3.2-25.ent 
+
+    rm -rf /etc/httpd
+
+=head4 installing Apache2 from source
+
+    tar -jxvf httpd-2.0.54.tar.bz2
+    cd httpd-2.0.54
+    ./configure \
+    --disable-suexec \
+    --with-perl=/usr/bin/perl \
+    --with-port=80 \
+    --with-program-name=apache2 \
+    --with-devrandom=/dev/urandom \
+    --prefix           /usr \
+    --exec_prefix      /usr \
+    --bindir           /usr/bin \
+    --sbindir          /usr/sbin \
+    --libdir           /usr/lib \
+    --libexecdir       /usr/lib/apache2/modules \
+    --mandir           /usr/share/man \
+    --infodir          /usr/share/info \
+    --includedir       /usr/include/apache2 \
+    --datadir          /var/www/localhost \
+    --sysconfdir       /etc/apache2/conf \
+    --localstatedir    /var
+    make
+    su
+    make install
+    exit
+    cd ..
+
+=head4 installing mod_perl 2.x
+
+    tar -zxvf mod_perl-2.0.1.tar.gz
+    cd mod_perl-2.0.1
+    perl Makefile.PL
+    (apxs = /usr/sbin/apxs)
+    make
+    su
+    make install
+    echo "LoadModule perl_module /usr/lib/apache2/modules/mod_perl.so" >> /etc/apache2/conf/apache2.conf
+    exit
+
+    apachectl configtest
+    Syntax OK
+
+=head4 installing and configuring Nebulous
+
+    tar -zvxf Nebulous-0.01.tar.gz
+    cd Nebulous-0.01
+    perl Build.PL -axps /usr/sbin/apxs
+    ./Build
+    ./Build test
+    su
+    ./Build install
+
+
+    vi /etc/apache2/conf/startup.pl
+    (change Group to "Group nebulous")
+
+    cp fooconf /etc/apache2/conf/startup.pl
+    echo "PerlPostConfigRequire /etc/apache2/conf/startup.pl" >> /etc/apache2/conf/apache2.conf
+
+
+=head3 configurating Apache to act as a Nebulous/SOAP server
+
+=item * add the "nebulous" group to /etc/group
+=item * configure Apache to run as the nebulous group. 
+
+    Group nebulous
+
+Note that in theory the C<perchild> MPM can run vhosts as differenet
+users/groups but this module is currently non-funcitonal.
+
+L<http://httpd.apache.org/docs/trunk/mod/perchild.html>
+
+=item * configure a path for Neublous
+
+Make sure mod_perl is being loaded into Apache.  In order to do this on
+gentoo, you must edit /etc/config.d/apache2 and add C<-DPERL> to
+C<APACHE2_OTPS>.
+
+    cat <<END >> /etc/apache2/conf/apache2.conf
+    <Location /nebulous>
+        SetHandler perl-script
+        PerlResponseHandler Apache2::SOAP
+        PerlSetVar dispatch_to "PerlHandler Nebulous::Server::SOAP"
+        PerlSetVar options "compress_threshold => 10000"
+    </Location>
+    END
+
+    apachectl configtest
+    Syntax OK
+
+=item * initialize Nebulous database connections from C<startup.pl>
+
+    my $dsn         = 'DBI:mysql:database=nebulous:host=localhost';
+    my $dbuser      = 'nebulous';
+    my $dbpasswd    = '@neb@';
+
+    Apache::DBI->connect_on_init( $dsn, $dbuser, $dbpasswd );
+    Nebulous::Server::SOAP->new_on_init(
+        dsn         => $dsn,
+        dbuser      => $dbuser,
+        dbpasswd    => $dbpasswd,
+        log_level   => 'all',
+    );
+
+# out of date from here down...
+
+#
+# CGI interface
+#
+
+ScriptAlias /nebulous /usr/local/apache/perl/imageserver.pl
+
+<Directory "/usr/local/apache/perl">
+        AllowOverride None
+        Options None
+        Order allow,deny
+        Allow from all
+</Directory>
+
+PerlModule Apache::PerlRun
+<Location /perl>
+        SetHandler perl-script
+        PerlHandler Apache::PerlRun
+        Options ExecCGI
+        allow from all
+        PerlSendHeader On
+</Location>
+
+#
+# Nebulous::Apache interface
+#
+
+PerlModule Apache::DBI
+PerlModule Nebulous::Apache
+
+<Location /nebulous>
+        SetHandler perl-script
+        PerlHandler Nebulous::Apache
+</Location>
+
+- setup the server
+
+As a CGI, will with either Apache 1.3.x or 2.0.x
+
+cp scripts/imagesever.pl /var/www/localhost/cgi-bin
+chmod 755 /var/www/localhost/cgi-bin/imageserver.pl
+touch /tmp/imageserver.log
+chmod 1777 /tmp/imageserver.log
+
+=head2 Testing the Nebulous Server
+
+You can test your configuration with the C<telnet> utility.
+
+    $ telnet localhost 80
+    Trying 127.0.0.1...
+    Connected to localhost.localdomain (127.0.0.1).
+    Escape character is '^]'.
+    GET /nebulous HTTP/1.0
+
+    HTTP/1.1 500 Internal Server Error
+    Date: Wed, 31 Aug 2005 01:10:47 GMT
+    Server: Apache/2.0.54 (Unix) mod_perl/2.0.1 Perl/v5.8.6
+    Content-Length: 645
+    Connection: close
+    Content-Type: text/html; charset=iso-8859-1
+    .
+    .
+
+    Connection closed by foreign host.
+
+If you get an HTTP 5xx code check your apache error_log
+
+
+=head2 MySQL Configuration/tuning
+
+in /etc/mysql/my.cnf
+under [mysqld]
+increase the number of alloweed connections, e.g.
+max_connections = 200
+
+Nebulous makes heavy use of MySQL's C<innodb> table type.  Try configuring
+MySQL with these parameters:
+
+    innodb_log_files_in_group = 2
+    innodb_buffer_pool_size = as big as possible (512M+)
+    innodb_log_buffer_size= 16M
+    innodb_log_file_size=10M
+
+=cut
Index: anches/neb_distrib_20081210/Nebulous-Server/docs/setup.txt
===================================================================
--- /branches/neb_distrib_20081210/Nebulous-Server/docs/setup.txt	(revision 23536)
+++ 	(revision )
@@ -1,373 +1,0 @@
-=pod
-
-=head1 Installing a Nebulous Server
-
-=head2 Create a UNIX group for Nebulous
-
-All Nebulous user's need to be in the same UNIX group as the Nebulous server.
-
-    groupadd -g 877 nebulous
-
-This is a silly hack to generate a gid.
-
-    perl -le 'map { $gid += ord } split //, shift; print $gid' nebulous
-
-=head2 Add the users that you want to be able to access Nebulous to that group
-
-    usermod -G nebulous foouser
-
-=head2 Setup directory permissions for the nebulous group
-
-As root:
-
-    mkdir /data/ipp000.0/nebulous
-    mkdir /data/ipp000.1/nebulous
-    mkdir /data/ipp002.0/nebulous
-    mkdir /data/ipp003.0/nebulous
-
-    # note that this is just an example, this won't work over NFS with
-    # root_squash enablded
-    chown nobody:nebulous /data/ipp00?.?/nebulous
-
-    chmod 0770 /data/ipp00?.?/nebulous
-    ls -lad /data/ipp00?.?/nebulous
-
-    mkdir /data/`hostname`.0/nebulous
-    chown nobody:nebulous /data/`hostname`.0/nebulous
-    chmod 0770 /data/`hostname`.0/nebulous
-
-=head2 Build and install the Nebulous Perl modules (see: README)
-
-=head2 Create and initialize the database
-
-    mysql -u root mysql -p
-    (enter your MySQL root password)
-
-    DROP DATABASE IF EXISTS nebulous;
-    CREATE DATABASE nebulous;
-    GRANT ALL PRIVILEGES ON nebulous.* TO 'nebulous'@'localhost'
-        IDENTIFIED BY '@neb@';
-    FLUSH PRIVILEGES;
-    exit
-
-    export NEB_DB=nebulous
-    export NEB_USER=nebulous
-    export NEB_PASS=@neb@
-
-    neb-initdb
-    neb-addvol --name ipp000.0 --uri file:///data/ipp000.0/nebulous
-    neb-addvol --name ipp000.1 --uri file:///data/ipp000.1/nebulous
-    neb-addvol --name ipp002.0 --uri file:///data/ipp002.0/nebulous
-    neb-addvol --name ipp003.0 --uri file:///data/ipp003.0/nebulous
-
-#    perl -e 'for (1..24) { $i = sprintf "po%02d", $_ ; system "neb-addvol --name $i --uri file:/$i/nebulous" }'
-    .
-    .
-
-If you get an error similar to this while initializing the database it means
-that the creation of the Nebulous's stored procedures failed:
-
-    ERROR 1146 (42S02): Table 'mysql.proc' doesn't exist
-
-It probably means that your instance of MySQL has been upgraded for an older
-version that did not support stored precedures.  You can fix this by running
-the C<mysql_fix_privilege_tables> script that you should have received with
-MySQL.
-
-In order for the test suite to pass the "test" account needs have the
-appropriate permissions to create and modify stored procedures.
-
-    update user set Create_routine_priv = 'Y', Alter_routine_priv = 'Y', Execute_priv = 'Y' where user = 'test';
-    update db set Create_routine_priv = 'Y', Alter_routine_priv = 'Y', Execute_priv = 'Y' where db = 'test';
-
-    flush privileges;
-
-Please see this webpage for further details:
-
-http://dev.mysql.com/doc/refman/5.0/en/stored-procedure-privileges.html
-
-
-=head2 Setup nebdiskd daemon
-
-Create the .netdiskrc file in the home directory of the user that nebdiskd
-will run as.  This does not need to be/should not be the root user.  Merely a
-user with permissions to see all of the approparite volumes.
-
-Example F<.netdiskrc>:
-
-    db: nebulous
-    dbpass: '@neb@'
-    dbuser: nebulous
-    mounts:
-      - /data/ipp000.0
-      - /data/ipp000.1
-      - /data/ipp002.0
-      - /data/ipp003.0
-    pidfile: /var/tmp/nebdiskd
-    poll_interval: 5
-
-Start the daemon by running the command C<nebdiskd>.  Use this command to
-verify that the daemon is working correclty.
-
-    neb-volstat
-
-nebdiskd should always been running when nebulous is use.  This command is an
-example of how to startup the daemon running as another user.  This could be
-use to start the daemon from the root account while the system init scripts
-are running.
-
-    sudo -H -u jhoblitt nebdiskd
-
-See the nebdiskd POD/man page for further details
-
-=head2 Install Apache2 with mod_perl
-
-=head3 Minimum software requirements:
-
-Nebulous will work with both Apache 1.3 & 2.x.  However, this guide will only
-cover configuraton with Apache 2.x and mod_perl 2.x.
-
-=over 4
-
-=item * Apache2 >= 2.0.54
-
-=item * mod_perl >= 2.0.0
-
-=item * Apache::DBI >= '0.97' [*]
-
-=back
-
-[*] required for connect_on_init() to work under mod_perl >= 2.0.1
-
-=head3 Apache 1.x 
-
-=item * building mod_perl-1.29 from source:
-
-    perl Makefile.PL USE_APXS=1 INSTALLDIRS=vendor WITH_APXS=/usr/sbin/apxs EVERYTHING=1 PERL_DEBUG=1
-
-    #MP_TRACE = 1 MP_DEBUG = 1 MP_USE_DSO = 1 MP_INST_APACHE2 = 1 MP_APXS = /usr/sbin/apxs2
-
-=head3 Apache 2.x 
-
-=head4 removing Apache on RHEl3
-
-On RHEL3 I had to remove these packages in order to fully remove MySQL.
-
-As C<root>:
-
-    rpm -e \
-    httpd-2.0.46-46.2.ent \
-    httpd-devel-2.0.46-46.2.ent \
-    redhat-config-httpd-1.1.0-4.30.2 \
-    mod_perl-1.99_09-10.ent \
-    mod_authz_ldap-0.22-5 \
-    mod_auth_pgsql-2.0.1-4.ent \
-    mod_python-3.0.3-5.ent \
-    mod_auth_mysql-20030510-2.ent \
-    mod_ssl-2.0.46-46.2.ent \
-    webalizer-2.01_10-15.ent \
-    mod_perl-1.99_09-10.ent \
-    mod_auth_pgsql-2.0.1-4.ent \
-    mod_python-3.0.3-5.ent \
-    mod_auth_mysql-20030510-2.ent \
-    mod_ssl-2.0.46-46.2.ent \
-    squirrelmail-1.4.3a-11.EL3 \
-    php-4.3.2-25.ent \
-    php-imap-4.3.2-25.ent \
-    php-ldap-4.3.2-25.ent \
-    php-mysql-4.3.2-25.ent \
-    php-odbc-4.3.2-25.ent \
-    php-pgsql-4.3.2-25.ent 
-
-    rm -rf /etc/httpd
-
-=head4 installing Apache2 from source
-
-    tar -jxvf httpd-2.0.54.tar.bz2
-    cd httpd-2.0.54
-    ./configure \
-    --disable-suexec \
-    --with-perl=/usr/bin/perl \
-    --with-port=80 \
-    --with-program-name=apache2 \
-    --with-devrandom=/dev/urandom \
-    --prefix           /usr \
-    --exec_prefix      /usr \
-    --bindir           /usr/bin \
-    --sbindir          /usr/sbin \
-    --libdir           /usr/lib \
-    --libexecdir       /usr/lib/apache2/modules \
-    --mandir           /usr/share/man \
-    --infodir          /usr/share/info \
-    --includedir       /usr/include/apache2 \
-    --datadir          /var/www/localhost \
-    --sysconfdir       /etc/apache2/conf \
-    --localstatedir    /var
-    make
-    su
-    make install
-    exit
-    cd ..
-
-=head4 installing mod_perl 2.x
-
-    tar -zxvf mod_perl-2.0.1.tar.gz
-    cd mod_perl-2.0.1
-    perl Makefile.PL
-    (apxs = /usr/sbin/apxs)
-    make
-    su
-    make install
-    echo "LoadModule perl_module /usr/lib/apache2/modules/mod_perl.so" >> /etc/apache2/conf/apache2.conf
-    exit
-
-    apachectl configtest
-    Syntax OK
-
-=head4 installing and configuring Nebulous
-
-    tar -zvxf Nebulous-0.01.tar.gz
-    cd Nebulous-0.01
-    perl Build.PL -axps /usr/sbin/apxs
-    ./Build
-    ./Build test
-    su
-    ./Build install
-
-
-    vi /etc/apache2/conf/startup.pl
-    (change Group to "Group nebulous")
-
-    cp fooconf /etc/apache2/conf/startup.pl
-    echo "PerlPostConfigRequire /etc/apache2/conf/startup.pl" >> /etc/apache2/conf/apache2.conf
-
-
-=head3 configurating Apache to act as a Nebulous/SOAP server
-
-=item * add the "nebulous" group to /etc/group
-=item * configure Apache to run as the nebulous group. 
-
-    Group nebulous
-
-Note that in theory the C<perchild> MPM can run vhosts as differenet
-users/groups but this module is currently non-funcitonal.
-
-L<http://httpd.apache.org/docs/trunk/mod/perchild.html>
-
-=item * configure a path for Neublous
-
-Make sure mod_perl is being loaded into Apache.  In order to do this on
-gentoo, you must edit /etc/config.d/apache2 and add C<-DPERL> to
-C<APACHE2_OTPS>.
-
-    cat <<END >> /etc/apache2/conf/apache2.conf
-    <Location /nebulous>
-        SetHandler perl-script
-        PerlResponseHandler Apache2::SOAP
-        PerlSetVar dispatch_to "PerlHandler Nebulous::Server::SOAP"
-        PerlSetVar options "compress_threshold => 10000"
-    </Location>
-    END
-
-    apachectl configtest
-    Syntax OK
-
-=item * initialize Nebulous database connections from C<startup.pl>
-
-    my $dsn         = 'DBI:mysql:database=nebulous:host=localhost';
-    my $dbuser      = 'nebulous';
-    my $dbpasswd    = '@neb@';
-
-    Apache::DBI->connect_on_init( $dsn, $dbuser, $dbpasswd );
-    Nebulous::Server::SOAP->new_on_init(
-        dsn         => $dsn,
-        dbuser      => $dbuser,
-        dbpasswd    => $dbpasswd,
-        log_level   => 'all',
-    );
-
-# out of date from here down...
-
-#
-# CGI interface
-#
-
-ScriptAlias /nebulous /usr/local/apache/perl/imageserver.pl
-
-<Directory "/usr/local/apache/perl">
-        AllowOverride None
-        Options None
-        Order allow,deny
-        Allow from all
-</Directory>
-
-PerlModule Apache::PerlRun
-<Location /perl>
-        SetHandler perl-script
-        PerlHandler Apache::PerlRun
-        Options ExecCGI
-        allow from all
-        PerlSendHeader On
-</Location>
-
-#
-# Nebulous::Apache interface
-#
-
-PerlModule Apache::DBI
-PerlModule Nebulous::Apache
-
-<Location /nebulous>
-        SetHandler perl-script
-        PerlHandler Nebulous::Apache
-</Location>
-
-- setup the server
-
-As a CGI, will with either Apache 1.3.x or 2.0.x
-
-cp scripts/imagesever.pl /var/www/localhost/cgi-bin
-chmod 755 /var/www/localhost/cgi-bin/imageserver.pl
-touch /tmp/imageserver.log
-chmod 1777 /tmp/imageserver.log
-
-=head2 Testing the Nebulous Server
-
-You can test your configuration with the C<telnet> utility.
-
-    $ telnet localhost 80
-    Trying 127.0.0.1...
-    Connected to localhost.localdomain (127.0.0.1).
-    Escape character is '^]'.
-    GET /nebulous HTTP/1.0
-
-    HTTP/1.1 500 Internal Server Error
-    Date: Wed, 31 Aug 2005 01:10:47 GMT
-    Server: Apache/2.0.54 (Unix) mod_perl/2.0.1 Perl/v5.8.6
-    Content-Length: 645
-    Connection: close
-    Content-Type: text/html; charset=iso-8859-1
-    .
-    .
-
-    Connection closed by foreign host.
-
-If you get an HTTP 5xx code check your apache error_log
-
-
-=head2 MySQL Configuration/tuning
-
-in /etc/mysql/my.cnf
-under [mysqld]
-increase the number of alloweed connections, e.g.
-max_connections = 200
-
-Nebulous makes heavy use of MySQL's C<innodb> table type.  Try configuring
-MySQL with these parameters:
-
-    innodb_log_files_in_group = 2
-    innodb_buffer_pool_size = as big as possible (512M+)
-    innodb_log_buffer_size= 16M
-    innodb_log_file_size=10M
-
-=cut
Index: anches/neb_distrib_20081210/Nebulous-Server/docs/tmp.txt
===================================================================
--- /branches/neb_distrib_20081210/Nebulous-Server/docs/tmp.txt	(revision 23536)
+++ 	(revision )
@@ -1,44 +1,0 @@
-client
---
-new
-
-new_file
-                new_file
-                open_file
-
-find_file
-    find_instance
-
-open_file
-    find_file
-
-delete_file
-                delete_file
-    nuke
-
-copy_file
-    open_file
-    new_file
-
-move_file
-    copy_file
-    delete_file
-
-replicate_file
-    open_file
-                replicate_file
-
-cull_file
-    find_instance
-    delete_instance
-
-delete_instance
-    nuke
-                delete_instance
-
-find_instance
-                find_instance
-
-# missing
-file_stat
-lock ?
Index: /branches/neb_distrib_20081210/Nebulous-Server/lib/Nebulous/Server.pm
===================================================================
--- /branches/neb_distrib_20081210/Nebulous-Server/lib/Nebulous/Server.pm	(revision 23536)
+++ /branches/neb_distrib_20081210/Nebulous-Server/lib/Nebulous/Server.pm	(revision 23537)
@@ -1,5 +1,5 @@
 # Copyright (c) 2004-2008  Joshua Hoblitt
 #
-# $Id: Server.pm,v 1.93.6.1 2008-12-14 22:52:37 eugene Exp $
+# $Id: Server.pm,v 1.96 2008-12-14 22:54:25 eugene Exp $
 
 package Nebulous::Server;
@@ -176,72 +176,79 @@
 
     my $uri;
-    eval {
-        {
-            # create storage_object
-            my $query = $db->prepare_cached( $sql->new_object ); 
-            $query->execute('NULL', $key->path);
-        }
-
-        my $so_id;
-        {
-            # get object ID
-            my $query = $db->prepare_cached( $sql->last_insert_id );
-            $query->execute;
-            ($so_id) = $query->fetchrow_array;
-            # XXX finish seems to be required when using LAST_INSERT_ID() or we
-            # get a warning about the stmt handling still be active the next
-            # time LAST_INSERT_ID() is invoked
-            $query->finish;
-        }
-
-        {
-            # create storage_object_attr
-            my $query = $db->prepare_cached( $sql->new_object_attr ); 
-            $query->execute($so_id);
-        }
-
-        {
-            
-            # create instance with no URI
+TRANS: while (1) {
+        eval {
+            {
+                # create storage_object
+                my $query = $db->prepare_cached( $sql->new_object ); 
+                $query->execute('NULL', $key->path);
+            }
+
+            my $so_id;
+            {
+                # get object ID
+                my $query = $db->prepare_cached( $sql->last_insert_id );
+                $query->execute;
+                ($so_id) = $query->fetchrow_array;
+                # XXX finish seems to be required when using LAST_INSERT_ID() or we
+                # get a warning about the stmt handling still be active the next
+                # time LAST_INSERT_ID() is invoked
+                $query->finish;
+            }
+
+            {
+                # create storage_object_attr
+                my $query = $db->prepare_cached( $sql->new_object_attr ); 
+                $query->execute($so_id);
+            }
+
+            {
+                
+                # create instance with no URI
 #            my $query = $db->prepare_cached( $sql->new_instance );
-            my $query = $db->prepare_cached( $sql->new_object_instance );
-            $query->execute($vol_id);
-        }
-
-        my $ins_id;
-        {
-            # get instance ID
-            my $query = $db->prepare_cached( $sql->last_insert_id );
-            $query->execute;
-            ($ins_id) = $query->fetchrow_array;
-            # XXX finish seems to be required when using LAST_INSERT_ID() or we
-            # get a warning about the stmt handling still be active the next
-            # time LAST_INSERT_ID() is invoked
-            $query->finish;
-        }
-
-        # Unfortunately, since we want to use the instance row's ID as part of the
-        # actual on disk file name we can't try to create the file until after
-        # we've create both a new storage_storage object and instance.
-
-        # TODO add some stuff here to retry if unsucessful
-        $uri = $self->_create_empty_instance_file($key->path, $so_id, $ins_id, $vol_path, $vol_xattr);
-        $log->debug("created $uri on volume ID: $vol_id");
-
-        {
-            # update the instance with URI & vol_id that the file is on
-            my $query = $db->prepare_cached( $sql->update_instance_uri );
-            # vol_id, uri, ins_id
-            $query->execute($vol_id, "$uri", $ins_id);
-        }
-
-        $db->commit;
-        $log->debug("commit");
-    };
-    if ($@) {
+                my $query = $db->prepare_cached( $sql->new_object_instance );
+                $query->execute($vol_id);
+            }
+
+            my $ins_id;
+            {
+                # get instance ID
+                my $query = $db->prepare_cached( $sql->last_insert_id );
+                $query->execute;
+                ($ins_id) = $query->fetchrow_array;
+                # XXX finish seems to be required when using LAST_INSERT_ID() or we
+                # get a warning about the stmt handling still be active the next
+                # time LAST_INSERT_ID() is invoked
+                $query->finish;
+            }
+
+            # Unfortunately, since we want to use the instance row's ID as part of the
+            # actual on disk file name we can't try to create the file until after
+            # we've create both a new storage_storage object and instance.
+
+            # TODO add some stuff here to retry if unsucessful
+            $uri = $self->_create_empty_instance_file($key->path, $so_id, $ins_id, $vol_path, $vol_xattr);
+            $log->debug("created $uri on volume ID: $vol_id");
+
+            {
+                # update the instance with URI & vol_id that the file is on
+                my $query = $db->prepare_cached( $sql->update_instance_uri );
+                # vol_id, uri, ins_id
+                $query->execute($vol_id, "$uri", $ins_id);
+            }
+
+            $db->commit;
+            $log->debug("commit");
+        };
+        if ($@) {
 #        and not $key->soft_volume
-        $db->rollback;
-        $log->debug("rollback");
-        $log->logdie("error: $@");
+            $db->rollback;
+            $log->debug("rollback");
+            if ($@ =~ /Deadlock found/) {
+                $log->warn("database deadlock retrying transaction: $@");
+                redo TRANS;
+            }
+            $log->logdie("error: $@");
+        }
+        last;
     }
 
@@ -282,24 +289,31 @@
     $newkey = parse_neb_key($newkey);
 
-    eval {
-        # rename storage_object
-        my $query = $db->prepare_cached($sql->rename_object); 
-        # this SQL statment takes the new key name as the first param
-        my $rows = $query->execute($newkey->path, $key->path);
-
-        # if we affected more then one row something very bad has happened.
-        unless ($rows == 1) {
-            $query->finish;
-            $log->logdie("affected row count is $rows instead of 1");
-        }
-
-        $db->commit;
-        $log->debug("commit");
-    };
-    if ($@) {
-        $db->rollback;
-        $log->debug("rollback");
-        $log->logdie("database error: $@");
-    }
+TRANS: while (1) {
+        eval {
+            # rename storage_object
+            my $query = $db->prepare_cached($sql->rename_object); 
+            # this SQL statment takes the new key name as the first param
+            my $rows = $query->execute($newkey->path, $key->path);
+
+            # if we affected more then one row something very bad has happened.
+            unless ($rows == 1) {
+                $query->finish;
+                $log->logdie("affected row count is $rows instead of 1");
+            }
+
+            $db->commit;
+            $log->debug("commit");
+        };
+        if ($@) {
+            $db->rollback;
+            $log->debug("rollback");
+            if ($@ =~ /Deadlock found/) {
+                $log->warn("database deadlock retrying transaction: $@");
+                redo TRANS;
+            }
+            $log->logdie("database error: $@");
+        }
+        last;
+    } 
 
     $log->debug("leaving");
@@ -462,5 +476,70 @@
 #     return 1;
 # }
-
+# 
+
+# ### this block below comes from the single branch, and implements
+#      the transactions in the new way (two passes?).  the above handles
+#      the case of multiple dbs
+# 
+# TRANS: while (1) {
+#         eval {
+#             {
+#                 # key1 -> key1.swap
+#                 my $query = $db->prepare_cached($sql->rename_object); 
+#                 # this SQL statment takes the new key name as the first param
+#                 my $rows = $query->execute($key1->path . ".swap", $key1->path);
+# 
+#                 # if we affected more then one row something very bad has happened.
+#                 unless ($rows == 1) {
+#                     $query->finish;
+#                     $log->logdie("affected row count is $rows instead of 1");
+#                 }
+#             }
+# 
+#             {
+#                 # key2 -> key1
+#                 my $query = $db->prepare_cached($sql->rename_object); 
+#                 # this SQL statment takes the new key name as the first param
+#                 my $rows = $query->execute($key1->path, $key2->path);
+# 
+#                 # if we affected more then one row something very bad has happened.
+#                 unless ($rows == 1) {
+#                     $query->finish;
+#                     $log->logdie("affected row count is $rows instead of 1");
+#                 }
+#             }
+# 
+#             {
+#                 # key1.swap -> key2
+#                 my $query = $db->prepare_cached($sql->rename_object); 
+#                 # this SQL statment takes the new key name as the first param
+#                 my $rows = $query->execute($key2->path, $key1->path . ".swap");
+# 
+#                 # if we affected more then one row something very bad has happened.
+#                 unless ($rows == 1) {
+#                     $query->finish;
+#                     $log->logdie("affected row count is $rows instead of 1");
+#                 }
+#             }
+# 
+#             $db->commit;
+#             $log->debug("commit");
+#         };
+#         if ($@) {
+#             $db->rollback;
+#             $log->debug("rollback");
+#             if ($@ =~ /Deadlock found/) {
+#                 $log->warn("database deadlock retrying transaction: $@");
+#                 redo TRANS;
+#             }
+#             $log->logdie("database error: $@");
+#         }
+#         last;
+#     }
+# 
+#     $log->debug("leaving");
+# 
+#     return 1;
+# }
 
 sub replicate_object
@@ -527,61 +606,68 @@
 
     my $uri;
-    eval {
-        my $so_id;
-        {
-            # verify that at least one instance is currently available
-            my $query = $db->prepare_cached( $sql->get_object_instances );
-            my $rows = $query->execute($key->path, 1);
-
-            unless ( $rows > 0 ) {
+TRANS: while (1) {
+        eval {
+            my $so_id;
+            {
+                # verify that at least one instance is currently available
+                my $query = $db->prepare_cached( $sql->get_object_instances );
+                my $rows = $query->execute($key->path, 1);
+
+                unless ( $rows > 0 ) {
+                    $query->finish;
+                    $log->logdie( "storage object does not exist" );
+                }
+
+                $so_id = $query->fetchrow_hashref->{ 'so_id' };
                 $query->finish;
-                $log->logdie( "storage object does not exist" );
-            }
-
-            $so_id = $query->fetchrow_hashref->{ 'so_id' };
-            $query->finish;
-        }
-
-        {
-            my $query = $db->prepare_cached( $sql->new_instance );
-            $query->execute($so_id, $vol_id);
-        }
-
-        my $ins_id;
-        {
-            my $query = $db->prepare_cached( $sql->last_insert_id );
-            $query->execute();
-            ($ins_id) = $query->fetchrow_array;
-            # XXX finish seems to be required when using LAST_INSERT_ID() or we
-            # get a warning about the stmt handling still being active the next
-            # time LAST_INSERT_ID() is invoked
-            $query->finish;
-        }
-
-        # Unfortunately, since we want to use the instance row's ID as part of
-        # the actual on disk file name we can't try to create the file until
-        # after we've create both a new storage_storage object and instance.
-
-        # TODO add some stuff here to retry if unsucessful
-        $uri = $self->_create_empty_instance_file($key->path, $so_id, $ins_id, $vol_path, $vol_xattr);
-
-        {
-            my $query = $db->prepare_cached( $sql->update_instance_uri );
-            # vol_id, uri, ins_id
-            $query->execute($vol_id, $uri, $ins_id);
-        }
-
-        $db->commit;
-        $log->debug("commit");
-    };
-    if ($@) {
-        $db->rollback;
-        # handle soft volumes
-        if (defined $vol_name and defined $key->soft_volume) {
-            $log->debug("retrying with 'any' volume");
-            return $self->replicate_object($key->path, 'any');
-        }
-        $log->debug("rollback");
-        $log->logdie("error: $@");
+            }
+
+            {
+                my $query = $db->prepare_cached( $sql->new_instance );
+                $query->execute($so_id, $vol_id);
+            }
+
+            my $ins_id;
+            {
+                my $query = $db->prepare_cached( $sql->last_insert_id );
+                $query->execute();
+                ($ins_id) = $query->fetchrow_array;
+                # XXX finish seems to be required when using LAST_INSERT_ID() or we
+                # get a warning about the stmt handling still being active the next
+                # time LAST_INSERT_ID() is invoked
+                $query->finish;
+            }
+
+            # Unfortunately, since we want to use the instance row's ID as part of
+            # the actual on disk file name we can't try to create the file until
+            # after we've create both a new storage_storage object and instance.
+
+            # TODO add some stuff here to retry if unsucessful
+            $uri = $self->_create_empty_instance_file($key->path, $so_id, $ins_id, $vol_path, $vol_xattr);
+
+            {
+                my $query = $db->prepare_cached( $sql->update_instance_uri );
+                # vol_id, uri, ins_id
+                $query->execute($vol_id, $uri, $ins_id);
+            }
+
+            $db->commit;
+            $log->debug("commit");
+        };
+        if ($@) {
+            $db->rollback;
+            # handle soft volumes
+            if (defined $vol_name and defined $key->soft_volume) {
+                $log->debug("retrying with 'any' volume");
+                return $self->replicate_object($key->path, 'any');
+            }
+            $log->debug("rollback");
+            if ($@ =~ /Deadlock found/) {
+                $log->warn("database deadlock retrying transaction: $@");
+                redo TRANS;
+            }
+            $log->logdie("error: $@");
+        }
+        last;
     }
 
@@ -624,67 +710,74 @@
     my $write_lock;
 
-    eval {
-        {
-            # this will set update locks
-            my $query = $db->prepare_cached( $sql->get_object_locks );
-            my $rows = $query->execute( $key->path );
-            unless ( $rows == 1 ) {
+TRANS: while (1) {
+        eval {
+            {
+                # this will set update locks
+                my $query = $db->prepare_cached( $sql->get_object_locks );
+                my $rows = $query->execute( $key->path );
+                unless ( $rows == 1 ) {
+                    $query->finish;
+                    $log->logdie( "storage object does not exist" );
+                }
+
+                my $row = $query->fetchrow_hashref;
                 $query->finish;
-                $log->logdie( "storage object does not exist" );
-            }
-
-            my $row = $query->fetchrow_hashref;
-            $query->finish;
-
-            $so_id      = $row->{ 'so_id' };
-            $read_lock  = $row->{ 'read_lock' };
-            $write_lock = $row->{ 'write_lock' };
-        }
-
-        if ($type eq 'write') {
-            # can't set a write lock twice and
-            # can't set a write lock if there are read locks
-            if ($write_lock) {
-                $log->logdie("can not write lock twice -- retry");
-            }
-            
-            if ($read_lock > 0) {
-                $log->logdie("can not write lock after read lock -- retry");
-            }
-
-            {
-                my $query = $db->prepare_cached( $sql->set_write_lock );
-                my $rows = $query->execute($key->path);
-            
-                # if we affected more then one row something very bad has happened.
-                unless ($rows == 1) {
-                    $log->logdie("affected row count is $rows instead of 1");
-                }
-
-            }
-        } elsif ($type eq 'read') {
-            # can't set a read lock if there's a write lock
-            if ($write_lock) {
-                $log->logdie("can not read lock after write lock -- retry");
-            }
-
-            {
-                my $query = $db->prepare_cached( $sql->increment_read_lock );
-                my $rows = $query->execute($key->path);
-            
-                # if we affected more then one row something very bad has happened.
-                unless ($rows == 1) {
-                    $log->logdie("affected row count is $rows instead of 1");
-                }
-            }
-        }
-
-        $db->commit;
-        $log->debug("commit");
-    };
-    if ($@) {
-        $db->rollback;
-        $log->debug("rollback");
-        $log->logdie("error: $@");
+
+                $so_id      = $row->{ 'so_id' };
+                $read_lock  = $row->{ 'read_lock' };
+                $write_lock = $row->{ 'write_lock' };
+            }
+
+            if ($type eq 'write') {
+                # can't set a write lock twice and
+                # can't set a write lock if there are read locks
+                if ($write_lock) {
+                    $log->logdie("can not write lock twice -- retry");
+                }
+                
+                if ($read_lock > 0) {
+                    $log->logdie("can not write lock after read lock -- retry");
+                }
+
+                {
+                    my $query = $db->prepare_cached( $sql->set_write_lock );
+                    my $rows = $query->execute($key->path);
+                
+                    # if we affected more then one row something very bad has happened.
+                    unless ($rows == 1) {
+                        $log->logdie("affected row count is $rows instead of 1");
+                    }
+
+                }
+            } elsif ($type eq 'read') {
+                # can't set a read lock if there's a write lock
+                if ($write_lock) {
+                    $log->logdie("can not read lock after write lock -- retry");
+                }
+
+                {
+                    my $query = $db->prepare_cached( $sql->increment_read_lock );
+                    my $rows = $query->execute($key->path);
+                
+                    # if we affected more then one row something very bad has happened.
+                    unless ($rows == 1) {
+                        $log->logdie("affected row count is $rows instead of 1");
+                    }
+                }
+            }
+
+            $db->commit;
+            $log->debug("commit");
+        };
+        if ($@) {
+            $db->rollback;
+            $log->debug("rollback");
+            if ($@ =~ /Deadlock found/) {
+                $log->warn("database deadlock retrying transaction: $@");
+                redo TRANS;
+            }
+            $log->logdie("error: $@");
+        }
+        last;
     }
 
@@ -727,70 +820,77 @@
     my $write_lock;
 
-    eval {
-        {
-            # this will set update locks
-            my $query = $db->prepare_cached( $sql->get_object_locks );
-            my $rows = $query->execute($key->path);
-            unless ($rows == 1) {
+TRANS: while (1) {
+        eval {
+            {
+                # this will set update locks
+                my $query = $db->prepare_cached( $sql->get_object_locks );
+                my $rows = $query->execute($key->path);
+                unless ($rows == 1) {
+                    $query->finish;
+                    $log->logdie("storage object does not exist");
+                }
+
+                my $row = $query->fetchrow_hashref;
                 $query->finish;
-                $log->logdie("storage object does not exist");
-            }
-
-            my $row = $query->fetchrow_hashref;
-            $query->finish;
-
-            $so_id      = $row->{ 'so_id' };
-            $read_lock  = $row->{ 'read_lock' };
-            $write_lock = $row->{ 'write_lock' };
-        }
-
-        if ($type eq 'write') {
-            # can't remove a write lock if it doesn't exist
-            if ($read_lock) {
-                $log->logdie("can not have a write lock under a read lock");
-            }
-
-            unless ($write_lock) {
-                $log->logdie("can not remove non-existant write lock");
-            }
-
-            {
-                my $query = $db->prepare_cached( $sql->delete_write_lock );
-                my $rows = $query->execute($key->path);
-            
-                # if we affected more then one row something very bad has happened.
-                unless ($rows == 1) {
-                    $log->logdie("affected row count is $rows instead of 1");
-                }
-            }
-        } elsif ($type eq 'read') {
-            # can't remove a read lock if there's a write lock and
-            # can't remove a read lock if there aren't any
-            if ($write_lock) {
-                $log->logdie("can not have a read lock under a write lock");
-            }
-               
-            if ($read_lock == 0) {
-                $log->logdie("can not remove non-existant read lock");
-            }
-
-            {
-                my $query = $db->prepare_cached( $sql->decrement_read_lock );
-                my $rows = $query->execute($key->path);
-            
-                # if we affected more then one row something very bad has happened.
-                unless ($rows == 1) {
-                    $log->logdie("affected row count is $rows instead of 1");
-                }
-
-            }
-        }
-        $db->commit;
-        $log->debug("commit");
-    };
-    if ($@) {
-        $db->rollback;
-        $log->debug("rollback");
-        $log->logdie("error: $@");
+
+                $so_id      = $row->{ 'so_id' };
+                $read_lock  = $row->{ 'read_lock' };
+                $write_lock = $row->{ 'write_lock' };
+            }
+
+            if ($type eq 'write') {
+                # can't remove a write lock if it doesn't exist
+                if ($read_lock) {
+                    $log->logdie("can not have a write lock under a read lock");
+                }
+
+                unless ($write_lock) {
+                    $log->logdie("can not remove non-existant write lock");
+                }
+
+                {
+                    my $query = $db->prepare_cached( $sql->delete_write_lock );
+                    my $rows = $query->execute($key->path);
+                
+                    # if we affected more then one row something very bad has happened.
+                    unless ($rows == 1) {
+                        $log->logdie("affected row count is $rows instead of 1");
+                    }
+                }
+            } elsif ($type eq 'read') {
+                # can't remove a read lock if there's a write lock and
+                # can't remove a read lock if there aren't any
+                if ($write_lock) {
+                    $log->logdie("can not have a read lock under a write lock");
+                }
+                   
+                if ($read_lock == 0) {
+                    $log->logdie("can not remove non-existant read lock");
+                }
+
+                {
+                    my $query = $db->prepare_cached( $sql->decrement_read_lock );
+                    my $rows = $query->execute($key->path);
+                
+                    # if we affected more then one row something very bad has happened.
+                    unless ($rows == 1) {
+                        $log->logdie("affected row count is $rows instead of 1");
+                    }
+
+                }
+            }
+            $db->commit;
+            $log->debug("commit");
+        };
+        if ($@) {
+            $db->rollback;
+            $log->debug("rollback");
+            if ($@ =~ /Deadlock found/) {
+                $log->warn("database deadlock retrying transaction: $@");
+                redo TRANS;
+            }
+            $log->logdie("error: $@");
+        }
+        last;
     }
 
@@ -835,38 +935,45 @@
     $key = parse_neb_key($key);
 
-    eval {
-        my $query;
-
-        if ($flags eq 'create') {
-            $query = $db->prepare_cached( $sql->new_object_xattr );
-        } else {
-            # replace
-            $query = $db->prepare_cached( $sql->replace_object_xattr );
-        }
-
-        # name, value, ext_id
-        my $rows = $query->execute($name, $value, $key->path);
-        $query->finish;
-
-        # if we affected more then one row something very bad has happened.
-        if ($flags eq 'create') {
-            unless ($rows == 1) {
-                $log->logdie( "affected row count is $rows instead of 1" );
-            }
-        } else {
-            # replace_object_xattr can effect either 1 or 2 rows.  2 rows in
-            # the case of a replace and 1 if the xattr didn't already exist.
-            unless ($rows == 1 or $rows == 2) {
-                $log->logdie( "affected row count is $rows instead of 2" );
-            }
-        }
-
-        $db->commit;
-        $log->debug("commit");
-    };
-    if ($@) {
-        $db->rollback;
-        $log->debug("rollback");
-        $log->logdie("database error: $@");
+TRANS: while (1) {
+        eval {
+            my $query;
+
+            if ($flags eq 'create') {
+                $query = $db->prepare_cached( $sql->new_object_xattr );
+            } else {
+                # replace
+                $query = $db->prepare_cached( $sql->replace_object_xattr );
+            }
+
+            # name, value, ext_id
+            my $rows = $query->execute($name, $value, $key->path);
+            $query->finish;
+
+            # if we affected more then one row something very bad has happened.
+            if ($flags eq 'create') {
+                unless ($rows == 1) {
+                    $log->logdie( "affected row count is $rows instead of 1" );
+                }
+            } else {
+                # replace_object_xattr can effect either 1 or 2 rows.  2 rows in
+                # the case of a replace and 1 if the xattr didn't already exist.
+                unless ($rows == 1 or $rows == 2) {
+                    $log->logdie( "affected row count is $rows instead of 2" );
+                }
+            }
+
+            $db->commit;
+            $log->debug("commit");
+        };
+        if ($@) {
+            $db->rollback;
+            $log->debug("rollback");
+            if ($@ =~ /Deadlock found/) {
+                $log->warn("database deadlock retrying transaction: $@");
+                redo TRANS;
+            }
+            $log->logdie("database error: $@");
+        }
+        last;
     }
 
@@ -998,23 +1105,30 @@
     $key = parse_neb_key($key);
 
-    eval {
-        my $query = $db->prepare_cached( $sql->remove_object_xattr );
-        # ext_id, name
-        my $rows = $query->execute($key->path, $name);
-        $query->finish;
-
-        # if we affected more then one row something very bad has happened.
-        unless ($rows == 1) {
-            $log->logdie( "affected row count is $rows instead of 1" );
-        }
-
-        $db->commit;
-        $log->debug("commit");
-    };
-    if ($@) {
-        $db->rollback;
-        $log->debug("rollback");
-        $log->logdie("database error: $@");
-    }
+TRANS: while (1) {
+        eval {
+            my $query = $db->prepare_cached( $sql->remove_object_xattr );
+            # ext_id, name
+            my $rows = $query->execute($key->path, $name);
+            $query->finish;
+
+            # if we affected more then one row something very bad has happened.
+            unless ($rows == 1) {
+                $log->logdie( "affected row count is $rows instead of 1" );
+            }
+
+            $db->commit;
+            $log->debug("commit");
+        };
+        if ($@) {
+            $db->rollback;
+            $log->debug("rollback");
+            if ($@ =~ /Deadlock found/) {
+                $log->warn("database deadlock retrying transaction: $@");
+                redo TRANS;
+            }
+            $log->logdie("database error: $@");
+        }
+        last;
+    } 
 
     $log->debug("leaving");
@@ -1186,64 +1300,71 @@
     $log->debug( "entered - @_" );
 
-    eval {
-        my $so_id;
-        my $instances;
-        # get so_id
-        {
-            my $query = $db->prepare_cached( $sql->get_object_from_uri );
-            my $rows = $query->execute( $uri );
-
-            unless ( $rows > 0 ) {
+TRANS: while (1) {
+        eval {
+            my $so_id;
+            my $instances;
+            # get so_id
+            {
+                my $query = $db->prepare_cached( $sql->get_object_from_uri );
+                my $rows = $query->execute( $uri );
+
+                unless ( $rows > 0 ) {
+                    $query->finish;
+                    $log->logdie( "no instance is associated with uri" );
+                }
+
+                $so_id = $query->fetchrow_hashref->{ 'so_id' };
                 $query->finish;
-                $log->logdie( "no instance is associated with uri" );
-            }
-
-            $so_id = $query->fetchrow_hashref->{ 'so_id' };
-            $query->finish;
-
-        }
-
-        {
-            my $query = $db->prepare_cached( $sql->get_instance_count );
-            $query->execute( $so_id );
-
-            $instances = $query->fetchrow_hashref->{ 'count(ins_id)' };
-            $query->finish;
-        }
-
-        # remove instance
-        {
-            my $query = $db->prepare_cached( $sql->delete_instance );
-            my $rows = $query->execute( $uri );
-            $query->finish;
-            
-            # if we affected something other then two rows something very bad
-            # has happened
-            unless ( $rows == 1 ) {
-                $log->logdie( "affected row count is $rows instead of 1" );
-            }
-        }
-
-        # if we just deleted the last instance associated with a storage object
-        # remove it too
-        if ( $instances == 1 ) {
-            # we just removed the last instance
-            my $query = $db->prepare_cached( $sql->delete_object );
-            my $rows = $query->execute( $so_id );
-            $query->finish;
-
-            # TODO: this will have to be changed in order to support hardlinks
-            unless ( $rows == 1 ) {
-                $log->logdie( "affected row count is $rows instead of 2" );
-            }
-        }
-
-        $db->commit;
-        $log->debug("commit");
-    };
-    if ( $@ ) {
-        $db->rollback;
-        $log->debug("rollback");
-        $log->logdie( "database error: $@" );
+
+            }
+
+            {
+                my $query = $db->prepare_cached( $sql->get_instance_count );
+                $query->execute( $so_id );
+
+                $instances = $query->fetchrow_hashref->{ 'count(ins_id)' };
+                $query->finish;
+            }
+
+            # remove instance
+            {
+                my $query = $db->prepare_cached( $sql->delete_instance );
+                my $rows = $query->execute( $uri );
+                $query->finish;
+                
+                # if we affected something other then two rows something very bad
+                # has happened
+                unless ( $rows == 1 ) {
+                    $log->logdie( "affected row count is $rows instead of 1" );
+                }
+            }
+
+            # if we just deleted the last instance associated with a storage object
+            # remove it too
+            if ( $instances == 1 ) {
+                # we just removed the last instance
+                my $query = $db->prepare_cached( $sql->delete_object );
+                my $rows = $query->execute( $so_id );
+                $query->finish;
+
+                # TODO: this will have to be changed in order to support hardlinks
+                unless ( $rows == 1 ) {
+                    $log->logdie( "affected row count is $rows instead of 2" );
+                }
+            }
+
+            $db->commit;
+            $log->debug("commit");
+        };
+        if ( $@ ) {
+            $db->rollback;
+            $log->debug("rollback");
+            if ($@ =~ /Deadlock found/) {
+                $log->warn("database deadlock retrying transaction: $@");
+                redo TRANS;
+            }
+            $log->logdie( "database error: $@" );
+        }
+        last;
     }
 
Index: /branches/neb_distrib_20081210/Nebulous-Server/lib/Nebulous/Server/Config.pm
===================================================================
--- /branches/neb_distrib_20081210/Nebulous-Server/lib/Nebulous/Server/Config.pm	(revision 23536)
+++ /branches/neb_distrib_20081210/Nebulous-Server/lib/Nebulous/Server/Config.pm	(revision 23537)
@@ -1,5 +1,5 @@
 # Copyright (C) 2005  Joshua Hoblitt
 #
-# $Id: Config.pm,v 1.3.22.1 2008-12-14 22:52:37 eugene Exp $
+# $Id: Config.pm,v 1.5 2008-12-14 22:54:25 eugene Exp $
 
 package Nebulous::Server::Config;
Index: /branches/neb_distrib_20081210/Nebulous-Server/lib/Nebulous/Server/Log.pm
===================================================================
--- /branches/neb_distrib_20081210/Nebulous-Server/lib/Nebulous/Server/Log.pm	(revision 23536)
+++ /branches/neb_distrib_20081210/Nebulous-Server/lib/Nebulous/Server/Log.pm	(revision 23537)
@@ -1,5 +1,5 @@
 # Copyright (c) 2004  Joshua Hoblitt
 #
-# $Id: Log.pm,v 1.7.22.1 2008-12-14 22:52:37 eugene Exp $
+# $Id: Log.pm,v 1.9 2008-12-14 22:54:25 eugene Exp $
 
 package Nebulous::Server::Log;
Index: /branches/neb_distrib_20081210/Nebulous-Server/lib/Nebulous/Server/SQL.pm
===================================================================
--- /branches/neb_distrib_20081210/Nebulous-Server/lib/Nebulous/Server/SQL.pm	(revision 23536)
+++ /branches/neb_distrib_20081210/Nebulous-Server/lib/Nebulous/Server/SQL.pm	(revision 23537)
@@ -1,5 +1,5 @@
 # Copyright (c) 2004  Joshua Hoblitt
 #
-# $Id: SQL.pm,v 1.75.6.1 2008-12-14 22:52:37 eugene Exp $
+# $Id: SQL.pm,v 1.78 2008-12-14 22:54:25 eugene Exp $
 
 package Nebulous::Server::SQL;
Index: anches/neb_distrib_20081210/Nebulous-Server/t/.cvsignore
===================================================================
--- /branches/neb_distrib_20081210/Nebulous-Server/t/.cvsignore	(revision 23536)
+++ 	(revision )
@@ -1,3 +1,0 @@
-TEST
-htdocs
-logs
Index: /branches/neb_distrib_20081210/Nebulous-Server/t/02_server_setup.t
===================================================================
--- /branches/neb_distrib_20081210/Nebulous-Server/t/02_server_setup.t	(revision 23536)
+++ /branches/neb_distrib_20081210/Nebulous-Server/t/02_server_setup.t	(revision 23537)
@@ -3,5 +3,5 @@
 # Copryight (C) 2004-2005  Joshua Hoblitt
 #
-# $Id: 02_server_setup.t,v 1.6.22.1 2008-12-14 22:52:37 eugene Exp $
+# $Id: 02_server_setup.t,v 1.8 2008-12-14 22:54:25 eugene Exp $
 
 use strict;
Index: /branches/neb_distrib_20081210/Nebulous-Server/t/10_server_is_valid_volume_name.t
===================================================================
--- /branches/neb_distrib_20081210/Nebulous-Server/t/10_server_is_valid_volume_name.t	(revision 23536)
+++ /branches/neb_distrib_20081210/Nebulous-Server/t/10_server_is_valid_volume_name.t	(revision 23537)
@@ -3,5 +3,5 @@
 # Copryight (C) 2004-2005  Joshua Hoblitt
 #
-# $Id: 10_server_is_valid_volume_name.t,v 1.5.22.1 2008-12-14 22:52:37 eugene Exp $
+# $Id: 10_server_is_valid_volume_name.t,v 1.7 2008-12-14 22:54:25 eugene Exp $
 
 use strict;
