e7a346
From 6fb1804d113ae996e085ef0f23fa8908d167f006 Mon Sep 17 00:00:00 2001
e7a346
From: Ravishankar N <ravishankar@redhat.com>
e7a346
Date: Thu, 14 Jun 2018 12:59:06 +0530
e7a346
Subject: [PATCH 299/305] afr: heal gfids when file is not present on all
e7a346
 bricks
e7a346
e7a346
Backport of: https://review.gluster.org/#/c/20271/
e7a346
commit f73814ad08d552d94d0139b2592175d206e7a166 (rhgs) introduced a regression
e7a346
wherein if a file is present in only 1 brick of replica *and* doesn't
e7a346
have a gfid associated with it, it doesn't get healed upon the next
e7a346
lookup from the client. Fix it.
e7a346
e7a346
Change-Id: I7d1111dcb45b1b8b8340a7d02558f05df70aa599
e7a346
BUG: 1592666
e7a346
Signed-off-by: Ravishankar N <ravishankar@redhat.com>
e7a346
Reviewed-on: https://code.engineering.redhat.com/gerrit/141899
e7a346
Tested-by: RHGS Build Bot <nigelb@redhat.com>
e7a346
Reviewed-by: Karthik Subrahmanya <ksubrahm@redhat.com>
e7a346
---
e7a346
 .../replicate/bug-1591193-assign-gfid-and-heal.t   | 128 +++++++++++++++++++++
e7a346
 xlators/cluster/afr/src/afr-self-heal-common.c     |  39 ++++++-
e7a346
 xlators/cluster/afr/src/afr-self-heal-data.c       |   8 +-
e7a346
 xlators/cluster/afr/src/afr-self-heal-entry.c      |   4 +-
e7a346
 xlators/cluster/afr/src/afr-self-heal-name.c       |   6 +-
e7a346
 xlators/cluster/afr/src/afr-self-heal.h            |   6 +-
e7a346
 6 files changed, 179 insertions(+), 12 deletions(-)
e7a346
 create mode 100644 tests/bugs/replicate/bug-1591193-assign-gfid-and-heal.t
