{"action":"created","comment":{"url":"https://api.github.com/repos/prometheus/prometheus/pulls/comments/25940229","id":25940229,"diff_hunk":"@@ -0,0 +1,513 @@\n+// Copyright 2014 The Prometheus Authors\n+// Licensed under the Apache License, Version 2.0 (the \"License\");\n+// you may not use this file except in compliance with the License.\n+// You may obtain a copy of the License at\n+//\n+// http://www.apache.org/licenses/LICENSE-2.0\n+//\n+// Unless required by applicable law or agreed to in writing, software\n+// distributed under the License is distributed on an \"AS IS\" BASIS,\n+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+// See the License for the specific language governing permissions and\n+// limitations under the License.\n+\n+package local\n+\n+import (\n+\t\"encoding/binary\"\n+\t\"fmt\"\n+\t\"io\"\n+\t\"math\"\n+\t\"sort\"\n+\n+\tclientmodel \"github.com/prometheus/client_golang/model\"\n+\n+\t\"github.com/prometheus/prometheus/storage/metric\"\n+)\n+\n+// The 37-byte header of a delta-encoded chunk looks like:\n+//\n+// - used buf bytes:           2 bytes\n+// - time double-delta bytes:  1 bytes\n+// - value double-delta bytes: 1 bytes\n+// - is integer:               1 byte\n+// - base time:                8 bytes\n+// - base value:               8 bytes\n+// - base time delta:          8 bytes\n+// - base value delta:         8 bytes\n+const (\n+\tdoubleDeltaHeaderBytes = 37\n+\n+\tdoubleDeltaHeaderBufLenOffset         = 0\n+\tdoubleDeltaHeaderTimeBytesOffset      = 2\n+\tdoubleDeltaHeaderValueBytesOffset     = 3\n+\tdoubleDeltaHeaderIsIntOffset          = 4\n+\tdoubleDeltaHeaderBaseTimeOffset       = 5\n+\tdoubleDeltaHeaderBaseValueOffset      = 13\n+\tdoubleDeltaHeaderBaseTimeDeltaOffset  = 21\n+\tdoubleDeltaHeaderBaseValueDeltaOffset = 29\n+)\n+\n+// A doubleDeltaEncodedChunk adaptively stores sample timestamps and values with\n+// a double-delta encoding of various types (int, float) and bit widths. A base\n+// value and timestamp and a base delta for each is saved in the header. The\n+// payload consists of double-deltas, i.e. deviations from the values and\n+// timestamps calculated by applying the base value and time and the base deltas.\n+// However, once 8 bytes would be needed to encode a double-delta value, a\n+// fall-back to the absolute numbers happens (so that timestamps are saved\n+// directly as int64 and values as float64).\n+// doubleDeltaEncodedChunk implements the chunk interface.\n+type doubleDeltaEncodedChunk []byte\n+\n+// newDoubleDeltaEncodedChunk returns a newly allocated doubleDeltaEncodedChunk.\n+func newDoubleDeltaEncodedChunk(tb, vb deltaBytes, isInt bool, length int) *doubleDeltaEncodedChunk {\n+\tif tb < 1 {\n+\t\tpanic(\"need at least 1 time delta byte\")\n+\t}\n+\tif length < doubleDeltaHeaderBytes+16 {\n+\t\tpanic(fmt.Errorf(\n+\t\t\t\"chunk length %d bytes is insufficient, need at least %d\",\n+\t\t\tlength, doubleDeltaHeaderBytes+16,\n+\t\t))\n+\t}\n+\tc := make(doubleDeltaEncodedChunk, doubleDeltaHeaderIsIntOffset+1, length)\n+\n+\tc[doubleDeltaHeaderTimeBytesOffset] = byte(tb)\n+\tc[doubleDeltaHeaderValueBytesOffset] = byte(vb)\n+\tif vb < d8 && isInt { // Only use int for fewer than 8 value double-delta bytes.\n+\t\tc[doubleDeltaHeaderIsIntOffset] = 1\n+\t} else {\n+\t\tc[doubleDeltaHeaderIsIntOffset] = 0\n+\t}\n+\treturn &c\n+}\n+\n+func (c doubleDeltaEncodedChunk) newFollowupChunk() chunk {\n+\treturn newDoubleDeltaEncodedChunk(d1, d0, true, cap(c))\n+}\n+\n+func (c doubleDeltaEncodedChunk) baseTime() clientmodel.Timestamp {\n+\treturn clientmodel.Timestamp(\n+\t\tbinary.LittleEndian.Uint64(\n+\t\t\tc[doubleDeltaHeaderBaseTimeOffset:],\n+\t\t),\n+\t)\n+}\n+\n+func (c doubleDeltaEncodedChunk) baseValue() clientmodel.SampleValue {\n+\treturn clientmodel.SampleValue(\n+\t\tmath.Float64frombits(\n+\t\t\tbinary.LittleEndian.Uint64(\n+\t\t\t\tc[doubleDeltaHeaderBaseValueOffset:],\n+\t\t\t),\n+\t\t),\n+\t)\n+}\n+\n+func (c doubleDeltaEncodedChunk) baseTimeDelta() clientmodel.Timestamp {\n+\treturn clientmodel.Timestamp(\n+\t\tbinary.LittleEndian.Uint64(\n+\t\t\tc[doubleDeltaHeaderBaseTimeDeltaOffset:],\n+\t\t),\n+\t)\n+}\n+\n+func (c doubleDeltaEncodedChunk) baseValueDelta() clientmodel.SampleValue {\n+\treturn clientmodel.SampleValue(\n+\t\tmath.Float64frombits(\n+\t\t\tbinary.LittleEndian.Uint64(\n+\t\t\t\tc[doubleDeltaHeaderBaseValueDeltaOffset:],\n+\t\t\t),\n+\t\t),\n+\t)\n+}\n+\n+func (c doubleDeltaEncodedChunk) timeBytes() deltaBytes {\n+\treturn deltaBytes(c[doubleDeltaHeaderTimeBytesOffset])\n+}\n+\n+func (c doubleDeltaEncodedChunk) valueBytes() deltaBytes {\n+\treturn deltaBytes(c[doubleDeltaHeaderValueBytesOffset])\n+}\n+\n+func (c doubleDeltaEncodedChunk) sampleSize() int {\n+\treturn int(c.timeBytes() + c.valueBytes())\n+}\n+\n+func (c doubleDeltaEncodedChunk) len() int {\n+\tif len(c) <= doubleDeltaHeaderIsIntOffset+1 {\n+\t\treturn 0\n+\t}\n+\tif len(c) <= doubleDeltaHeaderBaseValueOffset+8 {\n+\t\treturn 1\n+\t}\n+\treturn (len(c)-doubleDeltaHeaderBytes)/c.sampleSize() + 2\n+}\n+\n+func (c doubleDeltaEncodedChunk) isInt() bool {\n+\treturn c[doubleDeltaHeaderIsIntOffset] == 1\n+}\n+\n+// add implements chunk.\n+func (c doubleDeltaEncodedChunk) add(s *metric.SamplePair) []chunk {\n+\tif len(c) <= doubleDeltaHeaderIsIntOffset+1 {\n+\t\t// This is the first sample added to this chunk. Add it as base\n+\t\t// time and value.\n+\t\tc = c[:doubleDeltaHeaderBaseValueOffset+8]\n+\t\tbinary.LittleEndian.PutUint64(\n+\t\t\tc[doubleDeltaHeaderBaseTimeOffset:],\n+\t\t\tuint64(s.Timestamp),\n+\t\t)\n+\t\tbinary.LittleEndian.PutUint64(\n+\t\t\tc[doubleDeltaHeaderBaseValueOffset:],\n+\t\t\tmath.Float64bits(float64(s.Value)),\n+\t\t)\n+\t\treturn []chunk{&c}\n+\t}\n+\n+\ttb := c.timeBytes()\n+\tvb := c.valueBytes()\n+\n+\tif len(c) <= doubleDeltaHeaderBaseValueOffset+8 {\n+\t\t// This is the 2nd sample added to this chunk. Calculate the\n+\t\t// base deltas.\n+\t\tbaseTimeDelta := s.Timestamp - c.baseTime()\n+\t\tif baseTimeDelta < 0 {\n+\t\t\t// TODO(beorn7): We ignore this irregular case for now. Once\n+\t\t\t// https://github.com/prometheus/prometheus/issues/481 is\n+\t\t\t// fixed, we should panic here instead.\n+\t\t\treturn []chunk{&c}\n+\t\t}\n+\t\tc = c[:doubleDeltaHeaderBytes]\n+\t\tif tb >= d8 || bytesNeededForUnsignedTimestampDelta(baseTimeDelta) >= d8 {\n+\t\t\t// If already the base delta needs d8 (or we are at d8\n+\t\t\t// already, anyway), we better encode this timestamp\n+\t\t\t// directly rather than as a delta and switch everything\n+\t\t\t// to d8.\n+\t\t\tc[doubleDeltaHeaderTimeBytesOffset] = byte(d8)\n+\t\t\tbinary.LittleEndian.PutUint64(\n+\t\t\t\tc[doubleDeltaHeaderBaseTimeDeltaOffset:],\n+\t\t\t\tuint64(s.Timestamp),\n+\t\t\t)\n+\t\t} else {\n+\t\t\tbinary.LittleEndian.PutUint64(\n+\t\t\t\tc[doubleDeltaHeaderBaseTimeDeltaOffset:],\n+\t\t\t\tuint64(baseTimeDelta),\n+\t\t\t)\n+\t\t}\n+\t\tbaseValue := c.baseValue()\n+\t\tbaseValueDelta := s.Value - baseValue\n+\t\tif vb >= d8 || baseValue+baseValueDelta != s.Value {\n+\t\t\t// If we can't reproduce the original sample value (or\n+\t\t\t// if we are at d8 already, anyway), we better encode\n+\t\t\t// this value directly rather than as a delta and switch\n+\t\t\t// everything to d8.\n+\t\t\tc[doubleDeltaHeaderValueBytesOffset] = byte(d8)\n+\t\t\tc[doubleDeltaHeaderIsIntOffset] = 0\n+\t\t\tbinary.LittleEndian.PutUint64(\n+\t\t\t\tc[doubleDeltaHeaderBaseValueDeltaOffset:],\n+\t\t\t\tmath.Float64bits(float64(s.Value)),\n+\t\t\t)\n+\t\t} else {\n+\t\t\tbinary.LittleEndian.PutUint64(\n+\t\t\t\tc[doubleDeltaHeaderBaseValueDeltaOffset:],\n+\t\t\t\tmath.Float64bits(float64(baseValueDelta)),\n+\t\t\t)\n+\t\t}\n+\t\treturn []chunk{&c}\n+\t}\n+\n+\tremainingBytes := cap(c) - len(c)\n+\tsampleSize := c.sampleSize()\n+\n+\t// Do we generally have space for another sample in this chunk? If not,\n+\t// overflow into a new one.\n+\tif remainingBytes < sampleSize {\n+\t\toverflowChunks := c.newFollowupChunk().add(s)\n+\t\treturn []chunk{&c, overflowChunks[0]}\n+\t}\n+\n+\tdt := s.Timestamp - c.baseTime() - clientmodel.Timestamp(c.len())*c.baseTimeDelta()\n+\tdv := s.Value - c.baseValue() - clientmodel.SampleValue(c.len())*c.baseValueDelta()\n+\n+\t// If the new sample is incompatible with the current encoding, reencode the\n+\t// existing chunk data into new chunk(s).\n+\t//\n+\t// int->float.\n+\tif c.isInt() && !isInt64(dv) {\n+\t\treturn transcodeAndAdd(newDoubleDeltaEncodedChunk(tb, d4, false, cap(c)), &c, s)\n+\t}\n+\t// float32->float64.\n+\tif !c.isInt() && vb == d4 && !isFloat32(dv) {\n+\t\treturn transcodeAndAdd(newDoubleDeltaEncodedChunk(tb, d8, false, cap(c)), &c, s)\n+\t}\n+\tif tb < d8 || vb < d8 {\n+\t\t// Maybe more bytes per sample.\n+\t\tntb := bytesNeededForSignedTimestampDelta(dt)\n+\t\tnvb := bytesNeededForSampleValueDelta(dv, c.isInt())\n+\t\tif ntb > tb || nvb > vb {\n+\t\t\tntb = max(ntb, tb)\n+\t\t\tnvb = max(nvb, vb)\n+\t\t\treturn transcodeAndAdd(newDoubleDeltaEncodedChunk(ntb, nvb, c.isInt(), cap(c)), &c, s)\n+\t\t}\n+\t}\n+\toffset := len(c)\n+\tc = c[:offset+sampleSize]\n+\n+\tswitch tb {\n+\tcase d1:\n+\t\tc[offset] = byte(dt)\n+\tcase d2:\n+\t\tbinary.LittleEndian.PutUint16(c[offset:], uint16(dt))\n+\tcase d4:\n+\t\tbinary.LittleEndian.PutUint32(c[offset:], uint32(dt))\n+\tcase d8:\n+\t\t// Store the absolute value (no delta) in case of d8.\n+\t\tbinary.LittleEndian.PutUint64(c[offset:], uint64(s.Timestamp))\n+\tdefault:\n+\t\tpanic(\"invalid number of bytes for time delta\")\n+\t}\n+\n+\toffset += int(tb)\n+\n+\tif c.isInt() {\n+\t\tswitch vb {\n+\t\tcase d0:\n+\t\t\t// No-op. Constant delta is stored as base value.\n+\t\tcase d1:\n+\t\t\tc[offset] = byte(dv)\n+\t\tcase d2:\n+\t\t\tbinary.LittleEndian.PutUint16(c[offset:], uint16(dv))\n+\t\tcase d4:\n+\t\t\tbinary.LittleEndian.PutUint32(c[offset:], uint32(dv))\n+\t\t// d8 must not happen. Those samples are encoded as float64.\n+\t\tdefault:\n+\t\t\tpanic(\"invalid number of bytes for integer delta\")\n+\t\t}\n+\t} else {\n+\t\tswitch vb {\n+\t\tcase d4:\n+\t\t\tbinary.LittleEndian.PutUint32(c[offset:], math.Float32bits(float32(dv)))\n+\t\tcase d8:\n+\t\t\t// Store the absolute value (no delta) in case of d8.\n+\t\t\tbinary.LittleEndian.PutUint64(c[offset:], math.Float64bits(float64(s.Value)))\n+\t\tdefault:\n+\t\t\tpanic(\"invalid number of bytes for floating point delta\")\n+\t\t}\n+\t}\n+\treturn []chunk{&c}\n+}\n+\n+// clone implements chunk.\n+func (c doubleDeltaEncodedChunk) clone() chunk {\n+\tclone := make(doubleDeltaEncodedChunk, len(c), cap(c))\n+\tcopy(clone, c)\n+\treturn &clone\n+}\n+\n+// values implements chunk.\n+func (c doubleDeltaEncodedChunk) values() <-chan *metric.SamplePair {\n+\tn := c.len()\n+\tvaluesChan := make(chan *metric.SamplePair)\n+\tgo func() {\n+\t\tfor i := 0; i < n; i++ {\n+\t\t\tvaluesChan <- c.valueAtIndex(i)\n+\t\t}\n+\t\tclose(valuesChan)\n+\t}()\n+\treturn valuesChan\n+}\n+\n+func (c doubleDeltaEncodedChunk) valueAtIndex(idx int) *metric.SamplePair {\n+\tif idx == 0 {\n+\t\treturn &metric.SamplePair{\n+\t\t\tTimestamp: c.baseTime(),\n+\t\t\tValue:     c.baseValue(),\n+\t\t}\n+\t}\n+\tif idx == 1 {\n+\t\t// If time and/or value bytes are at d8, the time and value is\n+\t\t// saved directly rather than as a difference.\n+\t\ttimestamp := c.baseTimeDelta()\n+\t\tif c.timeBytes() < d8 {\n+\t\t\ttimestamp += c.baseTime()\n+\t\t}\n+\t\tvalue := c.baseValueDelta()\n+\t\tif c.valueBytes() < d8 {\n+\t\t\tvalue += c.baseValue()\n+\t\t}\n+\t\treturn &metric.SamplePair{\n+\t\t\tTimestamp: timestamp,\n+\t\t\tValue:     value,\n+\t\t}\n+\t}\n+\toffset := doubleDeltaHeaderBytes + (idx-2)*c.sampleSize()\n+\n+\tvar ts clientmodel.Timestamp\n+\tswitch c.timeBytes() {\n+\tcase d1:\n+\t\tts = c.baseTime() +\n+\t\t\tclientmodel.Timestamp(idx)*c.baseTimeDelta() +\n+\t\t\tclientmodel.Timestamp(int8(c[offset]))\n+\tcase d2:\n+\t\tts = c.baseTime() +\n+\t\t\tclientmodel.Timestamp(idx)*c.baseTimeDelta() +\n+\t\t\tclientmodel.Timestamp(int16(binary.LittleEndian.Uint16(c[offset:])))\n+\tcase d4:\n+\t\tts = c.baseTime() +\n+\t\t\tclientmodel.Timestamp(idx)*c.baseTimeDelta() +\n+\t\t\tclientmodel.Timestamp(int32(binary.LittleEndian.Uint32(c[offset:])))\n+\tcase d8:\n+\t\t// Take absolute value for d8.\n+\t\tts = clientmodel.Timestamp(binary.LittleEndian.Uint64(c[offset:]))\n+\tdefault:\n+\t\tpanic(\"Invalid number of bytes for time delta\")\n+\t}\n+\n+\toffset += int(c.timeBytes())\n+\n+\tvar v clientmodel.SampleValue\n+\tif c.isInt() {\n+\t\tswitch c.valueBytes() {\n+\t\tcase d0:\n+\t\t\tv = c.baseValue() +\n+\t\t\t\tclientmodel.SampleValue(idx)*c.baseValueDelta()\n+\t\tcase d1:\n+\t\t\tv = c.baseValue() +\n+\t\t\t\tclientmodel.SampleValue(idx)*c.baseValueDelta() +\n+\t\t\t\tclientmodel.SampleValue(int8(c[offset]))\n+\t\tcase d2:\n+\t\t\tv = c.baseValue() +\n+\t\t\t\tclientmodel.SampleValue(idx)*c.baseValueDelta() +\n+\t\t\t\tclientmodel.SampleValue(int16(binary.LittleEndian.Uint16(c[offset:])))\n+\t\tcase d4:\n+\t\t\tv = c.baseValue() +\n+\t\t\t\tclientmodel.SampleValue(idx)*c.baseValueDelta() +\n+\t\t\t\tclientmodel.SampleValue(int32(binary.LittleEndian.Uint32(c[offset:])))\n+\t\t// No d8 for ints.\n+\t\tdefault:\n+\t\t\tpanic(\"Invalid number of bytes for integer delta\")\n+\t\t}\n+\t} else {\n+\t\tswitch c.valueBytes() {\n+\t\tcase d4:\n+\t\t\tv = c.baseValue() +\n+\t\t\t\tclientmodel.SampleValue(idx)*c.baseValueDelta() +\n+\t\t\t\tclientmodel.SampleValue(math.Float32frombits(binary.LittleEndian.Uint32(c[offset:])))\n+\t\tcase d8:\n+\t\t\t// Take absolute value for d8.\n+\t\t\tv = clientmodel.SampleValue(math.Float64frombits(binary.LittleEndian.Uint64(c[offset:])))\n+\t\tdefault:\n+\t\t\tpanic(\"Invalid number of bytes for floating point delta\")\n+\t\t}\n+\t}\n+\treturn &metric.SamplePair{\n+\t\tTimestamp: ts,\n+\t\tValue:     v,\n+\t}\n+}\n+\n+// firstTime implements chunk.\n+func (c doubleDeltaEncodedChunk) firstTime() clientmodel.Timestamp {\n+\treturn c.baseTime()\n+}\n+\n+// lastTime implements chunk.\n+func (c doubleDeltaEncodedChunk) lastTime() clientmodel.Timestamp {\n+\treturn c.valueAtIndex(c.len() - 1).Timestamp\n+}\n+\n+// marshal implements chunk.\n+func (c doubleDeltaEncodedChunk) marshal(w io.Writer) error {\n+\tif len(c) > math.MaxUint16 {\n+\t\tpanic(\"chunk buffer length would overflow a 16 bit uint.\")\n+\t}\n+\tbinary.LittleEndian.PutUint16(c[doubleDeltaHeaderBufLenOffset:], uint16(len(c)))\n+\n+\tn, err := w.Write(c[:cap(c)])\n+\tif err != nil {\n+\t\treturn err\n+\t}\n+\tif n != cap(c) {\n+\t\treturn fmt.Errorf(\"wanted to write %d bytes, wrote %d\", len(c), n)\n+\t}\n+\treturn nil\n+}\n+\n+// unmarshal implements chunk.\n+func (c *doubleDeltaEncodedChunk) unmarshal(r io.Reader) error {\n+\t*c = (*c)[:cap(*c)]\n+\treadBytes := 0\n+\tfor readBytes < len(*c) {\n+\t\tn, err := r.Read((*c)[readBytes:])\n+\t\tif err != nil {\n+\t\t\treturn err\n+\t\t}\n+\t\treadBytes += n\n+\t}\n+\t*c = (*c)[:binary.LittleEndian.Uint16((*c)[doubleDeltaHeaderBufLenOffset:])]\n+\treturn nil\n+}\n+\n+// doubleDeltaEncodedChunkIterator implements chunkIterator.\n+type doubleDeltaEncodedChunkIterator struct {\n+\tchunk *doubleDeltaEncodedChunk\n+\t// TODO(beorn7): add more fields here to keep track of last position.\n+}\n+\n+// newIterator implements chunk.\n+func (c *doubleDeltaEncodedChunk) newIterator() chunkIterator {\n+\treturn &doubleDeltaEncodedChunkIterator{\n+\t\tchunk: c,\n+\t}\n+}\n+\n+// getValueAtTime implements chunkIterator.\n+func (it *doubleDeltaEncodedChunkIterator) getValueAtTime(t clientmodel.Timestamp) metric.Values {","path":"storage/local/doubledelta.go","position":466,"original_position":466,"commit_id":"13fcf1ddbcc987bedf72dfc03b587d38f62421a3","original_commit_id":"13fcf1ddbcc987bedf72dfc03b587d38f62421a3","user":{"login":"beorn7","id":5609886,"avatar_url":"https://avatars.githubusercontent.com/u/5609886?v=3","gravatar_id":"","url":"https://api.github.com/users/beorn7","html_url":"https://github.com/beorn7","followers_url":"https://api.github.com/users/beorn7/followers","following_url":"https://api.github.com/users/beorn7/following{/other_user}","gists_url":"https://api.github.com/users/beorn7/gists{/gist_id}","starred_url":"https://api.github.com/users/beorn7/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/beorn7/subscriptions","organizations_url":"https://api.github.com/users/beorn7/orgs","repos_url":"https://api.github.com/users/beorn7/repos","events_url":"https://api.github.com/users/beorn7/events{/privacy}","received_events_url":"https://api.github.com/users/beorn7/received_events","type":"User","site_admin":false},"body":"Every single idea I have so far about iterator optimization requires knowledge of chunk internals and will not work with the interface you described.\r\n\r\nI was tempted to start with the optimization right now, but I thought I keep the PR shorter. So I'd vote for keeping the iterators separate, with specific code changes imminent.","created_at":"2015-03-06T11:52:50Z","updated_at":"2015-03-06T11:52:50Z","html_url":"https://github.com/prometheus/prometheus/pull/579#discussion_r25940229","pull_request_url":"https://api.github.com/repos/prometheus/prometheus/pulls/579","_links":{"self":{"href":"https://api.github.com/repos/prometheus/prometheus/pulls/comments/25940229"},"html":{"href":"https://github.com/prometheus/prometheus/pull/579#discussion_r25940229"},"pull_request":{"href":"https://api.github.com/repos/prometheus/prometheus/pulls/579"}}},"pull_request":{"url":"https://api.github.com/repos/prometheus/prometheus/pulls/579","id":30499138,"html_url":"https://github.com/prometheus/prometheus/pull/579","diff_url":"https://github.com/prometheus/prometheus/pull/579.diff","patch_url":"https://github.com/prometheus/prometheus/pull/579.patch","issue_url":"https://api.github.com/repos/prometheus/prometheus/issues/579","number":579,"state":"open","locked":false,"title":"Implement double-delta encoded chunks.","user":{"login":"beorn7","id":5609886,"avatar_url":"https://avatars.githubusercontent.com/u/5609886?v=3","gravatar_id":"","url":"https://api.github.com/users/beorn7","html_url":"https://github.com/beorn7","followers_url":"https://api.github.com/users/beorn7/followers","following_url":"https://api.github.com/users/beorn7/following{/other_user}","gists_url":"https://api.github.com/users/beorn7/gists{/gist_id}","starred_url":"https://api.github.com/users/beorn7/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/beorn7/subscriptions","organizations_url":"https://api.github.com/users/beorn7/orgs","repos_url":"https://api.github.com/users/beorn7/repos","events_url":"https://api.github.com/users/beorn7/events{/privacy}","received_events_url":"https://api.github.com/users/beorn7/received_events","type":"User","site_admin":false},"body":"@juliusv Only a preview. Fuzz test has still issues.","created_at":"2015-03-04T20:03:21Z","updated_at":"2015-03-06T11:52:50Z","closed_at":null,"merged_at":null,"merge_commit_sha":"7b0711924b7cbf2bf4a32cd27bfc3a5ff9f5d31d","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/prometheus/prometheus/pulls/579/commits","review_comments_url":"https://api.github.com/repos/prometheus/prometheus/pulls/579/comments","review_comment_url":"https://api.github.com/repos/prometheus/prometheus/pulls/comments{/number}","comments_url":"https://api.github.com/repos/prometheus/prometheus/issues/579/comments","statuses_url":"https://api.github.com/repos/prometheus/prometheus/statuses/13fcf1ddbcc987bedf72dfc03b587d38f62421a3","head":{"label":"prometheus:beorn7/chunk-encoding","ref":"beorn7/chunk-encoding","sha":"13fcf1ddbcc987bedf72dfc03b587d38f62421a3","user":{"login":"prometheus","id":3380462,"avatar_url":"https://avatars.githubusercontent.com/u/3380462?v=3","gravatar_id":"","url":"https://api.github.com/users/prometheus","html_url":"https://github.com/prometheus","followers_url":"https://api.github.com/users/prometheus/followers","following_url":"https://api.github.com/users/prometheus/following{/other_user}","gists_url":"https://api.github.com/users/prometheus/gists{/gist_id}","starred_url":"https://api.github.com/users/prometheus/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/prometheus/subscriptions","organizations_url":"https://api.github.com/users/prometheus/orgs","repos_url":"https://api.github.com/users/prometheus/repos","events_url":"https://api.github.com/users/prometheus/events{/privacy}","received_events_url":"https://api.github.com/users/prometheus/received_events","type":"Organization","site_admin":false},"repo":{"id":6838921,"name":"prometheus","full_name":"prometheus/prometheus","owner":{"login":"prometheus","id":3380462,"avatar_url":"https://avatars.githubusercontent.com/u/3380462?v=3","gravatar_id":"","url":"https://api.github.com/users/prometheus","html_url":"https://github.com/prometheus","followers_url":"https://api.github.com/users/prometheus/followers","following_url":"https://api.github.com/users/prometheus/following{/other_user}","gists_url":"https://api.github.com/users/prometheus/gists{/gist_id}","starred_url":"https://api.github.com/users/prometheus/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/prometheus/subscriptions","organizations_url":"https://api.github.com/users/prometheus/orgs","repos_url":"https://api.github.com/users/prometheus/repos","events_url":"https://api.github.com/users/prometheus/events{/privacy}","received_events_url":"https://api.github.com/users/prometheus/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/prometheus/prometheus","description":"The Prometheus monitoring system and time series database.","fork":false,"url":"https://api.github.com/repos/prometheus/prometheus","forks_url":"https://api.github.com/repos/prometheus/prometheus/forks","keys_url":"https://api.github.com/repos/prometheus/prometheus/keys{/key_id}","collaborators_url":"https://api.github.com/repos/prometheus/prometheus/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/prometheus/prometheus/teams","hooks_url":"https://api.github.com/repos/prometheus/prometheus/hooks","issue_events_url":"https://api.github.com/repos/prometheus/prometheus/issues/events{/number}","events_url":"https://api.github.com/repos/prometheus/prometheus/events","assignees_url":"https://api.github.com/repos/prometheus/prometheus/assignees{/user}","branches_url":"https://api.github.com/repos/prometheus/prometheus/branches{/branch}","tags_url":"https://api.github.com/repos/prometheus/prometheus/tags","blobs_url":"https://api.github.com/repos/prometheus/prometheus/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/prometheus/prometheus/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/prometheus/prometheus/git/refs{/sha}","trees_url":"https://api.github.com/repos/prometheus/prometheus/git/trees{/sha}","statuses_url":"https://api.github.com/repos/prometheus/prometheus/statuses/{sha}","languages_url":"https://api.github.com/repos/prometheus/prometheus/languages","stargazers_url":"https://api.github.com/repos/prometheus/prometheus/stargazers","contributors_url":"https://api.github.com/repos/prometheus/prometheus/contributors","subscribers_url":"https://api.github.com/repos/prometheus/prometheus/subscribers","subscription_url":"https://api.github.com/repos/prometheus/prometheus/subscription","commits_url":"https://api.github.com/repos/prometheus/prometheus/commits{/sha}","git_commits_url":"https://api.github.com/repos/prometheus/prometheus/git/commits{/sha}","comments_url":"https://api.github.com/repos/prometheus/prometheus/comments{/number}","issue_comment_url":"https://api.github.com/repos/prometheus/prometheus/issues/comments{/number}","contents_url":"https://api.github.com/repos/prometheus/prometheus/contents/{+path}","compare_url":"https://api.github.com/repos/prometheus/prometheus/compare/{base}...{head}","merges_url":"https://api.github.com/repos/prometheus/prometheus/merges","archive_url":"https://api.github.com/repos/prometheus/prometheus/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/prometheus/prometheus/downloads","issues_url":"https://api.github.com/repos/prometheus/prometheus/issues{/number}","pulls_url":"https://api.github.com/repos/prometheus/prometheus/pulls{/number}","milestones_url":"https://api.github.com/repos/prometheus/prometheus/milestones{/number}","notifications_url":"https://api.github.com/repos/prometheus/prometheus/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/prometheus/prometheus/labels{/name}","releases_url":"https://api.github.com/repos/prometheus/prometheus/releases{/id}","created_at":"2012-11-24T11:14:12Z","updated_at":"2015-03-05T13:36:53Z","pushed_at":"2015-03-05T19:33:37Z","git_url":"git://github.com/prometheus/prometheus.git","ssh_url":"git@github.com:prometheus/prometheus.git","clone_url":"https://github.com/prometheus/prometheus.git","svn_url":"https://github.com/prometheus/prometheus","homepage":"http://prometheus.io/","size":14287,"stargazers_count":1787,"watchers_count":1787,"language":"Go","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":85,"mirror_url":null,"open_issues_count":77,"forks":85,"open_issues":77,"watchers":1787,"default_branch":"master"}},"base":{"label":"prometheus:master","ref":"master","sha":"38ab8cc39b074cc5253c5648592ef150968cf0eb","user":{"login":"prometheus","id":3380462,"avatar_url":"https://avatars.githubusercontent.com/u/3380462?v=3","gravatar_id":"","url":"https://api.github.com/users/prometheus","html_url":"https://github.com/prometheus","followers_url":"https://api.github.com/users/prometheus/followers","following_url":"https://api.github.com/users/prometheus/following{/other_user}","gists_url":"https://api.github.com/users/prometheus/gists{/gist_id}","starred_url":"https://api.github.com/users/prometheus/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/prometheus/subscriptions","organizations_url":"https://api.github.com/users/prometheus/orgs","repos_url":"https://api.github.com/users/prometheus/repos","events_url":"https://api.github.com/users/prometheus/events{/privacy}","received_events_url":"https://api.github.com/users/prometheus/received_events","type":"Organization","site_admin":false},"repo":{"id":6838921,"name":"prometheus","full_name":"prometheus/prometheus","owner":{"login":"prometheus","id":3380462,"avatar_url":"https://avatars.githubusercontent.com/u/3380462?v=3","gravatar_id":"","url":"https://api.github.com/users/prometheus","html_url":"https://github.com/prometheus","followers_url":"https://api.github.com/users/prometheus/followers","following_url":"https://api.github.com/users/prometheus/following{/other_user}","gists_url":"https://api.github.com/users/prometheus/gists{/gist_id}","starred_url":"https://api.github.com/users/prometheus/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/prometheus/subscriptions","organizations_url":"https://api.github.com/users/prometheus/orgs","repos_url":"https://api.github.com/users/prometheus/repos","events_url":"https://api.github.com/users/prometheus/events{/privacy}","received_events_url":"https://api.github.com/users/prometheus/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/prometheus/prometheus","description":"The Prometheus monitoring system and time series database.","fork":false,"url":"https://api.github.com/repos/prometheus/prometheus","forks_url":"https://api.github.com/repos/prometheus/prometheus/forks","keys_url":"https://api.github.com/repos/prometheus/prometheus/keys{/key_id}","collaborators_url":"https://api.github.com/repos/prometheus/prometheus/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/prometheus/prometheus/teams","hooks_url":"https://api.github.com/repos/prometheus/prometheus/hooks","issue_events_url":"https://api.github.com/repos/prometheus/prometheus/issues/events{/number}","events_url":"https://api.github.com/repos/prometheus/prometheus/events","assignees_url":"https://api.github.com/repos/prometheus/prometheus/assignees{/user}","branches_url":"https://api.github.com/repos/prometheus/prometheus/branches{/branch}","tags_url":"https://api.github.com/repos/prometheus/prometheus/tags","blobs_url":"https://api.github.com/repos/prometheus/prometheus/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/prometheus/prometheus/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/prometheus/prometheus/git/refs{/sha}","trees_url":"https://api.github.com/repos/prometheus/prometheus/git/trees{/sha}","statuses_url":"https://api.github.com/repos/prometheus/prometheus/statuses/{sha}","languages_url":"https://api.github.com/repos/prometheus/prometheus/languages","stargazers_url":"https://api.github.com/repos/prometheus/prometheus/stargazers","contributors_url":"https://api.github.com/repos/prometheus/prometheus/contributors","subscribers_url":"https://api.github.com/repos/prometheus/prometheus/subscribers","subscription_url":"https://api.github.com/repos/prometheus/prometheus/subscription","commits_url":"https://api.github.com/repos/prometheus/prometheus/commits{/sha}","git_commits_url":"https://api.github.com/repos/prometheus/prometheus/git/commits{/sha}","comments_url":"https://api.github.com/repos/prometheus/prometheus/comments{/number}","issue_comment_url":"https://api.github.com/repos/prometheus/prometheus/issues/comments{/number}","contents_url":"https://api.github.com/repos/prometheus/prometheus/contents/{+path}","compare_url":"https://api.github.com/repos/prometheus/prometheus/compare/{base}...{head}","merges_url":"https://api.github.com/repos/prometheus/prometheus/merges","archive_url":"https://api.github.com/repos/prometheus/prometheus/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/prometheus/prometheus/downloads","issues_url":"https://api.github.com/repos/prometheus/prometheus/issues{/number}","pulls_url":"https://api.github.com/repos/prometheus/prometheus/pulls{/number}","milestones_url":"https://api.github.com/repos/prometheus/prometheus/milestones{/number}","notifications_url":"https://api.github.com/repos/prometheus/prometheus/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/prometheus/prometheus/labels{/name}","releases_url":"https://api.github.com/repos/prometheus/prometheus/releases{/id}","created_at":"2012-11-24T11:14:12Z","updated_at":"2015-03-05T13:36:53Z","pushed_at":"2015-03-05T19:33:37Z","git_url":"git://github.com/prometheus/prometheus.git","ssh_url":"git@github.com:prometheus/prometheus.git","clone_url":"https://github.com/prometheus/prometheus.git","svn_url":"https://github.com/prometheus/prometheus","homepage":"http://prometheus.io/","size":14287,"stargazers_count":1787,"watchers_count":1787,"language":"Go","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":85,"mirror_url":null,"open_issues_count":77,"forks":85,"open_issues":77,"watchers":1787,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/prometheus/prometheus/pulls/579"},"html":{"href":"https://github.com/prometheus/prometheus/pull/579"},"issue":{"href":"https://api.github.com/repos/prometheus/prometheus/issues/579"},"comments":{"href":"https://api.github.com/repos/prometheus/prometheus/issues/579/comments"},"review_comments":{"href":"https://api.github.com/repos/prometheus/prometheus/pulls/579/comments"},"review_comment":{"href":"https://api.github.com/repos/prometheus/prometheus/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/prometheus/prometheus/pulls/579/commits"},"statuses":{"href":"https://api.github.com/repos/prometheus/prometheus/statuses/13fcf1ddbcc987bedf72dfc03b587d38f62421a3"}}}}