Skip to content

Commit 0a43a47

Browse files
Merge branch 'docs'
2 parents b4dce7d + e8309af commit 0a43a47

29 files changed

+162
-193
lines changed

.travis.yml

-12
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ addons:
88
packages:
99
- python-pygments
1010
install:
11-
- wget https://github.com/gohugoio/hugo/releases/download/v0.48/hugo_0.48_Linux-64bit.deb
12-
- sudo dpkg -i hugo_0.48_Linux-64bit.deb
1311
- go get golang.org/x/tools/cmd/cover
1412
- go get github.com/mattn/goveralls
1513
- go get golang.org/x/lint/golint
@@ -19,13 +17,3 @@ script:
1917
- go vet ./...
2018
- go test -v -race -covermode=atomic -coverprofile=coverage.out ./...
2119
- $(go env GOPATH | awk 'BEGIN{FS=":"} {print $1}')/bin/goveralls -coverprofile=coverage.out -service=travis-ci -repotoken=${COVERALLS_TOKEN}
22-
after_success:
23-
- cd docs && hugo && mv docs/* .
24-
deploy:
25-
provider: pages
26-
skip-cleanup: true
27-
github-token: $GITHUB_TOKEN
28-
keep-history: true
29-
on:
30-
branch: master
31-
local-dir: docs

README.md

+128-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,132 @@
1+
[![GoDoc](https://godoc.org/github.com/platinummonkey/go-concurrency-limits?status.svg)](https://godoc.org/github.com/platinummonkey/go-concurrency-limits)
12
[![Build Status](https://travis-ci.org/platinummonkey/go-concurrency-limits.svg?branch=master)](https://travis-ci.org/platinummonkey/go-concurrency-limits) [![Coverage Status](https://img.shields.io/coveralls/github/platinummonkey/go-concurrency-limits/master.svg)](https://coveralls.io/github/platinummonkey/go-concurrency-limits)
3+
[![Releases](https://img.shields.io/github/release/platinummonkey/go-concurrency-limits.svg)](https://github.com/platinummonkey/go-concurrency-limits/releases) [![Releases](https://img.shields.io/github/downloads/platinummonkey/go-concurrency-limits/total.svg)](https://github.com/platinummonkey/go-concurrency-limits/releases)
24

3-
# Overview
5+
# Background
46

5-
Go Implementation of Netflix/concurrency-limits Java Library that implements and integrates concepts from TCP congestion control to auto-detect concurrency limits to achieve optimal throughput with optimal latency.
7+
When thinking of service availability operators traditionally think in terms of RPS (requests per second). Stress tests
8+
are normally performed to determine the RPS at which point the service tips over. RPS limits are then set somewhere
9+
below this tipping point (say 75% of this value) and enforced via a token bucket. However, in large distributed systems
10+
that auto-scale this value quickly goes out of date and the service falls over by becoming non-responsive as it is
11+
unable to gracefully shed excess load. Instead of thinking in terms of RPS, we should be thinking in terms of
12+
concurrent request where we apply queuing theory to determine the number of concurrent requests a service can handle
13+
before a queue starts to build up, latencies increase and the service eventually exhausts a hard limit such as CPU,
14+
memory, disk or network. This relationship is covered very nicely with Little's Law where
15+
Limit = Average RPS * Average Latency.
616

7-
For more information [Docs](http://accelerate-experience.com/go-concurrency-limits/)
17+
Concurrency limits are very easy to enforce but difficult to determine as they would require operators to fully
18+
understand the hardware services run on and coordinate how they scale. Instead we'd prefer to measure or estimate the
19+
concurrency limits at each point in the network. As systems scale and hit limits each node will adjust and enforce its
20+
local view of the limit. To estimate the limit we borrow from common TCP congestion control algorithms by equating a
21+
system's concurrency limit to a TCP congestion window.
22+
23+
Before applying the algorithm we need to set some ground rules.
24+
25+
- We accept that every system has an inherent concurrency limit that is determined by a hard resources, such as number of CPU cores.
26+
- We accept that this limit can change as a system auto-scales.
27+
- For large and complex distributed systems it's impossible to know all the hard resources.
28+
- We can use latency measurements to determine when queuing happens.
29+
- We can use timeouts and rejected requests to aggressively back off.
30+
31+
# Limit Algorithms
32+
33+
## Vegas
34+
35+
Delay based algorithm where the bottleneck queue is estimated as
36+
37+
```
38+
L * (1 - minRTT/sampleRtt)
39+
```
40+
41+
At the end of each sampling window the limit is increased by 1 if the queue is less than alpha (typically a value
42+
between 2-3) or decreased by 1 if the queue is greater than beta (typically a value between 4-6 requests).
43+
44+
## Gradient2
45+
46+
This algorithm attempts to address bias and drift when using minimum latency measurements. To do this the algorithm
47+
tracks uses the measure of divergence between two exponential averages over a long and short time time window. Using
48+
averages the algorithm can smooth out the impact of outliers for bursty traffic. Divergence duration is used as a proxy
49+
to identify a queueing trend at which point the algorithm aggresively reduces the limit.
50+
51+
# Enforcement Strategies
52+
53+
## Simple
54+
55+
In the simplest use case we don't want to differentiate between requests and so enforce a single gauge of the number of
56+
inflight requests. Requests are rejected immediately once the gauge value equals the limit.
57+
58+
## Partitioned
59+
60+
For a slightly more complex system, it's desirable to partition requests to different backend/services. For example,
61+
you might shard by a customer id modulus 64 and the remainder you use as a unique backend identifier to target the
62+
the request. This allows for specific partitions to begin failing while others are operation normally.
63+
64+
## Percentage
65+
66+
For more complex systems it's desirable to provide certain quality of service guarantees while still making efficient
67+
use of resources. Here we guarantee specific types of requests get a certain percentage of the concurrency limit. For
68+
example, a system that takes both live and batch traffic may want to give live traffic 100% of the limit during heavy
69+
load and is OK with starving batch traffic. Or, a system may want to guarantee that 50% of the limit is given to write
70+
traffic so writes are never starved.
71+
72+
# Integrations
73+
74+
## GRPC
75+
76+
A concurrency limiter may be installed either on the server or client. The choice of limiter depends on your use case.
77+
For the most part it is recommended to use a dynamic delay based limiter such as the VegasLimit on the server and
78+
either a pure loss based (AIMDLimit) or combined loss and delay based limiter on the client.
79+
80+
### Server Limiter
81+
82+
The purpose of the server limiter is to protect the server from either increased client traffic (batch apps or retry
83+
storms) or latency spikes from a dependent service. With the limiter installed the server can ensure that latencies
84+
remain low by rejecting excess traffic with `Status.UNAVAILABLE` errors.
85+
86+
In this example a GRPC server is configured with a single adaptive limiter that is shared among batch and live traffic
87+
with live traffic guaranteed 90% of throughput and 10% guaranteed to batch. For simplicity we just expect the client to
88+
send a "group" header identifying it as 'live' or 'batch'. Ideally this should be done using TLS certificates and a
89+
server side lookup of identity to grouping. Any requests not identified as either live or batch may only use excess
90+
capacity.
91+
92+
```golang
93+
import (
94+
gclGrpc "github.com/platnummonkey/go-concurrency-limits/grpc"
95+
)
96+
97+
// setup grpc server with this option
98+
serverOption := grpc.UnaryInterceptor(
99+
gclGrpc.UnaryServerInterceptor(
100+
gclGrpc.WithLimiter(...),
101+
gclGrpc.WithServerResponseTypeClassifier(..),
102+
),
103+
)
104+
```
105+
106+
### Client Limiter
107+
108+
There are two main use cases for client side limiters. A client side limiter can protect the client service from its
109+
dependent services by failing fast and serving a degraded experience to its client instead of having its latency go up
110+
and its resources eventually exhausted. For batch applications that call other services a client side limiter acts as a
111+
backpressure mechanism ensuring that the batch application does not put unnecessary load on dependent services.
112+
113+
In this example a GRPC client will use a blocking version of the VegasLimit to block the caller when the limit has been
114+
reached.
115+
116+
```golang
117+
import (
118+
gclGrpc "github.com/platnummonkey/go-concurrency-limits/grpc"
119+
)
120+
121+
// setup grpc client with this option
122+
dialOption := grpc.WithUnaryInterceptor(
123+
gclGrpc.UnaryClientInterceptor(
124+
gclGrpc.WithLimiter(...),
125+
gclGrpc.WithClientResponseTypeClassifier(...),
126+
),
127+
)
128+
```
129+
130+
# References Used
131+
1. Original Java implementation - Netflix - https://github.com/netflix/concurrency-limits/
132+
1. Windowless Moving Percentile - Martin Jambon - https://mjambon.com/2016-07-23-moving-percentile/

core/doc.go

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Package core provides the package interfaces.
2+
package core

doc.go

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Package go_concurrency_limits provides primitives for concurrency control in complex systems.
2+
package go_concurrency_limits

docs/archetypes/default.md

-6
This file was deleted.
-219 KB
Binary file not shown.

docs/config.toml

-5
This file was deleted.

docs/content/_index.md

-157
This file was deleted.

docs/content/_references.md

-3
This file was deleted.

docs/themes/hugo-theme-learn

Submodule hugo-theme-learn deleted from 441fa98

examples/doc.go

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Package examples contains examples of using this package to solve concurrency problems.
2+
package examples

grpc/doc.go

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Package grpc provides GRPC client/server mixins to add concurrency control.
2+
package grpc

limit/doc.go

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Package limit provides several useful limit implementations.
2+
package limit

limit/functions/doc.go

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Package functions provides additional helper functions to the limit package.
2+
package functions

limiter/doc.go

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Package limiter provides common limiter implementations that are useful.
2+
package limiter

measurements/doc.go

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Package measurements provides measurement reading implementations
2+
package measurements

metric_registry/datadog/registry.go

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// Package datadog implements the metric registry interface for a Datadog provider.
12
package datadog
23

34
import (

metric_registry/doc.go

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Package metric_registry provides common implementations of metric registries.
2+
package metric_registry

metric_registry/gometrics/registry.go

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// Package gometrics implements the metric registry interface for a gometrics provider.
12
package gometrics
23

34
import (

patterns/doc.go

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Package patterns provides common patterns as higher level abstractions from the library building blocks.
2+
package patterns

patterns/pool/doc.go

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Package pool provides common pool patterns for concurrency control.
2+
package pool

patterns/example_fixed_pool_test.go renamed to patterns/pool/example_fixed_pool_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package patterns
1+
package pool
22

33
import (
44
"context"

patterns/example_generic_pool_test.go renamed to patterns/pool/example_generic_pool_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package patterns
1+
package pool
22

33
import (
44
"context"

patterns/example_lifo_fixed_pool_test.go renamed to patterns/pool/example_lifo_fixed_pool_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package patterns
1+
package pool
22

33
import (
44
"context"

0 commit comments

Comments
 (0)