[Bps-public-commit] r11278 - in SVN-PropDB: bin lib/Prophet lib/Prophet/Sync lib/Prophet/Sync/Source t

jesse at bestpractical.com jesse at bestpractical.com
Sun Mar 30 20:30:05 EDT 2008


Author: jesse
Date: Sun Mar 30 20:30:04 2008
New Revision: 11278

Modified:
   SVN-PropDB/   (props changed)
   SVN-PropDB/Makefile.PL
   SVN-PropDB/bin/prophet-merge
   SVN-PropDB/lib/Prophet/ChangeSet.pm
   SVN-PropDB/lib/Prophet/Conflict.pm
   SVN-PropDB/lib/Prophet/Handle.pm
   SVN-PropDB/lib/Prophet/Sync/Source.pm
   SVN-PropDB/lib/Prophet/Sync/Source/SVN.pm
   SVN-PropDB/lib/Prophet/Test.pm
   SVN-PropDB/t/cli.t

Log:
 r28809 at 70-5-79-37:  jesse | 2008-03-30 14:29:55 -1000
 * ping-pong sync now works


Modified: SVN-PropDB/Makefile.PL
==============================================================================
--- SVN-PropDB/Makefile.PL	(original)
+++ SVN-PropDB/Makefile.PL	Sun Mar 30 20:30:04 2008
@@ -3,6 +3,8 @@
 use inc::Module::Install;
 requires('Params::Validate');
 requires('Class::Accessor');
+requires('IPC::Run3');
+requires('Test::Exception');
 requires('Data::UUID');
 requires('Path::Class');
 requires('SVN::Core'); # SVN::Repos SVN::Fs SVN::Ra SVN::Delta::Editor SVN::Client SVN::Delta

Modified: SVN-PropDB/bin/prophet-merge
==============================================================================
--- SVN-PropDB/bin/prophet-merge	(original)
+++ SVN-PropDB/bin/prophet-merge	Sun Mar 30 20:30:04 2008
@@ -25,7 +25,7 @@
     fatal_error( $target->url . " does not accept changesets. Perhaps it's unwritable or something" );
 }
 
-import_changesets( from => $source, to => $target );
+$target->import_changesets( from => $source);
 
 exit(0);
 
@@ -42,32 +42,3 @@
 
 }
 
-
-sub import_changesets {
-    my %args   = validate( @_, { from => 1, to => 1 } );
-    my $source = $args{'from'};
-    my $target = $args{'to'};
-
-    my $changesets_to_integrate
-        = $source->fetch_changesets( after => $target->last_changeset_from_source( $source->uuid ) );
-
-    for my $changeset (@$changesets_to_integrate) {
-     use Data::Dumper; warn Dumper($changeset);
-     
-       next if ( $target->has_seen_changeset($changeset) );
-        if ( $target->changeset_will_conflict($changeset) ) {
-
-            my $conflicts = $target->conflicts_from_changeset($changeset);
-
-            # write out a nullification changeset beforehand,
-            # - that way, the source update will apply cleanly
-            # Then write out the source changeset
-            # Then write out a new changeset which reverts the parts of the source changeset which target should win
-        } else {
-            $target->integrate_changeset($changeset);
-        }
-
-    }
-}
-
-

Modified: SVN-PropDB/lib/Prophet/ChangeSet.pm
==============================================================================
--- SVN-PropDB/lib/Prophet/ChangeSet.pm	(original)
+++ SVN-PropDB/lib/Prophet/ChangeSet.pm	Sun Mar 30 20:30:04 2008
@@ -21,7 +21,7 @@
 
 =cut
 
-__PACKAGE__->mk_accessors(qw/sequence_no source_uuid original_source_uuid original_sequence_no is_nullification is_resolution/);
+__PACKAGE__->mk_accessors(qw/sequence_no source_uuid original_source_uuid original_sequence_no is_nullification  is_resolution/);
 
 =head2 new
 
@@ -79,4 +79,15 @@
     return @{ $self->{'changes'} || [] };
 }
 
+=head2 is_empty
+
+Returns true if this changeset has no changes
+
+=cut
+
+sub is_empty {
+    my $self = shift;
+    return $self->changes ? 0 : 1;
+}
+
 1;

Modified: SVN-PropDB/lib/Prophet/Conflict.pm
==============================================================================
--- SVN-PropDB/lib/Prophet/Conflict.pm	(original)
+++ SVN-PropDB/lib/Prophet/Conflict.pm	Sun Mar 30 20:30:04 2008
@@ -71,7 +71,7 @@
     # for everything from the changeset that is the same as the old value of the target replica
         # we can skip applying 
 
