gsutil cp:并发执行会导致本地文件损坏

时间:2014-05-15 14:57:16

标签: perl google-cloud-storage corruption cp gsutil

我有一个Perl脚本,它调用'gsutil cp'将选定的GCS复制到本地文件夹:

$cmd = "[bin-path]/gsutil cp -n gs://[gcs-file-path] [local-folder]";
$output = `$cmd 2>&1`;

脚本通过HTTP调用,因此可以多次启动(例如通过双击链接)。当发生这种情况时,本地文件最终可能正好是正确大小的两倍,因此显然会损坏。三件事看起来很奇怪:

  1. gsutil在写入时似乎没有锁定本地文件 它,允许另一个线程(在这种情况下是另一个gsutil实例) 写入同一个文件。

  2. '-n'似乎没有效果。我原以为它会阻止它 gsutil的第二个实例尝试复制操作。

  3. MD5签名检查失败:通常gsutil删除 目标文件,如果签名不匹配,但这是显而易见的 不总是发生。

  4. 有问题的文件大于2MB(通常约为5MB),因此可能会与自动简历功能进行一些交互。如果本地文件尚不存在,则Perl脚本仅调用gsutil,但这不会发生双击(因为GCS传输身份验证的时间滞后)。

    gsutil版本:FreeBSD 8.2上的3.42

    遇到类似问题的人?任何有见解的人?

    Edward Leigh

3 个答案:

答案 0 :(得分:2)

1)你是对的,我没有看到来源锁定。

2)这可能是由竞争条件引起的 - 进程1检查,看到文件不存在。进程2检查,看到文件不存在。流程1开始上传。流程2开始上传。文档说这是在实际上传过程之前的HEAD操作 - 这与实际上传不是原子的。

3)没有输入。

您可以通过让您的脚本在启动传输之前在文件上保持某种原子锁来解决问题 - 即您的检查将是以下内容:

use Lock::File qw(lockfile);

if (my $lock = lockfile("$localfile.lock", { blocking => 0 } )) {
     ... perform transfer ...
     undef $lock;
}
else {
    die "Unable to retrieve $localfile, file is locked";
}

答案 1 :(得分:1)

1)gsutil目前没有文件锁定。

2)-n不能防止gsutil与重叠目标同时运行的其他实例。

3)散列摘要是在作为性能优化下载时的字节数上计算的。这样可以避免下载完成后长时间运行的计算。如果哈希验证成功,则可以保证在一个点上成功写入字节。但是,如果某个(甚至是gsutil的另一个实例)在进程运行时就地修改了内容,那么消化器将不会检测到它。

答案 2 :(得分:0)

感谢Oesor和Travis回答他们之间的所有观点。作为Oesor建议的解决方案的附录,我为缺少Lock :: File的系统提供了这种替代方案:

use Fcntl ':flock'; # import LOCK_* constants

# if lock file exists ...
if (-e($lockFile))
{
  # abort if lock file still locked (or sleep and re-check)
  abort() if !unlink($lockFile);
  # otherwise delete local file and download again
  unlink($filePath);
}

# if file has not been downloaded already ...
if (!-e($filePath))
{
  $cmd = "[bin-path]/gsutil cp -n gs://[gcs-file-path] [local-dir]";

  abort() if !open(LOCKFILE, ">$lockFile");
  flock(LOCKFILE, LOCK_EX);
  my $output = `$cmd 2>&1`;
  flock(LOCKFILE, LOCK_UN);
  unlink($lockFile);
}