diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index a3fd71a9a9d..92c39552aeb 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -1484,6 +1484,10 @@ "Comment": "v3.0.7-72-ga0ff256", "Rev": "a0ff2567cfb70903282db057e799fd826784d41d" }, + { + "ImportPath": "github.com/juju/ratelimit", + "Rev": "77ed1c8a01217656d2080ad51981f6e99adaa177" + }, { "ImportPath": "github.com/kardianos/osext", "Rev": "8fef92e41e22a70e700a96b29f066cda30ea24ef" diff --git a/Godeps/LICENSES b/Godeps/LICENSES index 6a154fe22c5..4a302d0dfcb 100644 --- a/Godeps/LICENSES +++ b/Godeps/LICENSES @@ -50866,6 +50866,205 @@ http://creativecommons.org/publicdomain/zero/1.0 ================================================================================ +================================================================================ += vendor/github.com/juju/ratelimit licensed under: = + +All files in this repository are licensed as follows. If you contribute +to this repository, it is assumed that you license your contribution +under the same license unless you state otherwise. + +All files Copyright (C) 2015 Canonical Ltd. unless otherwise specified in the file. + +This software is licensed under the LGPLv3, included below. + +As a special exception to the GNU Lesser General Public License version 3 +("LGPL3"), the copyright holders of this Library give you permission to +convey to a third party a Combined Work that links statically or dynamically +to this Library without providing any Minimal Corresponding Source or +Minimal Application Code as set out in 4d or providing the installation +information set out in section 4e, provided that you comply with the other +provisions of LGPL3 and provided that you meet, for the Application the +terms and conditions of the license(s) which apply to the Application. + +Except as stated in this special exception, the provisions of LGPL3 will +continue to comply in full to this Library. If you modify this Library, you +may apply this exception to your version of this Library, but you are not +obliged to do so. If you do not wish to do so, delete this exception +statement from your version. This exception does not (and cannot) modify any +license terms which apply to the Application, with which you must still +comply. + + + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. + += vendor/github.com/juju/ratelimit/LICENSE 2d1c30374313ae40df7772dc92ef9fd5 - +================================================================================ + + ================================================================================ = vendor/github.com/kardianos/osext licensed under: = diff --git a/pkg/util/flowcontrol/BUILD b/pkg/util/flowcontrol/BUILD index 3caecf81cf2..433cd4350e9 100644 --- a/pkg/util/flowcontrol/BUILD +++ b/pkg/util/flowcontrol/BUILD @@ -20,7 +20,7 @@ go_library( deps = [ "//pkg/util/clock:go_default_library", "//pkg/util/integer:go_default_library", - "//pkg/util/ratelimit:go_default_library", + "//vendor:github.com/juju/ratelimit", ], ) diff --git a/pkg/util/flowcontrol/throttle.go b/pkg/util/flowcontrol/throttle.go index 42170a18922..881a2f57d7d 100644 --- a/pkg/util/flowcontrol/throttle.go +++ b/pkg/util/flowcontrol/throttle.go @@ -19,7 +19,7 @@ package flowcontrol import ( "sync" - "k8s.io/kubernetes/pkg/util/ratelimit" + "github.com/juju/ratelimit" ) type RateLimiter interface { diff --git a/pkg/util/ratelimit/BUILD b/pkg/util/ratelimit/BUILD deleted file mode 100644 index 760940e2e89..00000000000 --- a/pkg/util/ratelimit/BUILD +++ /dev/null @@ -1,25 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -licenses(["notice"]) - -load( - "@io_bazel_rules_go//go:def.bzl", - "go_binary", - "go_library", - "go_test", - "cgo_library", -) - -go_library( - name = "go_default_library", - srcs = ["bucket.go"], - tags = ["automanaged"], -) - -go_test( - name = "go_default_test", - srcs = ["bucket_test.go"], - library = "go_default_library", - tags = ["automanaged"], - deps = [], -) diff --git a/pkg/util/ratelimit/bucket.go b/pkg/util/ratelimit/bucket.go deleted file mode 100644 index 752a251d05c..00000000000 --- a/pkg/util/ratelimit/bucket.go +++ /dev/null @@ -1,170 +0,0 @@ -/* -Copyright 2016 The Kubernetes 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 ratelimit - -import ( - "math" - "sync" - "time" -) - -// Bucket models a token bucket -type Bucket struct { - unitsPerNano float64 - nanosPerUnit float64 - capacity int64 - - mutex sync.Mutex - available int64 - lastRefill int64 - // fractionalAvailable "buffers" any amounts that flowed into the bucket smaller than one unit - // This lets us retain precision even with pathological refill rates like (1E9 + 1) per second - fractionalAvailable float64 -} - -// NewBucketWithRate creates a new token bucket, with maximum capacity = initial capacity, and a refill rate of qps -// We use floats for refill calculations, which introduces the possibility of truncation and rounding errors. -// For "sensible" qps values though, is is acceptable: jbeda did some tests here https://play.golang.org/p/LSKUOGz2LG -func NewBucketWithRate(qps float64, capacity int64) *Bucket { - unitsPerNano := qps / 1E9 - nanosPerUnit := 1E9 / qps - b := &Bucket{ - unitsPerNano: unitsPerNano, - nanosPerUnit: nanosPerUnit, - capacity: capacity, - available: capacity, - lastRefill: time.Now().UnixNano(), - } - return b -} - -// Take takes n units from the bucket, reducing the available quantity even below zero, -// but then returns the amount of time we should wait -func (b *Bucket) Take(n int64) time.Duration { - b.mutex.Lock() - defer b.mutex.Unlock() - - var d time.Duration - if b.available >= n { - // Fast path when bucket has sufficient availability before refilling - } else { - b.refill() - - if b.available < n { - deficit := n - b.available - d = time.Duration(int64(float64(deficit) * b.nanosPerUnit)) - } - } - - b.available -= n - - return d -} - -// TakeAvailable immediately takes whatever quantity is available, up to max -func (b *Bucket) TakeAvailable(max int64) int64 { - b.mutex.Lock() - defer b.mutex.Unlock() - - var took int64 - if b.available >= max { - // Fast path when bucket has sufficient availability before refilling - took = max - } else { - b.refill() - - took = b.available - - if took < 0 { - took = 0 - } else if took > max { - took = max - } - } - - if took > 0 { - b.available -= took - } - - return took -} - -// Wait combines a call to Take with a sleep call -func (b *Bucket) Wait(n int64) { - d := b.Take(n) - if d != 0 { - time.Sleep(d) - } -} - -// Capacity returns the maximum capacity of the bucket -func (b *Bucket) Capacity() int64 { - return b.capacity -} - -// Available returns the quantity available in the bucket (which may be negative), but does not take it. -// This function is for diagnostic / informational purposes only - the returned capacity may immediately -// be inaccurate if another thread is operating on the bucket concurrently. -func (b *Bucket) Available() int64 { - b.mutex.Lock() - defer b.mutex.Unlock() - - b.refill() - - return b.available -} - -// refill replenishes the bucket based on elapsed time; mutex must be held -func (b *Bucket) refill() { - // Note that we really want a monotonic clock here, but go says no: - // https://github.com/golang/go/issues/12914 - now := time.Now().UnixNano() - - b.refillAtTimestamp(now) -} - -// refillAtTimestamp is the logic of the refill function, for testing -func (b *Bucket) refillAtTimestamp(now int64) { - nanosSinceLastRefill := now - b.lastRefill - if nanosSinceLastRefill <= 0 { - // we really want monotonic - return - } - - // Compute units that have flowed into bucket - refillFloat := (float64(nanosSinceLastRefill) * b.unitsPerNano) + b.fractionalAvailable - if refillFloat > float64(b.capacity) { - // float64 > MaxInt64 can be converted to negative int64; side step this - b.available = b.capacity - - // Don't worry about the fractional units with huge refill rates - } else { - whole, fraction := math.Modf(refillFloat) - refill := int64(whole) - b.fractionalAvailable = fraction - if refill != 0 { - // Refill with overflow - b.available += refill - if b.available >= b.capacity { - b.available = b.capacity - b.fractionalAvailable = 0 - } - } - - } - b.lastRefill = now -} diff --git a/pkg/util/ratelimit/bucket_test.go b/pkg/util/ratelimit/bucket_test.go deleted file mode 100644 index 00a5b799ab8..00000000000 --- a/pkg/util/ratelimit/bucket_test.go +++ /dev/null @@ -1,179 +0,0 @@ -/* -Copyright 2016 The Kubernetes 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 ratelimit - -import ( - "testing" - "time" -) - -func TestSimpleExhaustion(t *testing.T) { - capacity := int64(3) - b := NewBucketWithRate(1, capacity) - - // Empty the bucket - for i := int64(0); i < capacity; i++ { - testAvailable(t, b, capacity-i) - testTakeNoDelay(t, b, 1) - } - testAvailable(t, b, 0) - - // A take on an empty bucket should incur a delay - testTakeDelay(t, b, 1, 1*time.Second, 100*time.Millisecond) - testAvailable(t, b, -1) -} - -func TestRefill(t *testing.T) { - capacity := int64(3) - b := NewBucketWithRate(1, capacity) - clock := b.lastRefill - - // Empty the bucket - testAvailable(t, b, capacity) - for i := int64(0); i < capacity; i++ { - testTakeNoDelay(t, b, 1) - } - testAvailable(t, b, 0) - - // In one second, one unit should be refilled - clock += time.Second.Nanoseconds() - b.refillAtTimestamp(clock) - testAvailable(t, b, 1) - testTakeNoDelay(t, b, 1) - testAvailable(t, b, 0) - - // Partial refill periods don't result in lost time - for i := 0; i < 4; i++ { - clock += time.Millisecond.Nanoseconds() * 200 - b.refillAtTimestamp(clock) - testAvailable(t, b, 0) - } - clock += time.Millisecond.Nanoseconds() * 200 - b.refillAtTimestamp(clock) - testAvailable(t, b, 1) - testTakeNoDelay(t, b, 1) - testAvailable(t, b, 0) -} - -// TestSlowRefillRate checks we don't have problems with tiny refill rates -func TestSlowRefillRate(t *testing.T) { - for _, capacity := range []int64{int64(1), int64(1E18)} { - b := NewBucketWithRate(1E-9, capacity) - clock := b.lastRefill - - // Empty the bucket - testTakeNoDelay(t, b, b.available) - - // In one second, should refill nothing - clock += time.Second.Nanoseconds() - b.refillAtTimestamp(clock) - testAvailable(t, b, 0) - - // We need to have 1E18 nanos to see any refill - clock += 1E18 - b.refillAtTimestamp(clock) - testAvailable(t, b, 1) - testTakeNoDelay(t, b, 1) - testAvailable(t, b, 0) - } -} - -// TestFastRefillRate checks for refill rates that are around 1 / ns (our granularity) -func TestFastRefillRate(t *testing.T) { - for _, capacity := range []int64{int64(1), int64(1E18)} { - b := NewBucketWithRate(1E9, capacity) - - // Empty the bucket - testTakeNoDelay(t, b, b.available) - - // In one nanosecond, should refill exactly one unit - clock := b.lastRefill + 1 - b.refillAtTimestamp(clock) - testAvailable(t, b, 1) - testTakeNoDelay(t, b, 1) - testAvailable(t, b, 0) - } -} - -// TestRefillRatePrecision checks for rounding errors -func TestRefillRatePrecision(t *testing.T) { - capacity := int64(1E18) - b := NewBucketWithRate(1+1E9, capacity) - - // Empty the bucket - testTakeNoDelay(t, b, b.available) - - // In one nanosecond, should refill exactly one unit - clock := b.lastRefill + 1 - b.refillAtTimestamp(clock) - testAvailable(t, b, 1) - testTakeNoDelay(t, b, 1) - testAvailable(t, b, 0) - - // In one second, should refill the 1 extra also - clock += 1E9 - b.refillAtTimestamp(clock) - testAvailable(t, b, 1000000001) - testTakeNoDelay(t, b, 1000000001) - testAvailable(t, b, 0) -} - -// TestSlowRefillRate checks we don't have problems with ridiculously high refill rates -func TestHugeRefillRate(t *testing.T) { - for _, capacity := range []int64{int64(1), int64(1E18)} { - b := NewBucketWithRate(1E27, capacity) - - // Empty the bucket - testTakeNoDelay(t, b, b.available) - - // In one nanosecond, should refill to capacity - clock := b.lastRefill + 1 - b.refillAtTimestamp(clock) - testAvailable(t, b, capacity) - testTakeNoDelay(t, b, capacity) - testAvailable(t, b, 0) - - // In one second, should refill to capacity, but with huge overflow that must be discarded - clock += time.Second.Nanoseconds() - b.refillAtTimestamp(clock) - testAvailable(t, b, capacity) - testTakeNoDelay(t, b, capacity) - testAvailable(t, b, 0) - } -} - -func testAvailable(t *testing.T, b *Bucket, expected int64) { - available := b.available - if available != expected { - t.Errorf("unexpected available; expected=%d, actual=%d", expected, available) - } -} - -func testTakeDelay(t *testing.T, b *Bucket, take int64, expected time.Duration, tolerance time.Duration) { - actual := b.Take(take) - error := expected.Nanoseconds() - actual.Nanoseconds() - if error < 0 { - error = -error - } - if error > tolerance.Nanoseconds() { - t.Errorf("unexpected delay on take(%d); expected=%d, actual=%d", take, expected, actual) - } -} - -func testTakeNoDelay(t *testing.T, b *Bucket, take int64) { - testTakeDelay(t, b, take, 0, 0) -} diff --git a/pkg/util/workqueue/BUILD b/pkg/util/workqueue/BUILD index af8eb00324f..8b8e98d92a6 100644 --- a/pkg/util/workqueue/BUILD +++ b/pkg/util/workqueue/BUILD @@ -25,8 +25,8 @@ go_library( tags = ["automanaged"], deps = [ "//pkg/util/clock:go_default_library", - "//pkg/util/ratelimit:go_default_library", "//pkg/util/runtime:go_default_library", + "//vendor:github.com/juju/ratelimit", ], ) diff --git a/pkg/util/workqueue/default_rate_limiters.go b/pkg/util/workqueue/default_rate_limiters.go index 0b5b9bba29c..35caed4fa41 100644 --- a/pkg/util/workqueue/default_rate_limiters.go +++ b/pkg/util/workqueue/default_rate_limiters.go @@ -21,7 +21,7 @@ import ( "sync" "time" - "k8s.io/kubernetes/pkg/util/ratelimit" + "github.com/juju/ratelimit" ) type RateLimiter interface { @@ -35,7 +35,7 @@ type RateLimiter interface { } // DefaultControllerRateLimiter is a no-arg constructor for a default rate limiter for a workqueue. It has -// both overall and per-item rate limiting. The overall is a token bucket and the per-item is exponential +// both overall and per-item rate limitting. The overall is a token bucket and the per-item is exponential func DefaultControllerRateLimiter() RateLimiter { return NewMaxOfRateLimiter( NewItemExponentialFailureRateLimiter(5*time.Millisecond, 1000*time.Second), diff --git a/vendor/BUILD b/vendor/BUILD index aac6d8115c0..7f6a8f34863 100644 --- a/vendor/BUILD +++ b/vendor/BUILD @@ -4240,6 +4240,15 @@ go_binary( deps = ["//vendor:github.com/jteeuwen/go-bindata"], ) +go_library( + name = "github.com/juju/ratelimit", + srcs = [ + "github.com/juju/ratelimit/ratelimit.go", + "github.com/juju/ratelimit/reader.go", + ], + tags = ["automanaged"], +) + go_library( name = "github.com/kardianos/osext", srcs = [ diff --git a/vendor/github.com/juju/ratelimit/LICENSE b/vendor/github.com/juju/ratelimit/LICENSE new file mode 100644 index 00000000000..ade9307b390 --- /dev/null +++ b/vendor/github.com/juju/ratelimit/LICENSE @@ -0,0 +1,191 @@ +All files in this repository are licensed as follows. If you contribute +to this repository, it is assumed that you license your contribution +under the same license unless you state otherwise. + +All files Copyright (C) 2015 Canonical Ltd. unless otherwise specified in the file. + +This software is licensed under the LGPLv3, included below. + +As a special exception to the GNU Lesser General Public License version 3 +("LGPL3"), the copyright holders of this Library give you permission to +convey to a third party a Combined Work that links statically or dynamically +to this Library without providing any Minimal Corresponding Source or +Minimal Application Code as set out in 4d or providing the installation +information set out in section 4e, provided that you comply with the other +provisions of LGPL3 and provided that you meet, for the Application the +terms and conditions of the license(s) which apply to the Application. + +Except as stated in this special exception, the provisions of LGPL3 will +continue to comply in full to this Library. If you modify this Library, you +may apply this exception to your version of this Library, but you are not +obliged to do so. If you do not wish to do so, delete this exception +statement from your version. This exception does not (and cannot) modify any +license terms which apply to the Application, with which you must still +comply. + + + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/vendor/github.com/juju/ratelimit/README.md b/vendor/github.com/juju/ratelimit/README.md new file mode 100644 index 00000000000..a0fdfe2b128 --- /dev/null +++ b/vendor/github.com/juju/ratelimit/README.md @@ -0,0 +1,117 @@ +# ratelimit +-- + import "github.com/juju/ratelimit" + +The ratelimit package provides an efficient token bucket implementation. See +http://en.wikipedia.org/wiki/Token_bucket. + +## Usage + +#### func Reader + +```go +func Reader(r io.Reader, bucket *Bucket) io.Reader +``` +Reader returns a reader that is rate limited by the given token bucket. Each +token in the bucket represents one byte. + +#### func Writer + +```go +func Writer(w io.Writer, bucket *Bucket) io.Writer +``` +Writer returns a writer that is rate limited by the given token bucket. Each +token in the bucket represents one byte. + +#### type Bucket + +```go +type Bucket struct { +} +``` + +Bucket represents a token bucket that fills at a predetermined rate. Methods on +Bucket may be called concurrently. + +#### func NewBucket + +```go +func NewBucket(fillInterval time.Duration, capacity int64) *Bucket +``` +NewBucket returns a new token bucket that fills at the rate of one token every +fillInterval, up to the given maximum capacity. Both arguments must be positive. +The bucket is initially full. + +#### func NewBucketWithQuantum + +```go +func NewBucketWithQuantum(fillInterval time.Duration, capacity, quantum int64) *Bucket +``` +NewBucketWithQuantum is similar to NewBucket, but allows the specification of +the quantum size - quantum tokens are added every fillInterval. + +#### func NewBucketWithRate + +```go +func NewBucketWithRate(rate float64, capacity int64) *Bucket +``` +NewBucketWithRate returns a token bucket that fills the bucket at the rate of +rate tokens per second up to the given maximum capacity. Because of limited +clock resolution, at high rates, the actual rate may be up to 1% different from +the specified rate. + +#### func (*Bucket) Rate + +```go +func (tb *Bucket) Rate() float64 +``` +Rate returns the fill rate of the bucket, in tokens per second. + +#### func (*Bucket) Take + +```go +func (tb *Bucket) Take(count int64) time.Duration +``` +Take takes count tokens from the bucket without blocking. It returns the time +that the caller should wait until the tokens are actually available. + +Note that if the request is irrevocable - there is no way to return tokens to +the bucket once this method commits us to taking them. + +#### func (*Bucket) TakeAvailable + +```go +func (tb *Bucket) TakeAvailable(count int64) int64 +``` +TakeAvailable takes up to count immediately available tokens from the bucket. It +returns the number of tokens removed, or zero if there are no available tokens. +It does not block. + +#### func (*Bucket) TakeMaxDuration + +```go +func (tb *Bucket) TakeMaxDuration(count int64, maxWait time.Duration) (time.Duration, bool) +``` +TakeMaxDuration is like Take, except that it will only take tokens from the +bucket if the wait time for the tokens is no greater than maxWait. + +If it would take longer than maxWait for the tokens to become available, it does +nothing and reports false, otherwise it returns the time that the caller should +wait until the tokens are actually available, and reports true. + +#### func (*Bucket) Wait + +```go +func (tb *Bucket) Wait(count int64) +``` +Wait takes count tokens from the bucket, waiting until they are available. + +#### func (*Bucket) WaitMaxDuration + +```go +func (tb *Bucket) WaitMaxDuration(count int64, maxWait time.Duration) bool +``` +WaitMaxDuration is like Wait except that it will only take tokens from the +bucket if it needs to wait for no greater than maxWait. It reports whether any +tokens have been removed from the bucket If no tokens have been removed, it +returns immediately. diff --git a/vendor/github.com/juju/ratelimit/ratelimit.go b/vendor/github.com/juju/ratelimit/ratelimit.go new file mode 100644 index 00000000000..3ef32fbcc09 --- /dev/null +++ b/vendor/github.com/juju/ratelimit/ratelimit.go @@ -0,0 +1,245 @@ +// Copyright 2014 Canonical Ltd. +// Licensed under the LGPLv3 with static-linking exception. +// See LICENCE file for details. + +// The ratelimit package provides an efficient token bucket implementation +// that can be used to limit the rate of arbitrary things. +// See http://en.wikipedia.org/wiki/Token_bucket. +package ratelimit + +import ( + "math" + "strconv" + "sync" + "time" +) + +// Bucket represents a token bucket that fills at a predetermined rate. +// Methods on Bucket may be called concurrently. +type Bucket struct { + startTime time.Time + capacity int64 + quantum int64 + fillInterval time.Duration + + // The mutex guards the fields following it. + mu sync.Mutex + + // avail holds the number of available tokens + // in the bucket, as of availTick ticks from startTime. + // It will be negative when there are consumers + // waiting for tokens. + avail int64 + availTick int64 +} + +// NewBucket returns a new token bucket that fills at the +// rate of one token every fillInterval, up to the given +// maximum capacity. Both arguments must be +// positive. The bucket is initially full. +func NewBucket(fillInterval time.Duration, capacity int64) *Bucket { + return NewBucketWithQuantum(fillInterval, capacity, 1) +} + +// rateMargin specifes the allowed variance of actual +// rate from specified rate. 1% seems reasonable. +const rateMargin = 0.01 + +// NewBucketWithRate returns a token bucket that fills the bucket +// at the rate of rate tokens per second up to the given +// maximum capacity. Because of limited clock resolution, +// at high rates, the actual rate may be up to 1% different from the +// specified rate. +func NewBucketWithRate(rate float64, capacity int64) *Bucket { + for quantum := int64(1); quantum < 1<<50; quantum = nextQuantum(quantum) { + fillInterval := time.Duration(1e9 * float64(quantum) / rate) + if fillInterval <= 0 { + continue + } + tb := NewBucketWithQuantum(fillInterval, capacity, quantum) + if diff := math.Abs(tb.Rate() - rate); diff/rate <= rateMargin { + return tb + } + } + panic("cannot find suitable quantum for " + strconv.FormatFloat(rate, 'g', -1, 64)) +} + +// nextQuantum returns the next quantum to try after q. +// We grow the quantum exponentially, but slowly, so we +// get a good fit in the lower numbers. +func nextQuantum(q int64) int64 { + q1 := q * 11 / 10 + if q1 == q { + q1++ + } + return q1 +} + +// NewBucketWithQuantum is similar to NewBucket, but allows +// the specification of the quantum size - quantum tokens +// are added every fillInterval. +func NewBucketWithQuantum(fillInterval time.Duration, capacity, quantum int64) *Bucket { + if fillInterval <= 0 { + panic("token bucket fill interval is not > 0") + } + if capacity <= 0 { + panic("token bucket capacity is not > 0") + } + if quantum <= 0 { + panic("token bucket quantum is not > 0") + } + return &Bucket{ + startTime: time.Now(), + capacity: capacity, + quantum: quantum, + avail: capacity, + fillInterval: fillInterval, + } +} + +// Wait takes count tokens from the bucket, waiting until they are +// available. +func (tb *Bucket) Wait(count int64) { + if d := tb.Take(count); d > 0 { + time.Sleep(d) + } +} + +// WaitMaxDuration is like Wait except that it will +// only take tokens from the bucket if it needs to wait +// for no greater than maxWait. It reports whether +// any tokens have been removed from the bucket +// If no tokens have been removed, it returns immediately. +func (tb *Bucket) WaitMaxDuration(count int64, maxWait time.Duration) bool { + d, ok := tb.TakeMaxDuration(count, maxWait) + if d > 0 { + time.Sleep(d) + } + return ok +} + +const infinityDuration time.Duration = 0x7fffffffffffffff + +// Take takes count tokens from the bucket without blocking. It returns +// the time that the caller should wait until the tokens are actually +// available. +// +// Note that if the request is irrevocable - there is no way to return +// tokens to the bucket once this method commits us to taking them. +func (tb *Bucket) Take(count int64) time.Duration { + d, _ := tb.take(time.Now(), count, infinityDuration) + return d +} + +// TakeMaxDuration is like Take, except that +// it will only take tokens from the bucket if the wait +// time for the tokens is no greater than maxWait. +// +// If it would take longer than maxWait for the tokens +// to become available, it does nothing and reports false, +// otherwise it returns the time that the caller should +// wait until the tokens are actually available, and reports +// true. +func (tb *Bucket) TakeMaxDuration(count int64, maxWait time.Duration) (time.Duration, bool) { + return tb.take(time.Now(), count, maxWait) +} + +// TakeAvailable takes up to count immediately available tokens from the +// bucket. It returns the number of tokens removed, or zero if there are +// no available tokens. It does not block. +func (tb *Bucket) TakeAvailable(count int64) int64 { + return tb.takeAvailable(time.Now(), count) +} + +// takeAvailable is the internal version of TakeAvailable - it takes the +// current time as an argument to enable easy testing. +func (tb *Bucket) takeAvailable(now time.Time, count int64) int64 { + if count <= 0 { + return 0 + } + tb.mu.Lock() + defer tb.mu.Unlock() + + tb.adjust(now) + if tb.avail <= 0 { + return 0 + } + if count > tb.avail { + count = tb.avail + } + tb.avail -= count + return count +} + +// Available returns the number of available tokens. It will be negative +// when there are consumers waiting for tokens. Note that if this +// returns greater than zero, it does not guarantee that calls that take +// tokens from the buffer will succeed, as the number of available +// tokens could have changed in the meantime. This method is intended +// primarily for metrics reporting and debugging. +func (tb *Bucket) Available() int64 { + return tb.available(time.Now()) +} + +// available is the internal version of available - it takes the current time as +// an argument to enable easy testing. +func (tb *Bucket) available(now time.Time) int64 { + tb.mu.Lock() + defer tb.mu.Unlock() + tb.adjust(now) + return tb.avail +} + +// Capacity returns the capacity that the bucket was created with. +func (tb *Bucket) Capacity() int64 { + return tb.capacity +} + +// Rate returns the fill rate of the bucket, in tokens per second. +func (tb *Bucket) Rate() float64 { + return 1e9 * float64(tb.quantum) / float64(tb.fillInterval) +} + +// take is the internal version of Take - it takes the current time as +// an argument to enable easy testing. +func (tb *Bucket) take(now time.Time, count int64, maxWait time.Duration) (time.Duration, bool) { + if count <= 0 { + return 0, true + } + tb.mu.Lock() + defer tb.mu.Unlock() + + currentTick := tb.adjust(now) + avail := tb.avail - count + if avail >= 0 { + tb.avail = avail + return 0, true + } + // Round up the missing tokens to the nearest multiple + // of quantum - the tokens won't be available until + // that tick. + endTick := currentTick + (-avail+tb.quantum-1)/tb.quantum + endTime := tb.startTime.Add(time.Duration(endTick) * tb.fillInterval) + waitTime := endTime.Sub(now) + if waitTime > maxWait { + return 0, false + } + tb.avail = avail + return waitTime, true +} + +// adjust adjusts the current bucket capacity based on the current time. +// It returns the current tick. +func (tb *Bucket) adjust(now time.Time) (currentTick int64) { + currentTick = int64(now.Sub(tb.startTime) / tb.fillInterval) + + if tb.avail >= tb.capacity { + return + } + tb.avail += (currentTick - tb.availTick) * tb.quantum + if tb.avail > tb.capacity { + tb.avail = tb.capacity + } + tb.availTick = currentTick + return +} diff --git a/vendor/github.com/juju/ratelimit/reader.go b/vendor/github.com/juju/ratelimit/reader.go new file mode 100644 index 00000000000..6403bf78d4a --- /dev/null +++ b/vendor/github.com/juju/ratelimit/reader.go @@ -0,0 +1,51 @@ +// Copyright 2014 Canonical Ltd. +// Licensed under the LGPLv3 with static-linking exception. +// See LICENCE file for details. + +package ratelimit + +import "io" + +type reader struct { + r io.Reader + bucket *Bucket +} + +// Reader returns a reader that is rate limited by +// the given token bucket. Each token in the bucket +// represents one byte. +func Reader(r io.Reader, bucket *Bucket) io.Reader { + return &reader{ + r: r, + bucket: bucket, + } +} + +func (r *reader) Read(buf []byte) (int, error) { + n, err := r.r.Read(buf) + if n <= 0 { + return n, err + } + r.bucket.Wait(int64(n)) + return n, err +} + +type writer struct { + w io.Writer + bucket *Bucket +} + +// Writer returns a reader that is rate limited by +// the given token bucket. Each token in the bucket +// represents one byte. +func Writer(w io.Writer, bucket *Bucket) io.Writer { + return &writer{ + w: w, + bucket: bucket, + } +} + +func (w *writer) Write(buf []byte) (int, error) { + w.bucket.Wait(int64(len(buf))) + return w.w.Write(buf) +}