Blob Blame History Raw
From f005377a54f01edc046aa668c8ab924a3ddf52bb Mon Sep 17 00:00:00 2001
From: Kotresh HR <khiremat@redhat.com>
Date: Tue, 21 Aug 2018 06:09:44 -0400
Subject: [PATCH 370/385] libgfchangelog: Fix changelog history API

Problem:
If requested start time and end time doesn't fall into
first HTIME file, then history API fails even though
continuous changelogs are avaiable for the requested range
in other HTIME files. This is induced by changelog disable
and enable which creates fresh HTIME index file.

Cause and Analysis:
Each HTIME index file represents the availability of
continuous changelogs. If changelog is disabled and enabled,
a new HTIME index file is created represents non availability
of continuous changelogs. So as long as the requested start
and end falls into single HTIME index file and not across,
history API should succeed.

But History API checks for the changelogs only in first
HTIME index file and errors out if not available.

Fix:
Check in all HTIME index files for availability of continuous
changelogs for requested change.

Upstream Patch : https://review.gluster.org/#/c/glusterfs/+/21016/

>fixes: bz#1622549
>Signed-off-by: Kotresh HR <khiremat@redhat.com>

Change-Id: I80eeceb5afbd1b89f86a9dc4c320e161907d3559
BUG: 1627639
Signed-off-by: Sunny Kumar <sunkumar@redhat.com>
Reviewed-on: https://code.engineering.redhat.com/gerrit/149768
Tested-by: RHGS Build Bot <nigelb@redhat.com>
Reviewed-by: Sunil Kumar Heggodu Gopala Acharya <sheggodu@redhat.com>
---
 tests/basic/changelog/changelog-history.t          |  86 +++++++++++++++
 tests/utils/changelog/changelog.h                  | 120 +++++++++++++++++++++
 tests/utils/changelog/get-history.c                |  73 +++++++++++++
 .../changelog/lib/src/gf-history-changelog.c       |  59 ++++++++--
 4 files changed, 331 insertions(+), 7 deletions(-)
 create mode 100644 tests/basic/changelog/changelog-history.t
 create mode 100644 tests/utils/changelog/changelog.h
 create mode 100644 tests/utils/changelog/get-history.c

