From 03a638b95da83e150d4f69451c8733b5f09c37aa Mon Sep 17 00:00:00 2001 From: Maros Marsalek Date: Thu, 3 Nov 2016 16:24:17 +0100 Subject: HONEYCOMB-287 Infra micro-benchmarks Config (write) Operational (read) Add -Pbenchmark to maven execution to include benchmarks Change-Id: Ia4815ffc109e34629279b9418b962a9f91c38c30 Signed-off-by: Maros Marsalek --- infra/it/benchmark/asciidoc/Readme.adoc | 3 + infra/it/benchmark/pom.xml | 137 ++++++++++++++ .../data/DataBrokerConfigWriteBenchmark.java | 210 +++++++++++++++++++++ .../data/DataBrokerOperReadBenchmark.java | 198 +++++++++++++++++++ .../fd/honeycomb/benchmark/util/DataProvider.java | 197 +++++++++++++++++++ .../fd/honeycomb/benchmark/util/DataSubmitter.java | 40 ++++ .../fd/honeycomb/benchmark/util/FileManager.java | 41 ++++ .../io/fd/honeycomb/benchmark/util/NoopWriter.java | 63 +++++++ .../fd/honeycomb/benchmark/util/StaticReader.java | 83 ++++++++ infra/it/benchmark/src/main/resources/logback.xml | 26 +++ infra/it/pom.xml | 12 ++ 11 files changed, 1010 insertions(+) create mode 100644 infra/it/benchmark/asciidoc/Readme.adoc create mode 100644 infra/it/benchmark/pom.xml create mode 100644 infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/data/DataBrokerConfigWriteBenchmark.java create mode 100644 infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/data/DataBrokerOperReadBenchmark.java create mode 100644 infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/util/DataProvider.java create mode 100644 infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/util/DataSubmitter.java create mode 100644 infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/util/FileManager.java create mode 100644 infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/util/NoopWriter.java create mode 100644 infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/util/StaticReader.java create mode 100644 infra/it/benchmark/src/main/resources/logback.xml (limited to 'infra/it') diff --git a/infra/it/benchmark/asciidoc/Readme.adoc b/infra/it/benchmark/asciidoc/Readme.adoc new file mode 100644 index 000000000..44d2262ac --- /dev/null +++ b/infra/it/benchmark/asciidoc/Readme.adoc @@ -0,0 +1,3 @@ += benchmark + +Overview of benchmark \ No newline at end of file diff --git a/infra/it/benchmark/pom.xml b/infra/it/benchmark/pom.xml new file mode 100644 index 000000000..5ee3c0881 --- /dev/null +++ b/infra/it/benchmark/pom.xml @@ -0,0 +1,137 @@ + + + + + + + + io.fd.honeycomb.common + honeycomb-parent + 1.16.12-SNAPSHOT + ../../../common/honeycomb-parent + + + 4.0.0 + io.fd.honeycomb + benchmark + 1.16.12-SNAPSHOT + jar + + + 1.15 + benchmarks + + + + + org.openjdk.jmh + jmh-core + ${jmh.version} + + + org.openjdk.jmh + jmh-generator-annprocess + ${jmh.version} + provided + + + io.fd.honeycomb + minimal-distribution + ${project.version} + + + io.fd.honeycomb.it + honeycomb-test-model + ${project.version} + + + + + + + ${basedir}/src/main/resources + + true + + + + + maven-dependency-plugin + + + build-classpath + compile + + build-classpath + + + depClasspath + + + + + + org.codehaus.mojo + exec-maven-plugin + + + benchmark + test + + java + + + org.openjdk.jmh.Main + + + java.class.path + ${project.build.outputDirectory}${path.separator}${depClasspath} + + + + + + + -foe=true + + + + + + + + -rf=csv + -rff=${project.build.directory}/benchmark.csv + + + + + + + + + diff --git a/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/data/DataBrokerConfigWriteBenchmark.java b/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/data/DataBrokerConfigWriteBenchmark.java new file mode 100644 index 000000000..145cd2bcb --- /dev/null +++ b/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/data/DataBrokerConfigWriteBenchmark.java @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.benchmark.data; + +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.Key; +import com.google.inject.Module; +import com.google.inject.multibindings.Multibinder; +import com.google.inject.name.Names; +import io.fd.honeycomb.benchmark.util.DataProvider; +import io.fd.honeycomb.benchmark.util.DataSubmitter; +import io.fd.honeycomb.benchmark.util.FileManager; +import io.fd.honeycomb.benchmark.util.NoopWriter; +import io.fd.honeycomb.infra.distro.cfgattrs.HoneycombConfiguration; +import io.fd.honeycomb.infra.distro.data.ConfigAndOperationalPipelineModule; +import io.fd.honeycomb.translate.write.WriterFactory; +import io.fd.honeycomb.translate.write.registry.ModifiableWriterRegistryBuilder; +import java.io.IOException; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.ExecutionException; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleContainer; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainer; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.ContainerInList; +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; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Timeout; +import org.openjdk.jmh.annotations.Warmup; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Measures the performance of CONFIG writes into BA DataBroker, backed by HC infrastructure and then NOOP writers. + */ +@Timeout(time = 20) +@Warmup(iterations = 2, time = 10) +@Measurement(iterations = 2, time = 10) +@Fork(2) +@State(Scope.Thread) +@BenchmarkMode(Mode.Throughput) +public class DataBrokerConfigWriteBenchmark extends AbstractModule implements FileManager { + + private static final Logger LOG = LoggerFactory.getLogger(DataBrokerConfigWriteBenchmark.class); + + @Param({"1", "10"/*, "100"*/}) + private int submitFrequency; + + @Param({"true", "false"}) + private boolean persistence; + + @Param({"put"/*, "merge"*/}) + private String operation; + private DataSubmitter submitter; + + @Param({"CONFIGURATION"}) + private LogicalDatastoreType dsType; + + @Param({DataProvider.SIMPLE_CONTAINER, DataProvider.LIST_IN_CONTAINER , DataProvider.COMPLEX_LIST_IN_CONTAINER}) + private String data; + private DataProvider dataProvider; + + /* + * TODO HONEYCOMB-288 Visualization notes: + * - visualize as 3 graphs, 1 for each data + * - each graph should show 4 lines. for the combinations of parameters: submitFrequency and persistence + * (if that's too much split or reduce submitFrequecy values that are shown in graph) + * + * TODO data need to be prepared for such visualization. Maybe if each benchmark class exposed a method to prepare + * that data from aggregated results... it might be easy + * (just maven exec plugin + main that invokes some method on all benchmark classes) + */ + + // Infra modules to load + private final Module[] modules = new Module[] { + new io.fd.honeycomb.infra.distro.schema.YangBindingProviderModule(), + new io.fd.honeycomb.infra.distro.schema.SchemaModule(), + new io.fd.honeycomb.infra.distro.data.ConfigAndOperationalPipelineModule(), + new io.fd.honeycomb.infra.distro.data.context.ContextPipelineModule(), + this}; + + private List> noopWriters = new ArrayList<>(); + private DataBroker dataBroker; + private long counter = 0; + private WriteTransaction tx; + private HoneycombConfiguration instance; + + @Setup(Level.Iteration) + public void setup() { + LOG.info("Setting up"); + submitter = DataSubmitter.from(operation); + dataProvider = DataProvider.from(data); + Injector injector = Guice.createInjector(modules); + final HoneycombConfiguration cfg = injector.getInstance(HoneycombConfiguration.class); + LOG.info("Configuration for Honeycomb: {}", cfg); + dataBroker = injector.getInstance(Key.get(DataBroker.class, + Names.named(ConfigAndOperationalPipelineModule.HONEYCOMB_CONFIG))); + } + + @TearDown(Level.Iteration) + public void tearDown() { + LOG.info("Tearing down after {} executions", counter); + counter = 0; + LOG.info("Writer invocations: {}", noopWriters); + noopWriters.clear(); + + tx = null; + dataBroker = null; + deleteFile(Paths.get(instance.peristConfigPath)); + deleteFile(Paths.get(instance.peristContextPath)); + } + + @Benchmark + public void write() { + // Count executions + counter++; + + // New transaction after it was committed + if (tx == null) { + tx = dataBroker.newWriteOnlyTransaction(); + } + + submitter.submit(dsType, tx, dataProvider.getId(counter), dataProvider.getData(counter)); + + // Commit based on frequency set + if (counter % submitFrequency == 0) { + try { + tx.submit().get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException("Submit failed", e); + } + tx = null; + } + } + + /** + * Inject custom modules e.g. configuration. + */ + @Override + protected void configure() { + try { + instance = getHoneycombConfiguration(persistence); + bind(HoneycombConfiguration.class).toInstance(instance); + } catch (IOException e) { + throw new RuntimeException("Unable to prepare configuration", e); + } + + final Multibinder writeBinder = Multibinder.newSetBinder(binder(), WriterFactory.class); + writeBinder.addBinding().toInstance(registry -> { + // Add noop writers for all data written in this benchmark + addWriter(registry, new NoopWriter<>(InstanceIdentifier.create(SimpleContainer.class))); + addWriter(registry, new NoopWriter<>(InstanceIdentifier.create(ContainerWithList.class))); + addWriter(registry, new NoopWriter<>(InstanceIdentifier.create(ContainerWithList.class) + .child(ListInContainer.class))); + addWriter(registry, new NoopWriter<>(InstanceIdentifier.create(ContainerWithList.class) + .child(ListInContainer.class) + .child(ContainerInList.class))); + addWriter(registry, new NoopWriter<>(InstanceIdentifier.create(ContainerWithList.class) + .child(ListInContainer.class) + .child(ContainerInList.class) + .child(NestedList.class))); + }); + } + + private void addWriter(final ModifiableWriterRegistryBuilder registry, final NoopWriter handler) { + noopWriters.add(handler); + registry.add(handler); + } + + private static HoneycombConfiguration getHoneycombConfiguration(final boolean persistence) throws IOException { + final HoneycombConfiguration instance = new HoneycombConfiguration(); + instance.persistConfig = Optional.of(Boolean.toString(persistence)); + instance.persistContext = Optional.of(Boolean.toString(persistence)); + instance.peristConfigPath = FileManager.INSTANCE.createTempFile("config").toString(); + instance.peristContextPath = FileManager.INSTANCE.createTempFile("context").toString(); + return instance; + } +} diff --git a/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/data/DataBrokerOperReadBenchmark.java b/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/data/DataBrokerOperReadBenchmark.java new file mode 100644 index 000000000..2fd9fa239 --- /dev/null +++ b/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/data/DataBrokerOperReadBenchmark.java @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.benchmark.data; + +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.Key; +import com.google.inject.Module; +import com.google.inject.multibindings.Multibinder; +import com.google.inject.name.Names; +import io.fd.honeycomb.benchmark.util.DataProvider; +import io.fd.honeycomb.benchmark.util.FileManager; +import io.fd.honeycomb.benchmark.util.StaticReader; +import io.fd.honeycomb.infra.distro.cfgattrs.HoneycombConfiguration; +import io.fd.honeycomb.infra.distro.data.ConfigAndOperationalPipelineModule; +import io.fd.honeycomb.translate.read.Reader; +import io.fd.honeycomb.translate.read.ReaderFactory; +import io.fd.honeycomb.translate.read.registry.ModifiableReaderRegistryBuilder; +import java.io.IOException; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.ExecutionException; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithListBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleContainer; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainer; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Timeout; +import org.openjdk.jmh.annotations.Warmup; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Measures the performance of CONFIG writes into BA DataBroker, backed by HC infrastructure and then NOOP writers. + */ +@Timeout(time = 20) +@Warmup(iterations = 2, time = 10) +@Measurement(iterations = 2, time = 10) +@Fork(2) +@State(Scope.Thread) +@BenchmarkMode(Mode.Throughput) +public class DataBrokerOperReadBenchmark extends AbstractModule implements FileManager { + + private static final Logger LOG = LoggerFactory.getLogger(DataBrokerOperReadBenchmark.class); + + @Param({"OPERATIONAL"}) + private LogicalDatastoreType dsType; + + // Persistence does not make a difference when only reading operational + @Param({"false"}) + private boolean persistence; + + @Param({DataProvider.SIMPLE_CONTAINER, DataProvider.LIST_IN_CONTAINER, DataProvider.COMPLEX_LIST_IN_CONTAINER}) + private String data; + private DataProvider dataProvider; + + /* + * TODO HONEYCOMB-288 Visualization notes: + * - visualize as 1 graph + * - just 3 lines + */ + + // Infra modules to load + private final Module[] modules = new Module[] { + new io.fd.honeycomb.infra.distro.schema.YangBindingProviderModule(), + new io.fd.honeycomb.infra.distro.schema.SchemaModule(), + new ConfigAndOperationalPipelineModule(), + new io.fd.honeycomb.infra.distro.data.context.ContextPipelineModule(), + this}; + + private List> noopReaders = new ArrayList<>(); + private DataBroker dataBroker; + private long counter = 0; + private ReadOnlyTransaction tx; + private HoneycombConfiguration instance; + + @Setup(Level.Iteration) + public void setup() { + LOG.info("Setting up"); + dataProvider = DataProvider.from(data); + Injector injector = Guice.createInjector(modules); + final HoneycombConfiguration cfg = injector.getInstance(HoneycombConfiguration.class); + LOG.info("Configuration for Honeycomb: {}", cfg); + dataBroker = injector.getInstance(Key.get(DataBroker.class, + Names.named(ConfigAndOperationalPipelineModule.HONEYCOMB_CONFIG))); + tx = dataBroker.newReadOnlyTransaction(); + } + + @TearDown(Level.Iteration) + public void tearDown() { + LOG.info("Tearing down after {} executions", counter); + counter = 0; + LOG.info("Reader invocations: {}", noopReaders); + noopReaders.clear(); + + tx.close(); + tx = null; + dataBroker = null; + deleteFile(Paths.get(instance.peristConfigPath)); + deleteFile(Paths.get(instance.peristContextPath)); + } + + @Benchmark + public void read() { + try { + tx.read(dsType, dataProvider.getId(counter++)).get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException("Read failed", e); + } + } + + /** + * Inject custom modules e.g. configuration. + */ + @Override + protected void configure() { + try { + instance = getHoneycombConfiguration(persistence); + bind(HoneycombConfiguration.class).toInstance(instance); + } catch (IOException e) { + throw new RuntimeException("Unable to prepare configuration", e); + } + + final Multibinder writeBinder = Multibinder.newSetBinder(binder(), ReaderFactory.class); + writeBinder.addBinding().toInstance(registry -> { + + switch (data) { + case DataProvider.SIMPLE_CONTAINER: { + addReader(registry, new StaticReader<>( + InstanceIdentifier.create(SimpleContainer.class), + DataProvider.from(DataProvider.SIMPLE_CONTAINER))); + break; + } + case DataProvider.LIST_IN_CONTAINER: { + registry.addStructuralReader( + InstanceIdentifier.create(ContainerWithList.class), ContainerWithListBuilder.class); + addReader(registry, new StaticReader<>( + InstanceIdentifier.create(ContainerWithList.class).child(ListInContainer.class), + DataProvider.from(DataProvider.LIST_IN_CONTAINER))); + break; + } + case DataProvider.COMPLEX_LIST_IN_CONTAINER: { + registry.addStructuralReader( + InstanceIdentifier.create(ContainerWithList.class), ContainerWithListBuilder.class); + addReader(registry, new StaticReader<>( + InstanceIdentifier.create(ContainerWithList.class).child(ListInContainer.class), + DataProvider.from(DataProvider.COMPLEX_LIST_IN_CONTAINER))); + break; + } + } + }); + } + + private void addReader(final ModifiableReaderRegistryBuilder registry, final Reader handler) { + noopReaders.add(handler); + registry.add(handler); + } + + private static HoneycombConfiguration getHoneycombConfiguration(final boolean persistence) throws IOException { + final HoneycombConfiguration instance = new HoneycombConfiguration(); + instance.persistConfig = Optional.of(Boolean.toString(persistence)); + instance.persistContext = Optional.of(Boolean.toString(persistence)); + instance.peristConfigPath = FileManager.INSTANCE.createTempFile("config").toString(); + instance.peristContextPath = FileManager.INSTANCE.createTempFile("context").toString(); + return instance; + } +} diff --git a/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/util/DataProvider.java b/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/util/DataProvider.java new file mode 100644 index 000000000..7d3df2691 --- /dev/null +++ b/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/util/DataProvider.java @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.benchmark.util; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.collect.Lists; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleContainer; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleContainerBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainer; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainerBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainerKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.ContainerInListBuilder; +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; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.container.in.list.NestedListBuilder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public interface DataProvider { + + String SIMPLE_CONTAINER = "simple-container"; + String LIST_IN_CONTAINER = "list-in-container"; + String COMPLEX_LIST_IN_CONTAINER = "complex-list-in-container"; + + InstanceIdentifier getId(final long counter); + + DataObject getData(final long counter); + + static DataProvider from(String data) { + return new MapBackedDataProvider(data); + } + + final class MapBackedDataProvider implements DataProvider { + + private static final Map map; + + static { + map = new HashMap<>(); + + final InstanceIdentifier simpleContainerId = InstanceIdentifier.create(SimpleContainer.class); + map.put(SIMPLE_CONTAINER, new MultiValueDataProvider(Lists.newArrayList( + // Multiple values of container to ensure each time the value in DS after commit changes to trigger + // writers in test + new SingleValueDataProvider<>( + new SimpleContainerBuilder().setSimpleContainerName("first").build(), simpleContainerId), + new SingleValueDataProvider<>( + new SimpleContainerBuilder().setSimpleContainerName("second").build(), simpleContainerId), + new SingleValueDataProvider<>( + new SimpleContainerBuilder().setSimpleContainerName("third").build(), simpleContainerId)) + )); + map.put(LIST_IN_CONTAINER, new MultiValueDataProvider(getListInContainerValues(100_000))); + map.put(COMPLEX_LIST_IN_CONTAINER, + new MultiValueDataProvider(getComplexListInContainerValues(100_000))); + } + + private final DataProvider delegate; + + MapBackedDataProvider(final String data) { + checkArgument(map.containsKey(data)); + this.delegate = map.get(data); + } + + @Override + public InstanceIdentifier getId(final long counter) { + return delegate.getId(counter); + } + + @Override + public DataObject getData(final long counter) { + return delegate.getData(counter); + } + + @Override + public String toString() { + return "MapBackedDataProvider{" + + "delegate=" + delegate + + '}'; + } + } + + + static List getListInContainerValues(final int i) { + return IntStream.range(0, i) + .mapToObj(idx -> new SingleValueDataProvider<>( + new ListInContainerBuilder() + .setId((long) idx) + .build(), + InstanceIdentifier.create(ContainerWithList.class) + .child(ListInContainer.class, new ListInContainerKey((long) idx)) + )) + .collect(Collectors.toList()); + } + + static List getComplexListInContainerValues(final int i) { + return IntStream.range(0, i) + .mapToObj(idx -> new SingleValueDataProvider<>( + new ListInContainerBuilder() + .setId((long) idx) + .setContainerInList(new ContainerInListBuilder() + .setName("nested container") + .setNestedList(Lists.newArrayList( + getNestedList("1"), + getNestedList("2"), + getNestedList("3"))) + .build()) + .build(), + InstanceIdentifier.create(ContainerWithList.class) + .child(ListInContainer.class, new ListInContainerKey((long) idx)) + )) + .collect(Collectors.toList()); + } + + static NestedList getNestedList(final String value) { + return new NestedListBuilder() + .setNestedId(value) + .setNestedName(value + "N") + .build(); + } + + final class SingleValueDataProvider implements DataProvider { + + private final DataObject data; + private final InstanceIdentifier id; + + SingleValueDataProvider(final T data, final InstanceIdentifier id) { + this.data = data; + this.id = id; + } + + @Override + public InstanceIdentifier getId(final long counter) { + return id; + } + + @Override + public DataObject getData(final long counter) { + return data; + } + + @Override + public String toString() { + return "SingleValueDataProvider{" + + "data=" + data + + ", id=" + id + + '}'; + } + } + + final class MultiValueDataProvider implements DataProvider { + + private final List values; + private int valueSize; + + public MultiValueDataProvider(final List values) { + // Wrap as array list so that index lookup is fast + this.values = Lists.newArrayList(values); + this.valueSize = values.size(); + } + + @Override + public InstanceIdentifier getId(final long counter) { + return values.get((int) (counter % valueSize)).getId(counter); + } + + @Override + public DataObject getData(final long counter) { + return values.get((int) (counter % valueSize)).getData(counter); + } + + @Override + public String toString() { + return "MultiValueDataProvider{" + + "valueSize=" + valueSize + + '}'; + } + } +} diff --git a/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/util/DataSubmitter.java b/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/util/DataSubmitter.java new file mode 100644 index 000000000..ebad65335 --- /dev/null +++ b/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/util/DataSubmitter.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.benchmark.util; + +import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +@FunctionalInterface +public interface DataSubmitter { + + void submit(LogicalDatastoreType type, WriteTransaction tx, InstanceIdentifier id, DataObject data); + + public static DataSubmitter from(String operation) { + switch (operation) { + case "put": { + return (type, tx, id, data) -> tx.put(type, (InstanceIdentifier) id, data); + } + case "merge": { + return (type, tx, id, data) -> tx.merge(type, (InstanceIdentifier) id, data); + } + default: throw new UnsupportedOperationException("Operation: " + operation); + } + } +} diff --git a/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/util/FileManager.java b/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/util/FileManager.java new file mode 100644 index 000000000..8aa9f9370 --- /dev/null +++ b/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/util/FileManager.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.benchmark.util; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +public interface FileManager { + + FileManager INSTANCE = new FileManager() {}; + + default Path createTempFile(String config) throws IOException { + return Files.createTempFile("hcbenchmark", config); + } + + default void deleteFile(final Path toDelete) { + if (toDelete != null) { + try { + Files.delete(toDelete); + } catch (IOException e) { + // NOOP, dont care about temp files too much + } + } + } + +} diff --git a/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/util/NoopWriter.java b/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/util/NoopWriter.java new file mode 100644 index 000000000..f61bd1b5d --- /dev/null +++ b/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/util/NoopWriter.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.benchmark.util; + +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.honeycomb.translate.write.Writer; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.concurrent.NotThreadSafe; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Noop writer suitable for lists as well. + */ +@NotThreadSafe +public final class NoopWriter implements Writer { + + private final InstanceIdentifier id; + private long counter = 0; + + public NoopWriter(final InstanceIdentifier id) { + this.id = id; + } + + @Override + public void update(@Nonnull final InstanceIdentifier id, + @Nullable final DataObject dataBefore, + @Nullable final DataObject dataAfter, + @Nonnull final WriteContext ctx) throws WriteFailedException { + counter++; + // NOOP + } + + @Nonnull + @Override + public InstanceIdentifier getManagedDataObjectType() { + return id; + } + + @Override + public String toString() { + return "NoopWriter{" + + id.getTargetType().getSimpleName() + + ", counter=" + counter + + '}'; + } +} diff --git a/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/util/StaticReader.java b/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/util/StaticReader.java new file mode 100644 index 000000000..90e560152 --- /dev/null +++ b/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/util/StaticReader.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.benchmark.util; + +import com.google.common.base.Optional; +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.read.Reader; +import javax.annotation.Nonnull; +import javax.annotation.concurrent.NotThreadSafe; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Statically preconfigured reader. + */ +@NotThreadSafe +public final class StaticReader> implements Reader { + + private final InstanceIdentifier id; + private final DataProvider data; + private long counter = 0; + + public StaticReader(final InstanceIdentifier id, final DataProvider data) { + this.id = id; + this.data = data; + } + + @Nonnull + @Override + public InstanceIdentifier getManagedDataObjectType() { + return id; + } + + @Override + public String toString() { + return "NoopReader{" + + id.getTargetType().getSimpleName() + + ", counter=" + counter + + '}'; + } + + @Nonnull + @Override + public Optional read(@Nonnull final InstanceIdentifier id, + @Nonnull final ReadContext ctx) throws ReadFailedException { + counter++; + return Optional.of(data.getData(counter)); + } + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier id, @Nonnull final B builder, + @Nonnull final ReadContext ctx) + throws ReadFailedException { + throw new UnsupportedOperationException("No read current attrs!"); + } + + @Nonnull + @Override + public B getBuilder(final InstanceIdentifier id) { + throw new UnsupportedOperationException("No builder!"); + } + + @Override + public void merge(@Nonnull final Builder parentBuilder, @Nonnull final T readValue) { + throw new UnsupportedOperationException("No merge!"); + } +} diff --git a/infra/it/benchmark/src/main/resources/logback.xml b/infra/it/benchmark/src/main/resources/logback.xml new file mode 100644 index 000000000..755300321 --- /dev/null +++ b/infra/it/benchmark/src/main/resources/logback.xml @@ -0,0 +1,26 @@ + + + + ${project.build.directory}/logs/honeycomb.log + + + ${project.build.directory}/logs/honeycomb.%d.log.zip + 1 + + + + 1MB + + + + %date{"yyyy-MM-dd HH:mm:ss.SSS z"} [%thread] %-5level %logger{35} - %msg%n + + + + + + + + + + diff --git a/infra/it/pom.xml b/infra/it/pom.xml index 5e1173413..3846955bc 100644 --- a/infra/it/pom.xml +++ b/infra/it/pom.xml @@ -33,6 +33,18 @@ test-model it-test + + + + + + benchmark + + benchmark + + + + -- cgit 1.2.3-korg