[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

Re: SVN UPDATE ERROR:

From: Philip Martin <philip_at_codematters.co.uk>
Date: 2002-01-24 00:20:28 CET

Ben Collins-Sussman <sussman@collab.net> writes:

> cmpilato@collab.net writes:
>
> > Ben Collins-Sussman <sussman@collab.net> writes:
> >
> > > This is getting worse. It's happening approximately every two days
> > > now.
> > >
> > Benny, this happened to me the other day as well ('cept I didn't wait
> > the full 16 minutes...I restored from backup). Just letting you know
> > that this has now happened twice -- that tonight's occurance wasn't
> > just a one-time freak accident.
>
> I know. Hence the "every two days" I mentioned above. :-)
>
> It also happened to me and gstein last thursday night. Ugh.
>

Well, I posted a script last year that would lock up a repository. At
that stage I was trying concurrent access for two processes committing
to the same repository. I have played with the problem occasionally
since and it still happens. With the current version of the script I
can provoke deadlock over ra_dav with one process committing and
another process updating. I have had a look at debugging the problem
but have never got very far. I don't know enough about Berkeley DB.

In case someone else wants to try, here is how I do it. The script is
in Perl and is called stress.pl. I run it with three or more xterms,
all with shells on the same directory containing the script.

In the first xterm I type

 $ ./stress.pl -c -s1 -U http://localhost:8888/repostress

This causes a repository called repostress to be created in the
current directory, and httpd.conf must have an appropriate <Location>
entry. The script then checks out a working copy, and sits in a loop
making changes and commiting. When I see the message "Committed
revision 1." I start the script in the other xterms. In the second,
third, fourth, etc. xterms I type

 $ ./stress.pl -s1 -x0 -U http://localhost:8888/repostress

which checks out a working copy and then sits in a loop updating
it. There are a whole bunch of command line arguments to vary but
running one instance with "-c" and two instances with "-x0" generally
provokes deadlock on my machine. Leave of the -U option and it will
run over ra_local; that also deadlocks.

I have never managed to produce deadlock without a commit, so if this
is what we are seeing on svn.collab.net I would expect someone to
report a timed out commit.

This is the script:

#!/usr/bin/perl -w

# This script constructs a repository, and populates it with
# files. Then it loops making changes to a subset of the files and
# committing the tree. When two instances are run in parallel
# sometimes the commit will fail with a merge conflict. This is
# expected, and is automatically resolved by updating.

# The files start off containing:
# A0
# 0
# A1
# 1
# A2
# .
# .
# A9
# 9

# Each script has an ID in the range 0-9, and when it modifies a file
# it modifes the line that starts with its ID. Thus scripts with
# different IDs will make changes that can be merged automatically.

# The main loop is then:
#
# step 1: modify a random selection of files
#
# step 2: optional sleep or wait for RETURN keypress
#
# step 3: update the working copy automatically merging out-of-date files
#
# step 4: try to commit, if not successful go to step 3 otherwise go to step 1

# To allow break-out of potentially infinite loops, the script will
# terminate if it detects the presence of a "stop file", the path to
# which is specified with the -S option (default ./stop). This allows
# the script to be stopped without any danger of interrupting an svn
# sub-process, which experiment shows can cause problems with the
# database locking.

use Getopt::Std;
use File::Find;
use File::Path;
use Cwd;

# Repository check/create
sub init_repo
  {
    my ( $repo, $create ) = @_;
    if ( $create )
      {
        rmtree([$repo]) if -e $repo;
        my $svnadmin_cmd = "svnadmin create $repo";
        system( $svnadmin_cmd) and die "$svnadmin_cmd: failed\n";
      }
    else
      {
        my $svnadmin_cmd = "svnadmin youngest $repo";
        my $revision = readpipe $svnadmin_cmd;
        die "$svnadmin_cmd: failed\n" if not $revision =~ m{^[0-9]};
      }
    $repo = getcwd . "/$repo" if not $repo =~ m[^/];
    return $repo;
  }

# Check-out working copy
sub check_out
  {
    my ( $url ) = @_;
    my $wc_dir = "wcstress.$$";
    mkdir "$wc_dir", 0755 or die "mkdir stress.$$: $!\n";
    my $svn_cmd = "svn co $url -d $wc_dir";
    system( $svn_cmd ) and die "$svn_cmd: failed\n";
    return $wc_dir;
  }

# Print status and update. The update is to do any required merges.
sub status_update
  {
    my ( $wc_dir, $wait_for_key ) = @_;
    my $svn_cmd = "svn st -u $wc_dir";
    print "Status:\n";
    system( $svn_cmd ) and die "$svn_cmd: failed\n";
    print "Press return to update/commit\n" if $wait_for_key;
    read STDIN, $wait_for_key, 1 if $wait_for_key;
    print "Updating:\n";
    $svn_cmd = "svn up $wc_dir";
    system( $svn_cmd ) and die "$svn_cmd: failed\n";
  }

# Print status, update and commit. The update is to do any required merges.
sub status_update_commit
  {
    my ( $wc_dir, $wait_for_key ) = @_;
    status_update $wc_dir, $wait_for_key;
    print "Committing:\n";
    my $now_time = localtime;
    my $svn_cmd = "svn ci $wc_dir -m '$now_time'";
    return system( $svn_cmd );
  }

