2014-05-15 3 views
3

У меня есть сценарий Perl, который называет «GSUtil ф», чтобы скопировать выбранный из из ГКС в локальную папку:GSUtil ф: одновременное выполнение приводит к локальному повреждению файла

$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 удаляет целевой файл, если есть несоответствие подписи, но это явно не всегда происходит.

Файлы, о которых идет речь, больше 2 МБ (обычно около 5 МБ), поэтому может быть некоторое взаимодействие с функцией автоматического возобновления. Perl-скрипт вызывает gsutil только в том случае, если локальный файл еще не существует, но это не делает двойной щелчок (из-за задержки времени для аутентификации передачи GCS).

версия GSUtil: 3,42 на FreeBSD 8.2

Любой испытывает подобную проблему? Любой, у кого есть какие-либо идеи?

Эдвард Ли

ответ

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 не защищает от других экземпляров gsutil, выполняемых одновременно с пересекающимся пунктом назначения.

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); 
} 
Смежные вопросы