Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master' into additional-http-s…
Browse files Browse the repository at this point in the history
…em-conventions-open-telemetry#362
  • Loading branch information
kbrockhoff committed Dec 26, 2019
2 parents 5645bec + ccbf363 commit c77cb67
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 83 deletions.
151 changes: 76 additions & 75 deletions specification/api-metrics-user.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,8 @@ type server struct {
func newServer(meter metric.Meter) *server {
return &server{
meter: meter,
instruments: newInstruments(meter),

// ... other fields
instruments: newInstruments(meter),
// ... other fields
}
}

Expand All @@ -147,14 +146,14 @@ func (s *server) operate(ctx context.Context) {
// ... other work

s.instruments.counter1.Add(ctx, 1, s.meter.Labels(
label1.String("..."),
label2.String("...")))
label1.String("..."),
label2.String("...")))
}
```

### Metric calling conventions

This API is factored into three core concepts: instruments, handles,
This API is factored into three core types: instruments, bound instruments,
and label sets. In doing so, we provide several ways of capturing
measurements that are semantically equivalent and generate equivalent
metric events, but offer varying degrees of performance and
Expand All @@ -178,26 +177,22 @@ metric data into a reduced number of key dimensions. SDKs may be
designed to perform aggregation and/or grouping in the process, with
various trade-offs in terms of complexity and performance.

#### Metric handle calling convention

This approach requires locating an entry for the instrument and label
set in a table of some kind, finding the location where a metric
events are being aggregated. This lookup can be successfully
precomputed, giving rise to the Handle calling convention.
#### Bound instrument calling convention

In situations where performance is a requirement and a metric is
In situations where performance is a requirement and a metric instrument is
repeatedly used with the same set of labels, the developer may elect
to use _instrument handles_ as an optimization. For handles to be a
benefit, it requires that a specific instrument will be re-used with
specific labels. If an instrument will be used with the same label
set more than once, obtaining an instrument handle corresponding to
the label set ensures the highest performance available.
to use the _bound instrument_ calling convention as an optimization.
For bound instruments to be a benefit, it requires that a specific
instrument will be re-used with specific labels. If an instrument
will be used with the same label set more than once, obtaining an
bound instrument corresponding to the label set ensures the highest
performance available.

To obtain a handle given an instrument and label set, use the
`GetHandle()` method to return an interface that supports the `Add()`,
`Set()`, or `Record()` method of the instrument in question.
To bind an instrument and label set, use the `Bind(LabelSet)` method to
return an interface that supports the `Add()`, `Set()`, or `Record()`
method of the instrument in question.

Instrument handles may consume SDK resources indefinitely.
Bound instruments may consume SDK resources indefinitely.

```golang
func (s *server) processStream(ctx context.Context) {
Expand All @@ -206,21 +201,24 @@ func (s *server) processStream(ctx context.Context) {
labelA.String("..."),
labelB.String("..."),
)
counter2Handle := s.instruments.counter2.GetHandle(streamLabels)
// The result of Bind() is a bound instrument
// (e.g., a BoundInt64Counter).
counter2 := s.instruments.counter2.Bind(streamLabels)

for _, item := <-s.channel {
// ... other work

// High-performance metric calling convention: use of handles.
counter2Handle.Add(ctx, item.size())
// High-performance metric calling convention: use of bound
// instruments.
counter2.Add(ctx, item.size())
}
}
```

#### Direct metric calling convention
#### Direct instrument calling convention

When convenience is more important than performance, or there is no
re-use to potentially optimize with instrument handles, users may
re-use to potentially optimize with bound instruments, users may
elect to operate directly on metric instruments, supplying a label set
at the call site.

Expand All @@ -235,13 +233,47 @@ func (s *server) method(ctx context.Context) {
```

This method offers the greatest convenience possible. If performance
becomes a problem, one option is to use handles as described above.
becomes a problem, one option is to use bound instruments as described above.
Another performance option, in some cases, is to just re-use the
labels. In the example here, `meter.Labels(...)` constructs a
re-usable label set which may be an important performance
optimization.

#### Label set calling convention
#### RecordBatch calling convention

There is one final API for entering measurements, which is like the
direct access calling convention but supports multiple simultaneous
measurements. The use of a RecordBatch API supports entering multiple
measurements, implying a semantically atomic update to several
instruments.

