From f911a6a1a451198beb302289463fa52d6e6eec41 Mon Sep 17 00:00:00 2001 From: Joshua MacDonald Date: Wed, 18 Dec 2019 09:27:11 -0800 Subject: [PATCH 1/3] Metric 'Handle' to 'Bound Instrument' (OTEP 70) (#392) --- specification/api-metrics-user.md | 151 +++++++++++++++--------------- 1 file changed, 76 insertions(+), 75 deletions(-) diff --git a/specification/api-metrics-user.md b/specification/api-metrics-user.md index 1fc38d9d034..0f7d564d944 100644 --- a/specification/api-metrics-user.md +++ b/specification/api-metrics-user.md @@ -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 } } @@ -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 @@ -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) { @@ -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. @@ -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 @@ -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) { @@ -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 @@ -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) + } } ``` @@ -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 @@ -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 From 61f6f00e8226a47207873ac7a41b7087e4c5aec6 Mon Sep 17 00:00:00 2001 From: Armin Ruech Date: Wed, 25 Dec 2019 17:16:11 +0100 Subject: [PATCH 2/3] Fix reference to W3C TC spec in sdk-tracing.md (#388) * Fix reference to W3C TC spec in sdk-tracing.md * Update sdk-tracing.md --- specification/sdk-tracing.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/specification/sdk-tracing.md b/specification/sdk-tracing.md index 8bd1f88dbd7..732d7f7db20 100644 --- a/specification/sdk-tracing.md +++ b/specification/sdk-tracing.md @@ -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 @@ -386,3 +386,5 @@ public interface SpanExporter { void shutdown(); } ``` + +[trace-flags]: https://www.w3.org/TR/trace-context/#trace-flags From ccbf36374730488548703a65af40d9051d7ab944 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Wed, 25 Dec 2019 10:17:10 -0600 Subject: [PATCH 3/3] Fix discrepancy between status names (#385) Fixes #384 Co-authored-by: Sergey Kanzhelev Co-authored-by: Bogdan Drutu --- specification/api-tracing.md | 4 ++-- specification/data-http.md | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/specification/api-tracing.md b/specification/api-tracing.md index 88cc1186916..cd1e1342f5f 100644 --- a/specification/api-tracing.md +++ b/specification/api-tracing.md @@ -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 @@ -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` diff --git a/specification/data-http.md b/specification/data-http.md index 273d0138b81..84de73c4a89 100644 --- a/specification/data-http.md +++ b/specification/data-http.md @@ -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]. @@ -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 @@ -238,4 +238,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`. \ No newline at end of file +URL can replace `http.scheme`, `http.host` and `http.target`.