Skip to content

Add support for bound instruments to the metrics API #4126

Open
@jack-berg

Description

@jack-berg

Bound instruments is the idea of obtaining a reference "bound" to a particular set of attributes which is known ahead of time. By doing so, you can record measurements directly to the bound reference and avoid an internal lookup.

For example, suppose I know ahead of time that I need to record measurements to attribute sets {"color": "red", "shape", "square"}, {"color": "red", "shape", "circle"}, {"color": "blue", "shape", "square"}, {"color": "blue", "shape", "circle"}.

Without bound instruments, code to record these series might look like:

// Initialize counter and known attribute sets
LongCounter counter = meter.counterBuilder("my-counter").ofLongs().build();

Attributes redSquare = Attributes.builder().put("color", "red").put("shape", "square").build();
Attributes redCircle = Attributes.builder().put("color", "red").put("shape", "circle").build();
Attributes blueSquare = Attributes.builder().put("color", "blue").put("shape", "square").build();
Attributes blueCircle = Attributes.builder().put("color", "blue").put("shape", "circle").build();

// ... record measurements
counter.increment(1, redSquare);
counter.increment(1, redCircle);
counter.increment(1, blueSquare);
counter.increment(1, blueCircle);

Each time a measurement is recorded, the internals of the SDK must lookup the aggregated state corresponding to the attributes in a map.

With bound instruments, you can avoid this map lookup and improve the performance of record operations:

// Initialize counter and obtain bound references to known attribute sets
LongCounter counter = meter.counterBuilder("my-counter").ofLongs().build();

BoundLongCounter redSquare = counter.bind(Attributes.builder().put("color", "red").put("shape", "square").build());
BoundLongCounter redCircle = counter.bind(Attributes.builder().put("color", "red").put("shape", "circle").build());
BoundLongCounter blueSquare = counter.bind(Attributes.builder().put("color", "blue").put("shape", "square").build());
BoundLongCounter blueCircle = counter.bind(Attributes.builder().put("color", "blue").put("shape", "circle").build());

// ... record measurements
redSquare.increment(1);
redCircle.increment(1);
blueSquare.increment(1);
blueCircle.increment(1);

We should consider extending the metrics API to support this concept. I recently wrote a blog post comparing otel java metrics to other metrics systems in the java ecosystem (prometheus and micrometer), and one notable negative of otel was the lack of support for bound instruments. For java, avoiding the map lookup saves ~11 ns per record call on my machine.

Its worth noting that bound instruments serve a rather niche use case where: 1. The attribute sets are known ahead of time. 2. The application is very performance sensitive. If either of these is not true, then bound instruments are of limited value. (I previously argued that we should punt on support for bound instruments in the initial metrics API release for this very reason.) For example, some of the most popular metrics in otel are http.server.request.duration and http.client.request.duration, and neither of these would be able to benefit from bound instruments because the attribute values are computed at runtime using application context.

Metadata

Metadata

Assignees

No one assigned

    Labels

    spec:metricsRelated to the specification/metrics directorytriage:deciding:community-feedbackOpen to community discussion. If the community can provide sufficient reasoning, it may be accepted

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions