2014-05-15 56 views
3

我有一个Perl脚本,调用“的gsutil CP”复制选定从GCS到本地文件夹:的gsutil CP:并发执行导致本地文件损坏

$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版本:3.42在FreeBSD 8.2

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

Edward Leigh

回答

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)的gsutil目前不执行文件锁定。

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

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

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); 
}