145cd2bcb123c5d7981d283ed73fb78758a2cc87
[honeycomb.git] / infra / it / benchmark / src / main / java / io / fd / honeycomb / benchmark / data / DataBrokerConfigWriteBenchmark.java
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package io.fd.honeycomb.benchmark.data;
18
19 import com.google.inject.AbstractModule;
20 import com.google.inject.Guice;
21 import com.google.inject.Injector;
22 import com.google.inject.Key;
23 import com.google.inject.Module;
24 import com.google.inject.multibindings.Multibinder;
25 import com.google.inject.name.Names;
26 import io.fd.honeycomb.benchmark.util.DataProvider;
27 import io.fd.honeycomb.benchmark.util.DataSubmitter;
28 import io.fd.honeycomb.benchmark.util.FileManager;
29 import io.fd.honeycomb.benchmark.util.NoopWriter;
30 import io.fd.honeycomb.infra.distro.cfgattrs.HoneycombConfiguration;
31 import io.fd.honeycomb.infra.distro.data.ConfigAndOperationalPipelineModule;
32 import io.fd.honeycomb.translate.write.WriterFactory;
33 import io.fd.honeycomb.translate.write.registry.ModifiableWriterRegistryBuilder;
34 import java.io.IOException;
35 import java.nio.file.Paths;
36 import java.util.ArrayList;
37 import java.util.List;
38 import java.util.Optional;
39 import java.util.concurrent.ExecutionException;
40 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
41 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
42 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithList;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleContainer;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainer;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.ContainerInList;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.container.in.list.NestedList;
48 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
49 import org.openjdk.jmh.annotations.Benchmark;
50 import org.openjdk.jmh.annotations.BenchmarkMode;
51 import org.openjdk.jmh.annotations.Fork;
52 import org.openjdk.jmh.annotations.Level;
53 import org.openjdk.jmh.annotations.Measurement;
54 import org.openjdk.jmh.annotations.Mode;
55 import org.openjdk.jmh.annotations.Param;
56 import org.openjdk.jmh.annotations.Scope;
57 import org.openjdk.jmh.annotations.Setup;
58 import org.openjdk.jmh.annotations.State;
59 import org.openjdk.jmh.annotations.TearDown;
60 import org.openjdk.jmh.annotations.Timeout;
61 import org.openjdk.jmh.annotations.Warmup;
62 import org.slf4j.Logger;
63 import org.slf4j.LoggerFactory;
64
65 /**
66  * Measures the performance of CONFIG writes into BA DataBroker, backed by HC infrastructure and then NOOP writers.
67  */
68 @Timeout(time = 20)
69 @Warmup(iterations = 2, time = 10)
70 @Measurement(iterations = 2, time = 10)
71 @Fork(2)
72 @State(Scope.Thread)
73 @BenchmarkMode(Mode.Throughput)
74 public class DataBrokerConfigWriteBenchmark extends AbstractModule implements FileManager {
75
76     private static final Logger LOG = LoggerFactory.getLogger(DataBrokerConfigWriteBenchmark.class);
77
78     @Param({"1", "10"/*, "100"*/})
79     private int submitFrequency;
80
81     @Param({"true", "false"})
82     private boolean persistence;
83
84     @Param({"put"/*, "merge"*/})
85     private String operation;
86     private DataSubmitter submitter;
87
88     @Param({"CONFIGURATION"})
89     private LogicalDatastoreType dsType;
90
91     @Param({DataProvider.SIMPLE_CONTAINER, DataProvider.LIST_IN_CONTAINER , DataProvider.COMPLEX_LIST_IN_CONTAINER})
92     private String data;
93     private DataProvider dataProvider;
94
95     /*
96     * TODO HONEYCOMB-288 Visualization notes:
97     * - visualize as 3 graphs, 1 for each data
98     * - each graph should show 4 lines. for the combinations of parameters: submitFrequency and persistence
99     *   (if that's too much split or reduce submitFrequecy values that are shown in graph)
100     *
101     * TODO data need to be prepared for such visualization. Maybe if each benchmark class exposed a method to prepare
102     * that data from aggregated results... it might be easy
103     * (just maven exec plugin + main that invokes some method on all benchmark classes)
104     */
105
106     // Infra modules to load
107     private final Module[] modules = new Module[] {
108             new io.fd.honeycomb.infra.distro.schema.YangBindingProviderModule(),
109             new io.fd.honeycomb.infra.distro.schema.SchemaModule(),
110             new io.fd.honeycomb.infra.distro.data.ConfigAndOperationalPipelineModule(),
111             new io.fd.honeycomb.infra.distro.data.context.ContextPipelineModule(),
112             this};
113
114     private List<NoopWriter<?>> noopWriters = new ArrayList<>();
115     private DataBroker dataBroker;
116     private long counter = 0;
117     private WriteTransaction tx;
118     private HoneycombConfiguration instance;
119
120     @Setup(Level.Iteration)
121     public void setup() {
122         LOG.info("Setting up");
123         submitter = DataSubmitter.from(operation);
124         dataProvider = DataProvider.from(data);
125         Injector injector = Guice.createInjector(modules);
126         final HoneycombConfiguration cfg = injector.getInstance(HoneycombConfiguration.class);
127         LOG.info("Configuration for Honeycomb: {}", cfg);
128         dataBroker = injector.getInstance(Key.get(DataBroker.class,
129                 Names.named(ConfigAndOperationalPipelineModule.HONEYCOMB_CONFIG)));
130     }
131
132     @TearDown(Level.Iteration)
133     public void tearDown() {
134         LOG.info("Tearing down after {} executions", counter);
135         counter = 0;
136         LOG.info("Writer invocations: {}", noopWriters);
137         noopWriters.clear();
138
139         tx = null;
140         dataBroker = null;
141         deleteFile(Paths.get(instance.peristConfigPath));
142         deleteFile(Paths.get(instance.peristContextPath));
143     }
144
145     @Benchmark
146     public void write() {
147         // Count executions
148         counter++;
149
150         // New transaction after it was committed
151         if (tx == null) {
152             tx = dataBroker.newWriteOnlyTransaction();
153         }
154
155         submitter.submit(dsType, tx, dataProvider.getId(counter), dataProvider.getData(counter));
156
157         // Commit based on frequency set
158         if (counter % submitFrequency == 0) {
159             try {
160                 tx.submit().get();
161             } catch (InterruptedException | ExecutionException e) {
162                 throw new RuntimeException("Submit failed", e);
163             }
164             tx = null;
165         }
166     }
167
168     /**
169      * Inject custom modules e.g. configuration.
170      */
171     @Override
172     protected void configure() {
173         try {
174             instance = getHoneycombConfiguration(persistence);
175             bind(HoneycombConfiguration.class).toInstance(instance);
176         } catch (IOException e) {
177             throw new RuntimeException("Unable to prepare configuration", e);
178         }
179
180         final Multibinder<WriterFactory> writeBinder = Multibinder.newSetBinder(binder(), WriterFactory.class);
181         writeBinder.addBinding().toInstance(registry -> {
182             // Add noop writers for all data written in this benchmark
183             addWriter(registry, new NoopWriter<>(InstanceIdentifier.create(SimpleContainer.class)));
184             addWriter(registry, new NoopWriter<>(InstanceIdentifier.create(ContainerWithList.class)));
185             addWriter(registry, new NoopWriter<>(InstanceIdentifier.create(ContainerWithList.class)
186                     .child(ListInContainer.class)));
187             addWriter(registry, new NoopWriter<>(InstanceIdentifier.create(ContainerWithList.class)
188                     .child(ListInContainer.class)
189                     .child(ContainerInList.class)));
190             addWriter(registry, new NoopWriter<>(InstanceIdentifier.create(ContainerWithList.class)
191                     .child(ListInContainer.class)
192                     .child(ContainerInList.class)
193                     .child(NestedList.class)));
194         });
195     }
196
197     private void addWriter(final ModifiableWriterRegistryBuilder registry, final NoopWriter<?> handler) {
198         noopWriters.add(handler);
199         registry.add(handler);
200     }
201
202     private static HoneycombConfiguration getHoneycombConfiguration(final boolean persistence) throws IOException {
203         final HoneycombConfiguration instance = new HoneycombConfiguration();
204         instance.persistConfig = Optional.of(Boolean.toString(persistence));
205         instance.persistContext = Optional.of(Boolean.toString(persistence));
206         instance.peristConfigPath = FileManager.INSTANCE.createTempFile("config").toString();
207         instance.peristContextPath = FileManager.INSTANCE.createTempFile("context").toString();
208         return instance;
209     }
210 }