diff --git a/tests/basic/changelog/changelog-history.t b/tests/basic/changelog/changelog-history.t
new file mode 100644
index 0000000..3ce4098
--- /dev/null
+++ b/tests/basic/changelog/changelog-history.t
@@ -0,0 +1,86 @@
+#!/bin/bash
+. $(dirname $0)/../../include.rc
+. $(dirname $0)/../../volume.rc
+. $(dirname $0)/../../env.rc
+
+cleanup;
+
+HISTORY_BIN_PATH=$(dirname $0)/../../utils/changelog
+build_tester $HISTORY_BIN_PATH/get-history.c -lgfchangelog
+
+time_before_enable1=$(date '+%s')
+CHANGELOG_PATH_0="$B0/${V0}0/.glusterfs/changelogs"
+ROLLOVER_TIME=2
+
+TEST glusterd
+TEST pidof glusterd
+
+sleep 3
+time_before_enable2=$(date '+%s')
+
+sleep 3
+TEST $CLI volume create $V0 $H0:$B0/${V0}0
+TEST $CLI volume set $V0 changelog.changelog on
+TEST $CLI volume set $V0 changelog.rollover-time $ROLLOVER_TIME
+TEST $CLI volume start $V0
+
+sleep 3
+time_after_enable1=$(date '+%s')
+
+TEST $GFS --volfile-id=$V0 --volfile-server=$H0 $M0;
+touch $M0/file{1..10}
+
+sleep 3
+time_after_enable2=$(date '+%s')
+
+let time_future=time_after_enable2+600
+
+#Fails as start falls before changelog enable
+EXPECT "-3" $HISTORY_BIN_PATH/get-history $time_before_enable1 $time_before_enable2
+
+#Fails as start falls before changelog enable
+EXPECT "-3" $HISTORY_BIN_PATH/get-history $time_before_enable2 $time_after_enable1
+
+#Passes as start and end falls in same htime file
+EXPECT "0" $HISTORY_BIN_PATH/get-history $time_after_enable1 $time_after_enable2
+
+#Passes, gives the changelogs till continuous changelogs are available
+# but returns 1
+EXPECT "1" $HISTORY_BIN_PATH/get-history $time_after_enable2 $time_future
+
+#Disable and enable changelog
+TEST $CLI volume set $V0 changelog.changelog off
+sleep 6
+time_between_htime=$(date '+%s')
+sleep 6
+TEST $CLI volume set $V0 changelog.changelog on
+
+sleep 6
+touch $M0/test{1..10}
+time_in_sec_htime1=$(date '+%s')
+
+sleep 6
+touch $M0/test1{1..10}
+time_in_sec_htime2=$(date '+%s')
+
+sleep 3
+TEST $CLI volume set $V0 changelog.changelog off
+sleep 3
+time_after_disable=$(date '+%s')
+
+#Passes, gives the changelogs till continuous changelogs are available
+# but returns 1
+EXPECT "1" $HISTORY_BIN_PATH/get-history $time_after_enable1 $time_in_sec_htime2
+
+#Fails as start falls between htime files
+EXPECT "-3" $HISTORY_BIN_PATH/get-history $time_between_htime $time_in_sec_htime1
+
+#Passes as start and end falls in same htime file
+EXPECT "0" $HISTORY_BIN_PATH/get-history $time_in_sec_htime1 $time_in_sec_htime2
+
+#Passes, gives the changelogs till continuous changelogs are available
+EXPECT "0" $HISTORY_BIN_PATH/get-history $time_in_sec_htime2 $time_after_disable
+
+TEST rm $HISTORY_BIN_PATH/get-history
+
+cleanup;
diff --git a/tests/utils/changelog/changelog.h b/tests/utils/changelog/changelog.h
new file mode 100644
index 0000000..14094cf
--- /dev/null
+++ b/tests/utils/changelog/changelog.h
@@ -0,0 +1,120 @@
+/*
+   Copyright (c) 2013 Red Hat, Inc. <http://www.redhat.com>
+   This file is part of GlusterFS.
+
+   This file is licensed to you under your choice of the GNU Lesser
+   General Public License, version 3 or any later version (LGPLv3 or
+   later), or the GNU General Public License, version 2 (GPLv2), in all
+   cases as published by the Free Software Foundation.
+*/
+
+#ifndef _GF_CHANGELOG_H
+#define _GF_CHANGELOG_H
+
+struct gf_brick_spec;
+
+/**
+ * Max bit shiter for event selection
+ */
+#define CHANGELOG_EV_SELECTION_RANGE  5
+
+#define CHANGELOG_OP_TYPE_JOURNAL    (1<<0)
+#define CHANGELOG_OP_TYPE_OPEN       (1<<1)
+#define CHANGELOG_OP_TYPE_CREATE     (1<<2)
+#define CHANGELOG_OP_TYPE_RELEASE    (1<<3)
+#define CHANGELOG_OP_TYPE_BR_RELEASE (1<<4)  /* logical release (last close()),
+                                                sent by bitrot stub */
+#define CHANGELOG_OP_TYPE_MAX        (1<<CHANGELOG_EV_SELECTION_RANGE)
+
+
+struct ev_open {
+        unsigned char gfid[16];
+        int32_t flags;
+};
+
+struct ev_creat {
+        unsigned char gfid[16];
+        int32_t flags;
+};
+
+struct ev_release {
+        unsigned char gfid[16];
+};
+
+struct ev_release_br {
+        unsigned long version;
+        unsigned char gfid[16];
+        int32_t sign_info;
+};
+
+struct ev_changelog {
+        char path[PATH_MAX];
+};
+
+typedef struct changelog_event {
+        unsigned int ev_type;
+
+        union {
+                struct ev_open open;
+                struct ev_creat create;
+                struct ev_release release;
+                struct ev_changelog journal;
+                struct ev_release_br releasebr;
+        } u;
+} changelog_event_t;
+
+#define CHANGELOG_EV_SIZE  (sizeof (changelog_event_t))
+
+/**
+ * event callback, connected & disconnection defs
+ */
+typedef void (CALLBACK) (void *, char *,
+                        void *, changelog_event_t *);
+typedef void *(INIT) (void *, struct gf_brick_spec *);
+typedef void (FINI) (void *, char *, void *);
+typedef void (CONNECT) (void *, char *, void *);
+typedef void (DISCONNECT) (void *, char *, void *);
+
+struct gf_brick_spec {
+        char         *brick_path;
+        unsigned int  filter;
+
+        INIT       *init;
+        FINI       *fini;
+        CALLBACK   *callback;
+        CONNECT    *connected;
+        DISCONNECT *disconnected;
+
+        void *ptr;
+};
+
+/* API set */
+
+int
+gf_changelog_register (char *brick_path, char *scratch_dir,
+                       char *log_file, int log_levl, int max_reconnects);
+ssize_t
+gf_changelog_scan ();
+
+int
+gf_changelog_start_fresh ();
+
+ssize_t
+gf_changelog_next_change (char *bufptr, size_t maxlen);
+
+int
+gf_changelog_done (char *file);
+
+/* newer flexible API */
+int
+gf_changelog_init (void *xl);
+
+int
+gf_changelog_register_generic (struct gf_brick_spec *bricks, int count,
+                               int ordered, char *logfile, int lvl, void *xl);
+
+int
+gf_history_changelog (char *changelog_dir, unsigned long start,
+                      unsigned long end, int n_parallel,
+                      unsigned long *actual_end);
+#endif
diff --git a/tests/utils/changelog/get-history.c b/tests/utils/changelog/get-history.c
new file mode 100644
index 0000000..29dc609
--- /dev/null
+++ b/tests/utils/changelog/get-history.c
@@ -0,0 +1,73 @@
+/*
+   Copyright (c) 2013 Red Hat, Inc. <http://www.redhat.com>
+   This file is part of GlusterFS.
+
+   This file is licensed to you under your choice of the GNU Lesser
+   General Public License, version 3 or any later version (LGPLv3 or
+   later), or the GNU General Public License, version 2 (GPLv2), in all
+   cases as published by the Free Software Foundation.
+*/
+
+/**
+ * get set of new changes every 10 seconds (just print the file names)
+ *
+ * Compile it using:
+ *  gcc -o gethistory `pkg-config --cflags libgfchangelog` get-history.c \
+ *  `pkg-config --libs libgfchangelog`
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/un.h>
+#include <limits.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include "changelog.h"
+
+int
+main (int argc, char **argv)
+{
+        int     ret          = 0;
+        unsigned long end_ts = 0;
+        int start  = 0;
+        int end    = 0;
+
+        ret = gf_changelog_init (NULL);
+        if (ret) {
+                printf ("-1");
+                fflush(stdout);
+                return -1;
+        }
+
+        ret = gf_changelog_register ("/d/backends/patchy0",
+                                     "/tmp/scratch_v1",
+                                     "/var/log/glusterfs/changes.log",
+                                     9, 5);
+        if (ret) {
+                printf ("-2");
+                fflush(stdout);
+                return -1;
+        }
+
+        start = atoi(argv[1]);
+        end = atoi(argv[2]);
+
+        ret = gf_history_changelog ("/d/backends/patchy0/.glusterfs/changelogs",
+                                    start, end, 3, &end_ts);
+        if (ret < 0) {
+                printf ("-3");
+                fflush(stdout);
+                return -1;
+        } else if (ret == 1) {
+                printf ("1");
+                fflush(stdout);
+                return 0;
+        }
+
+out:
+        printf ("0");
+        fflush(stdout);
+        return 0;
+}
diff --git a/xlators/features/changelog/lib/src/gf-history-changelog.c b/xlators/features/changelog/lib/src/gf-history-changelog.c
index 4355396..c1a7070 100644
--- a/xlators/features/changelog/lib/src/gf-history-changelog.c
+++ b/xlators/features/changelog/lib/src/gf-history-changelog.c
@@ -772,6 +772,15 @@ gf_changelog_extract_min_max (const char *dname, const char *htime_dir,
         return ret;
 }
 
+/* gf_history_changelog returns actual_end and spawns threads to
+ * parse historical changelogs. The return values are as follows.
+ *     0 : On success
+ *     1 : Successful, but partial historical changelogs available,
+ *         end time falls into different htime file or future time
+ *    -2 : Error, requested historical changelog not available, not
+ *         even partial
+ *    -1 : On any error
+ */
 int
 gf_history_changelog (char* changelog_dir, unsigned long start,
                       unsigned long end, int n_parallel,
@@ -799,6 +808,7 @@ gf_history_changelog (char* changelog_dir, unsigned long start,
         pthread_t                       consume_th              = 0;
         char                            htime_dir[PATH_MAX]     = {0,};
         char                            buffer[PATH_MAX]        = {0,};
+        gf_boolean_t                    partial_history         = _gf_false;
 
         pthread_attr_t attr;
 
@@ -828,6 +838,11 @@ gf_history_changelog (char* changelog_dir, unsigned long start,
                 goto out;
         }
 
+        gf_smsg (this->name, GF_LOG_INFO, 0,
+                 CHANGELOG_LIB_MSG_TOTAL_LOG_INFO,
+                 "Requesting historical changelogs",
+                 "start=%lu", start, "end=%lu", end, NULL);
+
         /* basic sanity check */
         if (start > end || n_parallel <= 0) {
                 gf_msg (this->name, GF_LOG_ERROR, errno,
@@ -860,8 +875,14 @@ gf_history_changelog (char* changelog_dir, unsigned long start,
 
                 entry = sys_readdir (dirp, scratch);
 
-                if (!entry || errno != 0)
+                if (!entry || errno != 0) {
+                        gf_smsg (this->name, GF_LOG_ERROR, errno,
+                                 CHANGELOG_LIB_MSG_HIST_FAILED,
+                                 "Requested changelog range is not availbale",
+                                 "start=%lu", start, "end=%lu", end, NULL);
+                        ret = -2;
                         break;
+                }
 
                 ret = gf_changelog_extract_min_max (entry->d_name, htime_dir,
                                                     &fd, &total_changelog,
@@ -906,6 +927,23 @@ gf_history_changelog (char* changelog_dir, unsigned long start,
 
                         end2 = (end <= max_ts) ? end : max_ts;
 
+                        /* Check if end falls out of same HTIME file. The end
+                         * falling to a different htime file or changelog
+                         * disable-enable is detected only after 20 seconds.
+                         * This is required because, applications generally
+                         * asks historical changelogs till current time and
+                         * it is possible changelog is not rolled over yet.
+                         * So, buffer time of default rollover time plus 5
+                         * seconds is subtracted.  If the application requests
+                         * the end time with in half a minute of changelog
+                         * disable, it's not detected as changelog disable and
+                         * it's application's responsibility to retry after
+                         * 20 seconds before confirming it as partial history.
+                         */
+                        if ((end - 20) > max_ts) {
+                                partial_history = _gf_true;
+                        }
+
                         /**
                          * search @end2 in htime file returning it's index (@to)
                          */
@@ -972,12 +1010,15 @@ gf_history_changelog (char* changelog_dir, unsigned long start,
                         goto out;
 
                 } else {/* end of range check */
-                        gf_msg (this->name, GF_LOG_ERROR, errno,
-                        CHANGELOG_LIB_MSG_HIST_FAILED, "Requested changelog "
-                        "range is not available. START - %lu CHLOG_MIN - %lu "
-                        "CHLOG_MAX - %lu", start, min_ts, max_ts);
-                        ret = -2;
-                        goto out;
+                        gf_smsg (this->name, GF_LOG_ERROR, errno,
+                                 CHANGELOG_LIB_MSG_HIST_FAILED,
+                                 "Requested changelog range is not "
+                                 "available. Retrying next HTIME",
+                                 "start=%lu", start,
+                                 "end=%lu", end,
+                                 "chlog_min=%lu", min_ts,
+                                 "chlog_max=%lu", max_ts,
+                                 NULL);
                 }
         } /* end of readdir() */
 
@@ -1000,5 +1041,9 @@ out:
         hist_jnl->hist_done = 1;
         *actual_end = ts2;
 
+        if (partial_history) {
+                ret = 1;
+        }
+
         return ret;
 }
-- 
1.8.3.1