e3c68b
From 29ec87484b1ee3ad6417c37726db8aa9296f3a83 Mon Sep 17 00:00:00 2001
e3c68b
From: Kotresh HR <khiremat@redhat.com>
e3c68b
Date: Wed, 8 May 2019 11:26:06 +0530
e3c68b
Subject: [PATCH 163/169] geo-rep: Fix sync hang with tarssh
e3c68b
e3c68b
Problem:
e3c68b
Geo-rep sync hangs when tarssh is used as sync
e3c68b
engine at heavy workload.
e3c68b
e3c68b
Analysis and Root cause:
e3c68b
It's found out that the tar process was hung.
e3c68b
When debugged further, it's found out that stderr
e3c68b
buffer of tar process on master was full i.e., 64k.
e3c68b
When the buffer was copied to a file from /proc/pid/fd/2,
e3c68b
the hang is resolved.
e3c68b
e3c68b
This can happen when files picked by tar process
e3c68b
to sync doesn't exist on master anymore. If this count
e3c68b
increases around 1k, the stderr buffer is filled up.
e3c68b
e3c68b
Fix:
e3c68b
The tar process is executed using Popen with stderr as PIPE.
e3c68b
The final execution is something like below.
e3c68b
e3c68b
tar | ssh <args> root@slave tar --overwrite -xf - -C <path>
e3c68b
e3c68b
It was waiting on ssh process first using communicate() and then tar.
e3c68b
Note that communicate() reads stdout and stderr. So when stderr of tar
e3c68b
process is filled up, there is no one to read until untar via ssh is
e3c68b
completed. This can't happen and leads to deadlock.
e3c68b
Hence we should be waiting on both process parallely, so that stderr is
e3c68b
read on both processes.
e3c68b
e3c68b
Backport of:
e3c68b
 > Patch: https://review.gluster.org/22684
e3c68b
 > Change-Id: I609c7cc5c07e210c504771115b4d551a2e891adf
e3c68b
 > BUG: 1707728
e3c68b
 > Signed-off-by: Kotresh HR <khiremat@redhat.com>
e3c68b
e3c68b
Change-Id: I609c7cc5c07e210c504771115b4d551a2e891adf
e3c68b
fixes: bz#1708116
e3c68b
Signed-off-by: Kotresh HR <khiremat@redhat.com>
e3c68b
Reviewed-on: https://code.engineering.redhat.com/gerrit/172395
e3c68b
Tested-by: RHGS Build Bot <nigelb@redhat.com>
e3c68b
Reviewed-by: Atin Mukherjee <amukherj@redhat.com>
e3c68b
---
e3c68b
 geo-replication/syncdaemon/resource.py |  22 ++++--
e3c68b
 tests/00-geo-rep/georep-stderr-hang.t  | 128 +++++++++++++++++++++++++++++++++
e3c68b
 tests/geo-rep.rc                       |  17 +++++
e3c68b
 3 files changed, 163 insertions(+), 4 deletions(-)
e3c68b
 create mode 100644 tests/00-geo-rep/georep-stderr-hang.t
e3c68b
e3c68b
diff --git a/geo-replication/syncdaemon/resource.py b/geo-replication/syncdaemon/resource.py
e3c68b
index 522279b..b16db60 100644
e3c68b
--- a/geo-replication/syncdaemon/resource.py
e3c68b
+++ b/geo-replication/syncdaemon/resource.py
e3c68b
@@ -1540,15 +1540,29 @@ class SSH(object):
e3c68b
 
e3c68b
         p0.stdin.close()
e3c68b
         p0.stdout.close()  # Allow p0 to receive a SIGPIPE if p1 exits.
e3c68b
-        # wait for tar to terminate, collecting any errors, further
e3c68b
-        # waiting for transfer to complete
e3c68b
-        _, stderr1 = p1.communicate()
e3c68b
 
e3c68b
         # stdin and stdout of p0 is already closed, Reset to None and
e3c68b
         # wait for child process to complete
e3c68b
         p0.stdin = None
e3c68b
         p0.stdout = None
e3c68b
-        p0.communicate()
e3c68b
+
e3c68b
+        def wait_for_tar(p0):
e3c68b
+            _, stderr = p0.communicate()
e3c68b
+            if log_err:
e3c68b
+                for errline in stderr.strip().split("\n")[:-1]:
e3c68b
+                    if "No such file or directory" not in errline:
e3c68b
+                        logging.error(lf("SYNC Error",
e3c68b
+                                         sync_engine="Tarssh",
e3c68b
+                                         error=errline))
e3c68b
+
e3c68b
+        t = syncdutils.Thread(target=wait_for_tar, args=(p0, ))
e3c68b
+        # wait for tar to terminate, collecting any errors, further
e3c68b
+        # waiting for transfer to complete
e3c68b
+        t.start()
e3c68b
+
e3c68b
+        # wait for ssh process
e3c68b
+        _, stderr1 = p1.communicate()
e3c68b
+        t.join()
e3c68b
 
