Adding transactions further - settled on MVCC
rkinyon [Tue, 11 Apr 2006 15:19:35 +0000 (15:19 +0000)]
lib/DBM/Deep.pm
lib/DBM/Deep/Engine.pm
lib/DBM/Deep/File.pm
t/28_transactions.t

index 9921062..815cfd9 100644 (file)
@@ -143,67 +143,14 @@ sub TIEARRAY {
     return DBM::Deep::Array->TIEARRAY( @_ );
 }
 
-#XXX Unneeded now ...
-#sub DESTROY {
-#}
-
 sub lock {
-    ##
-    # If db locking is set, flock() the db file.  If called multiple
-    # times before unlock(), then the same number of unlocks() must
-    # be called before the lock is released.
-    ##
     my $self = shift->_get_self;
-    my ($type) = @_;
-    $type = LOCK_EX unless defined $type;
-
-    if (!defined($self->_fh)) { return; }
-
-    if ($self->_fileobj->{locking}) {
-        if (!$self->_fileobj->{locked}) {
-            flock($self->_fh, $type);
-
-            # refresh end counter in case file has changed size
-            my @stats = stat($self->_fh);
-            $self->_fileobj->{end} = $stats[7];
-
-            # double-check file inode, in case another process
-            # has optimize()d our file while we were waiting.
-            if ($stats[1] != $self->_fileobj->{inode}) {
-                $self->_fileobj->close;
-                $self->_fileobj->open;
-                $self->{engine}->setup_fh( $self );
-                flock($self->_fh, $type); # re-lock
-
-                # This may not be necessary after re-opening
-                $self->_fileobj->{end} = (stat($self->_fh))[7]; # re-end
-            }
-        }
-        $self->_fileobj->{locked}++;
-
-        return 1;
-    }
-
-    return;
+    return $self->_fileobj->lock( $self, @_ );
 }
 
 sub unlock {
-    ##
-    # If db locking is set, unlock the db file.  See note in lock()
-    # regarding calling lock() multiple times.
-    ##
     my $self = shift->_get_self;
-
-    if (!defined($self->_fh)) { return; }
-
-    if ($self->_fileobj->{locking} && $self->_fileobj->{locked} > 0) {
-        $self->_fileobj->{locked}--;
-        if (!$self->_fileobj->{locked}) { flock($self->_fh, LOCK_UN); }
-
-        return 1;
-    }
-
-    return;
+    return $self->_fileobj->unlock( $self, @_ );
 }
 
 sub _copy_value {
index 7249fe7..ddf16e4 100644 (file)
@@ -109,7 +109,7 @@ sub write_file_header {
         SIG_HEADER,
         pack('N', 1),  # header version
         pack('N', 12), # header size
-        pack('N', 0),  # file version
+        pack('N', 0),  # currently running transaction IDs
         pack('S', $self->{long_size}),
         pack('A', $self->{long_pack}),
         pack('S', $self->{data_size}),
@@ -138,20 +138,23 @@ sub read_file_header {
     );
 
     unless ( $file_signature eq SIG_FILE ) {
-        $self->{fileobj}->close;
+        $self->_fileobj->close;
         $self->_throw_error( "Signature not found -- file is not a Deep DB" );
     }
 
     unless ( $sig_header eq SIG_HEADER ) {
-        $self->{fileobj}->close;
+        $self->_fileobj->close;
         $self->_throw_error( "Old file version found." );
     }
 
     my $buffer2;
     $bytes_read += read( $fh, $buffer2, $size );
-    my ($file_version, @values) = unpack( 'N S A S A S', $buffer2 );
+    my ($running_transactions, @values) = unpack( 'N S A S A S', $buffer2 );
+
+    $self->_fileobj->set_transaction_offset( 13 );
+
     if ( @values < 5 || grep { !defined } @values ) {
-        $self->{fileobj}->close;
+        $self->_fileobj->close;
         $self->_throw_error("Corrupted file - bad header");
     }
 
index 1282a10..2862ca5 100644 (file)
@@ -28,6 +28,7 @@ sub new {
         filter_fetch_value => undef,
 
         transaction_id     => 0,
+        transaction_offset => 0,
     }, $class;
 
     # Grab the parameters we want to use
@@ -90,15 +91,88 @@ sub DESTROY {
     return;
 }
 
+##
+# If db locking is set, flock() the db file.  If called multiple
+# times before unlock(), then the same number of unlocks() must
+# be called before the lock is released.
+##
+sub lock {
+    my $self = shift;
+    my ($obj, $type) = @_;
+    $type = LOCK_EX unless defined $type;
+
+    if (!defined($self->{fh})) { return; }
+
+    if ($self->{locking}) {
+        if (!$self->{locked}) {
+            flock($self->{fh}, $type);
+
+            # refresh end counter in case file has changed size
+            my @stats = stat($self->{fh});
+            $self->{end} = $stats[7];
+
+            # double-check file inode, in case another process
+            # has optimize()d our file while we were waiting.
+            if ($stats[1] != $self->{inode}) {
+                $self->close;
+                $self->open;
+
+                #XXX This needs work
+                $obj->{engine}->setup_fh( $obj );
+
+                flock($self->{fh}, $type); # re-lock
+
+                # This may not be necessary after re-opening
+                $self->{end} = (stat($self->{fh}))[7]; # re-end
+            }
+        }
+        $self->{locked}++;
+
+        return 1;
+    }
+
+    return;
+}
+
+##
+# If db locking is set, unlock the db file.  See note in lock()
+# regarding calling lock() multiple times.
+##
+sub unlock {
+    my $self = shift;
+
+    if (!defined($self->{fh})) { return; }
+
+    if ($self->{locking} && $self->{locked} > 0) {
+        $self->{locked}--;
+        if (!$self->{locked}) { flock($self->{fh}, LOCK_UN); }
+
+        return 1;
+    }
+
+    return;
+}
+
+sub set_transaction_offset {
+    my $self = shift;
+    $self->{transaction_offset} = shift;
+}
+
 sub begin_transaction {
     my $self = shift;
 
+    my $fh = $self->{fh};
+
+    seek( $fh, $self->{transaction_offset}, SEEK_SET );
+
     $self->{transaction_id}++;
 }
 
 sub end_transaction {
     my $self = shift;
 
+#    seek( $fh, $self->{transaction_offset}, SEEK_SET );
+
     $self->{transaction_id} = 0;
 }
 
index 736beff..3915a00 100644 (file)
@@ -48,6 +48,12 @@ is( $db2->{other_x}, 'foo', "After DB1 transaction is over, DB2 can still see ot
 # Should the transaction be in the Root and not the Engine? How would that
 # work?
 
+# What about the following:
+#   $db->{foo} = {};
+#   $db2 = $db->{foo};
+#   $db2->begin_work;
+#   $db->{foo} = 3;
+
 __END__
 
 Plan for transactions: