Использование Flock в Bash, поэтому запрос выполняется только один раз - PullRequest
0 голосов
/ 27 июня 2018

Я пытаюсь настроить мой скрипт таким образом, чтобы

  • Если некоторые данные недоступны, попробуйте получить их
  • Если другой процесс уже извлекает его, подождите, пока этот процесс завершится
  • Использовать данные

А с здесь Я нашел очень хороший пример стада:

exec 200>$pidfile
flock -n 200 || exit 1
pid=$$
echo $pid 1>&200

И это не удастся, если он не сможет захватить замок (флаг -n).

Можно ли предположить, что это означает, что другой файл заблокировал $pidfile, и как я могу определить, что блокировка снята в другом процессе?

Я понимаю, что wait $pid будет ждать, пока этот процесс не будет завершен, и поэтому, если есть какой-то способ записать, какой процесс в данный момент удерживает блокировку, или просто обнаружить разблокировку, чтобы другие процессы знали, когда данные будут доступны, тогда я думаю, это будет работать.

Есть идеи?

Ответы [ 2 ]

0 голосов
/ 05 июля 2018

Мое решение использует два файла, pid.temp и data.temp:

backgroundGetData() {
    local data=$1

    # if global is null, check file.
    if [ -z "$data" ]; then
        data=$( cat $DATA_TEMP_FILE 2>/dev/null )
    fi

    # if file is empty, check process is making the request
    if [ -z "$data" ]; then
        for i in {1..5}; do
            echo "INFO - Process: $BASHPID - Attempting to lock data temp file" >&2
            local request_pid=$( cat $PID_FILE 2>/dev/null )
            if [ -z "$request_pid" ]; then request_pid=0; fi
            local exit_code=1
            if [ "$request_pid" -eq 0 ]; then
                ( flock -n 200 || exit 1
                    echo "INFO - Process: $BASHPID - Fetching data." >&2
                    echo "$BASHPID">"$PID_FILE"
                    getData > $DATA_TEMP_FILE
                ) 200>$DATA_TEMP_FILE
                exit_code=$?
            fi

            echo "INFO - Process: $BASHPID - returned $exit_code from lock attempt.">&2
            [ $request_pid -ne 0 ] && echo "INFO - Process: $BASHPID - $request_pid is possibly locking">&2
            if [ $exit_code -ne 0 ] && [ $request_pid -ne 0 ]; then
                echo "INFO - Process: $BASHPID - waiting on $request_pid to complete">&2
                tail --pid=${request_pid} -f /dev/null
                echo "INFO - Process: $BASHPID - finished waiting.">&2
                break
            elif [ $exit_code -eq 0 ]; then break;
            else
                sleep 2
            fi
        done
        data=$( cat $DATA_TEMP_FILE )
        if [ -z "$data" ]; then
            echo "WARN - Process: $BASHPID - Failed to retrieve data.">&2
        fi
    fi
    echo "$least_loaded"
}

И его можно использовать так:

DATA=""
DATA_TEMP_FILE="data.temp"
PID_FILE="pid.temp"
$( backgroundGetData $DATA ) & ## Begin making request

doThing() {
    if [ -z $DATA ]; then
        # Redirect 3 to stdout, then use sterr in backgroundGetData to 3 so that
        # logging messages can be shown and output can also be captued in variable.
        exec 3>&1
        DATA=$( backgroundGetData $DATA 2>&3)
    fi
}

for job in "$jobs"; do
    doThing &
done 

Это работает для меня, хотя я не уверен на 100%, насколько это безопасно.

0 голосов
/ 27 июня 2018

Согласно стаю (1) справочная страница ,

если блокировка не может быть немедленно получена, [при отсутствии -w таймаута], flock ожидает, пока блокировка не будет доступна

Вы можете использовать fuser, чтобы увидеть, какой процесс содержит дескриптор файла.

...