e3c68b
         if log_err:
e3c68b
             for errline in stderr1.strip().split("\n")[:-1]:
e3c68b
diff --git a/tests/00-geo-rep/georep-stderr-hang.t b/tests/00-geo-rep/georep-stderr-hang.t
e3c68b
new file mode 100644
e3c68b
index 0000000..496f0e6
e3c68b
--- /dev/null
e3c68b
+++ b/tests/00-geo-rep/georep-stderr-hang.t
e3c68b
@@ -0,0 +1,128 @@
e3c68b
+#!/bin/bash
e3c68b
+
e3c68b
+. $(dirname $0)/../include.rc
e3c68b
+. $(dirname $0)/../volume.rc
e3c68b
+. $(dirname $0)/../geo-rep.rc
e3c68b
+. $(dirname $0)/../env.rc
e3c68b
+
e3c68b
+SCRIPT_TIMEOUT=500
e3c68b
+
e3c68b
+AREQUAL_PATH=$(dirname $0)/../utils
e3c68b
+test "`uname -s`" != "Linux" && {
e3c68b
+    CFLAGS="$CFLAGS -lintl";
e3c68b
+}
e3c68b
+build_tester $AREQUAL_PATH/arequal-checksum.c $CFLAGS
e3c68b
+
e3c68b
+### Basic Tests with Distribute Replicate volumes
e3c68b
+
e3c68b
+##Cleanup and start glusterd
e3c68b
+cleanup;
e3c68b
+TEST glusterd;
e3c68b
+TEST pidof glusterd
e3c68b
+
e3c68b
+
e3c68b
+##Variables
e3c68b
+GEOREP_CLI="$CLI volume geo-replication"
e3c68b
+master=$GMV0
e3c68b
+SH0="127.0.0.1"
e3c68b
+slave=${SH0}::${GSV0}
e3c68b
+num_active=2
e3c68b
+num_passive=2
e3c68b
+master_mnt=$M0
e3c68b
+slave_mnt=$M1
e3c68b
+
e3c68b
+############################################################
e3c68b
+#SETUP VOLUMES AND GEO-REPLICATION
e3c68b
+############################################################
e3c68b
+
e3c68b
+##create_and_start_master_volume
e3c68b
+TEST $CLI volume create $GMV0 $H0:$B0/${GMV0}1;
e3c68b
+TEST $CLI volume start $GMV0
e3c68b
+
e3c68b
+##create_and_start_slave_volume
e3c68b
+TEST $CLI volume create $GSV0 $H0:$B0/${GSV0}1;
e3c68b
+TEST $CLI volume start $GSV0
e3c68b
+TEST $CLI volume set $GSV0 performance.stat-prefetch off
e3c68b
+TEST $CLI volume set $GSV0 performance.quick-read off
e3c68b
+TEST $CLI volume set $GSV0 performance.readdir-ahead off
e3c68b
+TEST $CLI volume set $GSV0 performance.read-ahead off
e3c68b
+
e3c68b
+##Mount master
e3c68b
+TEST glusterfs -s $H0 --volfile-id $GMV0 $M0
e3c68b
+
e3c68b
+##Mount slave
e3c68b
+TEST glusterfs -s $H0 --volfile-id $GSV0 $M1
e3c68b
+
e3c68b
+############################################################
e3c68b
+#BASIC GEO-REPLICATION TESTS
e3c68b
+############################################################
e3c68b
+
e3c68b
+TEST create_georep_session $master $slave
e3c68b
+EXPECT_WITHIN $GEO_REP_TIMEOUT 1 check_status_num_rows "Created"
e3c68b
+
e3c68b
+#Config gluster-command-dir
e3c68b
+TEST $GEOREP_CLI $master $slave config gluster-command-dir ${GLUSTER_CMD_DIR}
e3c68b
+
e3c68b
+#Config gluster-command-dir
e3c68b
+TEST $GEOREP_CLI $master $slave config slave-gluster-command-dir ${GLUSTER_CMD_DIR}
e3c68b
+
e3c68b
+#Set changelog roll-over time to 45 secs
e3c68b
+TEST $CLI volume set $GMV0 changelog.rollover-time 45
e3c68b
+
e3c68b
+#Wait for common secret pem file to be created
e3c68b
+EXPECT_WITHIN $GEO_REP_TIMEOUT  0 check_common_secret_file
e3c68b
+
e3c68b
+#Verify the keys are distributed
e3c68b
+EXPECT_WITHIN $GEO_REP_TIMEOUT  0 check_keys_distributed
e3c68b
+
e3c68b
+#Set sync-jobs to 1
e3c68b
+TEST $GEOREP_CLI $master $slave config sync-jobs 1
e3c68b
+
e3c68b
+#Start_georep
e3c68b
+TEST $GEOREP_CLI $master $slave start
e3c68b
+
e3c68b
+touch $M0
e3c68b
+EXPECT_WITHIN $GEO_REP_TIMEOUT  1 check_status_num_rows "Active"
e3c68b
+EXPECT_WITHIN $GEO_REP_TIMEOUT  1 check_status_num_rows "Changelog Crawl"
e3c68b
+
e3c68b
+#Check History Crawl.
e3c68b
+TEST $GEOREP_CLI $master $slave stop
e3c68b
+TEST create_data_hang "rsync_hang"
e3c68b
+TEST create_data "history_rsync"
e3c68b
+TEST $GEOREP_CLI $master $slave start
e3c68b
+EXPECT_WITHIN $GEO_REP_TIMEOUT  1 check_status_num_rows "Active"
e3c68b
+
e3c68b
+#Verify arequal for whole volume
e3c68b
+EXPECT_WITHIN $GEO_REP_TIMEOUT "x0" arequal_checksum ${master_mnt} ${slave_mnt}
e3c68b
+
e3c68b
+#Stop Geo-rep
e3c68b
+TEST $GEOREP_CLI $master $slave stop
e3c68b
+
e3c68b
+#Config tarssh as sync-engine
e3c68b
+TEST $GEOREP_CLI $master $slave config sync-method tarssh
e3c68b
+
e3c68b
+#Create tarssh hang data
e3c68b
+TEST create_data_hang "tarssh_hang"
e3c68b
+TEST create_data "history_tar"
e3c68b
+
e3c68b
+TEST $GEOREP_CLI $master $slave start
e3c68b
+EXPECT_WITHIN $GEO_REP_TIMEOUT  1 check_status_num_rows "Active"
e3c68b
+
e3c68b
+#Verify arequal for whole volume
e3c68b
+EXPECT_WITHIN $GEO_REP_TIMEOUT "x0" arequal_checksum ${master_mnt} ${slave_mnt}
e3c68b
+
e3c68b
+#Stop Geo-rep
e3c68b
+TEST $GEOREP_CLI $master $slave stop
e3c68b
+
e3c68b
+#Delete Geo-rep
e3c68b
+TEST $GEOREP_CLI $master $slave delete
e3c68b
+
e3c68b
+#Cleanup are-equal binary
e3c68b
+TEST rm $AREQUAL_PATH/arequal-checksum
e3c68b
+
e3c68b
+#Cleanup authorized keys
e3c68b
+sed -i '/^command=.*SSH_ORIGINAL_COMMAND#.*/d' ~/.ssh/authorized_keys
e3c68b
+sed -i '/^command=.*gsyncd.*/d' ~/.ssh/authorized_keys
e3c68b
+
e3c68b
+cleanup;
e3c68b
+#G_TESTDEF_TEST_STATUS_NETBSD7=BAD_TEST,BUG=000000
e3c68b
diff --git a/tests/geo-rep.rc b/tests/geo-rep.rc
e3c68b
index 2035b9f..e4f014e 100644
e3c68b
--- a/tests/geo-rep.rc
e3c68b
+++ b/tests/geo-rep.rc
e3c68b
@@ -101,6 +101,23 @@ function create_data()
e3c68b
     chown 1000:1000 ${master_mnt}/${prefix}_chown_f1_ಸಂತಸ
e3c68b
 }
e3c68b
 
e3c68b
+function create_data_hang()
e3c68b
+{
e3c68b
+    prefix=$1
e3c68b
+    mkdir ${master_mnt}/${prefix}
e3c68b
+    cd ${master_mnt}/${prefix}
e3c68b
+    # ~1k files is required with 1 sync-job and hang happens if
e3c68b
+    # stderr buffer of tar/ssh executed with Popen is full (i.e., 64k).
e3c68b
+    # 64k is hit when ~800 files were  not found while syncing data
e3c68b
+    # from master. So around 1k files is required to hit the condition.
e3c68b
+    for i in {1..1000}
e3c68b
+    do
e3c68b
+        echo "test data" > file$i
e3c68b
+        mv -f file$i file
e3c68b
+    done
e3c68b
+    cd -
e3c68b
+}
e3c68b
+
e3c68b
 function chown_file_ok()
e3c68b
 {
e3c68b
     local file_owner=$(stat --format "%u:%g" "$1")
e3c68b
-- 
e3c68b
1.8.3.1
e3c68b