Skip to content

Instantly share code, notes, and snippets.

@tjquinno
Last active August 11, 2020 16:13
Show Gist options
  • Save tjquinno/afbeed2dac059998e601fb96b78dabec to your computer and use it in GitHub Desktop.
Save tjquinno/afbeed2dac059998e601fb96b78dabec to your computer and use it in GitHub Desktop.
MP Metrics 2.3 Support Summary

MP Metrics 2.3 Support

There are three key additions for us:

  1. New SimpleTimer metric (combines a count and an elapsed time accumulator)
  2. @SimplyTimed annotation
  3. REST.request optional automatic metric in base registry for all REST endpoints. In MP, Helidon creates a SimpleTimer for each endpoint annotated with a JAX-RS annotation (@GET, @PUT, etc.), with name REST.request and tags class and method identifying the endpoint.

The spec leaves open how an MP implementation enables and disables the REST.request behavior. See below for our proposal.

Helidon MP

Recommendation:

In Helidon configuration, the metrics.rest-request.enabled key controls whether the behavior is on or off. Defaults to false.

Recommendation:

The full (not bare) MP Quickstart example and the MP quickstart archetype enable this feature. Enabling this in the MP quickstart example emphasizes that we support this optional feature of MP. Leaving it out of the bare archetype keeps that bare.

Question: What about the MP database quickstart?

Helidon SE

As with all other metrics in the MP spec, we implement SimpleTimer for SE.

The question is whether and how SE should support REST.request? We should not support REST.request behavior in SE without the developer making some change, to preserve the "no magic" philosophy in SE.

Recommendation: SE provides an easy way for developers to get a handler that deals with the metric

This approach requires very little boilerplate while still not introducing any magic.

Using the SE quickstart as an example...

  1. The Main class saves in a static field the MetricsSupport instance used in preparing the routing rules. (Currently, the MetricsSupport instance is used only locally in the createRouting method.
    static MetricsSupport metrics;
  1. The GreetService#update method changes to add handlers for each of the endpoint paths:
        rules
            .get("/", 
                    Main.metrics.restRequestMetricHandler(this, "getDefaultMessageHandler"), // added handler
                    this::getDefaultMessageHandler)
            .get("/{name}", 
                    Main.metrics.restRequestMetricHandler(this, "getMessageHandler"), // added handler
                    this::getMessageHandler)
            .put("/greeting", 
                    Main.metrics.restRequestMetricHandler(this, "updateGreetingHandler"), // added handler
                    this::updateGreetingHandler);

This requires little code and allows the developer to leave the endpoint code itself alone.

An annoying aspect of this approach is that the developer must repeat the "this" and the method name: once in the method reference from the original quickstart SE example and once again in the method invocation to get the handler new handler from MetricsSupport. Unfortunately, there is no nice, solid, supported way to get the class name and method name from a method reference.

The config key metrics.rest-request.enabled setting for SE defaults to true because, given that the developer has added code to activate the REST.request metrics, we should not require another action to enable them.

Recommendation: SE examples will not use this

Instead doc will illustrate how developers can use the convenience methods.

Alternatives

Developers modify their endpoint code (Helidon SE provides no special help)

  1. Add these instance field declarations to each resource class (this example is from the SE QuickStart):
    private MetricRegistry appRegistry = RegistryFactory.getInstance().getRegistry(Type.APPLICATION);
    private SimpleTimer getDefaultMessageSimpleTimer = appRegistry.simpleTimer("REST.request", 
            new Tag(getClass().getName(), "getDefaultMessageHandler");
    private SimpleTimer getMessageSimpleTimer = appRegistry.simpleTimer("REST.request",
            new Tag(getClass().getName(), "getMessageHandler");
    private SimpleTimer updateGreetingSimpleTimer = appRegistry.simpleTimer("REST.request",
            new Tag(getClass().getName(), "updategreetingHandler");
   

(Note that the spec for MP mandates the metadata for REST.request but because the SE developer "owns" the REST.request metric that's not up to us. 2. Wrap each endpoint method's code in simpleTimer.time:

private void getDefaultMessageHandler(ServerRequest request,
                                   ServerResponse response) {
        getDefaultMessageSimpleTimer.time(sendResponse(response, "World"));
    }

This involves considerable coding by the developer and intrudes into the endpoint logic which, ideally, would focus on business logic. (Note that declaring the metrics separately from the endpoint code makes sure that all the REST.request metrics are created, apart from whether clients invoke the corresponding endpoints.)

Developers leave endpoint code intact but modify routing rules (again, no special help from SE)

For example (again from the SE QuickStart):


// Use the same declarations as above.

    @Override
    public void update(Routing.Rules rules) {
        rules
            .get("/", (req, resp) -> getDefaultMessageSimpleTimer.time(this::getDefaultMessageHandler))
            .get("/{name}", (req, resp) -> getMessageSimpleTimer.time(this::getMessageHandler))
            .put("/greeting", (req, resp) -> updateGreetingSimpleTimer.time(this::updateGreetingHandler));
    }

This avoids changes in the endpoint methods themselves but still requires the developer to add quite a bit of code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment