3

我有一个 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 会删除目标文件,但这显然并不总是发生。

有问题的文件大于 2MB(通常约为 5MB),因此可能与自动恢复功能有一些交互。Perl 脚本仅在本地文件不存在时调用 gsutil,但这不会捕获双击(因为 GCS 传输身份验证的时间滞后)。

gsutil 版本:FreeBSD 8.2 上的 3.42

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

爱德华·利

4

3 回答 3

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";
}
于 2014-05-15T16:10:16.857 回答
1

1) gsutil 目前不做文件锁定。

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

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

于 2014-05-15T16:47:31.863 回答
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);
}
于 2014-05-15T18:24:12.093 回答