Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enforce predictable order for first() and last() #3823

Merged
merged 3 commits into from
Aug 26, 2015

Conversation

otoolep
Copy link
Contributor

@otoolep otoolep commented Aug 24, 2015

When two or more points have the same timestamp during first() and last() aggregations, the tie is broken by taking the one with the larger value.

Solution thanks to @jwilder

@otoolep
Copy link
Contributor Author

otoolep commented Aug 24, 2015

@dgnorton
Copy link
Contributor

@otoolep may be a silly question but does the test fail without the fix?

@otoolep
Copy link
Contributor Author

otoolep commented Aug 24, 2015

@dgnorton -- yes it does. Sometimes it passes, sometimes it doesn't. Without the fix:

~/repos/influxdb/src/github.com/influxdb/influxdb/cmd/influxd/run (deterministic_first_last)$ go test -run TestServer_Query_AggregatesIdenticalTime
[wal] 2015/08/24 16:52:28 WAL starting with 30720 ready series size, 0.50 compaction threshold, and 20971520 partition size threshold
[wal] 2015/08/24 16:52:28 WAL writing to /tmp/influxd-028175029/db0/rp0/1
--- FAIL: TestServer_Query_AggregatesIdenticalTime (0.96s)
        server_test.go:2256: last from multiple series with identical timestamp: unexpected results
                query:  SELECT last(value) FROM "series"
                exp:    {"results":[{"series":[{"name":"series","columns":["time","last"],"values":[["1970-01-01T00:00:00Z",1]]}]}]}
                actual: {"results":[{"series":[{"name":"series","columns":["time","last"],"values":[["1970-01-01T00:00:00Z",5]]}]}]}

FAIL
exit status 1
FAIL    github.com/influxdb/influxdb/cmd/influxd/run    0.962s
~/repos/influxdb/src/github.com/influxdb/influxdb/cmd/influxd/run (deterministic_first_last)$ go test -run TestServer_Query_AggregatesIdenticalTime
[wal] 2015/08/24 16:52:35 WAL starting with 30720 ready series size, 0.50 compaction threshold, and 20971520 partition size threshold
[wal] 2015/08/24 16:52:35 WAL writing to /tmp/influxd-205298755/db0/rp0/1
PASS
ok      github.com/influxdb/influxdb/cmd/influxd/run    1.042s
~/repos/influxdb/src/github.com/influxdb/influxdb/cmd/influxd/run (deterministic_first_last)$ 

@otoolep
Copy link
Contributor Author

otoolep commented Aug 24, 2015

Failed first time, passed second time.

@dgnorton
Copy link
Contributor

+1

@otoolep otoolep force-pushed the deterministic_first_last branch 3 times, most recently from 8276764 to 405ee97 Compare August 25, 2015 00:04
@otoolep
Copy link
Contributor Author

otoolep commented Aug 25, 2015

@pauldix has agreed that the behaviour should be deterministic. I will profile this and if this change is not a problem I will merge.

@otoolep
Copy link
Contributor Author

otoolep commented Aug 25, 2015

The impact of this change is significant. With our stress tool select count(value) from cpu takes 30% longer to run. There may be another approach.

@otoolep
Copy link
Contributor Author

otoolep commented Aug 25, 2015

ROUTINE ======================== github.com/influxdb/influxdb/tsdb.pointHeap.Less in /home/philip/repos/influxdb/src/github.com/influxdb/influxdb/tsdb/mapper.go
    14.62s     20.87s (flat, cum) 37.00% of Total
         .          .    651:   return &q
         .          .    652:}
         .          .    653:
         .          .    654:func (pq pointHeap) Len() int { return len(pq) }
         .          .    655:
     830ms      830ms    656:func (pq pointHeap) Less(i, j int) bool {
    10.43s     10.43s    657:   if pq[i].timestamp < pq[j].timestamp {
     310ms      310ms    658:           return true
     380ms      380ms    659:   } else if pq[i].timestamp > pq[j].timestamp {
     150ms      150ms    660:           return false
         .          .    661:   }
         .          .    662:   // Break the tie in a deterministic way.
     2.52s      8.77s    663:   return bytes.Compare(pq[i].value, pq[j].value) == -1
         .          .    664:}
         .          .    665:
         .          .    666:func (pq pointHeap) Swap(i, j int) { pq[i], pq[j] = pq[j], pq[i] }
         .          .    667:
         .          .    668:func (pq *pointHeap) Push(x interface{}) {

@otoolep
Copy link
Contributor Author

otoolep commented Aug 25, 2015

Some other ideas:

-- MapLast and MapFirst could return multiple values, as suggested by @dgnorton and @DanielMorsing. @pauldix not thrilled by this idea.
-- The comparison shown in the patch could only kick in if last() or first() is being invoked.

@otoolep
Copy link
Contributor Author

otoolep commented Aug 25, 2015

The general agreement is however that a 20-30% query hit across the board is not acceptable just to deal with this edge-case.

@otoolep otoolep mentioned this pull request Aug 25, 2015
If 2 or more points during this map-and-reduce share the same timestamp,
the tie is broken by looking at the value. This ensures that these
functions operate in a deterministic manner.

This solution due to @jwilder
@otoolep otoolep changed the title Enforce predictable series cursor order Enforce predictable order for first() and last() Aug 26, 2015
@otoolep
Copy link
Contributor Author

otoolep commented Aug 26, 2015

With this change we now only take the hit for last and first queries, and this is desired functionality.

@dgnorton
Copy link
Contributor

+1

otoolep added a commit that referenced this pull request Aug 26, 2015
Enforce predictable order for first() and last()
@otoolep otoolep merged commit 8f4fc3b into master Aug 26, 2015
@otoolep otoolep deleted the deterministic_first_last branch August 26, 2015 15:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants