<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>InfluxData Blog - Mark Rushakoff</title>
    <description>Posts by Mark Rushakoff on the InfluxData Blog</description>
    <link>https://www.influxdata.com/blog/author/markrushakoff/</link>
    <language>en-us</language>
    <lastBuildDate>Thu, 15 Aug 2019 10:26:33 -0700</lastBuildDate>
    <pubDate>Thu, 15 Aug 2019 10:26:33 -0700</pubDate>
    <ttl>1800</ttl>
    <item>
      <title>Reproducing a Flaky Test in Go</title>
      <description>&lt;p&gt;Oftentimes, a test that occasionally fails in CI can be reproduced locally with a simple &lt;code class="language-markup"&gt;go test -count=N ./path/to/flaky/package&lt;/code&gt;… but once in a while, it just doesn’t repro locally. Or maybe the full test suite for that package takes too long, and the test fails so rarely, that you need a more precise way of zeroing in on the bad test.&lt;/p&gt;

&lt;p&gt;It’s much better to confidently fix the test failure, than to make a good guess and hope for the best. If you have to try and fix it again on another day, you will waste time regathering context you had the first time around.&lt;/p&gt;

&lt;p&gt;In this document I will detail the approaches I have found to be effective to methodically reproduce a test failure, over many hours I’ve spent tracking down many flaky tests.&lt;/p&gt;

&lt;p&gt;But first, let’s look at the common patterns that result in flaky tests.&lt;/p&gt;
&lt;h2&gt;Flaky test categories&lt;/h2&gt;
&lt;p&gt;Ultimately, flaky tests are nondeterministic tests. In my experience, the root cause of the flakiness generally falls into one of two categories.&lt;/p&gt;
&lt;h3&gt;Nondeterministic data&lt;/h3&gt;
&lt;p&gt;With flaky tests, there are two interesting types of nondeterministic data: data that is itself consistent but is accessed nondeterministically, and data that is generated randomly but is accessed deterministically. Of course, those two types are not mutually exclusive.&lt;/p&gt;
&lt;h4&gt;Nondeterministic access&lt;/h4&gt;
&lt;p&gt;A test that doesn’t access its data in the same way every time is fine, so long as the test’s assertions account for the data being in a different order.&lt;/p&gt;

&lt;p&gt;Most Go developers are aware that map iteration order is random. Iterating a map to populate a slice, and then asserting against that slice’s order — especially when the map only has two or three elements — is a test pattern that will pass surprisingly often. Combine that with Go’s test caching and the human habit of blindly re-running a failed CI job, and you will have a perfectly annoying test failure that rarely pops up.&lt;/p&gt;

&lt;p&gt;Other than map iteration, goroutines performing concurrent work may finish in an arbitrary order. Perhaps you have two goroutines, one reading a very small file and one reading a very large file, and each sending some result on the same channel. Most of the time, the small file’s goroutine will finish and send its result first; if your test assumes that is always the case, then the test will sometimes fail.&lt;/p&gt;
&lt;h4&gt;Nondeterministic generation&lt;/h4&gt;
&lt;p&gt;Making good use of random data in tests is an art unto itself. &lt;a href="https://github.com/dvyukov/go-fuzz"&gt;go-fuzz&lt;/a&gt; is an excellent tool to discover bugs related to handling of arbitrary input. Using random values in tests is a lightweight way to potentially discover bugs in a similar way, but the downside is that you will learn about the bug only when the test only occasionally fails. For that reason, it’s important that you can easily get at the input that caused the failure.&lt;/p&gt;

&lt;p&gt;The single flaky test I tracked down, perhaps six years ago, that sticks out most in my mind involved deserializing a randomly populated YAML file. We would infrequently see this test fail with a mysterious failure message, and running it again would always pass. We were randomly generating a string of hex characters for a particular value. Most of the time, the YAML would look like &lt;code class="language-markup"&gt;key: a1b2c3&lt;/code&gt; and would be interpreted as a string…but every once in a while it would pick a sequence of all decimal digits, then a single letter E, and then the rest decimal digits. We didn’t surround the value with quotes, so the parser would interpret &lt;code class="language-markup"&gt;key: 12345e12&lt;/code&gt; as a floating-point number instead of a string!&lt;/p&gt;

&lt;p&gt;When using randomly generated values, make sure it’s easy to recover the input that caused the failure. Usually you can include the value in a call to &lt;code class="language-markup"&gt;t.Fatalf&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If it’s a more complicated test involving many files each containing some random content, I would at least put all the files in the same temporary directory. That way, if I need to reproduce a failure to inspect the files, I can just comment out the call to &lt;code class="language-markup"&gt;os.RemoveAll&lt;/code&gt; and add a &lt;code class="language-markup"&gt;t.Log(dirName)&lt;/code&gt; to know where to explore to see the bad input. If you’re already working on locally reproducing an intermittent failure anyway, I don’t see anything wrong with making some temporary edits to the test function.&lt;/p&gt;
&lt;h3&gt;Timing-based tests&lt;/h3&gt;
&lt;p&gt;In my experience, tests that are sensitive to timing tend to cause flaky failures more frequently than the previously mentioned logic bugs around data order.&lt;/p&gt;

&lt;p&gt;Usually it goes like this: your test starts a goroutine to do an operation that should complete quickly, perhaps in tens of milliseconds. You pick a resasonable timeout value — “If I don’t see a result in 100ms, the test has failed.” You run that test in a loop for 15 minutes on your machine and it passes every time. Then after merging that change to master, the test fails at least once the first day it’s running on CI. Someone bumps up the timeout to one second, and it still manages to fail a couple times per week. Now what?&lt;/p&gt;

&lt;p&gt;If you have a way to test a synchronous API instead of an asynchronous one — avoiding sensitivity to time — that is generally the best solution.&lt;/p&gt;

&lt;p&gt;If you must test asynchronously, be sure to poll rather than do a single long sleep. Don’t do this:&lt;/p&gt;
&lt;pre class="line-numbers"&gt;&lt;code class="language-markup"&gt;go longOperation.Start()

// Bad: this will always eat up 5 seconds in test, even if the operation completes instantly.
time.Sleep(5 * time.Second)

if !longOperation.IsDone() {
  t.Fatal("didn't see result in time")
}