e7a346
e7a346
diff --git a/tests/bugs/replicate/bug-1591193-assign-gfid-and-heal.t b/tests/bugs/replicate/bug-1591193-assign-gfid-and-heal.t
e7a346
new file mode 100644
e7a346
index 0000000..d3b5f9a
e7a346
--- /dev/null
e7a346
+++ b/tests/bugs/replicate/bug-1591193-assign-gfid-and-heal.t
e7a346
@@ -0,0 +1,128 @@
e7a346
+#!/bin/bash
e7a346
+
e7a346
+. $(dirname $0)/../../include.rc
e7a346
+. $(dirname $0)/../../volume.rc
e7a346
+. $(dirname $0)/../../afr.rc
e7a346
+
e7a346
+cleanup;
e7a346
+
e7a346
+function check_gfid_and_link_count
e7a346
+{
e7a346
+        local file=$1
e7a346
+
e7a346
+        file_gfid_b0=$(gf_get_gfid_xattr $B0/${V0}0/$file)
e7a346
+        TEST [ ! -z $file_gfid_b0 ]
e7a346
+        file_gfid_b1=$(gf_get_gfid_xattr $B0/${V0}1/$file)
e7a346
+        file_gfid_b2=$(gf_get_gfid_xattr $B0/${V0}2/$file)
e7a346
+        EXPECT $file_gfid_b0 echo $file_gfid_b1
e7a346
+        EXPECT $file_gfid_b0 echo $file_gfid_b2
e7a346
+
e7a346
+        EXPECT "2" stat -c %h $B0/${V0}0/$file
e7a346
+        EXPECT "2" stat -c %h $B0/${V0}1/$file
e7a346
+        EXPECT "2" stat -c %h $B0/${V0}2/$file
e7a346
+}
e7a346
+TESTS_EXPECTED_IN_LOOP=30
e7a346
+
e7a346
+##############################################################################
e7a346
+# Test on 1x3 volume
e7a346
+TEST glusterd
e7a346
+TEST pidof glusterd
e7a346
+TEST $CLI volume create $V0 replica 3 $H0:$B0/${V0}{0,1,2};
e7a346
+TEST $CLI volume start $V0;
e7a346
+
e7a346
+EXPECT_WITHIN $PROCESS_UP_TIMEOUT "1" brick_up_status $V0 $H0 $B0/${V0}0
e7a346
+EXPECT_WITHIN $PROCESS_UP_TIMEOUT "1" brick_up_status $V0 $H0 $B0/${V0}1
e7a346
+EXPECT_WITHIN $PROCESS_UP_TIMEOUT "1" brick_up_status $V0 $H0 $B0/${V0}2
e7a346
+
e7a346
+EXPECT_WITHIN $PROCESS_UP_TIMEOUT "Y" glustershd_up_status
e7a346
+EXPECT_WITHIN $CHILD_UP_TIMEOUT "1" afr_child_up_status_in_shd $V0 0
e7a346
+EXPECT_WITHIN $CHILD_UP_TIMEOUT "1" afr_child_up_status_in_shd $V0 1
e7a346
+EXPECT_WITHIN $CHILD_UP_TIMEOUT "1" afr_child_up_status_in_shd $V0 2
e7a346
+
e7a346
+TEST glusterfs --volfile-id=/$V0 --volfile-server=$H0 --attribute-timeout=0 --entry-timeout=0 $M0
e7a346
+EXPECT_WITHIN $PROCESS_UP_TIMEOUT "1" afr_child_up_status $V0 0
e7a346
+EXPECT_WITHIN $PROCESS_UP_TIMEOUT "1" afr_child_up_status $V0 1
e7a346
+EXPECT_WITHIN $PROCESS_UP_TIMEOUT "1" afr_child_up_status $V0 2
e7a346
+
e7a346
+
e7a346
+# Create files directly in the backend on different bricks
e7a346
+echo $RANDOM >> $B0/${V0}0/file1
e7a346
+echo $RANDOM >> $B0/${V0}1/file2
e7a346
+echo $RANDOM >> $B0/${V0}2/file3
e7a346
+
e7a346
+# To prevent is_fresh_file code path
e7a346
+sleep 2
e7a346
+
e7a346
+# Access them from mount to trigger name + gfid heal.
e7a346
+TEST stat $M0/file1
e7a346
+TEST stat $M0/file2
e7a346
+TEST stat $M0/file3
e7a346
+
e7a346
+# Launch index heal to complete any pending data/metadata heals.
e7a346
+TEST $CLI volume heal $V0
e7a346
+EXPECT_WITHIN $HEAL_TIMEOUT "^0$" get_pending_heal_count $V0
e7a346
+
e7a346
+# Check each file has a gfid and the .glusterfs hardlink
e7a346
+check_gfid_and_link_count file1
e7a346
+check_gfid_and_link_count file2
e7a346
+check_gfid_and_link_count file3
e7a346
+
e7a346
+TEST rm $M0/file1
e7a346
+TEST rm $M0/file2
e7a346
+TEST rm $M0/file3
e7a346
+cleanup;
e7a346
+
e7a346
+##############################################################################
e7a346
+# Test on 1x (2+1) volume
e7a346
+TEST glusterd
e7a346
+TEST pidof glusterd
e7a346
+TEST $CLI volume create $V0 replica 3 arbiter 1 $H0:$B0/${V0}{0,1,2};
e7a346
+TEST $CLI volume start $V0;
e7a346
+
e7a346
+EXPECT_WITHIN $PROCESS_UP_TIMEOUT "1" brick_up_status $V0 $H0 $B0/${V0}0
e7a346
+EXPECT_WITHIN $PROCESS_UP_TIMEOUT "1" brick_up_status $V0 $H0 $B0/${V0}1
e7a346
+EXPECT_WITHIN $PROCESS_UP_TIMEOUT "1" brick_up_status $V0 $H0 $B0/${V0}2
e7a346
+
e7a346
+EXPECT_WITHIN $PROCESS_UP_TIMEOUT "Y" glustershd_up_status
e7a346
+EXPECT_WITHIN $CHILD_UP_TIMEOUT "1" afr_child_up_status_in_shd $V0 0
e7a346
+EXPECT_WITHIN $CHILD_UP_TIMEOUT "1" afr_child_up_status_in_shd $V0 1
e7a346
+EXPECT_WITHIN $CHILD_UP_TIMEOUT "1" afr_child_up_status_in_shd $V0 2
e7a346
+
e7a346
+TEST glusterfs --volfile-id=/$V0 --volfile-server=$H0 --attribute-timeout=0 --entry-timeout=0 $M0
e7a346
+EXPECT_WITHIN $PROCESS_UP_TIMEOUT "1" afr_child_up_status $V0 0
e7a346
+EXPECT_WITHIN $PROCESS_UP_TIMEOUT "1" afr_child_up_status $V0 1
e7a346
+EXPECT_WITHIN $PROCESS_UP_TIMEOUT "1" afr_child_up_status $V0 2
e7a346
+
e7a346
+
e7a346
+# Create files directly in the backend on different bricks
e7a346
+echo $RANDOM >> $B0/${V0}0/file1
e7a346
+echo $RANDOM >> $B0/${V0}1/file2
e7a346
+touch $B0/${V0}2/file3
e7a346
+
e7a346
+# To prevent is_fresh_file code path
e7a346
+sleep 2
e7a346
+
e7a346
+# Access them from mount to trigger name + gfid heal.
e7a346
+TEST stat $M0/file1
e7a346
+TEST stat $M0/file2
e7a346
+
e7a346
+# Though file is created on all 3 bricks, lookup will fail as arbiter blames the
e7a346
+# other 2 bricks and ariter is not 'readable'.
e7a346
+# TEST ! stat $M0/file3
e7a346
+# But the checks for failing lookups when quorum is not met is not yet there in
e7a346
+# rhgs-3.4.0, so stat will succeed.
e7a346
+TEST  stat $M0/file3
e7a346
+
e7a346
+# Launch index heal to complete any pending data/metadata heals.
e7a346
+TEST $CLI volume heal $V0
e7a346
+EXPECT_WITHIN $HEAL_TIMEOUT "^0$" get_pending_heal_count $V0
e7a346
+
e7a346
+# Check each file has a gfid and the .glusterfs hardlink
e7a346
+check_gfid_and_link_count file1
e7a346
+check_gfid_and_link_count file2
e7a346
+check_gfid_and_link_count file3
e7a346
+
e7a346
+TEST rm $M0/file1
e7a346
+TEST rm $M0/file2
e7a346
+TEST rm $M0/file3
e7a346
+cleanup;
e7a346
diff --git a/xlators/cluster/afr/src/afr-self-heal-common.c b/xlators/cluster/afr/src/afr-self-heal-common.c
e7a346
index 32fd24a..50989d6 100644
e7a346
--- a/xlators/cluster/afr/src/afr-self-heal-common.c
e7a346
+++ b/xlators/cluster/afr/src/afr-self-heal-common.c
e7a346
@@ -22,7 +22,7 @@ afr_heal_synctask (xlator_t *this, afr_local_t *local);
e7a346
 int
