Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 2 additions & 0 deletions DEVELOPER.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ Below is a list of additional documentation to aid the development process:

- [Envoy filter example project (how to consume and extend Envoy as a submodule)](https://github.com/envoyproxy/envoy-filter-example)

- [Performance testing Envoy with `tcmalloc`/`pprof`](https://github.com/envoyproxy/envoy/tree/bazel/PPROF.md)

And some documents on components of Envoy architecture:

- [Envoy flow control](https://github.com/envoyproxy/envoy/blob/master/source/docs/flow_control.md)
Expand Down
28 changes: 24 additions & 4 deletions api/bazel/api_build_system.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,16 @@ def api_proto_library_internal(visibility = ["//visibility:private"], **kwargs):
# gRPC stub generation.
# TODO(htuch): Automatically generate go_proto_library and go_grpc_library
# from api_proto_library.
def api_proto_library(name, visibility = ["//visibility:private"], srcs = [], deps = [], has_services = 0, require_py = 1):
def api_proto_library(
name,
visibility = ["//visibility:private"],
srcs = [],
deps = [],
external_proto_deps = [],
external_cc_proto_deps = [],
has_services = 0,
linkstatic = None,
require_py = 1):
# This is now vestigial, since there are no direct consumers in
# the data plane API. However, we want to maintain native proto_library support
# in the proto graph to (1) support future C++ use of native rules with
Expand All @@ -99,15 +108,14 @@ def api_proto_library(name, visibility = ["//visibility:private"], srcs = [], de
native.proto_library(
name = name,
srcs = srcs,
deps = deps + [
deps = deps + external_proto_deps + [
"@com_google_protobuf//:any_proto",
"@com_google_protobuf//:descriptor_proto",
"@com_google_protobuf//:duration_proto",
"@com_google_protobuf//:empty_proto",
"@com_google_protobuf//:struct_proto",
"@com_google_protobuf//:timestamp_proto",
"@com_google_protobuf//:wrappers_proto",
"@googleapis//:api_httpbody_protos_proto",
"@googleapis//:http_api_protos_proto",
"@googleapis//:rpc_status_protos_lib",
"@com_github_gogo_protobuf//:gogo_proto",
Expand All @@ -123,17 +131,29 @@ def api_proto_library(name, visibility = ["//visibility:private"], srcs = [], de
pgv_cc_proto_library(
name = _Suffix(name, _CC_SUFFIX),
srcs = srcs,
linkstatic = linkstatic,
deps = [_LibrarySuffix(d, _CC_SUFFIX) for d in deps],
external_deps = [
external_deps = external_cc_proto_deps + [
"@com_google_protobuf//:cc_wkt_protos",
"@googleapis//:http_api_protos",
"@googleapis//:rpc_status_protos",
"@com_github_gogo_protobuf//:gogo_proto_cc",
],
visibility = ["//visibility:public"],
)
py_export_suffixes = []
if (require_py == 1):
api_py_proto_library(name, srcs, deps, has_services)
py_export_suffixes = ["_py", "_py_genproto"]

# Allow unlimited visibility for consumers
export_suffixes = ["", "_cc", "_cc_validate", "_cc_proto", "_cc_proto_genproto"] + py_export_suffixes
for s in export_suffixes:
native.alias(
name = name + "_export" + s,
actual = name + s,
visibility = ["//visibility:public"],
)

def api_cc_test(name, srcs, proto_deps):
native.cc_test(
Expand Down
14 changes: 7 additions & 7 deletions api/envoy/api/v2/route/route.proto
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ message RouteMatch {
// * The regex */b[io]t* matches the path */bot*
// * The regex */b[io]t* does not match the path */bite*
// * The regex */b[io]t* does not match the path */bit/bot*
string regex = 3;
string regex = 3 [(validate.rules).string.max_bytes = 1024];
}

// Indicates that prefix/path matching should be case insensitive. The default
Expand Down Expand Up @@ -310,7 +310,7 @@ message CorsPolicy {
// Specifies regex patterns that match allowed origins.
//
// An origin is allowed if either allow_origin or allow_origin_regex match.
repeated string allow_origin_regex = 8;
repeated string allow_origin_regex = 8 [(validate.rules).repeated .items.string.max_bytes = 1024];

// Specifies the content for the *access-control-allow-methods* header.
string allow_methods = 2;
Expand Down Expand Up @@ -338,7 +338,7 @@ message RouteAction {

// Indicates the upstream cluster to which the request should be routed
// to.
string cluster = 1;
string cluster = 1 [(validate.rules).string.min_bytes = 1];

// Envoy will determine the cluster to route to by reading the value of the
// HTTP header named by cluster_header from the request headers. If the
Expand All @@ -349,7 +349,7 @@ message RouteAction {
//
// Internally, Envoy always uses the HTTP/2 *:authority* header to represent the HTTP/1
// *Host* header. Thus, if attempting to match on *Host*, match on *:authority* instead.
string cluster_header = 2;
string cluster_header = 2 [(validate.rules).string.min_bytes = 1];

// Multiple upstream clusters can be specified for a given route. The
// request is routed to one of the upstream clusters based on weights
Expand Down Expand Up @@ -763,7 +763,7 @@ message VirtualCluster {
// * The regex */rides/\d+* matches the path */rides/0*
// * The regex */rides/\d+* matches the path */rides/123*
// * The regex */rides/\d+* does not match the path */rides/123/456*
string pattern = 1 [(validate.rules).string.min_bytes = 1];
string pattern = 1 [(validate.rules).string = {min_bytes: 1, max_bytes: 1024}];

// Specifies the name of the virtual cluster. The virtual cluster name as well
// as the virtual host name are used when emitting statistics. The statistics are emitted by the
Expand Down Expand Up @@ -959,7 +959,7 @@ message HeaderMatcher {
// * The regex *\d{3}* matches the value *123*
// * The regex *\d{3}* does not match the value *1234*
// * The regex *\d{3}* does not match the value *123.456*
string regex_match = 5;
string regex_match = 5 [(validate.rules).string.max_bytes = 1024];

// If specified, header match will be performed based on range.
// The rule will match if the request header value is within this range.
Expand Down Expand Up @@ -1009,7 +1009,7 @@ message HeaderMatcher {
message QueryParameterMatcher {
// Specifies the name of a key that must be present in the requested
// *path*'s query string.
string name = 1 [(validate.rules).string.min_bytes = 1];
string name = 1 [(validate.rules).string = {min_bytes: 1, max_bytes: 1024}];

// Specifies the value of the key. If the value is absent, a request
// that contains the key in its query string will match, whether the
Expand Down
2 changes: 1 addition & 1 deletion api/envoy/config/filter/accesslog/v2/accesslog.proto
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,6 @@ message ResponseFlagFilter {
// This field is optional. If it is not specified, then any response flag will pass
// the filter check.
repeated string flags = 1 [(validate.rules).repeated .items.string = {
in: ["LH", "UH", "UT", "LR", "UR", "UF", "UC", "UO", "NR", "DI", "FI", "RL", "UAEX"]
in: ["LH", "UH", "UT", "LR", "UR", "UF", "UC", "UO", "NR", "DI", "FI", "RL", "UAEX", "RLSE"]
}];
}
6 changes: 6 additions & 0 deletions api/envoy/config/filter/http/rate_limit/v2/rate_limit.proto
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,10 @@ message RateLimit {
// The timeout in milliseconds for the rate limit service RPC. If not
// set, this defaults to 20ms.
google.protobuf.Duration timeout = 4 [(gogoproto.stdduration) = true];

// The filter's behaviour in case the rate limiting service does
// not respond back. When it is set to true, Envoy will not allow traffic in case of
// communication failure between rate limiting service and the proxy.
// Defaults to false.
bool failure_mode_deny = 5;
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,10 @@ message RateLimit {
// The timeout in milliseconds for the rate limit service RPC. If not
// set, this defaults to 20ms.
google.protobuf.Duration timeout = 4 [(gogoproto.stdduration) = true];

// The filter's behaviour in case the rate limiting service does
// not respond back. When it is set to true, Envoy will not allow traffic in case of
// communication failure between rate limiting service and the proxy.
// Defaults to false.
bool failure_mode_deny = 5;
}
2 changes: 1 addition & 1 deletion api/envoy/config/metrics/v2/stats.proto
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ message TagSpecifier {
// ``http.user_agent.downstream_cx_total`` as the tag extracted name. The tag
// ``envoy.http_conn_manager_prefix`` will be added with the tag value
// ``connection_manager_1``.
string regex = 2;
string regex = 2 [(validate.rules).string.max_bytes = 1024];

// Specifies a fixed tag value for the ``tag_name``.
string fixed_value = 3;
Expand Down
3 changes: 3 additions & 0 deletions api/envoy/data/accesslog/v2/accesslog.proto
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,9 @@ message ResponseFlags {

// Indicates if the request was deemed unauthorized and the reason for it.
Unauthorized unauthorized_details = 13;

// Indicates that the request was rejected because there was an error in rate limit service.
bool rate_limit_service_error = 14;
}

// [#not-implemented-hide:]
Expand Down
2 changes: 1 addition & 1 deletion api/envoy/type/matcher/string.proto
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,6 @@ message StringMatcher {
// * The regex *\d{3}* matches the value *123*
// * The regex *\d{3}* does not match the value *1234*
// * The regex *\d{3}* does not match the value *123.456*
string regex = 4;
string regex = 4 [(validate.rules).string.max_bytes = 1024];
}
}
139 changes: 139 additions & 0 deletions bazel/PPROF.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# Memory consumption testing with `pprof`

To use `pprof` to analyze performance and memory consumption in Envoy, you can
use the built-in statically linked profiler, or dynamically link it in to a
specific place yourself.

# Linking

## Static Linking

Static linking is already available (because of a `HeapProfilerDump()` call
inside
[`Envoy::Profiler::Heap::forceLink()`](https://github.com/envoyproxy/envoy/blob/master/source/common/profiler/profiler.cc#L21-L26)).

### Compiling a statically-linked Envoy

Build the static binary using bazel:

$ bazel build //source/exe:envoy-static

### Running a statically-linked Envoy with `pprof`

And run the binary with a `HEAPPROFILE` environment variable, like so:

$ HEAPPROFILE=/tmp/mybin.hprof bazel-bin/source/exe/envoy-static <args>

`HEAPPROFILE` sets a location for the profiler output. A statically-linked
binary must be run with this environment variable; a dynamically-linked binary
will populate the working directory by default. (See *Methodology*.)

## Dynamic Linking

### Adding `tcmalloc_dep` to Envoy

A statically-linked Envoy will profile everything. In a dynamically-linked
Envoy, you must add the HeapProfiler instructions yourself.
`HeapProfilerStart()` will start recording allocations, `HeapProfilerStop()`
will stop recording, and `HeapProfilerDump()` will dump an output to the
specified directory. (See [Gperftools Heap
Profiler](https://gperftools.github.io/gperftools/heapprofile.html).)

To add a `HeapProfiler` breakpoint yourself, add `tcmalloc` as a
dependency under the `envoy_cc_library` rule:

`source/exe/BUILD`

```c++
envoy_cc_library(
name = "envoy_common_lib",
+ tcmalloc_dep = 1,
deps = [
...
)
```

It is then necessary to add `HeapProfilerStart()` and `HeapProfilerDump()`
breakpoints somewhere in Envoy. One place to start profiling is at the
instantiation of `MainCommonBase::MainCommonBase`:

`source/exe/main_common.cc`

```c++
// includes
#include "gperftools/heap-profiler.h"
...
MainCommonBase::MainCommonBase(...) : ... {
+ HeapProfilerStart("main_common_base"); // first line
...
}
```

`source/server/server.cc`

```c++
// includes
#include "gperftools/heap-profiler.h"
...
void InstanceImpl::Initialize(...) : ... {
...
+ HeapProfilerDump("main_common_base"); // last line
}
```

Once these changes have been made in your working directory, it might make sense to
save the diff as a patch (`git diff > file`), which can then be quickly
applied/unapplied for testing and commiting. (`git apply`, `git apply -R`)

Build the binary using bazel, and run the binary without any environment variables:

$ bazel build //source/exe:envoy
$ bazel-bin/source/exe/envoy <args>

This will dump your profiler output to the working directory.

# Methodology

For consistent testing, it makes sense to run Envoy for a constant amount of
time across trials:

$ timeout <num_seconds> bazel-bin/source/exe/envoy <options>

Envoy will print to stdout something like:

Starting tracking the heap

And then a series of stdouts like:

Dumping heap profile to <heap file 0001> (100 MB currently in use)
Dumping heap profile to <heap file 0002> (200 MB currently in use)
...

This will generate a series of files; if you statically-linked, these are
wherever `HEAPPROFILE` points to. Otherwise, they are in the current directory
by default. They'll be named something like `main_common_base.0001.heap`,
`main_common_base.0002.heap`, etc.

*NB:* There is no reason this needs to be titled `main_common_base`. Whatever
flag you supply `HeapProfilerStart` / `HeapProfilerDump` will become the
filename. Multiple sections of code could be profiled simultaneously by setting
multiple `HeapProfilerStart()` / `HeapProfilerStop()` breakpoints with unique
identifiers.

# Analyzing with `pprof`

[pprof](https://github.com/google/pprof) can read these heap files in a
number of ways. Most convenient for first-order inspection might be `pprof -top`
or `pprof -text`:

$ pprof -text bazel-bin/source/exe/envoy main_common_base* | head -n5
File: envoy
Build ID: ...
Type: inuse_space
Showing nodes accounting for 6402800.62kB, 98.59% of 6494044.58kB total
Dropped ... nodes (cum <= ...kB)

More complex flame/graph charts can be generated and viewed in a browser, which
is often more helpful than text-based output:

$ pprof -http=localhost:9999 bazel-bin/source/exe/envoy main_common_base*
Loading