-    Carp::cluck "have not implemented automatic conflict resolution yet";
+    warn "have not implemented automatic conflict resolution yet";
     $self->autoresolved(1);
 
 

Modified: SVN-PropDB/lib/Prophet/Handle.pm
==============================================================================
--- SVN-PropDB/lib/Prophet/Handle.pm	(original)
+++ SVN-PropDB/lib/Prophet/Handle.pm	Sun Mar 30 20:30:04 2008
@@ -254,6 +254,17 @@
 
 }
 
+=head2 uuid 
+
+Returns the uuid of the repilica
+
+=cut
+
+sub uuid {
+    my $self = shift;
+    return $self->repo_handle->fs->get_uuid;
+}
+
 =head2 get_node_props {uuid => $uuid, type => $type, root => $root }
 
 Returns a hashref of all properties for the record of type $type with uuid C<$uuid>.
@@ -273,7 +284,6 @@
     my $self = shift;
     my %args = validate( @_, { uuid => 1, type => 1, root => undef } );
     my $root = $args{'root'} || $self->current_root;
-    Carp::cluck unless $self->node_exists(%args);
     return $root->node_proplist( $self->file_for( uuid => $args{'uuid'}, type => $args{'type'} ) );
 }
 

Modified: SVN-PropDB/lib/Prophet/Sync/Source.pm
==============================================================================
--- SVN-PropDB/lib/Prophet/Sync/Source.pm	(original)
+++ SVN-PropDB/lib/Prophet/Sync/Source.pm	Sun Mar 30 20:30:04 2008
@@ -3,6 +3,8 @@
 
 package Prophet::Sync::Source;
 use base qw/Class::Accessor/;
+use Params::Validate;
+
 
 =head1 NAME
 
@@ -45,4 +47,36 @@
 }
 
 
+
+sub import_changesets {
+    my $self = shift;
+    my %args   = validate( @_, { from => 1 } );
+    my $source = $args{'from'};
+
+    my $changesets_to_integrate
+        = $source->fetch_changesets( after => $self->last_changeset_from_source( $source->uuid ) );
+
+    for my $changeset (@$changesets_to_integrate) {
+#     use Data::Dumper;warn Dumper($changeset) if ($DEBUG);
+     
+       next if ( $self->has_seen_changeset($changeset) );
+        if ( $self->changeset_will_conflict($changeset) ) {
+
+            my $conflicts = $self->conflicts_from_changeset($changeset);
+
+            # write out a nullification changeset beforehand,
+            # - that way, the source update will apply cleanly
+            # Then write out the source changeset
+            # Then write out a new changeset which reverts the parts of the source changeset which target should win
+        } else {
+            $self->integrate_changeset($changeset);
+        }
+
+    }
+}
+
+
+
+
+
 1;

Modified: SVN-PropDB/lib/Prophet/Sync/Source/SVN.pm
==============================================================================
--- SVN-PropDB/lib/Prophet/Sync/Source/SVN.pm	(original)
+++ SVN-PropDB/lib/Prophet/Sync/Source/SVN.pm	Sun Mar 30 20:30:04 2008
@@ -60,7 +60,7 @@
 sub fetch_changesets {
     my $self = shift;
     my %args = validate( @_, { after => 1});
-
+warn "===> grabbing changesets after $args{after}";
     my @results;
     my $last_editor;
 
@@ -93,8 +93,8 @@
     my $changeset = Prophet::ChangeSet->new(
         {   sequence_no          => $entry->{'revision'},
             source_uuid          => $self->uuid,
-            original_source_uuid => $revprops->{original_source_uuid},
-            original_sequence_no => $revprops->{original_sequence_no},
+            original_source_uuid => $revprops->{'prophet:original-source'},
+            original_sequence_no => $revprops->{'prophet:original-sequence-no'},
 
         });
 
@@ -155,8 +155,11 @@
     my $self = shift;
     my ($changeset) = validate_pos( @_, { isa => "Prophet::ChangeSet" } );
 
+    # If the changeset originated locally, we never want it
+    return 1 if $changeset->original_source_uuid eq $self->uuid;
+    # Otherwise, if the we have a merge ticket from the source, we don't want the changeset
     my $last = $self->last_changeset_from_source( $changeset->original_source_uuid || $changeset->source_uuid );
-
+        
     # if the source's sequence # is >= the changeset's sequence #, we can safely skip it
     return 1 if ( $last >= $changeset->sequence_no );
 
@@ -210,13 +213,31 @@
 
 If there are no conflicts, just apply the change.
 
-
 =cut
 
 sub integrate_changeset {
     my $self = shift;
     my ($changeset) = validate_pos(@_, { isa => 'Prophet::ChangeSet'});
 
+=begin comment
+
+    # when we start to integrate a changeset, we need to do a bit of housekeeping
+    # We never want to merge in:
+        # merge tickets that describe merges from the local node
+        
+        
+    # When we integrate changes, sometimes we will get handed changes we already know about.
+    #   - changes from local
+    #   - changes from some other party we've merged from
+    #   - merge tickets for the same
+    # we'll want to skip or remove those changesets
+        
+        
+=cut        
+    return if $changeset->original_source_uuid eq $self->prophet_handle->uuid;
+    $self->remove_redundant_data($changeset); #Things we have already seen
+    return if ($changeset->is_empty or $changeset->is_nullification);
+
     if (my $conflict = $self->conflicts_from_changeset($changeset ) ) {
         #figure out our conflict resolution
         # generate a nullification change
@@ -232,6 +253,15 @@
     }
 }
 
+sub remove_redundant_data {
+    my ($self, $changeset) = @_;
+    # XXX: encapsulation
+    $changeset->{changes} = [ grep {
+        !($_->node_type eq $Prophet::Handle::MERGETICKET_METATYPE &&
+          $_->node_uuid eq $self->prophet_handle->uuid)
+    } $changeset->changes ];
+}
+
 
 # XXX TODO this is hacky as hell and violates abstraction barriers in the name of doing things over the RA
 

Modified: SVN-PropDB/lib/Prophet/Test.pm
==============================================================================
--- SVN-PropDB/lib/Prophet/Test.pm	(original)
+++ SVN-PropDB/lib/Prophet/Test.pm	Sun Mar 30 20:30:04 2008
@@ -2,7 +2,7 @@
 use warnings;
 package Prophet::Test;
 use base qw/Test::More Exporter/;
-our @EXPORT = qw/as_alice as_bob as_charlie as_david run_ok run_script run_output_matches/;
+our @EXPORT = qw/as_alice as_bob as_charlie as_david run_ok repo_uri_for run_script run_output_matches replica_last_rev replica_merge_tickets replica_uuid_for/;
 
 use File::Path 'rmtree';
 use File::Temp qw/tempdir/;
@@ -10,6 +10,8 @@
 use Test::Exception;
 use IPC::Run3 'run3';
 
+use Prophet::CLI;
+
 
 our $REPO_BASE = File::Temp::tempdir();
 Test::More->import;
@@ -43,8 +45,7 @@
     my @cmd = ($^X, (map { "-I$_" } @INC), 'bin/'.$script);
 
     my $ret = run3 [@cmd, @$args], undef, \$stdout, \$stderr;
-
-    Carp::croak $! if $?;
+    Carp::croak $stderr if $?;
     return($ret, $stdout, $stderr);
 }
 
@@ -59,13 +60,15 @@
    my $args = shift if (ref $_[0] eq 'ARRAY');
    my $msg = shift if (@_);
    
-   lives_and {
+   @_ = sub {
    
       my ($ret, $stdout,$stderr) = (run_script($script, $args), $msg);
       @_ = ($ret);
+      diag($stdout);
       diag($stderr);
       goto &Test::More::ok;
 };
+    goto \&lives_and;
 }
 
 sub _mk_cmp_closure {
@@ -156,21 +159,62 @@
     return 'file://'.$path;
 }
 
+
+sub replica_uuid {
+    my $self = shift;
+    my $cli = Prophet::CLI->new();
+    return $cli->handle->uuid;   
+}
+
+=head2 replica_merge_tickets
+
+Returns a hash of key-value pairs of the form 
+
+ { uuid => revno,
+   uuid => revno,  
+}
+
+=cut
+
+sub replica_merge_tickets {
+  my $self = shift;
+  my $cli = Prophet::CLI->new();
+  my $tickets = Prophet::Collection->new(handle => $cli->handle, type => $Prophet::Handle::MERGETICKET_METATYPE);  
+  $tickets->matching(sub { 1 });
+  return { map {  $_->uuid => $_->prop('last-changeset') } @{$tickets->as_array_ref} };
+
+}
+
+sub replica_last_rev {
+    my $cli = Prophet::CLI->new();
+    return $cli->handle->repo_handle->fs->youngest_rev;
+}
+
+
 =head2 as_user USERNAME CODEREF
 
 Run this code block as USERNAME.  This routine sets up the %ENV hash so that when we go looking for a repository, we get the user's repo.
 
 =cut
 
