Merge pull request #818 from Deltik/fix/815

syncoid: Sort snapshots by `createtxg` if possible (fallback to `creation`)
This commit is contained in:
Jim Salter 2024-02-01 13:13:35 -05:00 committed by GitHub
commit 7c225a1d7b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 142 additions and 175 deletions

267
syncoid
View File

@ -415,10 +415,10 @@ sub syncdataset {
if (!defined($receivetoken)) { if (!defined($receivetoken)) {
# build hashes of the snaps on the source and target filesystems. # build hashes of the snaps on the source and target filesystems.
%snaps = getsnaps('source',$sourcehost,$sourcefs,$sourceisroot); %snaps = getsnaps('source',$sourcehost,$sourcefs,$sourceisroot,0);
if ($targetexists) { if ($targetexists) {
my %targetsnaps = getsnaps('target',$targethost,$targetfs,$targetisroot); my %targetsnaps = getsnaps('target',$targethost,$targetfs,$targetisroot,0);
my %sourcesnaps = %snaps; my %sourcesnaps = %snaps;
%snaps = (%sourcesnaps, %targetsnaps); %snaps = (%sourcesnaps, %targetsnaps);
} }
@ -438,7 +438,7 @@ sub syncdataset {
# Don't send the sync snap if it's filtered out by --exclude-snaps or # Don't send the sync snap if it's filtered out by --exclude-snaps or
# --include-snaps # --include-snaps
if (!snapisincluded($newsyncsnap)) { if (!snapisincluded($newsyncsnap)) {
$newsyncsnap = getnewestsnapshot($sourcehost,$sourcefs,$sourceisroot); $newsyncsnap = getnewestsnapshot(\%snaps);
if ($newsyncsnap eq 0) { if ($newsyncsnap eq 0) {
writelog('WARN', "CRITICAL: no snapshots exist on source $sourcefs, and you asked for --no-sync-snap."); writelog('WARN', "CRITICAL: no snapshots exist on source $sourcefs, and you asked for --no-sync-snap.");
if ($exitcode < 1) { $exitcode = 1; } if ($exitcode < 1) { $exitcode = 1; }
@ -447,7 +447,7 @@ sub syncdataset {
} }
} else { } else {
# we don't want sync snapshots created, so use the newest snapshot we can find. # we don't want sync snapshots created, so use the newest snapshot we can find.
$newsyncsnap = getnewestsnapshot($sourcehost,$sourcefs,$sourceisroot); $newsyncsnap = getnewestsnapshot(\%snaps);
if ($newsyncsnap eq 0) { if ($newsyncsnap eq 0) {
writelog('WARN', "CRITICAL: no snapshots exist on source $sourcefs, and you asked for --no-sync-snap."); writelog('WARN', "CRITICAL: no snapshots exist on source $sourcefs, and you asked for --no-sync-snap.");
if ($exitcode < 1) { $exitcode = 1; } if ($exitcode < 1) { $exitcode = 1; }
@ -584,8 +584,7 @@ sub syncdataset {
my $targetsize = getzfsvalue($targethost,$targetfs,$targetisroot,'-p used'); my $targetsize = getzfsvalue($targethost,$targetfs,$targetisroot,'-p used');
my $bookmark = 0; my %bookmark = ();
my $bookmarkcreation = 0;
$matchingsnap = getmatchingsnapshot($sourcefs, $targetfs, \%snaps); $matchingsnap = getmatchingsnapshot($sourcefs, $targetfs, \%snaps);
if (! $matchingsnap) { if (! $matchingsnap) {
@ -593,19 +592,18 @@ sub syncdataset {
my %bookmarks = getbookmarks($sourcehost,$sourcefs,$sourceisroot); my %bookmarks = getbookmarks($sourcehost,$sourcefs,$sourceisroot);
# check for matching guid of source bookmark and target snapshot (oldest first) # check for matching guid of source bookmark and target snapshot (oldest first)
foreach my $snap ( sort { $snaps{'target'}{$b}{'creation'}<=>$snaps{'target'}{$a}{'creation'} } keys %{ $snaps{'target'} }) { foreach my $snap ( sort { sortsnapshots(\%snaps, $b, $a) } keys %{ $snaps{'target'} }) {
my $guid = $snaps{'target'}{$snap}{'guid'}; my $guid = $snaps{'target'}{$snap}{'guid'};
if (defined $bookmarks{$guid}) { if (defined $bookmarks{$guid}) {
# found a match # found a match
$bookmark = $bookmarks{$guid}{'name'}; %bookmark = %{ $bookmarks{$guid} };
$bookmarkcreation = $bookmarks{$guid}{'creation'};
$matchingsnap = $snap; $matchingsnap = $snap;
last; last;
} }
} }
if (! $bookmark) { if (! %bookmark) {
# force delete is not possible for the root dataset # force delete is not possible for the root dataset
if ($args{'force-delete'} && index($targetfs, '/') != -1) { if ($args{'force-delete'} && index($targetfs, '/') != -1) {
writelog('INFO', "Removing $targetfs because no matching snapshots were found"); writelog('INFO', "Removing $targetfs because no matching snapshots were found");
@ -678,15 +676,18 @@ sub syncdataset {
my $nextsnapshot = 0; my $nextsnapshot = 0;
if ($bookmark) { if (%bookmark) {
my $bookmarkescaped = escapeshellparam($bookmark);
if (!defined $args{'no-stream'}) { if (!defined $args{'no-stream'}) {
# if intermediate snapshots are needed we need to find the next oldest snapshot, # if intermediate snapshots are needed we need to find the next oldest snapshot,
# do an replication to it and replicate as always from oldest to newest # do an replication to it and replicate as always from oldest to newest
# because bookmark sends doesn't support intermediates directly # because bookmark sends doesn't support intermediates directly
foreach my $snap ( sort { $snaps{'source'}{$a}{'creation'}<=>$snaps{'source'}{$b}{'creation'} } keys %{ $snaps{'source'} }) { foreach my $snap ( sort { sortsnapshots(\%snaps, $a, $b) } keys %{ $snaps{'source'} }) {
if ($snaps{'source'}{$snap}{'creation'} >= $bookmarkcreation) { my $comparisonkey = 'creation';
if (defined $snaps{'source'}{$snap}{'createtxg'} && defined $bookmark{'createtxg'}) {
$comparisonkey = 'createtxg';
}
if ($snaps{'source'}{$snap}{$comparisonkey} >= $bookmark{$comparisonkey}) {
$nextsnapshot = $snap; $nextsnapshot = $snap;
last; last;
} }
@ -694,13 +695,13 @@ sub syncdataset {
} }
if ($nextsnapshot) { if ($nextsnapshot) {
($exit, $stdout) = syncbookmark($sourcehost, $sourcefs, $targethost, $targetfs, $bookmark, $nextsnapshot); ($exit, $stdout) = syncbookmark($sourcehost, $sourcefs, $targethost, $targetfs, $bookmark{'name'}, $nextsnapshot);
$exit == 0 or do { $exit == 0 or do {
if (!$resume && $stdout =~ /\Qcontains partially-complete state\E/) { if (!$resume && $stdout =~ /\Qcontains partially-complete state\E/) {
writelog('WARN', "resetting partially receive state"); writelog('WARN', "resetting partially receive state");
resetreceivestate($targethost,$targetfs,$targetisroot); resetreceivestate($targethost,$targetfs,$targetisroot);
(my $ret) = syncbookmark($sourcehost, $sourcefs, $targethost, $targetfs, $bookmark, $nextsnapshot); (my $ret) = syncbookmark($sourcehost, $sourcefs, $targethost, $targetfs, $bookmark{'name'}, $nextsnapshot);
$ret == 0 or do { $ret == 0 or do {
if ($exitcode < 2) { $exitcode = 2; } if ($exitcode < 2) { $exitcode = 2; }
return 0; return 0;
@ -714,13 +715,13 @@ sub syncdataset {
$matchingsnap = $nextsnapshot; $matchingsnap = $nextsnapshot;
$matchingsnapescaped = escapeshellparam($matchingsnap); $matchingsnapescaped = escapeshellparam($matchingsnap);
} else { } else {
($exit, $stdout) = syncbookmark($sourcehost, $sourcefs, $targethost, $targetfs, $bookmark, $newsyncsnap); ($exit, $stdout) = syncbookmark($sourcehost, $sourcefs, $targethost, $targetfs, $bookmark{'name'}, $newsyncsnap);
$exit == 0 or do { $exit == 0 or do {
if (!$resume && $stdout =~ /\Qcontains partially-complete state\E/) { if (!$resume && $stdout =~ /\Qcontains partially-complete state\E/) {
writelog('WARN', "resetting partially receive state"); writelog('WARN', "resetting partially receive state");
resetreceivestate($targethost,$targetfs,$targetisroot); resetreceivestate($targethost,$targetfs,$targetisroot);
(my $ret) = syncbookmark($sourcehost, $sourcefs, $targethost, $targetfs, $bookmark, $newsyncsnap); (my $ret) = syncbookmark($sourcehost, $sourcefs, $targethost, $targetfs, $bookmark{'name'}, $newsyncsnap);
$ret == 0 or do { $ret == 0 or do {
if ($exitcode < 2) { $exitcode = 2; } if ($exitcode < 2) { $exitcode = 2; }
return 0; return 0;
@ -735,7 +736,7 @@ sub syncdataset {
# do a normal replication if bookmarks aren't used or if previous # do a normal replication if bookmarks aren't used or if previous
# bookmark replication was only done to the next oldest snapshot # bookmark replication was only done to the next oldest snapshot
if (!$bookmark || $nextsnapshot) { if (!%bookmark || $nextsnapshot) {
if ($matchingsnap eq $newsyncsnap) { if ($matchingsnap eq $newsyncsnap) {
# edge case: bookmark replication used the latest snapshot # edge case: bookmark replication used the latest snapshot
return 0; return 0;
@ -862,9 +863,9 @@ sub syncdataset {
if (defined $args{'delete-target-snapshots'}) { if (defined $args{'delete-target-snapshots'}) {
# Find the snapshots that exist on the target, filter with # Find the snapshots that exist on the target, filter with
# those that exist on the source. Remaining are the snapshots # those that exist on the source. Remaining are the snapshots
# that are only on the target. Then sort by creation date, as # that are only on the target. Then sort to remove the oldest
# to remove the oldest snapshots first. # snapshots first.
my @to_delete = sort { $snaps{'target'}{$a}{'creation'}<=>$snaps{'target'}{$b}{'creation'} } grep {!exists $snaps{'source'}{$_}} keys %{ $snaps{'target'} }; my @to_delete = sort { sortsnapshots(\%snaps, $a, $b) } grep {!exists $snaps{'source'}{$_}} keys %{ $snaps{'target'} };
while (@to_delete) { while (@to_delete) {
# Create batch of snapshots to remove # Create batch of snapshots to remove
my $snaps = join ',', splice(@to_delete, 0, 50); my $snaps = join ',', splice(@to_delete, 0, 50);
@ -1480,9 +1481,17 @@ sub readablebytes {
return $disp; return $disp;
} }
sub sortsnapshots {
my ($snaps, $left, $right) = @_;
if (defined $snaps->{'source'}{$left}{'createtxg'} && defined $snaps->{'source'}{$right}{'createtxg'}) {
return $snaps->{'source'}{$left}{'createtxg'} <=> $snaps->{'source'}{$right}{'createtxg'};
}
return $snaps->{'source'}{$left}{'creation'} <=> $snaps->{'source'}{$right}{'creation'};
}
sub getoldestsnapshot { sub getoldestsnapshot {
my $snaps = shift; my $snaps = shift;
foreach my $snap ( sort { $snaps{'source'}{$a}{'creation'}<=>$snaps{'source'}{$b}{'creation'} } keys %{ $snaps{'source'} }) { foreach my $snap (sort { sortsnapshots($snaps, $a, $b) } keys %{ $snaps{'source'} }) {
# return on first snap found - it's the oldest # return on first snap found - it's the oldest
return $snap; return $snap;
} }
@ -1496,7 +1505,7 @@ sub getoldestsnapshot {
sub getnewestsnapshot { sub getnewestsnapshot {
my $snaps = shift; my $snaps = shift;
foreach my $snap ( sort { $snaps{'source'}{$b}{'creation'}<=>$snaps{'source'}{$a}{'creation'} } keys %{ $snaps{'source'} }) { foreach my $snap (sort { sortsnapshots($snaps, $b, $a) } keys %{ $snaps{'source'} }) {
# return on first snap found - it's the newest # return on first snap found - it's the newest
writelog('INFO', "NEWEST SNAPSHOT: $snap"); writelog('INFO', "NEWEST SNAPSHOT: $snap");
return $snap; return $snap;
@ -1675,7 +1684,7 @@ sub pruneoldsyncsnaps {
sub getmatchingsnapshot { sub getmatchingsnapshot {
my ($sourcefs, $targetfs, $snaps) = @_; my ($sourcefs, $targetfs, $snaps) = @_;
foreach my $snap ( sort { $snaps{'source'}{$b}{'creation'}<=>$snaps{'source'}{$a}{'creation'} } keys %{ $snaps{'source'} }) { foreach my $snap ( sort { sortsnapshots($snaps, $b, $a) } keys %{ $snaps{'source'} }) {
if (defined $snaps{'target'}{$snap}) { if (defined $snaps{'target'}{$snap}) {
if ($snaps{'source'}{$snap}{'guid'} == $snaps{'target'}{$snap}{'guid'}) { if ($snaps{'source'}{$snap}{'guid'} == $snaps{'target'}{$snap}{'guid'}) {
return $snap; return $snap;
@ -1803,21 +1812,22 @@ sub dumphash() {
writelog('INFO', Dumper($hash)); writelog('INFO', Dumper($hash));
} }
sub getsnaps() { sub getsnaps {
my ($type,$rhost,$fs,$isroot,%snaps) = @_; my ($type,$rhost,$fs,$isroot,$use_fallback,%snaps) = @_;
my $mysudocmd; my $mysudocmd;
my $fsescaped = escapeshellparam($fs); my $fsescaped = escapeshellparam($fs);
if ($isroot) { $mysudocmd = ''; } else { $mysudocmd = $sudocmd; } if ($isroot) { $mysudocmd = ''; } else { $mysudocmd = $sudocmd; }
my $rhostOriginal = $rhost;
if ($rhost ne '') { if ($rhost ne '') {
$rhost = "$sshcmd $rhost"; $rhost = "$sshcmd $rhost";
# double escaping needed # double escaping needed
$fsescaped = escapeshellparam($fsescaped); $fsescaped = escapeshellparam($fsescaped);
} }
my $getsnapcmd = "$rhost $mysudocmd $zfscmd get -Hpd 1 -t snapshot guid,creation $fsescaped"; my $getsnapcmd = $use_fallback
? "$rhost $mysudocmd $zfscmd get -Hpd 1 all $fsescaped"
: "$rhost $mysudocmd $zfscmd get -Hpd 1 -t snapshot all $fsescaped";
if ($debug) { if ($debug) {
$getsnapcmd = "$getsnapcmd |"; $getsnapcmd = "$getsnapcmd |";
writelog('DEBUG', "getting list of snapshots on $fs using $getsnapcmd..."); writelog('DEBUG', "getting list of snapshots on $fs using $getsnapcmd...");
@ -1827,141 +1837,54 @@ sub getsnaps() {
open FH, $getsnapcmd; open FH, $getsnapcmd;
my @rawsnaps = <FH>; my @rawsnaps = <FH>;
close FH or do { close FH or do {
# fallback (solaris for example doesn't support the -t option) if (!$use_fallback) {
return getsnapsfallback($type,$rhostOriginal,$fs,$isroot,%snaps); writelog('WARN', "snapshot listing failed, trying fallback command");
return getsnaps($type, $rhost, $fs, $isroot, 1, %snaps);
}
die "CRITICAL ERROR: snapshots couldn't be listed for $fs (exit code $?)";
}; };
# this is a little obnoxious. get guid,creation returns guid,creation on two separate lines my %snap_data;
# as though each were an entirely separate get command. my %creationtimes;
my %creationtimes=(); for my $line (@rawsnaps) {
chomp $line;
my ($dataset, $property, $value) = split /\t/, $line;
die "CRITICAL ERROR: Unexpected line format in $line" unless defined $value;
foreach my $line (@rawsnaps) { my (undef, $snap) = split /@/, $dataset;
$line =~ /\Q$fs\E\@(\S*)/; die "CRITICAL ERROR: Unexpected dataset format in $line" unless $snap;
my $snapname = $1;
if (!snapisincluded($snapname)) { next; } if (!snapisincluded($snap)) { next; }
# only import snap guids from the specified filesystem $snap_data{$snap}{$property} = $value;
if ($line =~ /\Q$fs\E\@.*\tguid/) {
chomp $line;
my $guid = $line;
$guid =~ s/^.*\tguid\t*(\d*).*/$1/;
my $snap = $line;
$snap =~ s/^.*\@(.*)\tguid.*$/$1/;
$snaps{$type}{$snap}{'guid'}=$guid;
}
# only import snap creations from the specified filesystem
elsif ($line =~ /\Q$fs\E\@.*\tcreation/) {
chomp $line;
my $creation = $line;
$creation =~ s/^.*\tcreation\t*(\d*).*/$1/;
my $snap = $line;
$snap =~ s/^.*\@(.*)\tcreation.*$/$1/;
# the accuracy of the creation timestamp is only for a second, but # the accuracy of the creation timestamp is only for a second, but
# snapshots in the same second are highly likely. The list command # snapshots in the same second are highly likely. The list command
# has an ordered output so we append another three digit running number # has an ordered output so we append another three digit running number
# to the creation timestamp and make sure those are ordered correctly # to the creation timestamp and make sure those are ordered correctly
# for snapshot with the same creation timestamp # for snapshot with the same creation timestamp
if ($property eq 'creation') {
my $counter = 0; my $counter = 0;
my $creationsuffix; my $creationsuffix;
while ($counter < 999) { while ($counter < 999) {
$creationsuffix = sprintf("%s%03d", $creation, $counter); $creationsuffix = sprintf("%s%03d", $value, $counter);
if (!defined $creationtimes{$creationsuffix}) { if (!defined $creationtimes{$creationsuffix}) {
$creationtimes{$creationsuffix} = 1; $creationtimes{$creationsuffix} = 1;
last; last;
} }
$counter += 1; $counter += 1;
} }
$snap_data{$snap}{'creation'} = $creationsuffix;
$snaps{$type}{$snap}{'creation'}=$creationsuffix;
} }
} }
return %snaps; for my $snap (keys %snap_data) {
} if (!$use_fallback || $snap_data{$snap}{'type'} eq 'snapshot') {
$snaps{$type}{$snap}{'guid'} = $snap_data{$snap}{'guid'};
sub getsnapsfallback() { $snaps{$type}{$snap}{'createtxg'} = $snap_data{$snap}{'createtxg'};
# fallback (solaris for example doesn't support the -t option) $snaps{$type}{$snap}{'creation'} = $snap_data{$snap}{'creation'};
my ($type,$rhost,$fs,$isroot,%snaps) = @_;
my $mysudocmd;
my $fsescaped = escapeshellparam($fs);
if ($isroot) { $mysudocmd = ''; } else { $mysudocmd = $sudocmd; }
if ($rhost ne '') {
$rhost = "$sshcmd $rhost";
# double escaping needed
$fsescaped = escapeshellparam($fsescaped);
}
my $getsnapcmd = "$rhost $mysudocmd $zfscmd get -Hpd 1 type,guid,creation $fsescaped |";
writelog('WARN', "snapshot listing failed, trying fallback command");
writelog('DEBUG', "FALLBACK, getting list of snapshots on $fs using $getsnapcmd...");
open FH, $getsnapcmd;
my @rawsnaps = <FH>;
close FH or die "CRITICAL ERROR: snapshots couldn't be listed for $fs (exit code $?)";
my %creationtimes=();
my $state = 0;
foreach my $line (@rawsnaps) {
if ($state < 0) {
$state++;
next;
} }
if ($state eq 0) {
if ($line !~ /\Q$fs\E\@.*\ttype\s*snapshot/) {
# skip non snapshot type object
$state = -2;
next;
}
} elsif ($state eq 1) {
if ($line !~ /\Q$fs\E\@.*\tguid/) {
die "CRITICAL ERROR: snapshots couldn't be listed for $fs (guid parser error)";
}
chomp $line;
my $guid = $line;
$guid =~ s/^.*\tguid\t*(\d*).*/$1/;
my $snap = $line;
$snap =~ s/^.*\@(.*)\tguid.*$/$1/;
if (!snapisincluded($snap)) { next; }
$snaps{$type}{$snap}{'guid'}=$guid;
} elsif ($state eq 2) {
if ($line !~ /\Q$fs\E\@.*\tcreation/) {
die "CRITICAL ERROR: snapshots couldn't be listed for $fs (creation parser error)";
}
chomp $line;
my $creation = $line;
$creation =~ s/^.*\tcreation\t*(\d*).*/$1/;
my $snap = $line;
$snap =~ s/^.*\@(.*)\tcreation.*$/$1/;
if (!snapisincluded($snap)) { next; }
# the accuracy of the creation timestamp is only for a second, but
# snapshots in the same second are highly likely. The list command
# has an ordered output so we append another three digit running number
# to the creation timestamp and make sure those are ordered correctly
# for snapshot with the same creation timestamp
my $counter = 0;
my $creationsuffix;
while ($counter < 999) {
$creationsuffix = sprintf("%s%03d", $creation, $counter);
if (!defined $creationtimes{$creationsuffix}) {
$creationtimes{$creationsuffix} = 1;
last;
}
$counter += 1;
}
$snaps{$type}{$snap}{'creation'}=$creationsuffix;
$state = -1;
}
$state++;
} }
return %snaps; return %snaps;
@ -1980,7 +1903,7 @@ sub getbookmarks() {
} }
my $error = 0; my $error = 0;
my $getbookmarkcmd = "$rhost $mysudocmd $zfscmd get -Hpd 1 -t bookmark guid,creation $fsescaped 2>&1 |"; my $getbookmarkcmd = "$rhost $mysudocmd $zfscmd get -Hpd 1 -t bookmark all $fsescaped 2>&1 |";
writelog('DEBUG', "getting list of bookmarks on $fs using $getbookmarkcmd..."); writelog('DEBUG', "getting list of bookmarks on $fs using $getbookmarkcmd...");
open FH, $getbookmarkcmd; open FH, $getbookmarkcmd;
my @rawbookmarks = <FH>; my @rawbookmarks = <FH>;
@ -1995,48 +1918,46 @@ sub getbookmarks() {
die "CRITICAL ERROR: bookmarks couldn't be listed for $fs (exit code $?)"; die "CRITICAL ERROR: bookmarks couldn't be listed for $fs (exit code $?)";
} }
# this is a little obnoxious. get guid,creation returns guid,creation on two separate lines my %bookmark_data;
# as though each were an entirely separate get command. my %creationtimes;
my $lastguid; for my $line (@rawbookmarks) {
my %creationtimes=(); chomp $line;
my ($dataset, $property, $value) = split /\t/, $line;
die "CRITICAL ERROR: Unexpected line format in $line" unless defined $value;
foreach my $line (@rawbookmarks) { my (undef, $bookmark) = split /#/, $dataset;
# only import bookmark guids, creation from the specified filesystem die "CRITICAL ERROR: Unexpected dataset format in $line" unless $bookmark;
if ($line =~ /\Q$fs\E\#.*\tguid/) {
chomp $line;
$lastguid = $line;
$lastguid =~ s/^.*\tguid\t*(\d*).*/$1/;
my $bookmark = $line;
$bookmark =~ s/^.*\#(.*)\tguid.*$/$1/;
$bookmarks{$lastguid}{'name'}=$bookmark;
} elsif ($line =~ /\Q$fs\E\#.*\tcreation/) {
chomp $line;
my $creation = $line;
$creation =~ s/^.*\tcreation\t*(\d*).*/$1/;
my $bookmark = $line;
$bookmark =~ s/^.*\#(.*)\tcreation.*$/$1/;
# the accuracy of the creation timestamp is only for a second, but $bookmark_data{$bookmark}{$property} = $value;
# bookmarks in the same second are possible. The list command
# has an ordered output so we append another three digit running number # the accuracy of the creation timestamp is only for a second, but
# to the creation timestamp and make sure those are ordered correctly # bookmarks in the same second are possible. The list command
# for bookmarks with the same creation timestamp # has an ordered output so we append another three digit running number
# to the creation timestamp and make sure those are ordered correctly
# for bookmarks with the same creation timestamp
if ($property eq 'creation') {
my $counter = 0; my $counter = 0;
my $creationsuffix; my $creationsuffix;
while ($counter < 999) { while ($counter < 999) {
$creationsuffix = sprintf("%s%03d", $creation, $counter); $creationsuffix = sprintf("%s%03d", $value, $counter);
if (!defined $creationtimes{$creationsuffix}) { if (!defined $creationtimes{$creationsuffix}) {
$creationtimes{$creationsuffix} = 1; $creationtimes{$creationsuffix} = 1;
last; last;
} }
$counter += 1; $counter += 1;
} }
$bookmark_data{$bookmark}{'creation'} = $creationsuffix;
$bookmarks{$lastguid}{'creation'}=$creationsuffix;
} }
} }
for my $bookmark (keys %bookmark_data) {
my $guid = $bookmark_data{$bookmark}{'guid'};
$bookmarks{$guid}{'name'} = $bookmark;
$bookmarks{$guid}{'creation'} = $bookmark_data{$bookmark}{'creation'};
$bookmarks{$guid}{'createtxg'} = $bookmark_data{$bookmark}{'createtxg'};
}
return %bookmarks; return %bookmarks;
} }

View File

@ -2,7 +2,7 @@
# run's all the available tests # run's all the available tests
for test in */; do for test in $(find . -mindepth 1 -maxdepth 1 -type d -printf "%P\n" | sort -g); do
if [ ! -x "${test}/run.sh" ]; then if [ ! -x "${test}/run.sh" ]; then
continue continue
fi fi

View File

@ -0,0 +1,46 @@
#!/bin/bash
# test verifying snapshots with out-of-order snapshot creation datetimes
set -x
set -e
. ../../common/lib.sh
POOL_IMAGE="/tmp/jimsalterjrs_sanoid_815.img"
POOL_SIZE="64M"
POOL_NAME="jimsalterjrs_sanoid_815"
truncate -s "${POOL_SIZE}" "${POOL_IMAGE}"
zpool create -m none -f "${POOL_NAME}" "${POOL_IMAGE}"
function cleanUp {
zpool export "${POOL_NAME}"
rm -f "${POOL_IMAGE}"
}
# export pool and remove the image in any case
trap cleanUp EXIT
zfs create "${POOL_NAME}"/before
zfs snapshot "${POOL_NAME}"/before@this-snapshot-should-make-it-into-the-after-dataset
disableTimeSync
setdate 1155533696
zfs snapshot "${POOL_NAME}"/before@oldest-snapshot
zfs snapshot "${POOL_NAME}"/before@another-snapshot-does-not-matter
../../../syncoid --sendoptions="Lec" "${POOL_NAME}"/before "${POOL_NAME}"/after
# verify
saveSnapshotList "${POOL_NAME}" "snapshot-list.txt"
grep "${POOL_NAME}/before@this-snapshot-should-make-it-into-the-after-dataset" "snapshot-list.txt" || exit $?
grep "${POOL_NAME}/after@this-snapshot-should-make-it-into-the-after-dataset" "snapshot-list.txt" || exit $?
grep "${POOL_NAME}/before@oldest-snapshot" "snapshot-list.txt" || exit $?
grep "${POOL_NAME}/after@oldest-snapshot" "snapshot-list.txt" || exit $?
grep "${POOL_NAME}/before@another-snapshot-does-not-matter" "snapshot-list.txt" || exit $?
grep "${POOL_NAME}/after@another-snapshot-does-not-matter" "snapshot-list.txt" || exit $?
exit 0

View File

@ -2,7 +2,7 @@
# run's all the available tests # run's all the available tests
for test in */; do for test in $(find . -mindepth 1 -maxdepth 1 -type d -printf "%P\n" | sort -g); do
if [ ! -x "${test}/run.sh" ]; then if [ ! -x "${test}/run.sh" ]; then
continue continue
fi fi