diff options
-rw-r--r-- | tests/basic/changelog/changelog-history.t | 86 | ||||
-rw-r--r-- | tests/utils/changelog/changelog.h | 120 | ||||
-rw-r--r-- | tests/utils/changelog/get-history.c | 73 | ||||
-rw-r--r-- | xlators/features/changelog/lib/src/gf-history-changelog.c | 51 |
4 files changed, 325 insertions, 5 deletions
diff --git a/tests/basic/changelog/changelog-history.t b/tests/basic/changelog/changelog-history.t new file mode 100644 index 00000000000..3ce40981c90 --- /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 00000000000..14094cf3681 --- /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 00000000000..29dc60987ae --- /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 2f245a725f6..838de0c982a 100644 --- a/xlators/features/changelog/lib/src/gf-history-changelog.c +++ b/xlators/features/changelog/lib/src/gf-history-changelog.c @@ -780,6 +780,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, @@ -807,6 +816,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; @@ -836,6 +846,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_smsg (this->name, GF_LOG_ERROR, errno, @@ -871,8 +886,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, @@ -919,6 +940,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) */ @@ -992,14 +1030,13 @@ gf_history_changelog (char* changelog_dir, unsigned long start, } else {/* end of range check */ gf_smsg (this->name, GF_LOG_ERROR, errno, CHANGELOG_LIB_MSG_HIST_FAILED, - "Requested changelog " - "range is not available.", + "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); - ret = -2; - goto out; } } /* end of readdir() */ @@ -1019,5 +1056,9 @@ out: hist_jnl->hist_done = 1; *actual_end = ts2; + if (partial_history) { + ret = 1; + } + return ret; } |