設定:バックグラウンドプロセスパイプラインで生成されたファイルを含むbashシステムがあります。パイプラインの最初のステップでは、複数のプロセスが単一の名前付きパイプに書き込みます。パイプの次のステップには、名前付きパイプを読み取り、そのデータを別のワーカープロセスに渡してデータ操作を実行するリーダープロセスがあります。 (下記のサンプルスクリプトを参照してください)。 httpsを介してリモートリソースと通信してデータが準備されていないと判断した場合、これらのワーカーはデータをstage2パイプラインに返すことができます。
問題:パイプラインの2番目のステップでワーカープロセス内にssh呼び出しを追加するまで、この設定はマイナーロードでもうまく機能しました。これにより、Named Pipeを通過する多くのデータが消え始めました。これは、以前のワークロードの2%に負荷を大幅に削減しても発生します。
私はいくつかの読書をし、sshクライアントがfdを破壊することに関するあいまいな参照を見つけたので、これが関連しているかどうかはわかりません。
環境:現在、Ubuntu 20.04でbash 5.0.17を使用しています。また、Ubuntu 22.04システムでテストしましたが、同じ動作を確認しました。
簡略化されたスクリプト
#!/bin/bash
function stage1_worker()
{
# Do work including network calls via a 3rd party program which uses python
flock --exclusive $QUEUE -c "print \"%s\n\" $DATA > $QUEUE"
}
function stage2_reader_v1()
{
local QUEUE_DATA=""
while true ;
do
# logic
if read QUEUE_DATA ; then
stage2_worker $QUEUE_DATA &
fi
# logic
done < $QUEUE
}
function stage2_worker_v1()
{
local QUEUE_DATA=$1
local retry_needed=false
# logic
# Add back to the stage 2 queue if retry needed (reader rate limits to stage 2 worker to keep process count down)
if $retry_needed ; then
flock --exclusive $QUEUE -c "print \"%s\n\" $DATA > $QUEUE"
fi
}
QUEUE=$(mktemp -u)
mkfifo $QUEUE
trap "rm -f $QUEUE" EXIT
# Launch stage 2 reader in background process
stage2_reader &
# Launch several stage 1 workers...
stage1_worker $ARG &
# wait for all background processes to complete
wait
このように変更すると、突然キュー内のデータが失われ始め、エントリは処理されませんでした。
function stage2_worker_v2()
{
local QUEUE_DATA=$1
local retry_needed=false
# logic
local IP_FROM_QUEUE_DATA=... #logic
ssh -o ConnectTimeout=1 -o BatchMode=yes user@$IP_FROM_QUEUE_DATA '<remote command' >/dev/null 2>&1
if [[ $? -ne 0 ]] ; then
#handle failure
fi
# Add back to the stage 2 queue if retry needed (reader rate limits to stage 2 worker to keep process count down)
if $retry_needed ; then
flock --exclusive $QUEUE -c "print \"%s\n\" $DATA > $QUEUE"
fi
}
永久に開かれた fd を制限する努力で、リーダーをこれに変更することでスループットが向上しましたが、まだデータがありません。
function stage2_reader_v1()
{
local QUEUE_DATA=""
while true ;
do
# logic
if read -t 1 QUEUE_DATA <> $QUEUE ; then
stage2_worker $QUEUE_DATA &
fi
# logic
done
}
stage2_worker_v2からssh呼び出しを削除すると、すべてのデータが期待どおりにパイプを通過します。
なぜこれが起こるのか、解決策を教えてください。
この単純化されたスクリプトをすばやく作成し、実際のコードには存在しない小さな構文/名前付けエラーがある可能性があります。
ベストアンサー1
名前付きパイプを妨げてはいけませんが、標準入力(パイプからデータを読み取ろうとするなど)を妨げます。呼び出すと、stage2_worker
標準入力がパイプからプロセスにリダイレクトされますssh
。
while true; do
if read QUEUE_DATA ; then
stage2_worker $QUEUE_DATA &
fi
done < $QUEUE
function stage2_worker_v2()
{
...
ssh -o ConnectTimeout=1 -o BatchMode=yes user@$IP
ssh
標準入力を他の場所からリダイレクトしたりssh ... < /dev/null
(または基本的に同じ機能を使用ssh -n
)、ループ内の項目への標準入力を妨げないように、whileループ内でリダイレクトを実行します。いくつかの変更があります:
read
fd 3から読み込み、入力をそこにリダイレクトするように指示します(Bash):
while true; do
if read QUEUE_DATA -u 3; then
stage2_worker $QUEUE_DATA &
fi
done 3< $QUEUE
同じですが、異なるリダイレクトがあります。
while true; do
if read QUEUE_DATA <&3; then
stage2_worker $QUEUE_DATA &
fi
done 3< $QUEUE
またはパイプからリダイレクトするだけですread
。
while true; do
if read QUEUE_DATA < $QUEUE; then
stage2_worker $QUEUE_DATA &
fi
done
いずれにせよ、値にスペースやワイルドカードが含まれている場合に備えて、これらの変数拡張を引用することを検討できます。また、関数を宣言する標準的な方法はkshスタイルと標準的な方法を混在させたfuncname()
whileであり、Bash(AFAIR)でのみサポートされています。function funcname()