e7a346
 afr_lookup_and_heal_gfid (xlator_t *this, inode_t *parent, const char *name,
e7a346
                           inode_t *inode, struct afr_reply *replies,
e7a346
-                          int source,  void *gfid)
e7a346
+                          int source,  unsigned char *sources, void *gfid)
e7a346
 {
e7a346
         afr_private_t *priv = NULL;
e7a346
         call_frame_t   *frame    = NULL;
e7a346
@@ -37,6 +37,23 @@ afr_lookup_and_heal_gfid (xlator_t *this, inode_t *parent, const char *name,
e7a346
         priv = this->private;
e7a346
         wind_on = alloca0 (priv->child_count);
e7a346
         ia_type = replies[source].poststat.ia_type;
e7a346
+        if ((ia_type == IA_INVAL) &&
e7a346
+            (AFR_COUNT(sources, priv->child_count) == priv->child_count)) {
e7a346
+                /* If a file is present on some bricks of the replica but parent
e7a346
+                 * dir does not have pending xattrs, all bricks are sources and
e7a346
+                 * the 'source' we  selected earlier might be one where the file
e7a346
+                 * is not actually present. Hence check if file is present in
e7a346
+                 * any of the sources.*/
e7a346
+                for (i = 0; i < priv->child_count; i++) {
e7a346
+                        if (i == source)
e7a346
+                                continue;
e7a346
+                        if (sources[i] && replies[i].valid &&
e7a346
+                            replies[i].op_ret == 0) {
e7a346
+                                ia_type = replies[i].poststat.ia_type;
e7a346
+                                break;
e7a346
+                        }
e7a346
+                }
e7a346
+        }
e7a346
 
e7a346
         /* gfid heal on those subvolumes that do not have gfid associated
e7a346
          * with the inode and update those replies.
e7a346
@@ -1250,6 +1267,21 @@ afr_mark_split_brain_source_sinks_by_policy (call_frame_t *frame,
e7a346
         return fav_child;
e7a346
 }
e7a346
 
e7a346
+gf_boolean_t
e7a346
+afr_is_file_empty_on_all_children (afr_private_t *priv,
e7a346
+                                   struct afr_reply *replies)
e7a346
+{
e7a346
+        int i = 0;
e7a346
+
e7a346
+        for (i = 0; i < priv->child_count; i++) {
e7a346
+                if ((!replies[i].valid) || (replies[i].op_ret != 0) ||
e7a346
+                    (replies[i].poststat.ia_size != 0))
e7a346
+                        return _gf_false;
e7a346
+        }
e7a346
+
e7a346
+        return _gf_true;
e7a346
+}
e7a346
+
e7a346
 int
e7a346
 afr_mark_source_sinks_if_file_empty (xlator_t *this, unsigned char *sources,
e7a346
                                      unsigned char *sinks,
e7a346
@@ -1268,11 +1300,8 @@ afr_mark_source_sinks_if_file_empty (xlator_t *this, unsigned char *sources,
e7a346
                 return -1;
e7a346
 
e7a346
         if (type == AFR_DATA_TRANSACTION) {
e7a346
-                for (i = 0; i < priv->child_count; i++) {
e7a346
-                        if (replies[i].poststat.ia_size != 0)
e7a346
+                if (!afr_is_file_empty_on_all_children(priv, replies))
e7a346
                                 return -1;
e7a346
-                }
e7a346
-
e7a346
                 goto mark;
e7a346
         }
e7a346
 
e7a346
diff --git a/xlators/cluster/afr/src/afr-self-heal-data.c b/xlators/cluster/afr/src/afr-self-heal-data.c
e7a346
index f872a98..3ef7376 100644
e7a346
--- a/xlators/cluster/afr/src/afr-self-heal-data.c
e7a346
+++ b/xlators/cluster/afr/src/afr-self-heal-data.c
e7a346
@@ -670,6 +670,7 @@ __afr_selfheal_data (call_frame_t *frame, xlator_t *this, fd_t *fd,
e7a346
 	int source = -1;
e7a346
         gf_boolean_t did_sh = _gf_true;
e7a346
         gf_boolean_t is_arbiter_the_only_sink = _gf_false;
e7a346
+        gf_boolean_t empty_file = _gf_false;
e7a346
 
e7a346
 	priv = this->private;
e7a346
 
e7a346
@@ -710,6 +711,11 @@ __afr_selfheal_data (call_frame_t *frame, xlator_t *this, fd_t *fd,
e7a346
 		source = ret;
e7a346
 
e7a346
                 if (AFR_IS_ARBITER_BRICK(priv, source)) {
e7a346
+                        empty_file = afr_is_file_empty_on_all_children (priv,
e7a346
+                                                                locked_replies);
e7a346
+                        if (empty_file)
e7a346
+                                goto restore_time;
e7a346
+
e7a346
                         did_sh = _gf_false;
e7a346
                         goto unlock;
e7a346
                 }
e7a346
@@ -746,7 +752,7 @@ restore_time:
e7a346
 	afr_selfheal_restore_time (frame, this, fd->inode, source,
e7a346
 					healed_sinks, locked_replies);
e7a346
 
e7a346
-        if (!is_arbiter_the_only_sink) {
e7a346
+        if (!is_arbiter_the_only_sink || !empty_file) {
e7a346
                 ret = afr_selfheal_inodelk (frame, this, fd->inode, this->name,
e7a346
                                             0, 0, data_lock);
e7a346
                 if (ret < priv->child_count) {
e7a346
diff --git a/xlators/cluster/afr/src/afr-self-heal-entry.c b/xlators/cluster/afr/src/afr-self-heal-entry.c
e7a346
index 647dd71..f6d3a8a 100644
e7a346
--- a/xlators/cluster/afr/src/afr-self-heal-entry.c
e7a346
+++ b/xlators/cluster/afr/src/afr-self-heal-entry.c
e7a346
@@ -187,7 +187,7 @@ __afr_selfheal_heal_dirent (call_frame_t *frame, xlator_t *this, fd_t *fd,
e7a346
 
e7a346
         if (replies[source].op_ret == 0) {
e7a346
                 ret = afr_lookup_and_heal_gfid (this, fd->inode, name,
e7a346
-                                                inode, replies, source,
e7a346
+                                                inode, replies, source, sources,
e7a346
                                              &replies[source].poststat.ia_gfid);
e7a346
                 if (ret)
e7a346
                         return ret;
e7a346
@@ -320,7 +320,7 @@ __afr_selfheal_merge_dirent (call_frame_t *frame, xlator_t *this, fd_t *fd,
e7a346
 	}
e7a346
 
e7a346
         ret = afr_lookup_and_heal_gfid (this, fd->inode, name, inode, replies,
e7a346
-                                        source,
e7a346
+                                        source, sources,
e7a346
                                         &replies[source].poststat.ia_gfid);
e7a346
         if (ret)
e7a346
                 return ret;
e7a346
diff --git a/xlators/cluster/afr/src/afr-self-heal-name.c b/xlators/cluster/afr/src/afr-self-heal-name.c
e7a346
index 556d14b..bcd0e60 100644
e7a346
--- a/xlators/cluster/afr/src/afr-self-heal-name.c
e7a346
+++ b/xlators/cluster/afr/src/afr-self-heal-name.c
e7a346
@@ -19,7 +19,7 @@ __afr_selfheal_assign_gfid (xlator_t *this, inode_t *parent, uuid_t pargfid,
e7a346
                             const char *bname, inode_t *inode,
e7a346
                             struct afr_reply *replies, void *gfid,
e7a346
                             unsigned char *locked_on, int source,
e7a346
-                            gf_boolean_t is_gfid_absent)
e7a346
+                            unsigned char *sources, gf_boolean_t is_gfid_absent)
e7a346
 {
e7a346
 	int             ret          = 0;
e7a346
         int             up_count     = 0;
e7a346
@@ -48,7 +48,7 @@ __afr_selfheal_assign_gfid (xlator_t *this, inode_t *parent, uuid_t pargfid,
e7a346
         }
e7a346
 
e7a346
         afr_lookup_and_heal_gfid (this, parent, bname, inode, replies, source,
e7a346
-                                  gfid);
e7a346
+                                  sources, gfid);
e7a346
 
e7a346
 out:
e7a346
 	return ret;
e7a346
@@ -426,7 +426,7 @@ __afr_selfheal_name_do (call_frame_t *frame, xlator_t *this, inode_t *parent,
e7a346
         is_gfid_absent = (gfid_idx == -1) ? _gf_true : _gf_false;
e7a346
 	ret = __afr_selfheal_assign_gfid (this, parent, pargfid, bname, inode,
e7a346
                                           replies, gfid, locked_on, source,
e7a346
-                                          is_gfid_absent);
e7a346
+                                          sources, is_gfid_absent);
e7a346
         if (ret)
e7a346
                 return ret;
e7a346
 
e7a346
diff --git a/xlators/cluster/afr/src/afr-self-heal.h b/xlators/cluster/afr/src/afr-self-heal.h
e7a346
index b015976..cc99d9e 100644
e7a346
--- a/xlators/cluster/afr/src/afr-self-heal.h
e7a346
+++ b/xlators/cluster/afr/src/afr-self-heal.h
e7a346
@@ -113,7 +113,7 @@ afr_selfheal_entry (call_frame_t *frame, xlator_t *this, inode_t *inode);
e7a346
 int
e7a346
 afr_lookup_and_heal_gfid (xlator_t *this, inode_t *parent, const char *name,
e7a346
                           inode_t *inode, struct afr_reply *replies, int source,
e7a346
-                          void *gfid);
e7a346
+                          unsigned char *sources, void *gfid);
e7a346
 
e7a346
 int
e7a346
 afr_selfheal_inodelk (call_frame_t *frame, xlator_t *this, inode_t *inode,
e7a346
@@ -354,4 +354,8 @@ afr_mark_source_sinks_if_file_empty (xlator_t *this, unsigned char *sources,
e7a346
                                      struct afr_reply *replies,
e7a346
                                      afr_transaction_type type);
e7a346
 
e7a346
+gf_boolean_t
e7a346
+afr_is_file_empty_on_all_children (afr_private_t *priv,
e7a346
+                                   struct afr_reply *replies);
e7a346
+
e7a346
 #endif /* !_AFR_SELFHEAL_H */
e7a346
-- 
e7a346
1.8.3.1
e7a346