res := longOperation.Result()
if res != expected {
  t.Fatalf("expected %v, got %v", expected, res)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead, for APIs that let you check whether a call will block, I often use a pattern like:&lt;/p&gt;
&lt;pre class="line-numbers"&gt;&lt;code class="language-markup"&gt;go longOperation.Start()

deadline := time.Now().Add(5 * time.Second)
for {
  if time.Now().After(deadline) {
    t.Fatal("didn't see result in time")
  }

  if !longOperation.IsDone() {
    time.Sleep(100 * time.Millisecond)
    continue
  }

  res := longOperation.Result()
  if res != expected {
    t.Fatalf("expected %v, got %v", expected, res)
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the API is blocking but accepts a cancelable context, you should use a reasonable timeout so that the test will fail more quickly than the default 10 minute timeout:&lt;/p&gt;
&lt;pre class="line-numbers"&gt;&lt;code class="language-markup"&gt;go longOperation.Start()

ctx, cancel := context.WithTimeout(context.Background(), 5 * time.Second)
defer cancel()

res, err := longOperation.WaitForResult(ctx)
if err != nil {
  t.Fatal(err)
}
if res != expected {
  t.Fatalf("expected %v, got %v", expected, res)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the API blocks but does not accept a context, you can write a helper function to run the method and fail if it doesn’t complete in a given timeout (left as an exercise for the reader).&lt;/p&gt;
&lt;h2&gt;Reproducing a flaky test on your workstation&lt;/h2&gt;
&lt;p&gt;You’ve seen the test fail a couple times on CI. It usually passes if you re-run the CI job, but instead of kicking the can down the road, let’s fix it now. Before you can confidently say you’ve fixed the test, you need to confidently reproduce the test locally.&lt;/p&gt;
&lt;h3&gt;Set up your test loop&lt;/h3&gt;
&lt;p&gt;First, focus on the exact package that failed. That is, you want to run &lt;code class="language-markup"&gt;go test ./mypkg&lt;/code&gt;, not &lt;code class="language-markup"&gt;go test ./...&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then, use &lt;code class="language-markup"&gt;-run&lt;/code&gt; to focus on the exact test that failed. Usually I would just copy and paste the test name that’s failing, e.g. &lt;code class="language-markup"&gt;go test -run=TestFoo ./mypkg&lt;/code&gt;. However, note that the &lt;code class="language-markup"&gt;-run&lt;/code&gt; flag accepts a regular expression, so if your test name is also the prefix of another test, you can ensure you only run the exact match, by anchoring the name like go test &lt;code class="language-markup"&gt;-run='^TestFoo$' ./mypkg&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you run that more than once, you will surely notice that recent versions of Go cache the test results. Obviously we don’t want that while we are trying to reproduce a flaky test. You might use &lt;code class="language-markup"&gt;-count=1&lt;/code&gt; if you were just running the full test suite without caching, but you should pick a larger number. That number will vary by the exact test; my personal preference is a count that completes in around 10 seconds or so. Let’s say we’ve settled on 100 — your command now looks like &lt;code class="language-markup"&gt;go test -run=TestFoo -count=100 ./mypkg&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code class="language-markup"&gt;-count&lt;/code&gt; flag will run the specified number of iterations regardless of how many runs fail. Most of the time, you don’t get any extra information from multiple failures in a single run. For that reason, I prefer to use the &lt;code class="language-markup"&gt;-failfast&lt;/code&gt; flag so that the test process stops after the first failed run.&lt;/p&gt;

&lt;p&gt;Now, we can throw this in a very simple bash loop: &lt;code class="language-markup"&gt;while go test -run=TestFoo -count=100 -failfast ./mypkg; do date; done&lt;/code&gt;. You could put anything inside the body of the loop, but I like to see the date go by in the output so I can see that it hasn’t deadlocked. (And if the test failure you’re trying to reproduce is itself a deadlock, that loop which completes in about 10 seconds pairs well with &lt;code class="language-markup"&gt;-timeout=20s&lt;/code&gt; so you don’t have to wait around 10 minutes to see a stack trace.)&lt;/p&gt;

&lt;p&gt;At this point, if you can reproduce the failure within a minute or less, you’re in good shape to fix the test. If it takes much longer than that to reproduce, you can shave off a bit more time by compiling the test package before the loop. Under the hood, &lt;code class="language-markup"&gt;go test&lt;/code&gt; will compile and run the test package, so we can avoid some repeated work by compiling it ourselves. When we execute our compiled test package directly, we need to use the &lt;code class="language-markup"&gt;test.&lt;/code&gt; prefix on the test-specific flags, like so: &lt;code class="language-markup"&gt;go test -c ./mypkg &amp;amp;&amp;amp; while ./mypkg.test -test.run=TestFoo -test.count=100 -test.failfast; do date; done&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;When that test loop still doesn't reproduce the flaky test&lt;/h3&gt;
&lt;h4&gt;Use the data race detector&lt;/h4&gt;
&lt;p&gt;Sometimes the test failure is a hard time-based deadline, like waiting one second for something to happen. Using the flag &lt;code class="language-markup"&gt;-race&lt;/code&gt; to compile the test with the race detector enabled will generally slow down execution, possibly enough to reproduce the failure. And if you happen to detect any new data races along the way, that’s great too.&lt;/p&gt;
&lt;h4&gt;Stop focusing on the test&lt;/h4&gt;
&lt;p&gt;This is more helpful for data-ordering flakiness than it is for timing-based flakiness.&lt;/p&gt;

&lt;p&gt;In very rare circumstances, the flaky test’s failure has to do with pollution from another test. You might drop the &lt;code class="language-markup"&gt;-run&lt;/code&gt; flag altogether and run that loop for a while to see if the test fails. Then you can gradually skip more and more of the passing tests until you identify which one(s) are causing the pollution for the flaky test. Adding a &lt;code class="language-markup"&gt;t.Skip&lt;/code&gt; call works, but for this kind of temporary change, I usually rename &lt;code class="language-markup"&gt;TestBar&lt;/code&gt; to &lt;code class="language-markup"&gt;xTestBar&lt;/code&gt; so that Go’s test detection just stops noticing the test altogether and I don’t have to see the &lt;code class="language-markup"&gt;SKIP&lt;/code&gt; lines in the verbose output.&lt;/p&gt;
&lt;h4&gt;Throttle the CPU usage of the process&lt;/h4&gt;
&lt;p&gt;Some flaky tests only seem to show up in a resource-contended environment like your CI server, and never on an otherwise idle system like your workstation. For reproducing those kinds of failures, we have had good luck with &lt;a href="https://github.com/opsengine/cpulimit"&gt;cpulimit&lt;/a&gt; which should be available in your standard package manager (e.g. &lt;code class="language-markup"&gt;brew install cpulimit&lt;/code&gt; or &lt;code class="language-markup"&gt;apt install cpulimit&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;There’s nothing magical about cpulimit. If you’ve ever pressed control-Z in your terminal to stop a running process and restored it with &lt;code class="language-markup"&gt;fg&lt;/code&gt;, then you’re basically familiar with how cpulimit works: it repeatedly sends SIGSTOP to pause the process and SIGCONT to restore it again, effectively limiting how much CPU time the process is allowed to use. For most use cases, this is a close enough approximation of a limited CPU; and the cpulimit tool has the benefit of being cross-platform and easy to use.&lt;/p&gt;

&lt;p&gt;The interesting flags for cpulimit are:&lt;/p&gt;
&lt;ul&gt;
 	&lt;li&gt;&lt;code class="language-markup"&gt;-l&lt;/code&gt;/&lt;code class="language-markup"&gt;--limit&lt;/code&gt;, to control how much CPU is available&lt;/li&gt;
 	&lt;li&gt;&lt;code class="language-markup"&gt;-i&lt;/code&gt;/&lt;code class="language-markup"&gt;--include-children&lt;/code&gt; to apply to subprocesses of the target process&lt;/li&gt;
 	&lt;li&gt;&lt;code class="language-markup"&gt;-z&lt;/code&gt;/&lt;code class="language-markup"&gt;--lazy&lt;/code&gt; to exit if the target process dies&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Choosing an appropriate value for &lt;code class="language-markup"&gt;--limit&lt;/code&gt; is mostly trial and error. Too low of a value may cause excessive run durations and unintended timeouts, but too high of a value won’t necessarily reproduce the issue.
Keep in mind that the maximum value is 100 times the number of available cores on the system, so &lt;code class="language-markup"&gt;-l 100&lt;/code&gt; doesn’t represent 100% of available CPU, but rather 100% of one core.&lt;/p&gt;

&lt;p&gt;Then, you typically want to run your compiled test package under cpulimit, like &lt;code class="language-markup"&gt;cpulimit -l 50 -i -z ./mypkg.test -test.run=TestFoo -test.count=100&lt;/code&gt;. As mentioned above, prefer to build your test package out of band with &lt;code class="language-markup"&gt;go test -c&lt;/code&gt; without using cpulimit. If you were to run &lt;code class="language-markup"&gt;cpulimit -l 50 go test ./mypkg -run=TestFoo -count=100&lt;/code&gt; — note the missing &lt;code class="language-markup"&gt;-i&lt;/code&gt; flag — you would limit &lt;code class="language-markup"&gt;go test&lt;/code&gt; but the &lt;code class="language-markup"&gt;mypkg.test&lt;/code&gt; subprocess would run without limit.&lt;/p&gt;
&lt;h3&gt;Adjust the concurrency of the Go runtime&lt;/h3&gt;
&lt;p&gt;You can set the GOMAXPROCS environment variable as explained in the runtime package documentation:&lt;/p&gt;
&lt;blockquote&gt;The GOMAXPROCS variable limits the number of operating system threads that can execute user-level Go code simultaneously. There is no limit to the number of threads that can be blocked in system calls on behalf of Go code; those do not count against the GOMAXPROCS limit.&lt;/blockquote&gt;
&lt;p&gt;When attempting to reproduce a flaky test, this setting is typically useful only when you already suspect the flakiness to be related to concurrency.&lt;/p&gt;

&lt;p&gt;There are generally only three interesting settings for GOMAXPROCS:&lt;/p&gt;
&lt;ul&gt;
 	&lt;li&gt;One, which will allow only one goroutine to execute at any moment in time&lt;/li&gt;
 	&lt;li&gt;The default setting, which is the number of logical CPUs available&lt;/li&gt;
 	&lt;li&gt;Double (or more) the number of CPUs on your system, to try to introduce more contention for shared resources&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In my experience, flaky tests that seem to be affected by GOMAXPROCS tend to never reproduce for one extreme, occasionally reproduce at default, and often reproduce at the other extreme. It depends on the particular failure mode; some tests may be flaky when there is too much contention, and others may be flaky when there aren’t enough goroutines processing work.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Reproducing flaky tests is part science and part art. I hope that these tips help you next time you’re dealing with a test that keeps failing and blocking CI at inopportune times.&lt;/p&gt;
</description>
      <pubDate>Thu, 15 Aug 2019 10:26:33 -0700</pubDate>
      <link>https://www.influxdata.com/blog/reproducing-a-flaky-test-in-go/</link>
      <guid isPermaLink="true">https://www.influxdata.com/blog/reproducing-a-flaky-test-in-go/</guid>
      <category>Use Cases</category>
      <category>Developer</category>
      <author>Mark Rushakoff (InfluxData)</author>
    </item>
    <item>
      <title>Announcing Grade: A Tool to Track Your Go Benchmarks in InfluxDB</title>
      <description>&lt;p&gt;Have you written &lt;a href="https://golang.org/pkg/testing/#hdr-Benchmarks"&gt;Go benchmarks&lt;/a&gt;? How often do you run them?&lt;/p&gt;

&lt;p&gt;Many Go developers will write and run a benchmark when working on critical code,and then &lt;em&gt;maybe&lt;/em&gt; run the benchmark again when modifying that area of the code, to decide whether the change is likely to affect performance. If you follow that use pattern, &lt;a href="https://godoc.org/golang.org/x/tools/cmd/benchcmp"&gt;benchcmp&lt;/a&gt; is an excellent utility to compare benchmark output, but if you want to run your benchmarks in CI and track their performance over time with InfluxDB, &lt;a href="https://github.com/influxdata/grade"&gt;grade&lt;/a&gt; is the tool for you.&lt;/p&gt;

&lt;p&gt;To use grade, first you need the output from a benchmark run. For example, here are the results from running &lt;code&gt;go test -bench=. -run=^$ -benchmem ./models/... 2&amp;gt;/dev/null&lt;/code&gt; against the InfluxDB &lt;code&gt;v1.0.2&lt;/code&gt; tag:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-markup"&gt;PASS
BenchmarkMarshal-2                  	  500000	      2901 ns/op	     560 B/op	      13 allocs/op
BenchmarkParsePointNoTags-2         	 2000000	       733 ns/op	  31.36 MB/s	     208 B/op	       4 allocs/op
BenchmarkParsePointWithPrecisionN-2 	 2000000	       627 ns/op	  36.68 MB/s	     208 B/op	       4 allocs/op
BenchmarkParsePointWithPrecisionU-2 	 2000000	       636 ns/op	  36.15 MB/s	     208 B/op	       4 allocs/op
BenchmarkParsePointsTagsSorted2-2   	 2000000	       947 ns/op	  53.85 MB/s	     240 B/op	       4 allocs/op
BenchmarkParsePointsTagsSorted5-2   	 1000000	      1189 ns/op	  69.75 MB/s	     272 B/op	       4 allocs/op
BenchmarkParsePointsTagsSorted10-2  	 1000000	      1624 ns/op	  88.05 MB/s	     320 B/op	       4 allocs/op
BenchmarkParsePointsTagsUnSorted2-2 	 1000000	      1167 ns/op	  43.69 MB/s	     272 B/op	       5 allocs/op
BenchmarkParsePointsTagsUnSorted5-2 	 1000000	      1627 ns/op	  50.99 MB/s	     336 B/op	       5 allocs/op
BenchmarkParsePointsTagsUnSorted10-2	  500000	      2733 ns/op	  52.32 MB/s	     448 B/op	       5 allocs/op
BenchmarkParseKey-2                 	 1000000	      2361 ns/op	    1030 B/op	      24 allocs/op
ok  	github.com/influxdata/influxdb/models	19.809s&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(One detail easy to overlook about this output is that the &lt;code&gt;-2&lt;/code&gt; suffix on all the names indicates the test was run with &lt;code&gt;GOMAXPROCS&lt;/code&gt; set to 2).&lt;/p&gt;

&lt;p&gt;I ran this on an EC2 c4.large instance, under Go 1.6.2, which is what we used to build InfluxDB at that time.If I had this output stored as &lt;code&gt;models-1.0.2.txt&lt;/code&gt;, I could run:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-markup"&gt;grade \
  -influxurl '' \
  -goversion "$(go version | cut -d' ' -f3-)" \
  -hardwareid c4.large \
  -revision v1.0.2 \
  -timestamp "$(cd $GOPATH/src/github.com/influxdata/influxdb &amp;amp;&amp;amp; git log v1.0.2 -1 --format=%ct)" \
  &amp;lt; models-1.0.2.txt&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Line-by-line, the options are:&lt;/p&gt;
&lt;ul&gt;
 	&lt;li&gt;&lt;code&gt;-influxurl&lt;/code&gt; set to an empty string so that I can print the line protocol of what would be sent to a real host&lt;/li&gt;
 	&lt;li&gt;&lt;code&gt;-goversion&lt;/code&gt; set to the output of &lt;code&gt;go version&lt;/code&gt;, without the string prefix &lt;code&gt;go version&lt;/code&gt;&lt;/li&gt;
 	&lt;li&gt;&lt;code&gt;-hardwareid&lt;/code&gt; set to &lt;code&gt;c4.large&lt;/code&gt;, so that when querying the data I understand what hardware ran the benchmarks&lt;/li&gt;
 	&lt;li&gt;&lt;code&gt;-revision&lt;/code&gt; set to the tag of the commit being tested (but I could have just as well used the SHA of the commit)&lt;/li&gt;
 	&lt;li&gt;&lt;code&gt;-timestamp&lt;/code&gt; set to the Unix timestamp of the commit being tested.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The above command would produce the following line protocol:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-markup"&gt;go,goversion=go1.6.2\ linux/amd64,hwid=c4.large,name=ParsePointNoTags,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=208i,allocs_per_op=4i,mb_per_s=31.36,n=2000000i,ns_per_op=733,revision="v1.0.2" 1475695157000000000
go,goversion=go1.6.2\ linux/amd64,hwid=c4.large,name=ParsePointWithPrecisionN,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=208i,allocs_per_op=4i,mb_per_s=36.68,n=2000000i,ns_per_op=627,revision="v1.0.2" 1475695157000000000
go,goversion=go1.6.2\ linux/amd64,hwid=c4.large,name=ParsePointWithPrecisionU,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=208i,allocs_per_op=4i,mb_per_s=36.15,n=2000000i,ns_per_op=636,revision="v1.0.2" 1475695157000000000
go,goversion=go1.6.2\ linux/amd64,hwid=c4.large,name=ParsePointsTagsSorted2,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=240i,allocs_per_op=4i,mb_per_s=53.85,n=2000000i,ns_per_op=947,revision="v1.0.2" 1475695157000000000
go,goversion=go1.6.2\ linux/amd64,hwid=c4.large,name=ParsePointsTagsSorted5,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=272i,allocs_per_op=4i,mb_per_s=69.75,n=1000000i,ns_per_op=1189,revision="v1.0.2" 1475695157000000000
go,goversion=go1.6.2\ linux/amd64,hwid=c4.large,name=ParsePointsTagsSorted10,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=320i,allocs_per_op=4i,mb_per_s=88.05,n=1000000i,ns_per_op=1624,revision="v1.0.2" 1475695157000000000
go,goversion=go1.6.2\ linux/amd64,hwid=c4.large,name=ParsePointsTagsUnSorted2,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=272i,allocs_per_op=5i,mb_per_s=43.69,n=1000000i,ns_per_op=1167,revision="v1.0.2" 1475695157000000000
go,goversion=go1.6.2\ linux/amd64,hwid=c4.large,name=ParsePointsTagsUnSorted5,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=336i,allocs_per_op=5i,mb_per_s=50.99,n=1000000i,ns_per_op=1627,revision="v1.0.2" 1475695157000000000
go,goversion=go1.6.2\ linux/amd64,hwid=c4.large,name=ParsePointsTagsUnSorted10,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=448i,allocs_per_op=5i,mb_per_s=52.32,n=500000i,ns_per_op=2733,revision="v1.0.2" 1475695157000000000
go,goversion=go1.6.2\ linux/amd64,hwid=c4.large,name=ParseKey,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=1030i,allocs_per_op=24i,n=1000000i,ns_per_op=2361,revision="v1.0.2" 1475695157000000000&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And if we were to repeat that process for the other tags &lt;code&gt;v1.1.5&lt;/code&gt;, &lt;code&gt;v1.2.4&lt;/code&gt;, and &lt;code&gt;v1.3.5&lt;/code&gt;, we would produce line protocol like:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-markup"&gt;go,goversion=go1.7.4\ linux/amd64,hwid=c4.large,name=Marshal,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=416i,allocs_per_op=4i,n=1000000i,ns_per_op=1260,revision="v1.1.5" 1493408827000000000
go,goversion=go1.7.4\ linux/amd64,hwid=c4.large,name=NewPoint,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=3424i,allocs_per_op=28i,n=200000i,ns_per_op=6387,revision="v1.1.5" 1493408827000000000
go,goversion=go1.7.4\ linux/amd64,hwid=c4.large,name=ParsePointNoTags5000,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=1644800i,allocs_per_op=5002i,mb_per_s=52.81,n=1000i,ns_per_op=2272374,revision="v1.1.5" 1493408827000000000
go,goversion=go1.7.4\ linux/amd64,hwid=c4.large,name=ParsePointNoTags,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=336i,allocs_per_op=3i,mb_per_s=36.65,n=2000000i,ns_per_op=627,revision="v1.1.5" 1493408827000000000
go,goversion=go1.7.4\ linux/amd64,hwid=c4.large,name=ParsePointWithPrecisionN,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=336i,allocs_per_op=3i,mb_per_s=44.47,n=3000000i,ns_per_op=517,revision="v1.1.5" 1493408827000000000
go,goversion=go1.7.4\ linux/amd64,hwid=c4.large,name=ParsePointWithPrecisionU,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=336i,allocs_per_op=3i,mb_per_s=44.51,n=3000000i,ns_per_op=516,revision="v1.1.5" 1493408827000000000
go,goversion=go1.7.4\ linux/amd64,hwid=c4.large,name=ParsePointsTagsSorted2,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=368i,allocs_per_op=3i,mb_per_s=62.39,n=2000000i,ns_per_op=817,revision="v1.1.5" 1493408827000000000
go,goversion=go1.7.4\ linux/amd64,hwid=c4.large,name=ParsePointsTagsSorted5,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=400i,allocs_per_op=3i,mb_per_s=80.09,n=1000000i,ns_per_op=1036,revision="v1.1.5" 1493408827000000000
go,goversion=go1.7.4\ linux/amd64,hwid=c4.large,name=ParsePointsTagsSorted10,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=448i,allocs_per_op=3i,mb_per_s=98.62,n=1000000i,ns_per_op=1449,revision="v1.1.5" 1493408827000000000
go,goversion=go1.7.4\ linux/amd64,hwid=c4.large,name=ParsePointsTagsUnSorted2,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=400i,allocs_per_op=4i,mb_per_s=51.44,n=2000000i,ns_per_op=991,revision="v1.1.5" 1493408827000000000
go,goversion=go1.7.4\ linux/amd64,hwid=c4.large,name=ParsePointsTagsUnSorted5,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=464i,allocs_per_op=4i,mb_per_s=58.78,n=1000000i,ns_per_op=1412,revision="v1.1.5" 1493408827000000000
go,goversion=go1.7.4\ linux/amd64,hwid=c4.large,name=ParsePointsTagsUnSorted10,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=576i,allocs_per_op=4i,mb_per_s=59.45,n=1000000i,ns_per_op=2405,revision="v1.1.5" 1493408827000000000
go,goversion=go1.7.4\ linux/amd64,hwid=c4.large,name=ParseKey,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=611i,allocs_per_op=3i,n=2000000i,ns_per_op=705,revision="v1.1.5" 1493408827000000000

go,goversion=go1.7.4\ linux/amd64,hwid=c4.large,name=Marshal,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=416i,allocs_per_op=4i,n=1000000i,ns_per_op=1259,revision="v1.2.4" 1494272869000000000
go,goversion=go1.7.4\ linux/amd64,hwid=c4.large,name=NewPoint,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=3048i,allocs_per_op=22i,n=300000i,ns_per_op=5168,revision="v1.2.4" 1494272869000000000
go,goversion=go1.7.4\ linux/amd64,hwid=c4.large,name=ParsePointNoTags5000,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=1404800i,allocs_per_op=5002i,mb_per_s=50.18,n=1000i,ns_per_op=2391554,revision="v1.2.4" 1494272869000000000
go,goversion=go1.7.4\ linux/amd64,hwid=c4.large,name=ParsePointNoTags,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=288i,allocs_per_op=3i,mb_per_s=37.14,n=2000000i,ns_per_op=619,revision="v1.2.4" 1494272869000000000
go,goversion=go1.7.4\ linux/amd64,hwid=c4.large,name=ParsePointWithPrecisionN,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=288i,allocs_per_op=3i,mb_per_s=45.16,n=3000000i,ns_per_op=509,revision="v1.2.4" 1494272869000000000
go,goversion=go1.7.4\ linux/amd64,hwid=c4.large,name=ParsePointWithPrecisionU,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=288i,allocs_per_op=3i,mb_per_s=44.57,n=3000000i,ns_per_op=516,revision="v1.2.4" 1494272869000000000
go,goversion=go1.7.4\ linux/amd64,hwid=c4.large,name=ParsePointsTagsSorted2,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=320i,allocs_per_op=3i,mb_per_s=63.47,n=2000000i,ns_per_op=803,revision="v1.2.4" 1494272869000000000
go,goversion=go1.7.4\ linux/amd64,hwid=c4.large,name=ParsePointsTagsSorted5,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=352i,allocs_per_op=3i,mb_per_s=79.19,n=1000000i,ns_per_op=1048,revision="v1.2.4" 1494272869000000000
go,goversion=go1.7.4\ linux/amd64,hwid=c4.large,name=ParsePointsTagsSorted10,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=400i,allocs_per_op=3i,mb_per_s=97.4,n=1000000i,ns_per_op=1468,revision="v1.2.4" 1494272869000000000
go,goversion=go1.7.4\ linux/amd64,hwid=c4.large,name=ParsePointsTagsUnSorted2,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=352i,allocs_per_op=4i,mb_per_s=51.63,n=2000000i,ns_per_op=987,revision="v1.2.4" 1494272869000000000
go,goversion=go1.7.4\ linux/amd64,hwid=c4.large,name=ParsePointsTagsUnSorted5,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=416i,allocs_per_op=4i,mb_per_s=58.78,n=1000000i,ns_per_op=1411,revision="v1.2.4" 1494272869000000000
go,goversion=go1.7.4\ linux/amd64,hwid=c4.large,name=ParsePointsTagsUnSorted10,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=528i,allocs_per_op=4i,mb_per_s=59.81,n=1000000i,ns_per_op=2390,revision="v1.2.4" 1494272869000000000
go,goversion=go1.7.4\ linux/amd64,hwid=c4.large,name=ParseKey,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=611i,allocs_per_op=3i,n=2000000i,ns_per_op=700,revision="v1.2.4" 1494272869000000000
go,goversion=go1.7.4\ linux/amd64,hwid=c4.large,name=EscapeStringField_Plain,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=16i,allocs_per_op=1i,n=20000000i,ns_per_op=67.5,revision="v1.2.4" 1494272869000000000
go,goversion=go1.7.4\ linux/amd64,hwid=c4.large,name=EscapeString_Quotes,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=48i,allocs_per_op=3i,n=10000000i,ns_per_op=169,revision="v1.2.4" 1494272869000000000
go,goversion=go1.7.4\ linux/amd64,hwid=c4.large,name=EscapeString_Backslashes,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=80i,allocs_per_op=3i,n=10000000i,ns_per_op=196,revision="v1.2.4" 1494272869000000000
go,goversion=go1.7.4\ linux/amd64,hwid=c4.large,name=EscapeString_QuotesAndBackslashes,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=160i,allocs_per_op=5i,n=3000000i,ns_per_op=412,revision="v1.2.4" 1494272869000000000

go,goversion=go1.8.3\ linux/amd64,hwid=c4.large,name=Marshal,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=256i,allocs_per_op=2i,n=1000000i,ns_per_op=1043,revision="v1.3.5" 1504050409000000000
go,goversion=go1.8.3\ linux/amd64,hwid=c4.large,name=NewPoint,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=2888i,allocs_per_op=20i,n=300000i,ns_per_op=4945,revision="v1.3.5" 1504050409000000000
go,goversion=go1.8.3\ linux/amd64,hwid=c4.large,name=NewPointFromBinary,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=240i,allocs_per_op=1i,n=3000000i,ns_per_op=456,revision="v1.3.5" 1504050409000000000
go,goversion=go1.8.3\ linux/amd64,hwid=c4.large,name=ParsePointNoTags5000,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=1404800i,allocs_per_op=5002i,mb_per_s=46.66,n=500i,ns_per_op=2571739,revision="v1.3.5" 1504050409000000000
go,goversion=go1.8.3\ linux/amd64,hwid=c4.large,name=ParsePointNoTags,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=288i,allocs_per_op=3i,mb_per_s=35.96,n=2000000i,ns_per_op=639,revision="v1.3.5" 1504050409000000000
go,goversion=go1.8.3\ linux/amd64,hwid=c4.large,name=ParsePointWithPrecisionN,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=288i,allocs_per_op=3i,mb_per_s=43.28,n=3000000i,ns_per_op=531,revision="v1.3.5" 1504050409000000000
go,goversion=go1.8.3\ linux/amd64,hwid=c4.large,name=ParsePointWithPrecisionU,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=288i,allocs_per_op=3i,mb_per_s=42.4,n=3000000i,ns_per_op=542,revision="v1.3.5" 1504050409000000000
go,goversion=go1.8.3\ linux/amd64,hwid=c4.large,name=ParsePointsTagsSorted2,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=320i,allocs_per_op=3i,mb_per_s=61.32,n=2000000i,ns_per_op=831,revision="v1.3.5" 1504050409000000000
go,goversion=go1.8.3\ linux/amd64,hwid=c4.large,name=ParsePointsTagsSorted5,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=352i,allocs_per_op=3i,mb_per_s=77.86,n=1000000i,ns_per_op=1066,revision="v1.3.5" 1504050409000000000
go,goversion=go1.8.3\ linux/amd64,hwid=c4.large,name=ParsePointsTagsSorted10,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=400i,allocs_per_op=3i,mb_per_s=97.54,n=1000000i,ns_per_op=1466,revision="v1.3.5" 1504050409000000000
go,goversion=go1.8.3\ linux/amd64,hwid=c4.large,name=ParsePointsTagsUnSorted2,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=352i,allocs_per_op=4i,mb_per_s=49.99,n=1000000i,ns_per_op=1020,revision="v1.3.5" 1504050409000000000
go,goversion=go1.8.3\ linux/amd64,hwid=c4.large,name=ParsePointsTagsUnSorted5,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=416i,allocs_per_op=4i,mb_per_s=58.28,n=1000000i,ns_per_op=1424,revision="v1.3.5" 1504050409000000000
go,goversion=go1.8.3\ linux/amd64,hwid=c4.large,name=ParsePointsTagsUnSorted10,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=528i,allocs_per_op=4i,mb_per_s=58.9,n=500000i,ns_per_op=2427,revision="v1.3.5" 1504050409000000000
go,goversion=go1.8.3\ linux/amd64,hwid=c4.large,name=ParseKey,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=611i,allocs_per_op=3i,n=2000000i,ns_per_op=782,revision="v1.3.5" 1504050409000000000
go,goversion=go1.8.3\ linux/amd64,hwid=c4.large,name=EscapeStringField_Plain,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=16i,allocs_per_op=1i,n=20000000i,ns_per_op=66.3,revision="v1.3.5" 1504050409000000000
go,goversion=go1.8.3\ linux/amd64,hwid=c4.large,name=EscapeString_Quotes,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=48i,allocs_per_op=3i,n=10000000i,ns_per_op=169,revision="v1.3.5" 1504050409000000000
go,goversion=go1.8.3\ linux/amd64,hwid=c4.large,name=EscapeString_Backslashes,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=80i,allocs_per_op=3i,n=10000000i,ns_per_op=192,revision="v1.3.5" 1504050409000000000
go,goversion=go1.8.3\ linux/amd64,hwid=c4.large,name=EscapeString_QuotesAndBackslashes,pkg=github.com/influxdata/influxdb/models,procs=2 alloced_bytes_per_op=160i,allocs_per_op=5i,n=3000000i,ns_per_op=412,revision="v1.3.5" 1504050409000000000&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To get output similar to the Go benchmark output, go to the &lt;code&gt;influx&lt;/code&gt; CLI and execute: &lt;code&gt;select revision, ns_per_op, mb_per_s, alloced_bytes_per_op, allocs_per_op from go group by "name"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You will see tabular data like:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-markup"&gt;name: go
tags: name=ParseKey
time                 revision ns_per_op mb_per_s alloced_bytes_per_op allocs_per_op
----                 -------- --------- -------- -------------------- -------------
2016-10-05T19:19:17Z v1.0.2   2361               1030                 24
2017-04-28T19:47:07Z v1.1.5   705                611                  3
2017-05-08T19:47:49Z v1.2.4   700                611                  3
2017-08-29T23:46:49Z v1.3.5   782                611                  3

name: go
tags: name=ParsePointNoTags
time                 revision ns_per_op mb_per_s alloced_bytes_per_op allocs_per_op
----                 -------- --------- -------- -------------------- -------------
2016-10-05T19:19:17Z v1.0.2   733       31.36    208                  4
2017-04-28T19:47:07Z v1.1.5   627       36.65    336                  3
2017-05-08T19:47:49Z v1.2.4   619       37.14    288                  3
2017-08-29T23:46:49Z v1.3.5   639       35.96    288                  3&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That’s how easy it is to use grade. The decisions you’ll need to make if you use grade are:&lt;/p&gt;
&lt;ul&gt;
 	&lt;li&gt;Am I going to run benchmarks against every commit, one commit per day or per week, only against tags, or something else?&lt;/li&gt;
 	&lt;li&gt;What hardware is going to execute my benchmarks, and what operating system am I going to test?&lt;/li&gt;
 	&lt;li&gt;Am I going to run with different values of &lt;code&gt;GOMAXPROCS&lt;/code&gt; or just the default value on my benchmark runner?&lt;/li&gt;
 	&lt;li&gt;Is a 1-second sample long enough, or will I use the &lt;code&gt;-benchtime&lt;/code&gt; flag for a longer duration?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And the last decision you’ll need to make is how you’ll act on the data you’re collecting. In the future, we will share TICK scripts to use with Kapacitor so that you can get an alert if a new benchmark indicates a performance decrease.&lt;/p&gt;
</description>
      <pubDate>Thu, 28 Sep 2017 04:00:06 -0700</pubDate>
      <link>https://www.influxdata.com/blog/announcing-grade-a-tool-to-track-your-go-benchmarks-in-influxdb/</link>
      <guid isPermaLink="true">https://www.influxdata.com/blog/announcing-grade-a-tool-to-track-your-go-benchmarks-in-influxdb/</guid>
      <category>Product</category>
      <category>Use Cases</category>
      <category>Developer</category>
      <author>Mark Rushakoff (InfluxData)</author>
    </item>
    <item>
      <title>InfluxDB and the /debug/vars Endpoint</title>
      <description>&lt;p&gt;Like many Go programs with an HTTP server, InfluxDB exposes some diagnostic information over the &lt;code class="language-markup"&gt;/debug/vars&lt;/code&gt; endpoint. Because the information we expose there is simply JSON, it should be very straightforward to expose InfluxDB’s diagnostics to other custom utilities that you might want to use to monitor your InfluxDB instance.&lt;/p&gt;

&lt;p&gt;Go makes it trivial to add a &lt;code class="language-markup"&gt;/debug/vars&lt;/code&gt; to your program via &lt;a href="https://golang.org/pkg/expvar/"&gt;the &lt;/a&gt;&lt;a href="https://golang.org/pkg/expvar/"&gt;&lt;code class="language-markup"&gt;/debug/vars&lt;/code&gt; &lt;/a&gt;&lt;a href="https://golang.org/pkg/expvar/"&gt;package&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;Package expvar provides a standardized interface to public variables, such as operation counters in servers. It exposes these variables via HTTP at /debug/vars in JSON format.&lt;/blockquote&gt;
&lt;p class="line-numbers"&gt;&lt;code class="language-markup"&gt;expvar&lt;/code&gt; works great for most use cases. Even though InfluxDB started using &lt;code class="language-markup"&gt;expvar&lt;/code&gt; directly, there was, and still is, a missing feature that caused us to stop using the standard &lt;code class="language-markup"&gt;expvar&lt;/code&gt; package: it does not allow you to remove published variables. There were at least &lt;a href="https://github.com/influxdata/influxdb/issues/6988"&gt;a&lt;/a&gt; &lt;a href="https://github.com/influxdata/influxdb/issues/6507"&gt;couple&lt;/a&gt; of issues filed about stats being reported for entities that were deleted. A low signal-to-noise ratio is never helpful when you're trying to debug an active problem.&lt;/p&gt;
&lt;p&gt;In &lt;a href="https://github.com/influxdata/influxdb/pull/6964"&gt;InfluxDB PR 6964&lt;/a&gt; (which was first present in the 1.0 release) we moved away from using &lt;code class="language-markup"&gt;expvar&lt;/code&gt; directly, in favor of a custom implementation with &lt;code class="language-markup"&gt;expvar&lt;/code&gt;-compatible output.&lt;/p&gt;

&lt;p&gt;Kapacitor took a slightly different approach: &lt;a href="https://github.com/influxdata/kapacitor/blob/v1.2.0/expvar/expvar.go"&gt;forking the standard library &lt;/a&gt;&lt;a href="https://github.com/influxdata/kapacitor/blob/v1.2.0/expvar/expvar.go"&gt;&lt;code class="language-markup"&gt;expvar&lt;/code&gt; &lt;/a&gt;&lt;a href="https://github.com/influxdata/kapacitor/blob/v1.2.0/expvar/expvar.go"&gt;package&lt;/a&gt; to add a &lt;code class="language-markup"&gt;Delete&lt;/code&gt; method on the &lt;code class="language-markup"&gt;Map&lt;/code&gt; type.&lt;/p&gt;
&lt;h2 class="line-numbers"&gt;&lt;code class="language-markup"&gt;/debug/vars&lt;/code&gt;&lt;code&gt;&lt;/code&gt; and the TICK Stack&lt;/h2&gt;
&lt;h3&gt;&lt;a id="user-content-influxdbs-expvar-format" class="anchor" href="https://gist.github.com/mark-rushakoff/a9135351b247a072c34013a39c5018b5#influxdbs-expvar-format"&gt;&lt;/a&gt;InfluxDB's &lt;code class="language-markup"&gt;expvar&lt;/code&gt; Format&lt;/h3&gt;
&lt;p&gt;The output of InfluxDB’s &lt;code class="language-markup"&gt;/debug/vars&lt;/code&gt; endpoint is one JSON object that roughly corresponds with the contents of the &lt;code class="language-markup"&gt;_internal&lt;/code&gt; database. The&lt;code class="language-markup"&gt;_internal&lt;/code&gt; database stores the values every 10 seconds by default, but the &lt;code class="language-markup"&gt;/debug/vars&lt;/code&gt; endpoint, like the &lt;code class="language-markup"&gt;SHOW STATS&lt;/code&gt; query, gives you an instant-in-time view of those stats.&lt;/p&gt;

&lt;p&gt;First, there are two key-value pairs that match the standard &lt;code class="language-markup"&gt;expvar&lt;/code&gt; output:&lt;/p&gt;
&lt;p class="line-numbers"&gt;&lt;code class="language-markup"&gt;cmdline&lt;/code&gt; is an array of strings representing the command line arguments used to invoke the process. If you're running &lt;code class="language-markup"&gt;influxd&lt;/code&gt; as a service on Linux, the value for &lt;code&gt;cmdline&lt;/code&gt; will look something like: &lt;code class="language-markup"&gt;["/usr/bin/influxd","-config","/etc/influxdb/influxdb.conf"]&lt;/code&gt;.&lt;/p&gt;
&lt;p class="line-numbers"&gt;&lt;code class="language-markup"&gt;memstats&lt;/code&gt; is a JSON object corresponding to &lt;a href="https://golang.org/pkg/runtime/#MemStats"&gt;the Go&lt;/a&gt;&lt;a href="https://golang.org/pkg/runtime/#MemStats"&gt;&lt;code class="language-markup"&gt;runtime.MemStats&lt;/code&gt; &lt;/a&gt;&lt;a href="https://golang.org/pkg/runtime/#MemStats"&gt;struct&lt;/a&gt;.&lt;/p&gt;
&lt;p class="line-numbers"&gt;The rest of the &lt;code class="language-markup"&gt;/debug/vars&lt;/code&gt; output have keys representing the details of the object being measured, with values in "InfluxDB expvar format". The "InfluxDB expvar format" is an object with this structure:&lt;/p&gt;

&lt;ul&gt;
 	&lt;li&gt;&lt;code&gt;name&lt;/code&gt;: a string describing what's being measured, i.e. the corresponding InfluxDB measurement&lt;/li&gt;
 	&lt;li&gt;&lt;code&gt;tags&lt;/code&gt;: an object with string keys and values, corresponding to the tags to use for the fields&lt;/li&gt;
 	&lt;li&gt;&lt;code&gt;values&lt;/code&gt;: an object whose keys and values correspond with fields for the measurement&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;a id="user-content-kapacitors-kapacitorv1debugvars-endpoint" class="anchor" href="https://gist.github.com/mark-rushakoff/a9135351b247a072c34013a39c5018b5#kapacitors-kapacitorv1debugvars-endpoint"&gt;&lt;/a&gt;Kapacitor's &lt;code class="language-markup"&gt;/kapacitor/v1/debug/vars&lt;/code&gt; Endpoint&lt;/h3&gt;
&lt;p&gt;Kapacitor uses a mix of “standard” expvar format with simple top-level key-value pairs, and InfluxDB-formatted expvars for received writes underneath the &lt;code class="language-markup"&gt;kapacitor&lt;/code&gt; key.&lt;/p&gt;
&lt;h3&gt;&lt;a id="user-content-telegrafs-influxdb-input-plugin" class="anchor" href="https://gist.github.com/mark-rushakoff/a9135351b247a072c34013a39c5018b5#telegrafs-influxdb-input-plugin"&gt;&lt;/a&gt;Telegraf's InfluxDB Input Plugin&lt;/h3&gt;
&lt;p&gt;Telegraf’s &lt;a href="https://github.com/influxdata/telegraf/tree/master/plugins/inputs/influxdb"&gt;Influxdb input plugin supports&lt;/a&gt; ingesting InfluxDB-formatted expvars from a remote HTTP endpoint. It also handles &lt;code class="language-markup"&gt;memstats&lt;/code&gt; as a special case. For a single instance of InfluxDB, this will result only in information redundant with the &lt;code class="language-markup"&gt;_internal&lt;/code&gt; database, but this is a straightforward approach to monitoring multiple InfluxDB instances in one central location.&lt;/p&gt;

&lt;p&gt;Unlike InfluxDB and Kapacitor, Telegraf does not (currently) expose a &lt;code class="language-markup"&gt;/debug/vars&lt;/code&gt; HTTP endpoint.&lt;/p&gt;
&lt;h3&gt;&lt;a id="user-content-emitting-influxdb-formatted-expvars-in-your-own-application" class="anchor" href="https://gist.github.com/mark-rushakoff/a9135351b247a072c34013a39c5018b5#emitting-influxdb-formatted-expvars-in-your-own-application"&gt;&lt;/a&gt;Emitting InfluxDB-Formatted expvars in Your Own Application&lt;/h3&gt;
&lt;p&gt;I haven’t seen any standalone projects that allow you to emit InfluxDB-formatted expvars, but it doesn’t take much code to do that yourself if you understand the format. The entire file of &lt;a href="https://github.com/influxdata/influxdb/blob/89bc392ec4ff4040ccba26652dd464d1d7adbea7/influxvar.go"&gt;our first, pure-expvar implementation&lt;/a&gt; is less than 50 lines, and the corresponding code to &lt;a href="https://github.com/influxdata/influxdb/blob/89bc392ec4ff4040ccba26652dd464d1d7adbea7/services/httpd/handler.go#L590-L603"&gt;serve expvars over HTTP&lt;/a&gt; is only about 11 lines.&lt;/p&gt;

&lt;p&gt;Have you implemented code to emit InfluxDB-formatted expvars, in Go or in any other language? Share your solutions over on &lt;a href="https://community.influxdata.com/t/influxdb-and-the-debug-vars-endpoint/467"&gt;this thread&lt;/a&gt; on &lt;a href="https://community.influxdata.com"&gt;community.influxdata.com&lt;/a&gt;.&lt;/p&gt;
</description>
      <pubDate>Mon, 03 Apr 2017 15:43:46 -0700</pubDate>
      <link>https://www.influxdata.com/blog/influxdb-debugvars-endpoint/</link>
      <guid isPermaLink="true">https://www.influxdata.com/blog/influxdb-debugvars-endpoint/</guid>
      <category>Product</category>
      <category>Use Cases</category>
      <category>Developer</category>
      <author>Mark Rushakoff (InfluxData)</author>
    </item>
  </channel>
</rss>
