HAMMER Utility - Refactor the histogram code for mirror-stream.
authorMatthew Dillon <dillon@apollo.backplane.com>
Thu, 11 Feb 2010 05:13:49 +0000 (21:13 -0800)
committerMatthew Dillon <dillon@apollo.backplane.com>
Thu, 11 Feb 2010 05:13:49 +0000 (21:13 -0800)
* Refactor the histogram code.  This code is responsible for breaking
  down a large initial mirroring stream into smaller chunks so the
  transaction id can be synced more often.  This way if the stream
  is interrupted it can be restarted at a more recent point instead
  of having to restart further back (or at the beginning).

* Add -S splitsize (default 100MB) to specify the desired breakdown
  for the histogram.

sbin/hammer/cmd_mirror.c
sbin/hammer/hammer.8
sbin/hammer/hammer.c
sbin/hammer/hammer.h

index 55546d0..2428198 100644 (file)
@@ -58,8 +58,6 @@ static ssize_t writebw(int fd, const void *buf, size_t nbytes,
 static int getyn(void);
 static void mirror_usage(int code);
 
-#define BULK_MINIMUM   20000
-
 /*
  * Generate a mirroring data stream from the specific source over the
  * entire key range, but restricted to the specified transaction range.
@@ -107,7 +105,7 @@ again:
 
        fd = getpfs(&pfs, filesystem);
 
-       if (streaming && VerboseOpt) {
+       if (streaming && VerboseOpt && VerboseOpt < 2) {
                fprintf(stderr, "\nRunning");
                fflush(stderr);
        }
@@ -203,8 +201,12 @@ again:
 
        if (streaming == 0 || VerboseOpt >= 2) {
                fprintf(stderr,
-                       "Mirror-read: Mirror from %016jx to %016jx\n",
+                       "Mirror-read: Mirror from %016jx to %016jx",
                        (uintmax_t)mirror.tid_beg, (uintmax_t)mirror.tid_end);
+               if (histogram > 0)
+                       fprintf(stderr, " (bulk)");
+               fprintf(stderr, "\n");
+               fflush(stderr);
        }
        if (mirror.key_beg.obj_id != (int64_t)HAMMER_MIN_OBJID) {
                fprintf(stderr, "Mirror-read: Resuming at object %016jx\n",
@@ -346,7 +348,7 @@ done:
                 * scratch.
                 */
                if (TwoWayPipeOpt && streaming && histogram > 0) {
-                       if (VerboseOpt)
+                       if (VerboseOpt && VerboseOpt < 2)
                                fprintf(stderr, " (bulk incremental)");
                        goto again;
                }
@@ -383,55 +385,57 @@ done:
 }
 
 /*
- * Ok, this isn't really a histogram.  What we are trying to do
- * here is find the first tid_end for the scan that returns
- * at least some data.  The frontend of the TID space will generally
- * return nothing so we can't just divide out the full mirroring
- * range.  Once we find the point where a real data stream starts
- * to get generated we can divide out the range from that point.
- *
- * When starting a new mirroring operation completely from scratch
- * this code will take some time to run, but once some mirroring
- * data is synchronized on the target you will be able to interrupt
- * the stream and restart it and the later invocations of this
- * code will be such that it should run much faster.
+ * What we are trying to do here is figure out how much data is
+ * going to be sent for the TID range and to break the TID range
+ * down into reasonably-sized slices (from the point of view of
+ * data sent) so a lost connection can restart at a reasonable
+ * place and not all the way back at the beginning.
  */