For example:

```golang
func (s *server) method(ctx context.Context) {
// ... other work

labelSet := s.meter.Labels(...)

// ... more work

s.meter.RecordBatch(ctx, labelSet,
s.instruments.counter1.Measurement(1),
s.instruments.gauge1.Measurement(10),
s.instruments.measure2.Measurement(123.45),
)
}
```

Using the RecordBatch calling convention is semantically identical to
a sequence of direct calls, with the addition of atomicity. Because
values are entered in a single call,
the SDK is potentially able to implement an atomic update, from the
exporter's point of view. Calls to `RecordBatch` may potentially
reduce costs because the SDK can enqueue a single bulk update, or take
a lock only once, for example.

#### Label set re-use is encouraged

A significant factor in the cost of metrics export is that labels,
which arrive as an unordered list of keys and values, must be
Expand All @@ -258,16 +290,16 @@ enough that we give it first-class treatment in the API. The
user.

Re-usable `LabelSet` objects provide a potential optimization for
scenarios where handles might not be effective. For example, if the
label set will be re-used but only used once per metric, handles do
scenarios where bound instruments might not be effective. For example, if the
label set will be re-used but only used once per metric, bound instruments do
not offer any optimization. It may be best to pre-compute a
canonicalized `LabelSet` once and re-use it with the direct calling
convention.

Constructing an instrument handle is considered the higher-performance
option, when the handle will be used more than once. Still, consider
Constructing a bound instrument is considered the higher-performance
option, when the bound instrument will be used more than once. Still, consider
re-using the result of `Meter.Labels(...)` when constructing more than
one instrument handle.
one bound instrument.

```golang
func (s *server) method(ctx context.Context) {
Expand Down Expand Up @@ -296,11 +328,11 @@ unspecified_, a distinct value type of the exported data model.

##### Option: Convenience method to bypass `meter.Labels(...)`

As a language-optional feature, the direct and handle calling
As a language-optional feature, the direct and bound instrument calling
convention APIs may support alternate convenience methods to pass raw
labels at the call site. These may be offered as overloaded methods
for `Add()`, `Set()`, and `Record()` (direct calling convention) or
`GetHandle()` (handle calling convention), in both cases bypassing a
`Bind()` (bound instrument calling convention), in both cases bypassing a
call to `meter.Labels(...)`. For example:

```java
Expand All @@ -311,7 +343,10 @@ call to `meter.Labels(...)`. For example:
// ... or

