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