Phreakradio
Phreakradio

Reputation: 186

Binding instances during Caliper benchmarks

I've recently been tasked with benchmarking some features of our API, and I've been using Caliper to do so. It seems fairly straightforward and is actually quite powerful, I've been following the tutorials here:

How to use caliper

Tutorial from the creator

I'm working with Guice in our current app, so when I try to run the benchmark, I make sure to inject the services I need

Provided below is my code. I've tried setting the variables with their own @injected annotation, I've tried initiating them directly (although there's too many dependencies to deal with, and I'd have to initiate them as well). @Parms annotation won't work because I need to have a string type to iterate through (the parms only takes strings, there's documentation of what to do if it's another type, but it needs a .toString type method)

package com.~~~.~~~.api.benchmark;

import com.~~~.ds.mongo.orm.user.User;
import com.~~~.~~~.api.exceptions.auth.AccessDeniedException;
import com.~~~.~~~.api.service.authorization.UserService;
import com.~~~.~~~.api.service.campaign.CampaignMetadataService;
import com.google.caliper.BeforeExperiment;
import com.google.caliper.Benchmark;
import com.google.caliper.Param;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;

public class CampaignBenchmark {

    // === SERVICE INJECTIONS

    @Inject
    private CampaignMetadataService campaignMetadataService;

    @Inject
    private UserService userService;

    // =============================================
    // === BENCHMARK PARMS

    @Param({
            "7ca8c319",
            "49191829"
    })
    String userId;

    @Param({
            "485",
            "500"
    })
    String hashAdvertiserId;

    // =============================================
    // === TESTING PARMS

    private User user;

    // =============================================
    // === SETUP

    @Inject
    public CampaignBenchmark(){
        Injector injector = Guice.createInjector();
        this.userService = injector.getInstance(UserService.class);
        this.campaignMetadataService = injector.getInstance(CampaignMetadataService.class);
    }

    @BeforeExperiment
    void setUp(){
        this.user = userService.getUserByHashedId(userId);
    }

    // =============================================
    // === BENCHMARKS

    @Benchmark
    int fetchAllCampaign(int reps) throws AccessDeniedException {
        VideoIqUser user = this.user;
        String hashAdvertiserId = this.hashAdvertiserId;

        int dummy = 0;

        for(int i=0 ; i<reps ; i++){
            dummy |= campaignMetadataService.fetchAllCampaigns(user, hashAdvertiserId).size();
        }

        return dummy;
    }
}

When we try to run it with

mvn exec:java -Dexec.mainClass="com.google.caliper.runner.CaliperMain" -Dexec.args="com.~~~.~~~.api.benchmark.CampaignBenchmark"

We get the following

WARNING: All illegal access operations will be denied in a future release
Experiment selection: 
  Benchmark Methods:   [fetchAllCampaign]
  Instruments:   [allocation, runtime]
  User parameters:   {hashAdvertiserId=[485, 500], userId=[7ca8c319, 49191829]}
  Virtual machines:  [default]
  Selection type:    Full cartesian product

This selection yields 16 experiments.
Could not create an instance of the benchmark class following reasons:
  1) Explicit bindings are required and com.~~~.~~~.api.service.campaign.CampaignMetadataService is not explicitly bound.
  2) Explicit bindings are required and com.~~~.~~~.api.service.authorization.UserService is not explicitly bound.

The question is: at what point should I be doing injections, and how do I go about that? Should I have a wrapper class setup?

Quick Update

I forgot to mention that is part of a DropWizard (0.7.1) application. We use resources and inject them into the environment

Ex:

environment.jersey().register(injector.getInstance(CampaignManagementResource.class));

These resources contain the services needed to run them, and they are included as @Inject, though we never actually specify a binding anywhere else.

@Inject
private CampaignMetadataService apiCampaignMetadataService;

Is there something I should adjust for DropWizard, or should I just mock up the services?

Upvotes: 0

Views: 136

Answers (1)

iluxa
iluxa

Reputation: 6969

Think of Guice as nothing but a Hashtable. Working with Guice has these parts:

  • creating the hashtable
  • putting things into it
  • getting things out of it

Your code creates the hashtable and queries it, but never puts anything in it:

public CampaignBenchmark() {
    // Creating the hashtable
    Injector injector = Guice.createInjector();
    // Retrieving from the hashtable
    this.userService = injector.getInstance(UserService.class);
    this.campaignMetadataService = injector.getInstance(CampaignMetadataService.class);
}

The Guice hashtable is populated in Module classes. Undoubtedly, you have a CampaignModule or AdvertisingModule lying around somewhere. It probably looks like this:

public class CampaignModule extends AbstractModule {
  @Override protected void configure() {
    bind(CampaignMetadataService.class).to(CampaignMetadataServiceImplementation.class);
}

What that does is put a <CampaignMetadataService, CampaignMetadataServiceImplementation> entry into the Guice hashtable. Going forward, whoever asks for an instance of CampaignMetadataService receives an instance of CampaignMetadataServiceImplementation.

So in your code, you need to let Guice know about this module:

public CampaignBenchmark() {
    // Creating the hashtable and letting modules populate it
    Injector injector = Guice.createInjector(new CampaignModule(), new UserModule());
    // Retrieving from the hashtable
    this.userService = injector.getInstance(UserService.class);
    this.campaignMetadataService = injector.getInstance(CampaignMetadataService.class);
}

Everything else you do is fine. Side note: @Inject annotations on your constructor and fields don't do anything, since you never ask Guice to supply you with instances of CampaignBenchmark. These can just be deleted.

Upvotes: 0

Related Questions