perforce备份问题

时间:2010-12-14 15:08:01

标签: perforce

出于安全考虑,是否足以备份perforce服务器目录下的所有文件?

3 个答案:

答案 0 :(得分:6)

简答:没有 答案很简单:Manual详细介绍了有关Perforce数据备份和恢复的所有信息。简而言之,对于不耐烦的人:

  1. p4验证// ... (验证服务器的完整性)
  2. p4 admin checkpoint
    (制作检查点;确保此步骤成功)
  3. 备份检查点文件和旧日志文件
    (如果你运行Perforce with Journal files,你应该这样做)
  4. 备份您的版本文件
    (这是实际数据,不要与Perforce服务器目录中的db。*文件混淆。)
  5. 但请阅读本手册,尤其是有关各种还原方案的内容。记得: 备份通常可以正常工作,这是恢复失败。

答案 1 :(得分:4)

除了jhwist在p4手动答案(permalink)中的正确性之外,我想补充一些我在使用Perforce几年时学到的东西。

...

根据存储库的大小,在p4数据库上执行验证可能需要几个小时,在此期间将锁定,并且没有人能够执行任何查询。锁定P4数据库可能会对用户产生多种流量效应,例如:如果有人在此期间使用或尝试使用P4 P4SCC插件(即视觉工作室集成),它将旋转,用户最终会不得不强行退出以重新获得控制权。

<强>解决方案

  1. 在另一个端口(p4d_2)
  2. 上生成P4D的第二个实例
  3. 暂停/终止主实例(p4d_1)。
  4. 使用p4d_2执行p4 verify //...和检查点。
  5. 备份存储阵列上的物理版本文件。
  6. 杀死p4d_2。
  7. 重新启动p4d_1。
  8. 另外:由于这很可能是在晚上或周末运行的自动化流程,因此无法强调您需要彻底阅读检查点日志文件以确保它是成功的否则当你需要执行恢复时你会遇到困难(阅读下一点)。备份不应该是一个设置和忘记过程。

    有关Perforce备份的更多信息,请参阅Perforce白皮书:High Availability And Disaster Recovery Solutions For Perforce

    HTH,

答案 2 :(得分:0)

FWIW我在自己的开发工作站上使用了额外的备份策略。我有一个每晚运行的perl脚本,并从给定的工作空间列表中查找我从Perforce中检出的所有文件。然后,该文件列表将作为我的常规工作站备份过程的一部分进行备份。用于查找已签出文件的Perl脚本对我来说非常棘手。我没有写它并且对Perl不是特别熟悉。

如果有人有兴趣,我可以在这里发布脚本以及我如何调用它。

请注意,此脚本是在Perforce推出其“搁置”功能之前开发的。我现在可能会更好地拥有一个脚本,每晚“搁置”我的工作(除了我当前的备份策略或代替它)。

这是脚本:

# This script copies any files that are opened for any action (other than
# delete) in the specified client workspace to another specified directory.
# The directory structure of the workspace is duplicated in the target
# directory.  Furthermore, a file is not copied if it already exists in the
# target directory unless the file in the workspace is newer than the one
# in the target directory.

# Note: This script looks at *all* pending changelists in the specified
# workspace.
# Note: This script uses the client specification Root to get the local
# pathname of the files.  So if you are using a substituted drive for the
# client root, it must be properly substituted before running this script.

# Argument 1: Client workspace name
# Argument 2: Target directory (full path)

use File::Path;
# use File::Copy;
use File::Basename;
use Win32;

if ($#ARGV != 1) {
    die("usage: $0 client_name target_directory\n");
}

my $client = shift(@ARGV);
my $target_dir = shift(@ARGV);
my @opened_files = ();
my $client_root = "";
my $files_copied = 0;

# I need to know the root directory of the client, so that I can derive the
# local pathname of the file.  Strange that "p4 -ztag opened" doesn't give
# me the local pathname; I would have expected it to.

open(CLIENT_SPEC, "p4 -c $client client -o|")
        || die("Cannot retrieve client specification: $!");
while (<CLIENT_SPEC>) {
    my ($tag, $value) = split(/\s/, $_, 2);
    if ($tag eq "Root:") {
        $value = chop_line($value);
        $client_root = $value;
    }
}
close(CLIENT_SPEC);
if ($client_root eq "") {
    die("Unable to determine root of client $client\n");
} elsif (substr($client_root, -1) ne "\\") {
    $client_root = $client_root . "\\";
} 

# Use the -ztag option so that we can get the client file path as well as
# the depot path.

open(OPENED_FILES, "p4 -c $client -ztag opened|")
        || die("Cannot get list of opened files: $!");
while (<OPENED_FILES>) {
    # What we do is to get the client path and append it onto the
    # @opened_files array.  Then when we get the action, if it is a delete,
    # we pop the last entry back off the array.  This assumes that the tags
    # come out with clientFile before action.

    $_ = chop_line($_);
    my ($prefix, $tag, $value) = split(/\s/, $_, 3);
    if ($tag eq "clientFile") {
        push(@opened_files, $value);
    }
    if ( ($tag eq "action") && ($value eq "delete") ) {
        pop(@opened_files);
    }
}
close(OPENED_FILES);

# Okay, now we have the list of opened files.  Process each file to
# copy it to the destination.

foreach $client_path (@opened_files) {

    # Trim off the client name and replace it with the client root
    # directory.  Also replace forward slashes with backslashes.

    $client_path = substr($client_path, length($client) + 3);
    $client_path =~ s/\//\\/g;
    my $local_path = $client_root . $client_path;

    # Okay, now $client_path is the partial pathname starting at the
    # client's root.  That's the path we also want to use starting at the
    # target path for the destination.

    my $dest_path = $target_dir . "\\" . $client_path;
    my $copy_it = 0;

    if (-e $dest_path) {
        # Target exists.  Is the local path newer?
        my @target_stat = stat($dest_path);
        my @local_stat = stat($local_path);

        if ($local_stat[9] > $target_stat[9]) {
            $copy_it = 1;
        }
    } else {
        # Target does not exist, definitely copy it.  But we may have to
        # create some directories.  Use File::Path to do that.

        my ($basename, $dest_dir) = fileparse($dest_path);
        if (! (-e $dest_dir)) {
            mkpath($dest_dir) || die("Cannot create directory $dest_dir\n");
        }
        $copy_it = 1;
    }

    if ($copy_it) {
        Win32::CopyFile($local_path, $dest_path, 1)
                || warn("Could not copy file $local_path: $!\n");
        $files_copied++;
    }
}
print("$files_copied files copied.\n");

exit(0);

################ Subroutines #########################################

# chop_line removes any trailing carriage-returns or newlines from its
# argument and returns the possibly-modified string.

sub chop_line {
    my $string = shift;

    $string =~ s/[\r\n]*\z//;
    return $string;
}

运行:

REM Make sure that we are pointing to the current Perforce server
P4 set -s P4PORT=MyPerforceServer:ThePortThatPerforceIsOn


p4 set p4client=MyPerforceWorkspace

REM Copy checked out files to a local directory that will be backed up
.\p4backup.pl MyPerforceWorkspace c:\PerforceBackups\MyPerforceWorkspace_backup