lib/torrent/observability/download_performance.go (78 lines of code) (raw):

// Copyright (c) 2016-2019 Uber Technologies, Inc. // // 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 observability import ( "time" "github.com/uber-go/tally" "github.com/uber/kraken/utils/memsize" ) const ( _xsmall, _small, _medium, _large, _xlarge, _xxlarge = "0B-5MiB", "5MiB-100MiB", "100MiB-1GB", "1GiB-5GiB", "5GiB-10GiB", "10GiB+" ) var ( _sizeBoundaries = []uint64{0, 5 * memsize.MB, 100 * memsize.MB, memsize.GB, 5 * memsize.GB, 10 * memsize.GB} _sizeTags = []string{_xsmall, _small, _medium, _large, _xlarge, _xxlarge} _downloadLatencyBuckets tally.DurationBuckets // In MiB/s. _downloadThroughputBuckets tally.ValueBuckets ) func init() { _downloadLatencyBuckets = append(_downloadLatencyBuckets, 0, 500*time.Millisecond, 1*time.Second, 2*time.Second, 4*time.Second, 7*time.Second, 12*time.Second, 15*time.Second) _downloadLatencyBuckets = append(_downloadLatencyBuckets, tally.MustMakeLinearDurationBuckets(20*time.Second, 5*time.Second, 9)...) _downloadLatencyBuckets = append(_downloadLatencyBuckets, tally.MustMakeLinearDurationBuckets(70*time.Second, 10*time.Second, 6)...) _downloadLatencyBuckets = append(_downloadLatencyBuckets, tally.MustMakeLinearDurationBuckets(140*time.Second, 20*time.Second, 6)...) _downloadLatencyBuckets = append(_downloadLatencyBuckets, tally.MustMakeLinearDurationBuckets(270*time.Second, 30*time.Second, 8)...) _downloadLatencyBuckets = append(_downloadLatencyBuckets, tally.MustMakeLinearDurationBuckets(540*time.Second, 60*time.Second, 7)...) _downloadLatencyBuckets = append(_downloadLatencyBuckets, tally.MustMakeLinearDurationBuckets(1020*time.Second, 120*time.Second, 5)...) _downloadLatencyBuckets = append(_downloadLatencyBuckets, tally.MustMakeLinearDurationBuckets(1800*time.Second, 300*time.Second, 3)...) _downloadLatencyBuckets = append(_downloadLatencyBuckets, tally.MustMakeLinearDurationBuckets(2700*time.Second, 300*time.Second, 4)...) _downloadThroughputBuckets = append(_downloadThroughputBuckets, tally.MustMakeLinearValueBuckets(0, 1, 10)...) // [0, 10) _downloadThroughputBuckets = append(_downloadThroughputBuckets, tally.MustMakeLinearValueBuckets(10, 2, 5)...) // [10, 20) _downloadThroughputBuckets = append(_downloadThroughputBuckets, tally.MustMakeLinearValueBuckets(20, 5, 6)...) // [20, 50) _downloadThroughputBuckets = append(_downloadThroughputBuckets, tally.MustMakeLinearValueBuckets(50, 10, 15)...) // [50, 200) _downloadThroughputBuckets = append(_downloadThroughputBuckets, tally.MustMakeLinearValueBuckets(200, 50, 4)...) // [200, 400) } func getSizeTag(sizeBytes uint64) string { for i := len(_sizeBoundaries) - 1; i >= 0; i-- { if sizeBytes >= _sizeBoundaries[i] { return _sizeTags[i] } } return _sizeTags[0] } type DownloadType string const ( TORRENT_DOWNLOAD DownloadType = "TORRENT_DOWNLOAD" METAINFO_DOWNLOAD DownloadType = "METAINFO_DOWNLOAD" ) // EmitDownloadPerformance emits metrics on the download performance for either a torrent (blob) download // or a metainfo download. Both latency and throughput are measured for the end-to-end download. func EmitDownloadPerformance(stats tally.Scope, downloadType DownloadType, sizeBytes int64, t time.Duration) { sizeTag := getSizeTag(uint64(sizeBytes)) mbPerSecond := (float64(sizeBytes) / (float64(memsize.MB))) / t.Seconds() switch downloadType { case TORRENT_DOWNLOAD: emitBlobDownloadPerformance(stats, mbPerSecond, sizeTag, t) case METAINFO_DOWNLOAD: emitMetainfoDownloadPerformance(stats, mbPerSecond, sizeTag, t) } } func emitBlobDownloadPerformance(stats tally.Scope, mbPerSecond float64, sizeTag string, t time.Duration) { stats.Tagged(map[string]string{ "size": sizeTag, "version": "4", }).Histogram("download_time", _downloadLatencyBuckets).RecordDuration(t) stats.Tagged(map[string]string{ "size": sizeTag, }).Histogram("download_throughput", _downloadThroughputBuckets).RecordValue(mbPerSecond) } func emitMetainfoDownloadPerformance(stats tally.Scope, mbPerSecond float64, sizeTag string, t time.Duration) { // Metrics are tagged by torrent_size, as origin needs to download the blob from storage (e.g. GCS) to // calculate its metainfo, before returning it to tracker, which in turn, returns it to agent. Thus, even if // metainfo itself is <1 KiB, its download may take 10s of minutes on huge blobs. stats.Tagged(map[string]string{ "torrent_size": sizeTag, }).Histogram("metainfo_download_time", _downloadLatencyBuckets).RecordDuration(t) stats.Tagged(map[string]string{ "torrent_size": sizeTag, }).Histogram("metainfo_download_throughput", _downloadThroughputBuckets).RecordValue(mbPerSecond) }