// pass raw labels, no explicit `LabelSet`
handle := s.instruments.gauge1.getHandle(labelA.value(...), labelB.value(...))
BoundIntCounter counter = s.instruments.gauge1.bind(labelA, ..., labelB, ...)
for (...) {
counter.add(1)
}
}
```

Expand All @@ -338,40 +373,6 @@ availability of type-checking in the source language. Passing
unordered labels (i.e., a list of bound keys and values) to the
`Meter.Labels(...)` constructor is considered the safer alternative.

#### RecordBatch calling convention

There is one final API for entering measurements, which is like the
direct access calling convention but supports multiple simultaneous
measurements. The use of a RecordBatch API supports entering multiple
measurements, implying a semantically atomic update to several
instruments.

The preceding example could be rewritten:

```golang
func (s *server) method(ctx context.Context) {
// ... other work

labelSet := s.meter.Labels(...)

// ... more work

s.meter.RecordBatch(ctx, labelSet,
s.instruments.counter1.Measurement(1),
s.instruments.gauge1.Measurement(10),
s.instruments.measure2.Measurement(123.45),
)
}
```

Using the RecordBatch calling convention is semantically identical to
the sequence of direct calls in the preceding example, with the
addition of atomicity. Because values are entered in a single call,
the SDK is potentially able to implement an atomic update, from the
exporter's point of view. Calls to `RecordBatch` may potentially
reduce costs because the SDK can enqueue a single bulk update, or take
a lock only once, for example.

## Detailed specification

See the [SDK-facing Metrics API](api-metrics-meter.md) specification
Expand Down Expand Up @@ -427,15 +428,15 @@ that it used, and the metric name is the only required field.
See the Metric API [specification overview](api-metrics.md) for more
information about the kind-specific monotonic and absolute options.

### Instrument handle calling convention
### Bound instrument API

Counter, gauge, and measure instruments each support allocating
handles for the high-performance calling convention. The
`Instrument.GetHandle(LabelSet)` method returns an interface which
bound instruments for the high-performance calling convention. The
`Instrument.Bind(LabelSet)` method returns an interface which
implements the `Add()`, `Set()` or `Record()` method, respectively,
for counter, gauge, and measure instruments.

### Instrument direct calling convention
### Direct instrument API

Counter, gauge, and measure instruments support the appropriate
`Add()`, `Set()`, and `Record()` method for submitting individual
Expand Down
4 changes: 2 additions & 2 deletions specification/api-tracing.md
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,7 @@ codes](https://github.com/grpc/grpc/blob/master/doc/statuscodes.md):
- The operation completed successfully.
- `Cancelled`
- The operation was cancelled (typically by the caller).
- `UnknownError`
- `Unknown`
- An unknown error.
- `InvalidArgument`
- Client specified an invalid argument. Note that this differs from
Expand Down Expand Up @@ -506,7 +506,7 @@ codes](https://github.com/grpc/grpc/blob/master/doc/statuscodes.md):
fixed if the system state changes.
- `Unimplemented`
- Operation is not implemented or not supported/enabled in this service.
- `InternalError`
- `Internal`
- Internal errors. Means some invariants expected by underlying system has been
broken.
- `Unavailable`
Expand Down
10 changes: 5 additions & 5 deletions specification/data-http.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ Don't set a status message if the reason can be inferred from `http.status_code`
| 501 Not Implemented | `Unimplemented` |
| 503 Service Unavailable | `Unavailable` |
| 504 Gateway Timeout | `DeadlineExceeded` |
| Other 5xx code | `InternalError` [1] |
| Any status code the client fails to interpret (e.g., 093 or 573) | `UnknownError` |
| Other 5xx code | `Internal` [1] |
| Any status code the client fails to interpret (e.g., 093 or 573) | `Unknown` |

Note that the items marked with [1] are different from the mapping defined in the [OpenCensus semantic conventions][oc-http-status].

Expand Down Expand Up @@ -102,14 +102,14 @@ For status, the following special cases have canonical error codes assigned:

| Client error | Trace status code |
|-----------------------------|--------------------|
| DNS resolution failed | `UnknownError` |
| DNS resolution failed | `Unknown` |
| Request cancelled by caller | `Cancelled` |
| URL cannot be parsed | `InvalidArgument` |
| Request timed out | `DeadlineExceeded` |

This is not meant to be an exhaustive list
but if there is no clear mapping for some error conditions,
instrumentation developers are encouraged to use `UnknownError`
instrumentation developers are encouraged to use `Unknown`
and open a PR or issue in the specification repository.

## HTTP server
Expand Down Expand Up @@ -240,4 +240,4 @@ If set, it would be
`"https://example.com:8080/webshop/articles/4?s=1"`
but due to `http.scheme`, `http.host` and `http.target` being set, it would be redundant.
As explained above, these separate values are preferred but if for some reason the URL is available but the other values are not,
URL can replace `http.scheme`, `http.host` and `http.target`.
URL can replace `http.scheme`, `http.host` and `http.target`.
4 changes: 3 additions & 1 deletion specification/sdk-tracing.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ The OpenTelemetry API has two properties responsible for the data collection:
all spans with this flag set. However, [Span Exporter](#span-exporter) will
not receive them unless the `Sampled` flag was set.
* `Sampled` flag in `TraceFlags` on `SpanContext`. This flag is propagated via
the `SpanContext` to child Spans. For more details see the [W3C
the `SpanContext` to child Spans. For more details see the [W3C Trace Context
specification][trace-flags]. This flag indicates that the `Span` has been
`sampled` and will be exported. [Span Processor](#span-processor) and [Span
Exporter](#span-exporter) will receive spans with the `Sampled` flag set for
Expand Down Expand Up @@ -386,3 +386,5 @@ public interface SpanExporter {
void shutdown();
}
```

[trace-flags]: https://www.w3.org/TR/trace-context/#trace-flags

0 comments on commit c77cb67

Please sign in to comment.