// Copyright 2017, OpenCensus Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package trace import ( "time" ) // samplePeriod is the minimum time between accepting spans in a single bucket. const samplePeriod = time.Second // defaultLatencies contains the default latency bucket bounds. // TODO: consider defaults, make configurable var defaultLatencies = [...]time.Duration{ 10 * time.Microsecond, 100 * time.Microsecond, time.Millisecond, 10 * time.Millisecond, 100 * time.Millisecond, time.Second, 10 * time.Second, time.Minute, } // bucket is a container for a set of spans for a particular error code or latency range. type bucket struct { nextTime time.Time // next time we can accept a span buffer []*SpanData // circular buffer of spans nextIndex int // location next SpanData should be placed in buffer overflow bool // whether the circular buffer has wrapped around } func makeBucket(bufferSize int) bucket { return bucket{ buffer: make([]*SpanData, bufferSize), } } // add adds a span to the bucket, if nextTime has been reached. func (b *bucket) add(s *SpanData) { if s.EndTime.Before(b.nextTime) { return } if len(b.buffer) == 0 { return } b.nextTime = s.EndTime.Add(samplePeriod) b.buffer[b.nextIndex] = s b.nextIndex++ if b.nextIndex == len(b.buffer) { b.nextIndex = 0 b.overflow = true } } // size returns the number of spans in the bucket. func (b *bucket) size() int { if b.overflow { return len(b.buffer) } return b.nextIndex } // span returns the ith span in the bucket. func (b *bucket) span(i int) *SpanData { if !b.overflow { return b.buffer[i] } if i < len(b.buffer)-b.nextIndex { return b.buffer[b.nextIndex+i] } return b.buffer[b.nextIndex+i-len(b.buffer)] } // resize changes the size of the bucket to n, keeping up to n existing spans. func (b *bucket) resize(n int) { cur := b.size() newBuffer := make([]*SpanData, n) if cur < n { for i := 0; i < cur; i++ { newBuffer[i] = b.span(i) } b.buffer = newBuffer b.nextIndex = cur b.overflow = false return } for i := 0; i < n; i++ { newBuffer[i] = b.span(i + cur - n) } b.buffer = newBuffer b.nextIndex = 0 b.overflow = true } // latencyBucket returns the appropriate bucket number for a given latency. func latencyBucket(latency time.Duration) int { i := 0 for i < len(defaultLatencies) && latency >= defaultLatencies[i] { i++ } return i } // latencyBucketBounds returns the lower and upper bounds for a latency bucket // number. // // The lower bound is inclusive, the upper bound is exclusive (except for the // last bucket.) func latencyBucketBounds(index int) (lower time.Duration, upper time.Duration) { if index == 0 { return 0, defaultLatencies[index] } if index == len(defaultLatencies) { return defaultLatencies[index-1], 1<<63 - 1 } return defaultLatencies[index-1], defaultLatencies[index] }