+our %REPLICA_UUIDS;
+
 sub as_user {
   my $username = shift;
   my $coderef = shift;
 
   local $ENV{'PROPHET_REPO'} = repo_path_for($username);
- $coderef->();
+ my (@ret)=  $coderef->();
+ $REPLICA_UUIDS{$username} = replica_uuid();
+ return @ret;
 }
 
 
+sub replica_uuid_for {
+    my $user = shift;
+    return $REPLICA_UUIDS{$user};
+
+}
 
 =head2 as_alice CODE, as_bob CODE, as_charlie CODE, as_david CODE
 

Modified: SVN-PropDB/t/cli.t
==============================================================================
--- SVN-PropDB/t/cli.t	(original)
+++ SVN-PropDB/t/cli.t	Sun Mar 30 20:30:04 2008
@@ -5,15 +5,12 @@
 
 use Prophet::Test tests => 19;
 
-use_ok('Prophet::CLI');
-
 as_alice {
     run_ok('prophet-node-create', [qw(--type Bug --status new --from alice )], "Created a record as alice"); 
     run_output_matches('prophet-node-search', [qw(--type Bug --regex .)], [qr/new/], " Found our record");
     # update the node
     # show the node history
     # show the node
-
 };
 
 
@@ -23,11 +20,13 @@
     # update the node
     # show the node history
     # show the node
+
 };
 
 as_alice {
     # sync from bob
-    run_ok('prophet-merge', ['--from', Prophet::Test::repo_uri_for('bob'), '--to', Prophet::Test::repo_uri_for('alice')], "Sync ran ok!");
+    diag('Alice syncs from bob');
+    run_ok('prophet-merge', ['--from', repo_uri_for('bob'), '--to', repo_uri_for('alice')], "Sync ran ok!");
     # check our local replicas
     my ($ret, $out, $err) = run_script('prophet-node-search', [qw(--type Bug --regex .)]);
     like($out, qr/open/) ;
@@ -35,15 +34,12 @@
     my @out = split(/\n/,$out);
     is (scalar @out, 2, "We found only two rows of output");
     
-    my $cli = Prophet::CLI->new();
-    isa_ok($cli->handle, 'Prophet::Handle');
-
-    my $last_rev = $cli->handle->repo_handle->fs->youngest_rev;
-
-    diag("Rerun the exact same sync operation. we should still only end up with two records and NO new transactions");
+    my $last_rev = replica_last_rev();
 
+    diag('Alice syncs from bob again. There will be no new changes from bob');
+    
     # sync from bob
-    run_ok('prophet-merge', ['--from', Prophet::Test::repo_uri_for('bob'), '--to', Prophet::Test::repo_uri_for('alice')], "Sync ran ok!");
+    run_ok('prophet-merge', ['--from', repo_uri_for('bob'), '--to', repo_uri_for('alice')], "Sync ran ok!");
     # check our local replicas
     ($ret, $out, $err) = run_script('prophet-node-search', [qw(--type Bug --regex .)]);
     like($out, qr/open/) ;
@@ -51,23 +47,28 @@
     @out = split(/\n/,$out);
     is (scalar @out, 2, "We found only two rows of output");
 
-    is( $cli->handle->repo_handle->fs->youngest_rev, $last_rev, "We have not recorded another transaction");
+    is(replica_last_rev() , $last_rev, "We have not recorded another transaction");
+    is_deeply( replica_merge_tickets(), { replica_uuid_for('bob') => as_bob { replica_last_rev()}  } );
     
 };
 
 
-
+diag('Bob syncs from alice');
 
 as_bob {
+    my $last_rev = replica_last_rev();
+
     my ($ret, $out, $err) = run_script('prophet-node-search', [qw(--type Bug --regex .)]);
     unlike($out, qr/new/, "bob doesn't have alice's yet") ;
 
     # sync from bob
-    run_ok('prophet-merge', ['--to', Prophet::Test::repo_uri_for('bob'), '--from', Prophet::Test::repo_uri_for('alice')], "Sync ran ok!");
+    run_ok('prophet-merge', ['--to', repo_uri_for('bob'), '--from', repo_uri_for('alice')], "Sync ran ok!");
     # check our local replicas
     ($ret, $out, $err) = run_script('prophet-node-search', [qw(--type Bug --regex .)]);
     like($out, qr/open/) ;
     like($out, qr/new/) ;
+    system("svn log -v ".repo_uri_for("bob"));
+    is( replica_last_rev, $last_rev + 1, "only one rev from alice is sycned" );
 };
 
 



More information about the Bps-public-commit mailing list