+
+#define HIST_COUNT     (256 * 1024)
+
 static int
 generate_histogram(int fd, const char *filesystem,
                   hammer_tid_t **histogram_ary,
                   struct hammer_ioc_mirror_rw *mirror_base)
 {
        struct hammer_ioc_mirror_rw mirror;
+       union hammer_ioc_mrecord_any *mrec;
        hammer_tid_t tid_beg;
        hammer_tid_t tid_end;
-       hammer_tid_t tid_half;
+       hammer_tid_t tid1;
+       hammer_tid_t tid2;
+       u_int64_t tid_bytes[HIST_COUNT + 2];    /* needs 2 extra */
+       u_int64_t total;
+       u_int64_t accum;
        int i;
+       int res;
+       int off;
+       int len;
 
        mirror = *mirror_base;
        tid_beg = mirror.tid_beg;
        tid_end = mirror.tid_end;
+       mirror.head.flags |= HAMMER_IOC_MIRROR_NODATA;
+       bzero(tid_bytes, sizeof(tid_bytes));
 
-       if (*histogram_ary)
-               free(*histogram_ary);
-       if (tid_beg + BULK_MINIMUM >= tid_end)
+       if (*histogram_ary == NULL) {
+               *histogram_ary = malloc(sizeof(hammer_tid_t) *
+                                       (HIST_COUNT + 2));
+       }
+       if (tid_beg >= tid_end)
                return(0);
 
-       if (VerboseOpt)
-               fprintf(stderr, "Doing Range Test\n");
-       while (tid_end - tid_beg > BULK_MINIMUM) {
-               tid_half = tid_beg + (tid_end - tid_beg) * 2 / 3;
-               mirror.count = 0;
-               mirror.tid_beg = tid_beg;
-               mirror.tid_end = tid_half;
+       fprintf(stderr, "Prescan to break up bulk transfer");
+       if (VerboseOpt > 1)
+               fprintf(stderr, " (%juMB chunks)",
+                       (uintmax_t)(SplitupOpt / (1024 * 1024)));
+       fprintf(stderr, "\n");
 
-               if (VerboseOpt > 1) {
-                       fprintf(stderr, "RangeTest %016jx/%016jx - %016jx (%jd) ",
-                               (uintmax_t)tid_beg,
-                               (uintmax_t)tid_end,
-                               (uintmax_t)tid_half,
-                               (intmax_t)(tid_half - tid_beg));
-               }
-               fflush(stderr);
+       total = 0;
+       accum = 0;
+       for (;;) {
+               mirror.count = 0;
                if (ioctl(fd, HAMMERIOC_MIRROR_READ, &mirror) < 0) {
                        fprintf(stderr, "Mirror-read %s failed: %s\n",
                                filesystem, strerror(errno));
@@ -443,28 +447,80 @@ generate_histogram(int fd, const char *filesystem,
                                filesystem, mirror.head.error);
                        exit(1);
                }
-               if (VerboseOpt > 1)
-                       fprintf(stderr, "%d\n", mirror.count);
-               if (mirror.count > SERIALBUF_SIZE / 2) {
-                       tid_end = tid_half;
-               } else {
-                       tid_beg = tid_half;
+               for (off = 0;
+                    off < mirror.count;
+                    off += HAMMER_HEAD_DOALIGN(mrec->head.rec_size)
+               ) {
+                       mrec = (void *)((char *)mirror.ubuf + off);
+                       len = HAMMER_HEAD_DOALIGN(mrec->head.rec_size);
+
+                       /*
+                        * We requested that no record data be returned
+                        * with the b-tree record.  If suppored by the
+                        * VFS normal records will be turned into special
+                        * NODATA records.  We want to calculate the
+                        * record+data length for the histogram so add
+                        * it back in.
+                        */
+                       if (mrec->head.type == HAMMER_MREC_TYPE_REC_NODATA) {
+                               len += HAMMER_HEAD_DOALIGN(
+                                                   mrec->rec.leaf.data_len);
+                       }
+
+                       tid1 = mrec->rec.leaf.base.create_tid;
+                       if (tid1 < tid_beg || tid1 >= tid_end)
+                               tid1 = 0;
+                       tid2 = mrec->rec.leaf.base.delete_tid;
+                       if (tid2 < tid_beg || tid2 >= tid_end)
+                               tid2 = 0;
+                       if (tid1 == 0)
+                               tid1 = tid2;
+                       else if (tid2 && tid1 > tid2)
+                               tid1 = tid2;
+                       if (tid1) {
+                               i = (tid1 - tid_beg) * HIST_COUNT /
+                                   (tid_end - tid_beg);
+                               if (i >= 0 && i < HIST_COUNT) {
+                                       tid_bytes[i] += len;
+                                       total += len;
+                                       accum += len;
+                               }
+                       }
+               }
+               if (VerboseOpt > 1) {
+                       if (accum > SplitupOpt) {
+                               fprintf(stderr, ".");
+                               fflush(stderr);
+                               accum = 0;
+                       }
                }
+               if (mirror.count == 0)
+                       break;
+               mirror.key_beg = mirror.key_cur;
        }
 
-       tid_end = mirror_base->tid_end;
-       fprintf(stderr, "histogram range %016llx - %016llx\n",
-               (long long)tid_beg, (long long)tid_end);
-
        /*
-        * The final array generates our incremental ending tids in
-        * reverse order.  The caller also picks them off in reverse order.
+        * Reduce to SplitupOpt (default 100MB) chunks.  This code may
+        * use up to two additional elements.
         */
-       *histogram_ary = malloc(sizeof(hammer_tid_t) * 20);
-       for (i = 0; i < 20; ++i) {
-               (*histogram_ary)[i] = tid_end - (tid_end - tid_beg) / 20 * i;
+       res = 0;
+       (*histogram_ary)[res] = tid_end;
+       (*histogram_ary)[++res] = 0;
+       for (i = HIST_COUNT - 1; i >= 0; --i) {
+               (*histogram_ary)[res] += tid_bytes[i];
+               if ((*histogram_ary)[res] >= SplitupOpt) {
+                       (*histogram_ary)[res] = tid_beg +
+                                               i * (tid_end - tid_beg) /
+                                               HIST_COUNT;
+                       (*histogram_ary)[++res] = 0;
+               }
        }
-       return(20);
+       assert(res <= HIST_COUNT + 1);
+       if (VerboseOpt > 1)
+               fprintf(stderr, "\n");  /* newline after ... */
+       fprintf(stderr, "Prescan %d chunks, total %ju MBytes\n",
+               res, (uintmax_t)total / (1024 * 1024));
+       return(res);
 }
 
 static void
@@ -1327,7 +1383,7 @@ update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid, int pfs_id)
                }
                if (VerboseOpt >= 2) {
                        fprintf(stderr,
-                               "Mirror-write: Completed, updated snapshot "
+                               "\nMirror-write: Completed, updated snapshot "
                                "to %016jx\n",
                                (uintmax_t)snapshot_tid);
                }
index c6054ba..f382dee 100644 (file)
 .Nm
 .Op Fl 2BqrvXy
 .Op Fl b Ar bandwidth
-.Op Fl C Ar cachesize Ns Op Ns Cm \&: Ns Ar readahead
 .Op Fl c Ar cyclefile
 .Op Fl f Ar blkdevs
 .\" .Op Fl s Ar linkpath
 .Op Fl i Ar delay
 .Op Fl p Ar ssh-port
 .Op Fl t Ar seconds
+.Op Fl C Ar cachesize Ns Op Ns Cm \&: Ns Ar readahead
+.Op Fl S Ar splitsize
 .Ar command
 .Op Ar argument ...
 .Sh DESCRIPTION
@@ -75,12 +76,6 @@ automatic negotiation of transaction id ranges.
 This option is automatically enabled by the
 .Cm mirror-copy
 command.
-.It Fl B
-Bulk Transfer.
-.Cm Mirror-stream
-will not attempt to break-up large initial bulk transfers into smaller pieces.
-This can save time but if the link is lost in the middle of the
-initial bulk transfer you will have to start over from scratch.
 .It Fl b Ar bandwidth
 Specify a bandwidth limit in bytes per second for mirroring streams.
 This option is typically used to prevent batch mirroring operations from
@@ -91,23 +86,6 @@ or
 .Cm g
 to specify values in kilobytes, megabytes, and gigabytes per second.
 If no suffix is specified, bytes per second is assumed.
-.It Fl C Ar cachesize Ns Op Ns Cm \&: Ns Ar readahead
-Set the memory cache size for any raw
-.Tn I/O .
-The default is 16m.
-A suffix of
-.Cm k
-for kilobytes and
-.Cm m
-for megabytes is allowed,
-else the cache size is specified in bytes.
-.Pp
-The read-behind/read-ahead defaults to 4
-.Nm HAMMER
-blocks.
-.Pp
-This option is typically only used with diagnostic commands
-as kernel-supported commands will use the kernel's buffer cache.
 .It Fl c Ar cyclefile
 When pruning and reblocking you can instruction
 .Nm
@@ -154,6 +132,52 @@ option to prune or reblock a portion of the file system incrementally.
 .It Fl v
 Increase verboseness.
 May be specified multiple times.
+.It Fl y
+Force "yes" for any interactive question.
+.It Fl B
+Bulk Transfer.
+.Cm Mirror-stream
+will not attempt to break-up large initial bulk transfers into smaller pieces.
+This can save time but if the link is lost in the middle of the
+initial bulk transfer you will have to start over from scratch.
+.It Fl C Ar cachesize Ns Op Ns Cm \&: Ns Ar readahead
+Set the memory cache size for any raw
+.Tn I/O .
+The default is 16m.
+A suffix of
+.Cm k
+for kilobytes and
+.Cm m
+for megabytes is allowed,
+else the cache size is specified in bytes.
+.Pp
+The read-behind/read-ahead defaults to 4
+.Nm HAMMER
+blocks.
+.Pp
+This option is typically only used with diagnostic commands
+as kernel-supported commands will use the kernel's buffer cache.
+.It Fl S Ar splitsize
+Specify the bulk splitup size in bytes for mirroring streams.
+When a mirror-stream is first started
+.Nm
+will do an initial run-through of the data to calculate good
+transaction ids to split the bulk transfers at, creating
+restart points in case the stream is interrupted.
+If we don't do this and the stream is interrupted it might
+have to start all over again.
+The default is a splitsize of 100m.
+.Pp
+At the moment the run-through is disk-bandwidth-heavy but some
+future version will limit the run-through to just the B-Tree
+records and not the record data.
+.Pp
+The splitsize may be suffixed with
+.Cm k , m ,
+or
+.Cm g
+to specify values in kilobytes, megabytes, or gigabytessecond.
+If no suffix is specified, bytes is assumed.
 .It Fl X
 Enable compression for any remote ssh specifications.  Unfortunately
 the
@@ -161,8 +185,6 @@ the
 option has already been reserved for other purposes so we had to use
 a different letter.  This option is typically used with the
 mirroring directives.
-.It Fl y
-Force "yes" for any interactive question.
 .El
 .Pp
 The commands are as follows:
index c63f931..025f07f 100644 (file)
@@ -59,6 +59,7 @@ int RunningIoctl;
 int DidInterrupt;
 int BulkOpt;
 u_int64_t BandwidthOpt;
+u_int64_t SplitupOpt = 100 * 1024 * 1024;
 const char *CyclePath;
 const char *LinkPath;
 
@@ -71,7 +72,7 @@ main(int ac, char **av)
        int ch;
        int cacheSize = 0;
 
-       while ((ch = getopt(ac, av, "b:c:dhf:i:p:qrs:t:v2yBC:FX")) != -1) {
+       while ((ch = getopt(ac, av, "b:c:dhf:i:p:qrs:t:v2yBC:FS:X")) != -1) {
                switch(ch) {
                case '2':
                        TwoWayPipeOpt = 1;
@@ -101,6 +102,28 @@ main(int ac, char **av)
                                usage(1);
                        }
                        break;
+               case 'S':
+                       SplitupOpt = strtoull(optarg, &ptr, 0);
+                       switch(*ptr) {
+                       case 'g':
+                       case 'G':
+                               SplitupOpt *= 1024;
+                               /* fall through */
+                       case 'm':
+                       case 'M':
+                               SplitupOpt *= 1024;
+                               /* fall through */
+                       case 'k':
+                       case 'K':
+                               SplitupOpt *= 1024;
+                               break;
+                       case '\0':
+                               /* bytes per second if no suffix */
+                               break;
+                       default:
+                               usage(1);
+                       }
+                       break;
                case 'c':
                        CyclePath = optarg;
                        break;
@@ -497,7 +520,8 @@ usage(int exit_code)
        fprintf(stderr, 
                "hammer -h\n"
                "hammer [-2BqrvXy] [-b bandwidth] [-C cachesize[:readahead]] [-c cyclefile]\n"
-               "       [-f blkdevs] [-i delay] [-t seconds] command [argument ...]\n"
+               "       [-f blkdevs] [-i delay] [-t seconds] [-S splitup]\n"
+               "       command [argument ...]\n"
                "hammer synctid <filesystem> [quick]\n"
                "hammer -f blkdevs blockmap\n"
                "hammer bstats [interval]\n"
index 50794a6..eed667c 100644 (file)
@@ -74,6 +74,7 @@ extern int DidInterrupt;
 extern int ForceOpt;
 extern int BulkOpt;
 extern u_int64_t BandwidthOpt;
+extern u_int64_t SplitupOpt;
 extern const char *LinkPath;
 extern const char *CyclePath;