Bug 499: Initial draft of in-memory datastore and data broker
[controller.git] / opendaylight / md-sal / sal-dom-broker / src / test / java / org / opendaylight / controller / md / sal / dom / broker / impl / DOMBrokerPerformanceTest.java
1 package org.opendaylight.controller.md.sal.dom.broker.impl;
2
3 import static org.junit.Assert.assertEquals;
4 import static org.junit.Assert.assertTrue;
5 import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
6 import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
7
8 import java.util.ArrayList;
9 import java.util.List;
10 import java.util.concurrent.Callable;
11 import java.util.concurrent.Executors;
12
13 import org.junit.Before;
14 import org.junit.Test;
15 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
16 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
17 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
18 import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
19 import org.opendaylight.controller.md.sal.dom.store.impl.TestModel;
20 import org.opendaylight.controller.sal.core.spi.data.DOMStore;
21 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
22 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
23 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
24 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
27
28 import com.google.common.base.Optional;
29 import com.google.common.collect.ImmutableMap;
30 import com.google.common.util.concurrent.Futures;
31 import com.google.common.util.concurrent.ListenableFuture;
32 import com.google.common.util.concurrent.ListeningExecutorService;
33 import com.google.common.util.concurrent.MoreExecutors;
34
35 public class DOMBrokerPerformanceTest {
36
37     private static final Logger log = LoggerFactory.getLogger(DOMBrokerPerformanceTest.class);
38
39     private static NormalizedNode<?, ?> outerList(final int i) {
40         return ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, i);
41     }
42
43     private static InstanceIdentifier outerListPath(final int i) {
44         return InstanceIdentifier.builder(TestModel.OUTER_LIST_PATH)//
45                 .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, i) //
46                 .build();
47     }
48
49     private SchemaContext schemaContext;
50     private DOMDataBrokerImpl domBroker;
51
52     private static <V> V measure(final String name, final Callable<V> callable) throws Exception {
53         // TODO Auto-generated method stub
54         log.debug("Measurement:{} Start", name);
55         long startNano = System.nanoTime();
56         try {
57             return callable.call();
58         } finally {
59             long endNano = System.nanoTime();
60             log.info("Measurement:\"{}\" Time:{} ms", name, (endNano - startNano) / 1000000.0d);
61         }
62     }
63
64     @Before
65     public void setupStore() {
66         InMemoryDOMDataStore operStore = new InMemoryDOMDataStore("OPER", MoreExecutors.sameThreadExecutor());
67         InMemoryDOMDataStore configStore = new InMemoryDOMDataStore("CFG", MoreExecutors.sameThreadExecutor());
68         schemaContext = TestModel.createTestContext();
69
70         operStore.onGlobalContextUpdated(schemaContext);
71         configStore.onGlobalContextUpdated(schemaContext);
72
73         ImmutableMap<LogicalDatastoreType, DOMStore> stores = ImmutableMap.<LogicalDatastoreType, DOMStore> builder() //
74                 .put(CONFIGURATION, configStore) //
75                 .put(OPERATIONAL, operStore) //
76                 .build();
77         ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());
78         domBroker = new DOMDataBrokerImpl(stores, executor);
79     }
80
81     @Test
82     public void testPerformance() throws Exception {
83         measure("Test Suite (all tests)", new Callable<Void>() {
84
85             @Override
86             public Void call() throws Exception {
87                 smallTestSuite(10, 1000);
88                 //smallTestSuite(10, 100);
89                 smallTestSuite(100, 100);
90                 //smallTestSuite(100, 100);
91                 //smallTestSuite(1000, 10);
92                 smallTestSuite(1000, 10);
93                 //smallTestSuite(1000, 1000);
94                 return null;
95             }
96         });
97     }
98
99     private void smallTestSuite(final int txNum, final int innerListWriteNum) throws Exception {
100         measure("TestSuite (Txs:" + txNum + " innerWrites:" + innerListWriteNum + ")", new Callable<Void>() {
101
102             @Override
103             public Void call() throws Exception {
104                 measureOneTransactionTopContainer();
105                 measureSeparateWritesOneLevel(txNum, innerListWriteNum);
106                 return null;
107             }
108         });
109     }
110
111     private void measureSeparateWritesOneLevel(final int txNum, final int innerNum) throws Exception {
112         final List<DOMDataReadWriteTransaction> transactions = measure("Txs:"+ txNum + " Allocate",
113                 new Callable<List<DOMDataReadWriteTransaction>>() {
114                     @Override
115                     public List<DOMDataReadWriteTransaction> call() throws Exception {
116                         List<DOMDataReadWriteTransaction> builder = new ArrayList<>(txNum);
117                         for (int i = 0; i < txNum; i++) {
118                             DOMDataReadWriteTransaction writeTx = domBroker.newReadWriteTransaction();
119                             builder.add(writeTx);
120                         }
121                         return builder;
122                     }
123                 });
124         assertEquals(txNum, transactions.size());
125         measure("Txs:"+ txNum + " Writes:1", new Callable<Void>() {
126             @Override
127             public Void call() throws Exception {
128                 int i = 0;
129                 for (DOMDataReadWriteTransaction writeTx :transactions) {
130                     // Writes /test/outer-list/i in writeTx
131                     writeTx.put(OPERATIONAL, outerListPath(i), outerList(i));
132                     i++;
133                 }
134                 return null;
135             }
136         });
137
138         measure("Txs:"+ txNum +  " Writes:" + innerNum, new Callable<Void>() {
139             @Override
140             public Void call() throws Exception {
141                 int i = 0;
142                 for (DOMDataReadWriteTransaction writeTx :transactions) {
143                     // Writes /test/outer-list/i in writeTx
144                     InstanceIdentifier path = InstanceIdentifier.builder(outerListPath(i))
145                             .node(TestModel.INNER_LIST_QNAME).build();
146                     writeTx.put(OPERATIONAL, path, ImmutableNodes.mapNodeBuilder(TestModel.INNER_LIST_QNAME).build());
147                     for (int j = 0; j < innerNum; j++) {
148                         InstanceIdentifier innerPath = InstanceIdentifier.builder(path)
149                                 .nodeWithKey(TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME, String.valueOf(j))
150                                 .build();
151                         writeTx.put(
152                                 OPERATIONAL,
153                                 innerPath,
154                                 ImmutableNodes.mapEntry(TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME,
155                                         String.valueOf(j)));
156                     }
157                     i++;
158                 }
159                 return null;
160             }
161         });
162
163         measure("Txs:" + txNum + " Submit, Finish", new Callable<Void>() {
164             @Override
165             public Void call() throws Exception {
166                 List<ListenableFuture<?>> allFutures = measure(txNum + " Submits",
167                         new Callable<List<ListenableFuture<?>>>() {
168                             @Override
169                             public List<ListenableFuture<?>> call() throws Exception {
170                                 List<ListenableFuture<?>> builder = new ArrayList<>(txNum);
171                                 for (DOMDataReadWriteTransaction tx :transactions) {
172                                     builder.add(tx.commit());
173                                 }
174                                 return builder;
175                             }
176                         });
177                 Futures.allAsList(allFutures).get();
178                 return null;
179             }
180         });
181
182         final DOMDataReadTransaction readTx = measure("Txs:1 (ro), Allocate", new Callable<DOMDataReadTransaction>() {
183             @Override
184             public DOMDataReadTransaction call() throws Exception {
185                 return domBroker.newReadOnlyTransaction();
186
187             }
188         });
189
190
191         measure("Txs:1 (ro) Reads:" + txNum + " (1-level)" , new Callable<Void>() {
192             @Override
193             public Void call() throws Exception {
194                 for (int i = 0; i < txNum; i++) {
195                     ListenableFuture<Optional<NormalizedNode<?, ?>>> potential = readTx.read(OPERATIONAL,
196                             outerListPath(i));
197                     assertTrue("outerList/" + i, potential.get().isPresent());
198                 }
199                 return null;
200             }
201         });
202
203         measure("Txs:1 (ro) Reads:" + txNum * innerNum + " (2-level)", new Callable<Void>() {
204             @Override
205             public Void call() throws Exception {
206                 for (int i = 0; i < txNum; i++) {
207                     for (int j = 0; j < innerNum; j++) {
208                         InstanceIdentifier path = InstanceIdentifier
209                                 .builder(outerListPath(i))
210                                 //
211                                 .node(TestModel.INNER_LIST_QNAME)
212                                 .nodeWithKey(TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME, String.valueOf(j))
213                                 .build();
214                         ListenableFuture<Optional<NormalizedNode<?, ?>>> potential = readTx.read(OPERATIONAL, path);
215                         assertTrue("outer-list/" + i + "/inner-list/" + j, potential.get().isPresent());
216                     }
217                 }
218                 return null;
219             }
220         });
221     }
222
223     private void measureOneTransactionTopContainer() throws Exception {
224
225         final DOMDataReadWriteTransaction writeTx = measure("Txs:1 Allocate", new Callable<DOMDataReadWriteTransaction>() {
226             @Override
227             public DOMDataReadWriteTransaction call() throws Exception {
228                 return domBroker.newReadWriteTransaction();
229             }
230         });
231
232         measure("Txs:1 Write", new Callable<Void>() {
233             @Override
234             public Void call() throws Exception {
235                 writeTx.put(OPERATIONAL, TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
236                 writeTx.put(OPERATIONAL, TestModel.OUTER_LIST_PATH,
237                         ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME).build());
238                 return null;
239             }
240         });
241
242         measure("Txs:1 Reads:1", new Callable<Void>() {
243             @Override
244             public Void call() throws Exception {
245                 // Reads /test in writeTx
246                 ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = writeTx.read(OPERATIONAL,
247                         TestModel.TEST_PATH);
248                 assertTrue(writeTxContainer.get().isPresent());
249                 return null;
250             }
251         });
252
253         measure("Txs:1 Reads:1", new Callable<Void>() {
254             @Override
255             public Void call() throws Exception {
256                 // Reads /test in writeTx
257                 ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = writeTx.read(OPERATIONAL,
258                         TestModel.TEST_PATH);
259                 assertTrue(writeTxContainer.get().isPresent());
260                 return null;
261             }
262         });
263
264         measure("Txs:1 Submit, Finish", new Callable<Void>() {
265             @Override
266             public Void call() throws Exception {
267                 measure("Txs:1 Submit", new Callable<ListenableFuture<?>>() {
268                     @Override
269                     public ListenableFuture<?> call() throws Exception {
270                         return writeTx.commit();
271                     }
272                 }).get();
273                 return null;
274             }
275         });
276     }
277 }