[Bps-public-commit] r15381 - in Prophet/trunk: .
spang at bestpractical.com
spang at bestpractical.com
Fri Aug 22 15:54:00 EDT 2008
Author: spang
Date: Fri Aug 22 15:53:57 2008
New Revision: 15381
Modified:
Prophet/trunk/ (props changed)
Prophet/trunk/lib/Prophet/Replica.pm
Log:
r48858 at loki: spang | 2008-08-22 15:38:22 -0400
cleanups, dead code removal, and more complete pod for Prophet::Replica
Modified: Prophet/trunk/lib/Prophet/Replica.pm
==============================================================================
--- Prophet/trunk/lib/Prophet/Replica.pm (original)
+++ Prophet/trunk/lib/Prophet/Replica.pm Fri Aug 22 15:53:57 2008
@@ -4,54 +4,60 @@
use Data::UUID;
use Path::Class;
+use constant state_db_uuid => 'state';
+
+use Prophet::App;
+
has state_handle => (
is => 'rw',
isa => 'Prophet::Replica',
+ documentation => 'Where metadata about foreign replicas is stored.',
);
has resolution_db_handle => (
is => 'rw',
isa => 'Prophet::Replica',
+ documentation => 'Where conflict resolutions are stored.',
);
has is_resdb => (
is => 'rw',
isa => 'Bool',
+ documentation => 'Whether this replica is a resolution db or not.'
);
has is_state_handle => (
is => 'rw',
isa => 'Bool',
+ documentation => 'Whether this replica is a state handle or not.',
);
has db_uuid => (
is => 'rw',
isa => 'Str',
writer => 'set_db_uuid',
+ documentation => 'The uuid of this replica.',
);
has url => (
is => 'rw',
isa => 'Str',
+ documentation => 'Where this replica comes from.',
);
has _alt_urls => (
is => 'rw',
isa => 'ArrayRef',
default => sub { [] },
+ documentation => 'Alternate places to try looking for this replica.',
);
-
has app_handle => (
is => 'ro',
isa => 'Prophet::App',
weak_ref => 1
);
-
-use constant state_db_uuid => 'state';
-use Prophet::App;
-
our $MERGETICKET_METATYPE = '_merge_tickets';
=head1 NAME
@@ -59,21 +65,18 @@
Prophet::Replica
=head1 DESCRIPTION
-
-A base class for all Prophet replicas
-=cut
+A base class for all Prophet replicas.
=head1 METHODS
-=head2 BUILD
+=head3 new
-Instantiates a new replica
+Determines what replica class to use and instantiates it. Returns the
+new replica object.
=cut
-sub _unimplemented { my $self = shift; die ref($self). " does not implement ". shift; }
-
around new => sub {
my $orig = shift;
my $class = shift;
@@ -93,9 +96,9 @@
}
};
-=head2 _url_to_replica_class
+=head3 _url_to_replica_class
-Returns the replica class for the given url.
+Returns the replica class for the given url based on its scheme.
=cut
@@ -114,6 +117,13 @@
return undef;
}
+=head3 import_changesets { from => L<Prophet::Replica> ... }
+
+Given a L<Prophet::Replica> to import changes from, traverse all the
+changesets we haven't seen before and integrate them into this replica.
+
+=cut
+
sub import_changesets {
my $self = shift;
my %args = validate(
@@ -153,6 +163,17 @@
);
}
+=head3 import_resolutions_from_remote_replica { from => L<Prophet::Replica> ... }
+
+Takes a L<Prophet::Replica> object (and possibly some optional arguments)
+and imports its resolution changesets into this replica's resolution
+database.
+
+Returns immediately if either the source replica or the target replica lack
+a resolution database.
+
+=cut
+
sub import_resolutions_from_remote_replica {
my $self = shift;
my %args = validate(
@@ -176,12 +197,21 @@
);
}
-=head2 integrate_changeset L<Prophet::ChangeSet>
+=head3 integrate_changeset L<Prophet::ChangeSet>
+
+Given a L<Prophet::ChangeSet>, integrate each and every change within that
+changeset into the handle's replica.
-If there are conflicts, generate a nullification change, figure out a conflict resolution and apply the nullification, original change and resolution all at once (as three separate changes).
+If there are conflicts, generate a nullification change, figure out a conflict
+resolution and apply the nullification, original change and resolution all at
+once (as three separate changes).
If there are no conflicts, just apply the change.
+This routine also records that we've seen this changeset (and hence everything
+before it) from both the peer who sent it to us AND the replica which originally
+created it.
+
=cut
sub integrate_changeset {
@@ -199,14 +229,15 @@
my $changeset = $args{'changeset'};
-
- $self->log("Considering changeset ".$changeset->original_sequence_no . " from " . substr($changeset->original_source_uuid,0,6));
+ $self->log("Considering changeset ".$changeset->original_sequence_no .
+ " from " . substr($changeset->original_source_uuid,0,6));
# 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 record
+ # - merge tickets that describe merges from the local record
- # When we integrate changes, sometimes we will get handed changes we already know about.
+ # 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
@@ -218,8 +249,6 @@
$self->remove_redundant_data($changeset); #Things we have already seen
return unless $changeset->has_changes;
-
-
if ( my $conflict = $self->conflicts_from_changeset($changeset) ) {
$self->log("Integrating conflicting changeset ".$changeset->original_sequence_no . " from " . substr($changeset->original_source_uuid,0,6));
$args{conflict_callback}->($conflict) if $args{'conflict_callback'};
@@ -232,14 +261,15 @@
}
]
)
-
}
my $resolutions = $conflict->generate_resolution( $args{resdb} );
#figure out our conflict resolution
- # IMPORTANT: these should be an atomic unit. dying here would be poor. BUT WE WANT THEM AS THREEDIFFERENT SVN REVS
- # integrate the nullification change
+ # IMPORTANT: these should be an atomic unit. dying here would be poor.
+ # BUT WE WANT THEM AS THREE DIFFERENT CHANGESETS
+
+ # integrate the nullification change
$self->record_changes( $conflict->nullification_changeset );
# integrate the original change
@@ -248,27 +278,25 @@
# integrate the conflict resolution change
$self->record_resolutions( $conflict->resolution_changeset );
- $args{'reporting_callback'}->( changeset => $changeset, conflict => $conflict ) if ( $args{'reporting_callback'} );
+ $args{'reporting_callback'}->( changeset => $changeset,
+ conflict => $conflict ) if ( $args{'reporting_callback'} );
} else {
- $self->log("Integrating changeset ".$changeset->original_sequence_no . " from " . substr($changeset->original_source_uuid,0,6));
+ $self->log("Integrating changeset ".$changeset->original_sequence_no .
+ " from " . substr($changeset->original_source_uuid,0,6));
$self->record_changeset_and_integration($changeset);
$args{'reporting_callback'}->( changeset => $changeset ) if ( $args{'reporting_callback'} );
-
}
}
-=head2 integrate_changeset L<Prophet::ChangeSet>
+=head3 record_changeset_and_integration L<Prophet::ChangeSet>
-Given a L<Prophet::ChangeSet>, integrates each and every change within that changeset into the handle's replica.
+Given a L<Prophet::ChangeSet>, integrate each and every change within that
+changeset into the handle's replica.
-This routine also records that we've seen this changeset (and hence everything before it) from both the peer who sent it to us AND the replica who originally created it.
-
-=cut
-
-
-
-=head2 record_changeset_and_integration
+If the state handle is in the middle of an edit, the integration of this
+changeset is recorded as part of that edit; if not, it is recorded as a new
+edit.
=cut
@@ -287,13 +315,12 @@
$state_handle->commit_edit() unless ($inside_edit);
$self->_set_original_source_metadata_for_current_edit($changeset);
$self->commit_edit;
-
return;
}
-=head2 last_changeset_from_source $SOURCE_UUID
+=head3 last_changeset_from_source $SOURCE_UUID
-Returns the last changeset id seen from the source identified by $SOURCE_UUID
+Returns the last changeset id seen from the source identified by $SOURCE_UUID.
=cut
@@ -305,10 +332,10 @@
return $last;
}
+=head3 has_seen_changeset L<Prophet::ChangeSet>
-=head2 has_seen_changeset Prophet::ChangeSet
-
-Returns true if we've previously integrated this changeset, even if we originally recieved it from a different peer
+Returns true if we've previously integrated this changeset, even if we
+originally received it from a different peer.
=cut
@@ -316,16 +343,18 @@
my $self = shift;
my ($changeset) = validate_pos( @_, { isa => "Prophet::ChangeSet" } );
- $self->log("Checking to see if we've ever seen changeset " .$changeset->original_sequence_no . " from ".substr($changeset->original_source_uuid,0,6));
+ $self->log("Checking to see if we've ever seen changeset " .
+ $changeset->original_sequence_no . " from " .
+ substr($changeset->original_source_uuid,0,6));
# If the changeset originated locally, we never want it
if ($changeset->original_source_uuid eq $self->uuid ) {
-
- $self->log("\t - We have. (It originated locally)");
+ $self->log("\t - We have. (It originated locally.)");
return 1
}
- # Otherwise, if the we have a merge ticket from the source, we don't want the changeset
- # if the source's sequence # is >= the changeset's sequence #, we can safely skip it
+ # Otherwise, if the we have a merge ticket from the source, we don't want
+ # the changeset if the source's sequence # is >= the changeset's sequence
+ # #, we can safely skip it
elsif ( $self->last_changeset_from_source( $changeset->original_source_uuid ) >= $changeset->original_sequence_no ) {
$self->log("\t - We have seen this or a more recent changeset from remote.");
return 1;
@@ -335,9 +364,10 @@
}
}
-=head2 changeset_will_conflict Prophet::ChangeSet
+=head3 changeset_will_conflict L<Prophet::ChangeSet>
-Returns true if any change that's part of this changeset won't apply cleanly to the head of the current replica
+Returns true if any change that's part of this changeset won't apply cleanly to
+the head of the current replica.
=cut
@@ -348,10 +378,9 @@
return 1 if ( $self->conflicts_from_changeset($changeset) );
return undef;
-
}
-=head2 conflicts_from_changeset Prophet::ChangeSet
+=head3 conflicts_from_changeset L<Prophet::ChangeSet>
Returns a L<Prophet::Conflict/> object if the supplied L<Prophet::ChangeSet/>
will generate conflicts if applied to the current replica.
@@ -364,7 +393,8 @@
my $self = shift;
my ($changeset) = validate_pos( @_, { isa => "Prophet::ChangeSet" } );
- my $conflict = Prophet::Conflict->new( { changeset => $changeset, prophet_handle => $self} );
+ my $conflict = Prophet::Conflict->new( { changeset => $changeset,
+ prophet_handle => $self} );
$conflict->analyze_changeset();
@@ -373,38 +403,46 @@
$self->log("Conflicting changeset: ".JSON::to_json($conflict, {allow_blessed => 1}));
return $conflict;
-
}
+=head3 remove_redundant_data L<Prophet::Changeset>
+
+Prunes unnecessary data from this changeset as a sanity check (merge tickets
+from foreign replicas, resolution records in non-resolution databases, changes
+we've already seen).
+
+=cut
+
sub remove_redundant_data {
my ( $self, $changeset ) = @_;
-
my @new_changes;
foreach my $change ($changeset->changes) {
- # when would we run into resolution records in a nonresb? XXX
- next if ($change->record_type eq '_prophet_resolution' && !$self->is_resdb);
+ # when would we run into resolution records in a nonresdb? XXX
+ next if ($change->record_type eq '_prophet_resolution' && !$self->is_resdb);
- # never integrate a merge ticket that comes from a foriegn database.
- # implict merge tickets are the devil and are lies. Merge tickets are always generated locally
- # by importing a change that originated on that replica
- # (The actual annoying technical problem is that the locally created merge ticket is written out in a separate transaction
- # at ~ the same time as the original imported one is being written.
- # This makes svn go boom
- next if( $change->record_type eq $MERGETICKET_METATYPE);# && $change->record_uuid eq $self->uuid );
+ # never integrate a merge ticket that comes from a foreign database.
+ # implict merge tickets are the devil and are lies. Merge tickets are
+ # always generated locally by importing a change that originated on
+ # that replica.
+ # (The actual annoying technical problem is that the
+ # locally created merge ticket is written out in a separate
+ # transaction at ~ at the same time as the original imported one is
+ # being written. This makes svn go boom.)
+ next if ( $change->record_type eq $MERGETICKET_METATYPE); # && $change->record_uuid eq $self->uuid );
push (@new_changes, $change);
}
-
- $changeset->changes(\@new_changes);
+ $changeset->changes(\@new_changes);
}
-=head2 traverse_new_changesets ( for => $replica, callback => sub { my $changeset = shift; ... } )
+=head3 traverse_new_changesets ( for => $replica, callback => sub { my $changeset = shift; ... } )
-Traverse the new changesets for C<$replica> and call C<callback> for each new changesets.
+Traverse the new changesets for C<$replica> and call C<callback> for each new
+changeset.
-XXX: this also provide hinting callbacks for the caller to know in
-advance how many changesets are there for traversal.
+This also provide hinting callbacks for the caller to know in advance how many
+changesets are there for traversal.
=cut
@@ -442,43 +480,20 @@
);
}
-=head2 new_changesets_for Prophet::Replica
-
-DEPRECATED: use traverse_new_changesets instead
-
-Returns the local changesets that have not yet been seen by the replica we're passing in.
-
-=cut
-
-
-sub new_changesets_for {
- my $self = shift;
-
- # the first argument is always the replica
- unshift @_, 'replica';
- my %args = validate(@_, {
- replica => { isa => 'Prophet::Replica' },
- force => 0,
- });
-
- my @result;
- $self->traverse_new_changesets( for => $args{replica}, callback => sub { push @result, $_[0] }, force => $args{force} );
-
- return \@result;
-}
-
-=head2 should_send_changeset { to => Prophet::Replica, changeset => Prophet::ChangeSet }
-
-Returns true if the replica C<to> hasn't yet seen the changeset C<changeset>
+=head3 should_send_changeset { to => L<Prophet::Replica>, changeset => L<Prophet::ChangeSet> }
+Returns true if the replica C<to> hasn't yet seen the changeset C<changeset>.
=cut
sub should_send_changeset {
my $self = shift;
- my %args = validate( @_, { to => { isa => 'Prophet::Replica' }, changeset => { isa => 'Prophet::ChangeSet' } } );
-
- $self->log("Should I send " .$args{changeset}->original_sequence_no . " from ".substr($args{changeset}->original_source_uuid,0,6) . " to " .substr($args{'to'}->uuid, 0, 6));
+ my %args = validate( @_, { to => { isa => 'Prophet::Replica' },
+ changeset => { isa => 'Prophet::ChangeSet' } });
+
+ $self->log("Should I send " .$args{changeset}->original_sequence_no .
+ " from ".substr($args{changeset}->original_source_uuid,0,6) . " to " .
+ substr($args{'to'}->uuid, 0, 6));
return undef if ( $args{'changeset'}->is_nullification || $args{'changeset'}->is_resolution );
return undef if $args{'to'}->has_seen_changeset( $args{'changeset'} );
@@ -486,16 +501,16 @@
return 1;
}
-=head2 fetch_changesets { after => SEQUENCE_NO }
+=head3 fetch_changesets { after => SEQUENCE_NO }
+
+Fetch all changesets from the source.
-Fetch all changesets from the source.
-
Returns a reference to an array of L<Prophet::ChangeSet/> objects.
-See also L<traverse_new_changesets> for replica implementations to provide streamly interface
-
+See also L<traverse_new_changesets> for replica implementations to provide
+streamly interface.
-=cut
+=cut
sub fetch_changesets {
my $self = shift;
@@ -507,12 +522,13 @@
return \@results;
}
-=head2 export_to { path => $PATH }
+=head3 export_to { path => $PATH }
-This routine will export a copy of this prophet database replica to a flat file on disk suitable for
-publishing via HTTP or over a local filesystem for other Prophet replicas to clone or incorporate changes from.
+This routine will export a copy of this prophet database replica to a flat file
+on disk suitable for publishing via HTTP or over a local filesystem for other
+Prophet replicas to clone or incorporate changes from.
-See C<Prophet::ReplicaExporter>
+See also C<Prophet::ReplicaExporter>.
=cut
@@ -525,30 +541,25 @@
$exporter->export();
}
-=head1 methods to be implemented by a replica backend
-
-
-
-=cut
-
+=head2 methods to be implemented by a replica backend
-=head2 uuid
+=head3 uuid
-Returns this replica's uuid
+Returns this replica's uuid.
=cut
sub uuid {}
-=head2 latest_sequence_no
+=head3 latest_sequence_no
-Returns the sequence # of the most recently committed changeset
+Returns the sequence # of the most recently committed changeset.
=cut
-sub latest_sequence_no {return undef }
+sub latest_sequence_no { return undef }
-=head2 find_or_create_luid { uuid => UUID }
+=head3 find_or_create_luid { uuid => UUID }
Finds or creates a LUID for the given UUID.
@@ -568,7 +579,7 @@
return $mapping->{ $args{'uuid'} };
}
-=head2 find_uuid_by_luid { luid => LUID }
+=head3 find_uuid_by_luid { luid => LUID }
Finds the UUID for the given LUID. Returns C<undef> if the LUID is not known.
@@ -582,6 +593,13 @@
return $mapping->{ $args{'luid'} };
}
+=head3 _create_luid ( 'uuid' => 'luid' )
+
+Given a UUID => LUID hash mapping, return a new unused LUID (one
+higher than the mapping's current highest luid).
+
+=cut
+
sub _create_luid {
my $self = shift;
my $map = shift;
@@ -589,6 +607,15 @@
return ++$map->{'_meta'}{'maximum_luid'};
}
+=head3 _do_userdata_read $PATH $DEFAULT
+
+Returns a reference to the parsed JSON contents of the file
+given by C<$PATH> in the replica's userdata directory.
+
+Returns C<$DEFAULT> if the file does not exist.
+
+=cut
+
sub _do_userdata_read {
my $self = shift;
my $path = shift;
@@ -596,9 +623,16 @@
my $json = $self->read_userdata_file( path => $path ) || $default;
require JSON;
return JSON::from_json($json, { utf8 => 1 });
-
}
+=head3 _do_userdata_write $PATH $VALUE
+
+serializes C<$VALUE> to JSON and writes it to the file given by C<$PATH>
+in the replica's userdata directory, creating parent directories as
+necessary.
+
+=cut
+
sub _do_userdata_write {
my $self = shift;
my $path = shift;
@@ -611,30 +645,69 @@
path => $path,
content => $content,
);
-
}
+=head3 _upstream_replica_cache_file
+
+A string representing the name of the file where replica URLs that have been
+previously pulled from are cached.
+
+=cut
+
sub _upstream_replica_cache_file { "upstream-replica-cache" }
+=head3 _read_cached_upstream_replicas
+
+Returns a list of cached upstream replica URLs, or an empty list if
+there are no cached URLs.
+
+=cut
+
sub _read_cached_upstream_replicas {
my $self = shift;
return @{ $self->_do_userdata_read( $self->_upstream_replica_cache_file, '[]' ) || [] };
}
+=head3 _write_cached_upstream_replicas @REPLICAS
+
+writes the replica URLs given by C<@REPLICAS> to the upstream replica
+cache file.
+
+=cut
+
sub _write_cached_upstream_replicas {
my $self = shift;
my @replicas = @_;
return $self->_do_userdata_write( $self->_upstream_replica_cache_file, [@replicas] );
-
}
+=head3 _guid2luid_file
+
+The file in the replica's userdata directory which contains a serialized
+JSON UUID => LUID hash mapping.
+
+=cut
+
sub _guid2luid_file { "local-id-cache" }
+=head3 _read_guid2luid_mappings
+
+Returns a UUID => LUID hashref for this replica.
+
+=cut
+
sub _read_guid2luid_mappings {
my $self = shift;
return $self->_do_userdata_read( $self->_guid2luid_file, '{}' );
}
+=head3 _write_guid2luid_mappings ( 'uuid' => 'luid' )
+
+Writes the given UUID => LUID hash map to C</_guid2luid_file> as serialized
+JSON.
+
+=cut
+
sub _write_guid2luid_mappings {
my $self = shift;
my $map = shift;
@@ -642,6 +715,12 @@
return $self->_do_userdata_write( $self->_guid2luid_file, $map );
}
+=head3 _read_luid2guid_mappings
+
+Returns a LUID => UUID hashref for this replica.
+
+=cut
+
sub _read_luid2guid_mappings {
my $self = shift;
my $guid2luid = $self->_read_guid2luid_mappings(@_);
@@ -650,54 +729,57 @@
return \%luid2guid;
}
-=head2 traverse_changesets { after => SEQUENCE_NO, callback => sub {} }
-
-Walk through each changeset in the replica after SEQUENCE_NO, calling the C<callback> for each one in turn.
+=head3 traverse_changesets { after => SEQUENCE_NO, callback => sub {} }
+Walk through each changeset in the replica after SEQUENCE_NO, calling the
+C<callback> for each one in turn.
=cut
sub traverse_changesets {
my $class = blessed($_[0]);
Carp::confess "$class has failed to implement a 'traverse_changesets' method for their replica type.";
-
}
-=head2 can_write_changesets
-Returns true if this source is one we know how to write to (and have permission to write to)
+=head3 can_read_changesets
-Returns false otherwise
+Returns true if this source is one we know how to read from (and have
+permission to do so).
=cut
-sub can_read_records { undef }
-sub can_write_records { undef }
sub can_read_changesets { undef }
-sub can_write_changesets { undef }
-
+=head3 can_write_changesets
-=head1 CODE BELOW THIS LINE USED TO BE IN HANDLE
-
+Returns true if this source is one we know how to write to (and have permission
+to write to).
+Returns false otherwise.
+=cut
-=head2 record_resolutions Prophet::ChangeSet
+sub can_write_changesets { undef }
-Given a resolution changeset
+=head3 record_resolutions L<Prophet::ChangeSet>
-record all the resolution changesets as well as resolution records in the local resolution database;
+Given a resolution changeset, record all the resolution changesets as well as
+resolution records in the local resolution database.
-Called ONLY on local resolution creation. (Synced resolutions are just synced as records)
+Called ONLY on local resolution creation. (Synced resolutions are just synced
+as records.)
=cut
sub record_resolutions {
my $self = shift;
my ($changeset) = validate_pos(@_, { isa => 'Prophet::ChangeSet'});
- $self->_unimplemented("record_resolutions (since there is no writable handle)") unless ($self->can_write_changesets);
- # If we have a resolution db handle, record the resolutions there.
- # Otherwise, record them locally
+
+ $self->_unimplemented("record_resolutions (since there is no writable handle)")
+ unless ($self->can_write_changesets);
+
+ # If we have a resolution db handle, record the resolutions there.
+ # Otherwise, record them locally
my $res_handle = $self->resolution_db_handle || $self;
return unless $changeset->has_changes;
@@ -707,11 +789,14 @@
$res_handle->_record_resolution($_) for $changeset->changes;
$self->commit_edit();
}
-=head2 _record_resolution Prophet::Change
-
-Called ONLY on local resolution creation. (Synced resolutions are just synced as records)
+
+=head3 _record_resolution L<Prophet::Change>
+
+Called ONLY on local resolution creation. (Synced resolutions are just synced
+as records.)
=cut
+
sub _record_resolution {
my $self = shift;
my ($change) = validate_pos(@_, { isa => 'Prophet::Change'});
@@ -730,14 +815,16 @@
}
);
}
-=head1 Routines dealing with integrating changesets into a replica
-=head2 record_changes Prophet::ChangeSet
+=head2 routines dealing with integrating changesets into a replica
+
+=head3 record_changes L<Prophet::ChangeSet>
Inside an edit (transaction), integrate all changes in this transaction
-and then call the _after_record_changes() hook
+and then call the _after_record_changes() hook.
=cut
+
sub record_changes {
my $self = shift;
my ($changeset) = validate_pos(@_, { isa => 'Prophet::ChangeSet'});
@@ -753,25 +840,35 @@
die($@) if ($@);
}
-=head2 integrate_changes Prophet::ChangeSet
+=head3 integrate_changes L<Prophet::ChangeSet>
-This routine is called by record_changes with a L<Prophet::ChangeSet> object.
-It integrates all changes from that object into the current replica.
+This routine is called by L</record_changes> with a L<Prophet::ChangeSet>
+object. It integrates all changes from that object into the current replica.
-All bookkeeping, such as opening and closing an edit, is done by L</record_changes>.
+All bookkeeping, such as opening and closing an edit, is done by
+L</record_changes>.
-If your replica type needs to play games to integrate multiple changes as a single
-record, this is what you'd override.
+If your replica type needs to play games to integrate multiple changes as a
+single record, this is what you'd override.
=cut
sub integrate_changes {
- my ($self, $changeset) = validate_pos( @_, {isa => 'Prophet::Replica'}, { isa => 'Prophet::ChangeSet' } );
+ my ($self, $changeset) = validate_pos( @_, {isa => 'Prophet::Replica'},
+ { isa => 'Prophet::ChangeSet' } );
$self->_integrate_change($_, $changeset) for ( $changeset->changes );
-
}
+
+=head2 _integrate_change L<Prophet::Change>
+
+Integrates the given change into the current replica. Used in
+L</integrate_changes>.
+
+=cut
+
sub _integrate_change {
- my ($self, $change, $changeset) = validate_pos(@_, {isa => 'Prophet::Replica'}, { isa => 'Prophet::Change'}, { isa => 'Prophet::ChangeSet'} );
+ my ($self, $change) = validate_pos(@_, { isa => 'Prophet::Replica' },
+ { isa => 'Prophet::Change' } );
my %new_props = map { $_->name => $_->new_value } $change->prop_changes;
if ( $change->change_type eq 'add_file' ) {
@@ -788,16 +885,16 @@
} else {
Carp::confess( "Unknown change type: " . $change->change_type );
}
-
}
-=head2 record_integration_of_changeset L<Prophet::ChangeSet>
+
+=head3 record_integration_of_changeset L<Prophet::ChangeSet>
This routine records the immediately upstream and original source
uuid and sequence numbers for this changeset. Prophet uses this
data to make sane choices about later replay and merge operations
-
=cut
+
sub record_integration_of_changeset {
my $self = shift;
my ($changeset) = validate_pos( @_, { isa => 'Prophet::ChangeSet' } );
@@ -806,16 +903,17 @@
return $self->_record_metadata_for( $MERGETICKET_METATYPE, $changeset->original_source_uuid, 'last-changeset', $changeset->original_sequence_no );
}
-=head1 metadata storage routines
-=cut
-=head2 metadata_storage $RECORD_TYPE, $PROPERTY_NAME
+=head2 metadata storage routines
-Returns a function which takes a UUID and an optional value to get (or set) metadata rows in a metadata table.
-We use this to record things like merge tickets
+=head3 metadata_storage $RECORD_TYPE, $PROPERTY_NAME
+Returns a function which takes a UUID and an optional value to get (or set)
+metadata rows in a metadata table. We use this to record things like merge
+tickets.
=cut
+
sub metadata_storage {
my $self = shift;
my ( $type, $prop_name ) = validate_pos( @_, 1, 1 );
@@ -828,18 +926,35 @@
};
}
+
+=head3 _retrieve_metadata_for $RECORD_TYPE, $UUID, $PROPERTY_NAME
+
+Takes a record type, a UUID, and a metadata property name, and returns the
+value of C<$PROPERTY_NAME> for that record. Intended for use with
+metadata / state replicas.
+
+Returns undef if the record cannot be loaded.
+
+=cut
+
sub _retrieve_metadata_for {
my $self = shift;
my ( $name, $source_uuid, $prop_name ) = validate_pos( @_, 1, 1, 1 );
my $entry = Prophet::Record->new( handle => $self, type => $name );
unless ( $entry->load( uuid => $source_uuid )) {
- return undef;
+ return undef;
}
-
return $entry->prop($prop_name);
-
}
+
+=head3 _record_metadata_for NAME, UUID, PROP, CONTENT
+
+Stores CONTENT in the record UUID's PROP property. Intended for storing
+metadata in metadata / state replicas.
+
+=cut
+
sub _record_metadata_for {
my $self = shift;
my ( $name, $source_uuid, $prop_name, $content )
@@ -861,41 +976,46 @@
);
}
}
-=head1 The following functions need to be implemented by any Prophet backing store.
-=head2 uuid
+=head2 routines which need to be implemented by any Prophet backend store
-Returns this replica's UUID
+=head3 uuid
-=head2 create_record { type => $TYPE, uuid => $uuid, props => { key-value pairs }}
+Returns this replica's UUID.
-Create a new record of type C<$type> with uuid C<$uuid> within the current replica.
+=head3 create_record { type => $TYPE, uuid => $UUID, props => { key-value pairs } }
-Sets the record's properties to the key-value hash passed in as the C<props> argument.
+Create a new record of type C<$TYPE> with uuid C<$UUID> within the current
+replica.
-If called from within an edit, it uses the current edit. Otherwise it manufactures and finalizes one of its own.
+Sets the record's properties to the key-value hash passed in as the C<props>
+argument.
+If called from within an edit, it uses the current edit. Otherwise it
+manufactures and finalizes one of its own.
+=head3 delete_record {uuid => $UUID, type => $TYPE }
-=head2 delete_record {uuid => $uuid, type => $type }
-
-Deletes the record C<$uuid> of type C<$type> from the current replica.
+Deletes the record C<$UUID> of type C<$TYPE> from the current replica.
Manufactures its own new edit if C<$self->current_edit> is undefined.
-=head2 set_record_props { uuid => $uuid, type => $type, props => {hash of kv pairs }}
-
+=head3 set_record_props { uuid => $UUID, type => $TYPE, props => {hash of kv pairs }}
-Updates the record of type C<$type> with uuid C<$uuid> to set each property defined by the props hash. It does NOT alter any property not defined by the props hash.
+Updates the record of type C<$TYPE> with uuid C<$UUID> to set each property
+defined by the props hash. It does NOT alter any property not defined by the
+props hash.
Manufactures its own current edit if none exists.
+=head3 get_record_props { uuid => $UUID, type => $TYPE, root => $ROOT }
-=head2 get_record_props {uuid => $uuid, type => $type, root => $root }
+Returns a hashref of all properties for the record of type C<$TYPE> with uuid
+C<$UUID>.
-Returns a hashref of all properties for the record of type $type with uuid C<$uuid>.
-
-'root' is an optional argument which you can use to pass in an alternate historical version of the replica to inspect. Code to look at the immediately previous version of a record might look like:
+'root' is an optional argument which you can use to pass in an alternate
+historical version of the replica to inspect. Code to look at the immediately
+previous version of a record might look like:
$handle->get_record_props(
type => $record->type,
@@ -903,46 +1023,56 @@
root => $self->repo_handle->fs->revision_root( $self->repo_handle->fs->youngest_rev - 1 )
);
-=head2 record_exists {uuid => $uuid, type => $type, root => $root }
+=head3 record_exists {uuid => $UUID, type => $TYPE, root => $ROOT }
-Returns true if the record in question exists. False otherwise
+Returns true if the record in question exists and false otherwise.
+=head3 list_records { type => $TYPE }
-=head2 list_records { type => $type }
+Returns a reference to a list of all the records of type $TYPE.
-Returns a reference to a list of all the records of type $type
+=head3 list_records
-=head2 list_records
+Returns a reference to a list of all the known types in your Prophet database.
-Returns a reference to a list of all the known types in your Prophet database
+=head3 type_exists { type => $type }
+Returns true if we have any records of type C<$TYPE>.
-=head2 type_exists { type => $type }
+=head2 routines which need to be implemented by any _writable_ prophet backend store
-Returns true if we have any records of type C<$type>
+=head2 optional routines which are provided for you to override with backend-store specific behaviour
+=head3 _after_record_changes L<Prophet::ChangeSet>
+Called after the replica has integrated a new changeset but before closing the
+current transaction/edit.
-=cut
-=head2 The following functions need to be implemented by any _writable_ prophet backing store
+The SVN backend, for example, used this to record author metadata about this
+changeset.
=cut
-=head2 The following optional routines are provided for you to override with backing-store specific behaviour
-
-=head3 _after_record_changes Prophet::ChangeSet
+sub _after_record_changes {
+ return 1;
+}
-Called after the replica has integrated a new changeset but before closing the current transaction/edit.
+=head3 _set_original_source_metadata_for_current_edit
-The SVN backend, for example, uses this to record author metadata about this changeset.
+Sets C<original_source_uuid> and C<original_sequence_no> for the current edit.
=cut
-sub _after_record_changes {
- return 1;
-}
sub _set_original_source_metadata_for_current_edit {}
+=head2 helper routines
+
+=head3 log $MSG
+
+Logs the given message to C<STDERR> (but only if the C<PROPHET_DEBUG>
+environmental variable is set).
+
+=cut
sub log {
my $self = shift;
@@ -950,6 +1080,11 @@
print STDERR "# ".substr($self->uuid,0,6)." (".$self->scheme.":".$self->url." )".": " .$msg."\n" if ($ENV{'PROPHET_DEBUG'});
}
+=head2 log_fatal $MSG
+
+Logs the given message and dies with a stack trace.
+
+=cut
sub log_fatal {
my $self = shift;
@@ -961,10 +1096,15 @@
Carp::confess(@_);
}
+=head2 changeset_creator
+
+The string to use as the creator of a changeset.
+
+=cut
+
sub changeset_creator { $ENV{PROPHET_USER} || $ENV{USER} }
__PACKAGE__->meta->make_immutable;
no Moose;
1;
-
More information about the Bps-public-commit
mailing list