# Get a list of all versioned files in the working copy
{
  my @get_list_of_files_helper_array;
  sub GetListOfFilesHelper
    {
      $File::Find::prune = 1 if $File::Find::name =~ m[/.svn];
      return if $File::Find::prune or -d;
      push @get_list_of_files_helper_array, $File::Find::name;
    }
  sub GetListOfFiles
    {
      my ( $wc_dir ) = @_;
      @get_list_of_files_helper_array = ();
      find( \&GetListOfFilesHelper, $wc_dir);
      return @get_list_of_files_helper_array;
    }
}

# Populate a working copy
sub populate
  {
    my ( $dir, $dir_width, $file_width, $depth ) = @_;
    return if not $depth--;

    for $nfile ( 1..$file_width )
      {
        my $filename = "$dir/foo$nfile";
        open( FOO, ">$filename" ) or die "open $filename: $!\n";

        for $line ( 0..9 )
          {
            print FOO "A$line\n$line\n" or die "write to $filename: $!\n";
          }
        close FOO or die "close $filename:: $!\n";

        my $svn_cmd = "svn add $filename";
        system( $svn_cmd ) and die "$svn_cmd: failed\n";
      }

    if ( $depth )
      {
        for $ndir ( 1..$dir_width )
          {
            my $dirname = "$dir/bar$ndir";
            mkdir "$dirname", 0755 or die "mkdir $dirname: $!\n";

            my $svn_cmd = "svn add $dirname";
            system( $svn_cmd ) and die "$svn_cmd: failed\n";

            populate( "$dirname", $dir_width, $file_width, $depth );
          }
      }
  }

# Modify a versioned file in the working copy
sub ModFile
  {
    my ( $filename, $mod_number, $id ) = @_;

    # Read file into memory replacing the line that starts with our ID
    open( FOO, "<$filename" ) or die "open $filename: $!\n";
    @lines = map { s[(^$id.*)][$1,$mod_number]; $_ } <FOO>;
    close FOO or die "close $filename: $!\n";

    # Write the memory back to the file
    open( FOO, ">$filename" ) or die "open $filename: $!\n";
    print FOO or die "print $filename: $!\n" foreach @lines;
    close FOO or die "close $filename: $!\n";
  }

sub ParseCommandLine
  {
    my %cmd_opts;

    # defaults
    $cmd_opts{'D'} = 2; # number of subdirs per dir
    $cmd_opts{'F'} = 2; # number of files per dir
    $cmd_opts{'N'} = 2; # depth
    $cmd_opts{'R'} = "repostress"; # repository name
    $cmd_opts{'S'} = "stop"; # path of file to stop the script
    $cmd_opts{'U'} = "none"; # URL
    $cmd_opts{'c'} = 0; # create repository
    $cmd_opts{'i'} = 0; # ID
    $cmd_opts{'s'} = -1; # sleep interval
    $cmd_opts{'n'} = 200; # sets of changes
    $cmd_opts{'x'} = 4; # files to modify

    getopts( 'ci:n:s:x:D:F:N:R:U:', \%cmd_opts )
      or die "
usage: stress [-c] [-i num] [-n num] [-s secs] [-x num]
              [-D num] [-F num] [-N num] [-R path] [-S path] [-U url]
where
  -c cause repository creation
  -i the ID
  -n the number of sets of changes to commit
  -s the sleep delay (-1 wait for key, 0 none)
  -x the number of files to modify
  -D the number of sub-directories per directory in the tree
  -F the number of files per directory in the tree
  -N the depth of the tree
  -R the path to the repository
  -S the path to the file whose presence stops this script
  -U the URL to the repository (file:///<-R path> by default)
";

    # default ID if not set
    $cmd_opts{'i'} = 1 + 5 * $cmd_opts{'c'} if not $cmd_opts{'i'};

    return %cmd_opts;
  }

############################################################################
# Main

srand 123456789;

my %cmd_opts = ParseCommandLine();

my $repo = init_repo $cmd_opts{'R'}, $cmd_opts{'c'};

# Make URL from path if URL not explicitly specified
$cmd_opts{'U'} = "file://$repo" if $cmd_opts{'U'} eq "none";

my $wc_dir = check_out $cmd_opts{'U'};

if ( $cmd_opts{'c'} )
  {
    populate $wc_dir, $cmd_opts{'D'}, $cmd_opts{'F'}, $cmd_opts{'N'};
    status_update_commit $wc_dir, 0 and die "populate checkin failed\n";
  }

my @wc_files = GetListOfFiles $wc_dir;
die "not enough files in repository\n" if $#wc_files < $cmd_opts{'x'};

my $wait_for_key = $cmd_opts{'s'} < 0;

my $stop_file = $cmd_opts{'S'};

for $mod_number ( 1..$cmd_opts{'n'} )
  {
    my @chosen;
    for ( 1..$cmd_opts{'x'} )
      {
        # Extract random file from list and modify it
        my $mod_file = splice @wc_files, int rand $#wc_files, 1;
        ModFile $mod_file, $mod_number, $cmd_opts{'i'};
        push @chosen, $mod_file;
      }
    # Reinstate list of files, the order doesn't matter
    push @wc_files, @chosen;

    if ( $cmd_opts{'x'} > 0 ) {
      # Loop committing until successful or the stop file is created
      1 while not -e $stop_file and status_update_commit $wc_dir, $wait_for_key;
    } else {
      status_update $wc_dir, $wait_for_key;
    }
    sleep $cmd_opts{'s'} if $cmd_opts{'s'} > 0;

    last if -e $stop_file;
  }

-- 
Philip
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Sat Oct 21 14:36:58 2006

This is an archived mail posted to the Subversion Dev mailing list.

This site is subject to the Apache Privacy Policy and the Apache Public Forum Archive Policy.