diff --git a/docs/modules/ROOT/images/sql_and_scan_query_perf_comp.png b/docs/modules/ROOT/images/sql_and_scan_query_perf_comp.png new file mode 100644 index 000000000..cbec8d34d Binary files /dev/null and b/docs/modules/ROOT/images/sql_and_scan_query_perf_comp.png differ diff --git a/docs/modules/ROOT/pages/performance/sql_and_scan_query_perf_comp.csv b/docs/modules/ROOT/pages/performance/sql_and_scan_query_perf_comp.csv new file mode 100644 index 000000000..de57d57fc --- /dev/null +++ b/docs/modules/ROOT/pages/performance/sql_and_scan_query_perf_comp.csv @@ -0,0 +1,7 @@ +OPERATION TYPE MIN MAX AVG MEDIAN MODE P99 +SQL_QUERY_EXEC 1.0000 16.0000 1.8739 1.0000 1.0000 1.0000 +SCAN_QUERY_EXEC 530182.0000 785735.0000 561550.7678 552705.0000 543704.0000 531308.3565 +SQL_CURSOR_FETCH 0.0000 1.0000 0.6717 1.0000 1.0000 0.0000 +SCAN_CURSOR_FETCH 0.0000 8.0000 0.1411 0.0000 0.0000 0.0000 +SQL_INSERT 0.0000 929676278.00 929.676278 0.0 0.0 0.0 +SCAN_INSERT 0.0000 812451365.00 812.451365 0.0 0.0 0.0 diff --git a/docs/modules/ROOT/pages/performance/sql_and_scan_query_pref_comp.adoc b/docs/modules/ROOT/pages/performance/sql_and_scan_query_pref_comp.adoc new file mode 100644 index 000000000..eabce1bcb --- /dev/null +++ b/docs/modules/ROOT/pages/performance/sql_and_scan_query_pref_comp.adoc @@ -0,0 +1,51 @@ += SQL Fields Query and Scan Query Performance Comparision +Prasad Kommoju +2021-11-30 +:toc: right +:imagesdir: ../../images + + +== Introduction +In Alcor there are some cases where queries are made on the cases using a field other than the key. Since Ignite is the KV database, these types of queries will require full scan (linear search) of the cache in question and this introduces big latencies as the number of entries in the cache grows. + +Ignite supports secondary keys, called QuerySqlFields which will use a tradional B+Tree indexes which can answer point lookup and also range, minumum, maximum, greater than, and less than queries mush faster. + +This report is about comparing the performance of the queries on non-key fields using Ignite's SCAN query and through SQL queries. + +== Test setup +Operations are on NodeInfo object. Benchmark tool, DPM, NCM and NMM run on the same physical machine in the lab. Ignite is run on two different physical machines in the lab (partitioned). +Number of entries in both SQL and Scan are 1M, 1000 entries are queried on. + +The first query always takes way too much time compared to the subsequent +ones and hence ignored when computing the statistics. + +All times are in micro seconds. Time axis (y) in the histogram is in logscale. + +SQL_QUERY_EXEC time represents time required to execute the SQLFieldsQuery +(cache.query() API). + +SQL_CURSOR_FETCH time represents time required to extract the result from +result set (cursor, cursor.get() API). + +SCAN_QUERY_EXEC time represents time required to execute (QueryCursor +instantiation, cache.withKeepBinary().query(...) + +SCAN_CURSOR_FETCH time represents time required to extract the result from +result set (cursor, cursor.getAll() API). + +For Insert operation, MAX represents total time to insert 1M entires into the cache and AVG represents the average time to insert one single entry. All other statistcs for Insert are not relevant and are shown as zero. + +== Results +|=== +|OPERATION TYPE| MIN| MAX| AVG| MEDIAN| MODE| P99 +|SQL_QUERY_EXEC| 1.0000| 16.0000| 1.8739| 1.0000| 1.0000| 1.0000 +|SCAN_QUERY_EXEC| 530182.0000| 785735.0000| 561550.7678| 552705.0000| 543704.0000| 531308.3565 +|SQL_CURSOR_FETCH| 0.0000| 1.0000| 0.6717| 1.0000| 1.0000| 0.0000 +|SCAN_CURSOR_FETCH| 0.0000 | 8.0000 | 0.1411| 0.0000| 0.0000| 0.0000 +|SQL_INSERT| 0.0000 | 929676278.00| 929.6762| 0.0| 0.0| 0.0 +|SCAN_INSERT| 0.0000| 812451365.00| 812.4514| 0.0| 0.0| 0.0 +|=== + +== Plot of the results +image::sql_and_scan_query_perf_comp.png[] + diff --git a/lib/pom.xml b/lib/pom.xml index 6ea9d0e1d..5467210c5 100644 --- a/lib/pom.xml +++ b/lib/pom.xml @@ -143,6 +143,12 @@ Copyright(c) 2020 Futurewei Cloud 2.1.1 compile + + com.google.guava + guava + 29.0-jre + compile + diff --git a/lib/src/main/java/com/futurewei/alcor/common/db/ignite/IgniteClientCacheFactory.java b/lib/src/main/java/com/futurewei/alcor/common/db/ignite/IgniteClientCacheFactory.java index 799def6a4..d41c09ef5 100644 --- a/lib/src/main/java/com/futurewei/alcor/common/db/ignite/IgniteClientCacheFactory.java +++ b/lib/src/main/java/com/futurewei/alcor/common/db/ignite/IgniteClientCacheFactory.java @@ -47,23 +47,23 @@ public IgniteClientCacheFactory(IgniteClient igniteClient, int interval, int exp @Override public ICache getCache(Class v) { - return new IgniteClientDbCache<>(igniteClient, v.getName()); + return new IgniteClientDbCache(igniteClient, v, v.getName()); } @Override public ICache getCache(Class v, String cacheName) { - return new IgniteClientDbCache<>(igniteClient, cacheName); + return new IgniteClientDbCache(igniteClient, v, cacheName); } @Override public ICache getCache(Class v, CacheConfiguration cacheConfig) { - return new IgniteClientDbCache<>(igniteClient, cacheConfig); + return new IgniteClientDbCache<>(igniteClient, v, cacheConfig); } @Override public ICache getExpireCache(Class v, long timeout, TimeUnit timeUnit) { ExpiryPolicy ep = CreatedExpiryPolicy.factoryOf(new Duration(timeUnit, timeout)).create(); - return new IgniteClientDbCache<>(igniteClient, v.getName(), ep); + return new IgniteClientDbCache<>(igniteClient, v, v.getName(), ep); } @Override @@ -75,4 +75,4 @@ public IDistributedLock getDistributedLock(Class t) { public Transaction getTransaction() { return new IgniteClientTransaction(igniteClient); } -} +} \ No newline at end of file diff --git a/lib/src/main/java/com/futurewei/alcor/common/db/ignite/IgniteClientDbCache.java b/lib/src/main/java/com/futurewei/alcor/common/db/ignite/IgniteClientDbCache.java index ac0b95508..2519a591b 100644 --- a/lib/src/main/java/com/futurewei/alcor/common/db/ignite/IgniteClientDbCache.java +++ b/lib/src/main/java/com/futurewei/alcor/common/db/ignite/IgniteClientDbCache.java @@ -22,11 +22,18 @@ free of charge, to any person obtaining a copy of this software and associated d import com.futurewei.alcor.common.db.ignite.query.ScanQueryBuilder; import com.futurewei.alcor.common.logging.Logger; import com.futurewei.alcor.common.logging.LoggerFactory; +import com.futurewei.alcor.common.stats.DurationStatistics; +import com.futurewei.alcor.common.utils.CommonUtil; +import com.futurewei.alcor.common.utils.ControllerUtil; import org.apache.ignite.binary.BinaryObject; import org.apache.ignite.cache.CachePeekMode; +import org.apache.ignite.cache.QueryEntity; +import org.apache.ignite.cache.QueryIndex; import org.apache.ignite.cache.query.Query; import org.apache.ignite.cache.query.QueryCursor; import org.apache.ignite.cache.query.ScanQuery; +import org.apache.ignite.cache.query.SqlFieldsQuery; +import org.apache.ignite.cache.query.annotations.QuerySqlField; import org.apache.ignite.client.ClientCache; import org.apache.ignite.client.ClientCacheConfiguration; import org.apache.ignite.client.ClientException; @@ -37,66 +44,115 @@ free of charge, to any person obtaining a copy of this software and associated d import javax.cache.Cache; import javax.cache.expiry.ExpiryPolicy; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.lang.reflect.Field; +import java.sql.Array; +import java.util.*; import java.util.logging.Level; import java.util.stream.Collectors; +import static org.apache.ignite.internal.util.lang.GridFunc.asList; + public class IgniteClientDbCache implements IgniteICache { private static final Logger logger = LoggerFactory.getLogger(); - + private static final String NON_SCALAR_ROWSET = "too many rows found!"; private static final int RESULT_THRESHOLD_SIZE = 100000; + private static final String SQL_SCHEMA_NAME = "alcor"; + private static final int SQL_INDEX_MAX_INLINE_SIZE = 36; // UUID length private ClientCache cache; private final IgniteClientTransaction transaction; + private static class SqlField { + public String type; + public boolean isIndexed; + } + private LinkedHashMap sqlFields = null; // needed for index creation and querying + private boolean checkedForSqlFields = false; - public IgniteClientDbCache(IgniteClient igniteClient, String name) { + public IgniteClientDbCache(IgniteClient igniteClient, Class v, String name) { + String className = v.getName(); try { - this.cache = igniteClient.getOrCreateCache(name); - logger.log(Level.INFO, "Cache " + name + " AtomicityMode is " + this.cache.getConfiguration().getAtomicityMode()); + if (!checkedForSqlFields) { + checkedForSqlFields = true; + extractSqlFields(className); + if (sqlFields != null && sqlFields.size() != 0) { + ClientCacheConfiguration clientCacheConfig = new ClientCacheConfiguration(); + clientCacheConfig.setName(CommonUtil.getSqlNameFromCacheName(name)); + this.cache = getOrCreateIndexedCache(igniteClient, className, clientCacheConfig, null); + if (this.cache == null) { + logger.log(Level.WARNING, "Create cache for client " + className + " with index failed, falling back"); + } + } + } + if (this.cache == null) + this.cache = igniteClient.getOrCreateCache(className); + } catch (ClientException e) { - logger.log(Level.WARNING, "Create cache for client " + name + " failed:" + e.getMessage()); + logger.log(Level.WARNING, "Create cache for client " + className + " failed: " + e.getMessage()); } - Assert.notNull(this.cache, "Create cache for client " + name + "failed"); + Assert.notNull(this.cache, "Create cache for client " + className + " failed"); + logger.log(Level.INFO, "Cache " + className + " AtomicityMode is " + this.cache.getConfiguration().getAtomicityMode()); this.transaction = new IgniteClientTransaction(igniteClient); } - public IgniteClientDbCache(IgniteClient igniteClient, CacheConfiguration cacheConfig) { + public IgniteClientDbCache(IgniteClient igniteClient, Class v, CacheConfiguration cacheConfig) { try { + String className = v.getName(); ClientCacheConfiguration clientCacheConfig = new ClientCacheConfiguration(); - clientCacheConfig.setName(cacheConfig.getName()); + clientCacheConfig.setName(CommonUtil.getSqlNameFromCacheName(cacheConfig.getName())); clientCacheConfig.setAtomicityMode(cacheConfig.getAtomicityMode()); logger.log(Level.INFO, "Getting or creating cache " + clientCacheConfig.getName() + " AtomicityMode is " + clientCacheConfig.getAtomicityMode()); - this.cache = igniteClient.getOrCreateCache(clientCacheConfig); - logger.log(Level.INFO, "Retrieved cache " + this.cache.getConfiguration().getName() + " AtomicityMode is " + this.cache.getConfiguration().getAtomicityMode()); + if (!checkedForSqlFields) { + checkedForSqlFields = true; + extractSqlFields(className); + if (sqlFields != null && sqlFields.size() != 0) { + this.cache = getOrCreateIndexedCache(igniteClient, className, clientCacheConfig, null); + if (this.cache == null) { + logger.log(Level.WARNING, "Create cache for client " + className + " with index failed, falling back"); + } + } + } + if (this.cache == null) + this.cache = igniteClient.getOrCreateCache(clientCacheConfig); + } catch (ClientException e) { - logger.log(Level.WARNING, "Create cache for client " + cacheConfig.getName() + " failed:" + e.getMessage()); + logger.log(Level.WARNING, "Create cache for client " + cacheConfig.getName() + " failed: " + e.getMessage()); } - Assert.notNull(this.cache, "Create cache for client " + cacheConfig.getName() + "failed"); + Assert.notNull(this.cache, "Create cache for client " + cacheConfig.getName() + " failed"); + logger.log(Level.INFO, "Retrieved cache " + this.cache.getConfiguration().getName() + " AtomicityMode is " + this.cache.getConfiguration().getAtomicityMode()); this.transaction = new IgniteClientTransaction(igniteClient); } - public IgniteClientDbCache(IgniteClient igniteClient, String name, ExpiryPolicy ep) { + public IgniteClientDbCache(IgniteClient igniteClient, Class v, String name, ExpiryPolicy ep) { try { - this.cache = igniteClient.getOrCreateCache(name).withExpirePolicy(ep); - logger.log(Level.INFO, "Cache " + name + " AtomicityMode is " + this.cache.getConfiguration().getAtomicityMode()); + if (!checkedForSqlFields) { + checkedForSqlFields = true; + extractSqlFields(v.getName()); + if (sqlFields != null && sqlFields.size() != 0) { + ClientCacheConfiguration clientCacheConfig = new ClientCacheConfiguration(); + clientCacheConfig.setName(name); + getOrCreateIndexedCache(igniteClient, v.getName(), clientCacheConfig, ep); + } + } + if (this.cache == null) { + this.cache = igniteClient.getOrCreateCache(name).withExpirePolicy(ep); + logger.log(Level.INFO, "Cache " + name + " AtomicityMode is " + this.cache.getConfiguration().getAtomicityMode()); + } } catch (ClientException e) { - logger.log(Level.WARNING, "Create cache for client " + name + " failed:" + e.getMessage()); + logger.log(Level.WARNING, "Create cache for client " + name + " failed: " + e.getMessage()); } - Assert.notNull(this.cache, "Create cache for client " + name + "failed"); + Assert.notNull(this.cache, "Create cache for client " + name + " failed"); this.transaction = new IgniteClientTransaction(igniteClient); } + @Override public V get(K key) throws CacheException { try { return cache.get(key); } catch (ClientException e) { - logger.log(Level.WARNING, "IgniteCache get operation error:" + e.getMessage()); + logger.log(Level.WARNING, "IgniteCache get operation error: " + e.getMessage()); throw new CacheException(e.getMessage()); } } @@ -106,7 +162,7 @@ public void put(K key, V value) throws CacheException { try { cache.put(key, value); } catch (ClientException e) { - logger.log(Level.WARNING, "IgniteCache put operation error:" + e.getMessage()); + logger.log(Level.WARNING, "IgniteCache put operation error: " + e.getMessage()); throw new CacheException(e.getMessage()); } } @@ -121,7 +177,7 @@ public boolean containsKey(K key) throws CacheException { try { return cache.containsKey(key); } catch (ClientException e) { - logger.log(Level.WARNING, "IgniteCache containsKey operation error:" + e.getMessage()); + logger.log(Level.WARNING, "IgniteCache containsKey operation error: " + e.getMessage()); throw new CacheException(e.getMessage()); } } @@ -144,7 +200,7 @@ public void putAll(Map items) throws CacheException { try { cache.putAll(items); } catch (ClientException e) { - logger.log(Level.WARNING, "IgniteCache putAll operation error:" + e.getMessage()); + logger.log(Level.WARNING, "IgniteCache putAll operation error: " + e.getMessage()); throw new CacheException(e.getMessage()); } } @@ -154,13 +210,16 @@ public boolean remove(K key) throws CacheException { try { return cache.remove(key); } catch (ClientException e) { - logger.log(Level.WARNING, "IgniteCache remove operation error:" + e.getMessage()); + logger.log(Level.WARNING, "IgniteCache remove operation error: " + e.getMessage()); throw new CacheException(e.getMessage()); } } @Override public V get(Map filterParams) throws CacheException { + if (checkForSqlFieldsQuery(filterParams)) { + return getBySqlFields(filterParams); + } IgniteBiPredicate predicate = MapPredicate.getInstance(filterParams); return get(predicate); } @@ -171,7 +230,7 @@ public V get(IgniteBiPredicate igniteBiPredicate) throws CacheE cache.withKeepBinary().query(ScanQueryBuilder.newScanQuery(igniteBiPredicate)); List> result = cursor.getAll(); if(result.size() > 1){ - throw new CacheException("more than one rows found!"); + throw new CacheException(NON_SCALAR_ROWSET); } if(result.isEmpty()){ @@ -183,12 +242,15 @@ public V get(IgniteBiPredicate igniteBiPredicate) throws CacheE BinaryObject binaryObject = (BinaryObject)obj; return binaryObject.deserialize(); }else{ - throw new CacheException("no support for object type:" + obj.getClass().getName()); + throw new CacheException("no support for object type: " + obj.getClass().getName()); } } @Override public Map getAll(Map filterParams) throws CacheException { + if (checkForSqlFieldsQuery(filterParams)) { + return getBySqlFieldsAll(filterParams); + } IgniteBiPredicate predicate = MapPredicate.getInstance(filterParams); return getAll(predicate); } @@ -199,7 +261,7 @@ public Map getAll(IgniteBiPredicate igniteBiPredicate) th cache.withKeepBinary().query(ScanQueryBuilder.newScanQuery(igniteBiPredicate)); List> result = cursor.getAll(); if(result.size() >= RESULT_THRESHOLD_SIZE){ - throw new CacheException("too many rows found!"); + throw new CacheException(NON_SCALAR_ROWSET); } Map values = new HashMap<>(result.size()); for(Cache.Entry entry: result){ @@ -208,7 +270,7 @@ public Map getAll(IgniteBiPredicate igniteBiPredicate) th BinaryObject binaryObject = (BinaryObject)obj; values.put((K)entry.getKey(), binaryObject.deserialize()); }else{ - throw new CacheException("no support for object type:" + obj.getClass().getName()); + throw new CacheException("no support for object type: " + obj.getClass().getName()); } } return values; @@ -223,4 +285,183 @@ public long size() { public Transaction getTransaction() { return transaction; } + + private boolean checkForSqlFieldsQuery(Map filterParams) { + if (!checkedForSqlFields) + return false; + if (sqlFields == null || sqlFields.size() == 0 || filterParams.size() == 0) + return false; + + /* + * All filterParams must be found in sqlfields as "index". + */ + int idxCount = 0; + for (String f : filterParams.keySet()) { + if (sqlFields.containsKey(f)) + ++idxCount; + } + + return idxCount != 0 && idxCount == filterParams.size(); + } + + @DurationStatistics + private V getBySqlFields(Map filterParams) throws CacheException { + Map result = runSQLFieldsQuery(filterParams); + if (result == null || result.isEmpty()) + return null; + if(result.size() > 1) { + throw new CacheException(NON_SCALAR_ROWSET); + } + return result.get(0); + } + + @DurationStatistics + public Map getBySqlFieldsAll(Map filterParams) throws CacheException { + return runSQLFieldsQuery(filterParams); + } + + /* + * SELECT value_sqlfield + * FROM "ClassNameOfTheCache".classnameofthecache + * WHERE key_sqlfield = filterParam.value + */ + private Map runSQLFieldsQuery(Map filterParams) throws CacheException { + + String sql = buildSqlFieldsQuery(filterParams); + SqlFieldsQuery query = new SqlFieldsQuery(sql); + Map results = new HashMap<>(); + + try (QueryCursor> cursor = cache.query(query)) { + for (List row : cursor) { + results.put((K)row.get(0), (V)row.get(1)); + } + return results; + } + catch (ClientException e) { + logger.log(Level.WARNING, "SqlFieldsQuery error: " + e.getMessage()); + throw new CacheException(e.getMessage()); + } + } + + @DurationStatistics + private String buildSqlFieldsQuery(Map filterParams) { + StringBuilder sb = new StringBuilder("select _key, _val from " + SQL_SCHEMA_NAME + + "." + cache.getConfiguration().getQueryEntities()[0].getTableName() + " where "); + boolean needAnd = false; + for (String c : filterParams.keySet()) { + if (!sqlFields.get(c).isIndexed) + continue; + SqlField f = sqlFields.get(c); + if (needAnd) + sb.append(" and "); + needAnd = true; + + sb.append(c).append(" = "); + boolean needQuotes = f.type.equals("java.lang.String"); + sb.append("'"); + sb.append(filterParams.get(c)[0]); + if (needQuotes) + sb.append("'"); + } + + return sb.toString(); + } + + /** + * + * @param igniteClient + * @param className + * If the class has QuerySqlField annotations, add query entry fields and indexes. + */ + private ClientCache getOrCreateIndexedCache(IgniteClient igniteClient, String className, ClientCacheConfiguration cacheConfig, ExpiryPolicy ep) { + String cacheName = CommonUtil.getSqlNameFromCacheName(cacheConfig.getName()); + cacheConfig.setSqlIndexMaxInlineSize(SQL_INDEX_MAX_INLINE_SIZE); + + logger.log(Level.INFO, "Creating cache " + cacheName + " with index"); + + Class v; + try { + v = Class.forName(className); + } catch (Exception e) { + logger.log(Level.INFO, "Failed to get class for " + className); + return null; + } + + QueryEntity qryEnt = new QueryEntity().setTableName(cacheName); + qryEnt.setValueType(className); + + LinkedHashMap qryFields = new LinkedHashMap<>(); + ArrayList idxFields = new ArrayList<>(); + try { + for (Map.Entry e : sqlFields.entrySet()) { + String cname = e.getKey(); + SqlField f = e.getValue(); + qryFields.put(cname, f.type); + if (f.isIndexed) { + idxFields.add(new QueryIndex(cname)); + } + } + + qryEnt.setFields(qryFields); + qryEnt.setIndexes(idxFields); + } + catch (Exception e) { + logger.log(Level.WARNING, "Failed to create index on cache: " + cacheName + ": " + e.getMessage()); + return null; + } + + cacheConfig.setQueryEntities(qryEnt); + cacheConfig.setSqlSchema(SQL_SCHEMA_NAME); + + // also make not of this cache, somewhere, somehow? + // have a static cache? + ClientCache cache; + + if (ep != null) { + cache = igniteClient.getOrCreateCache(cacheConfig).withExpirePolicy(ep); + } + else { + cache = igniteClient.getOrCreateCache(cacheConfig); + } + + String result = cache == null ? "FAILED" : "WORKED"; + + logger.log(Level.INFO, "Creating index on " + cacheName + " " + result); + + return cache; + } + + + private void extractSqlFields(String className) { + LinkedHashMap localFields = new LinkedHashMap<>(); + logger.log(Level.INFO, "Checking for QuerySqlField annotations: " + className); + Field[] fields = null; + try { + Class v = Class.forName(className); + + fields = ControllerUtil.getAllDeclaredFields(v); + + for (Field f : fields) { + QuerySqlField annot = f.getAnnotation(QuerySqlField.class); + if (annot != null) { + if (annot.index()) { + SqlField sqlField = new SqlField(); + sqlField.isIndexed = true; + sqlField.type = f.getType().getTypeName(); + localFields.put(f.getName(), sqlField); + } else { + SqlField sqlField = new SqlField(); + sqlField.isIndexed = false; + sqlField.type = f.getType().getTypeName(); + localFields.put(f.getName(), sqlField); + } + } + } + + sqlFields = localFields; + logger.log(Level.INFO, "QuerySqlField Found " + sqlFields.size() + " sqlFields"); + } catch (Exception e) { + logger.log(Level.INFO, "Failed to get Class info for class " + className + ", or declared fields"); + } + } } \ No newline at end of file diff --git a/lib/src/main/java/com/futurewei/alcor/common/entity/CustomerResource.java b/lib/src/main/java/com/futurewei/alcor/common/entity/CustomerResource.java index 0c715c4b0..c72e698b8 100644 --- a/lib/src/main/java/com/futurewei/alcor/common/entity/CustomerResource.java +++ b/lib/src/main/java/com/futurewei/alcor/common/entity/CustomerResource.java @@ -17,6 +17,7 @@ free of charge, to any person obtaining a copy of this software and associated d package com.futurewei.alcor.common.entity; import com.fasterxml.jackson.annotation.JsonProperty; +import org.apache.ignite.cache.query.annotations.QuerySqlField; import java.util.Objects; @@ -110,4 +111,4 @@ public String toString() { ", description='" + description + '\'' + '}'; } -} +} \ No newline at end of file diff --git a/lib/src/main/java/com/futurewei/alcor/common/utils/CommonUtil.java b/lib/src/main/java/com/futurewei/alcor/common/utils/CommonUtil.java index f7278f0bc..115d2c2f7 100644 --- a/lib/src/main/java/com/futurewei/alcor/common/utils/CommonUtil.java +++ b/lib/src/main/java/com/futurewei/alcor/common/utils/CommonUtil.java @@ -103,7 +103,7 @@ public static boolean isNullOrEmpty(String string) { } /** - * Reture CacheConfiguration for transaction + * Return CacheConfiguration for transaction * @param cacheName input String * @return return cache configuration */ @@ -114,4 +114,35 @@ public static CacheConfiguration getCacheConfiguration(String cacheName) { cfg.setAtomicityMode(CacheAtomicityMode.TRANSACTIONAL); return cfg; } -} + + /** + * Return a simple name of the class or member given a canonical name as string. + * @param canon + * @return simpleName + */ + + public static String getSimpleFromCanonicalName(String canon) { + int lastDot = canon.lastIndexOf("."); + if (lastDot != -1) + return canon.substring(lastDot + 1); + else + return canon; + } + + /** + * Replace all dots with underscore to make a schema name + * @param className + * @return + */ + public static String getSqlNameFromCacheName(String className) { + return className.replaceAll("[\\.-]", "_"); + } + + /** + * @param name of a cache. Could contain dots and dashes. + * @return name with dots and dashes replaced with underscores. + */ + public static String getSqlNameFromGUID(String name) { + return name.replaceAll("\\.-", "_"); + } +} \ No newline at end of file diff --git a/scripts/busybox-ping-test/container_ops.py b/scripts/busybox-ping-test/container_ops.py index 89b07575a..62e0d78ef 100755 --- a/scripts/busybox-ping-test/container_ops.py +++ b/scripts/busybox-ping-test/container_ops.py @@ -94,6 +94,8 @@ def run_ping_test(target_machines, ip_addrs, container_names): expected_output = "2 packets transmitted, 2 packets received" if expected_output in str(output1) and expected_output in str(output2): print (colored("PING TEST SUCCESSFULL", 'green')) + return 0 else: print(colored('PING TEST FAILED', 'red')) + return 1 diff --git a/scripts/busybox-ping-test/helper_functions.py b/scripts/busybox-ping-test/helper_functions.py index a6c0df037..f3ad22047 100755 --- a/scripts/busybox-ping-test/helper_functions.py +++ b/scripts/busybox-ping-test/helper_functions.py @@ -167,6 +167,9 @@ def check_alcor_services(): if retcode > 0: print("Failed to execute command", repr(str(command))) print_output(res[1]) + elif "9001 9002 9003 9004 9005 9006 9007 9008 9009 9010 9011 9012 9014 9015" in str(res): + print("SUCCESS for: ", command, "\n") + return True elif "9001 9002 9003 9004 9005 9006 9007 9008 9009 9010 9011 9012 9015 9016" in str(res): print("SUCCESS for: ", command, "\n") return True diff --git a/scripts/busybox-ping-test/ping_test.py b/scripts/busybox-ping-test/ping_test.py index 3b18c88d7..5f58acc8d 100755 --- a/scripts/busybox-ping-test/ping_test.py +++ b/scripts/busybox-ping-test/ping_test.py @@ -209,9 +209,12 @@ def main(): print("Goal states: ", ip_mac_db) print("Container names: ", container_names) busybox_container_deploy(aca_node_ips, ip_mac_db, container_names) - run_ping_test(aca_node_ips, goal_state_ips, container_names) + status = run_ping_test(aca_node_ips, goal_state_ips, container_names) + if status != 0: + print("ERROR: Quitting test\n") + sys.exit(1) if __name__ == "__main__": main() - + sys.exit(0) diff --git a/scripts/sql_and_scan_query_perf_comp.gps b/scripts/sql_and_scan_query_perf_comp.gps new file mode 100644 index 000000000..e63b95324 --- /dev/null +++ b/scripts/sql_and_scan_query_perf_comp.gps @@ -0,0 +1,21 @@ +set terminal png truecolor enhanced size 700, 450 +set output 'sql_and_scan_query_perf_comp.png' +set style fill solid 1.00 border lt -1 +set key fixed right top vertical Right noreverse noenhanced autotitle nobox +set style histogram clustered gap 1 title textcolor lt -1 +set datafile missing '-' +# set datafile separator "|" +set grid +show grid +set style data histograms +set xtics norangelimit rotate by -45 noenhanced +# set xtics () +show xtics +set title "Plot of SqlFieldsQiery and Scan query performance\n\ +Input 1M entries, 1000 queries (excluding 1 outlier in SQL data)" +set xlabel "Operation" +set ylabel "Time in Micro seconds" +set logscale y +NO_ANIMATION=1 + +plot newhistogram "", 'sql_and_scan_query_perf_comp.csv' using 2:xtic(1) ti "min", '' u 3:xtic(1) ti "max", '' u 4:xtic(1) ti "avg", '' u 5:xtic(1) ti "median", '' u 6:xtic(1) ti "P95", '' u 7:xtic(1) ti "P99" diff --git a/services/network_config_manager/pom.xml b/services/network_config_manager/pom.xml index c48b34329..57f523b8b 100644 --- a/services/network_config_manager/pom.xml +++ b/services/network_config_manager/pom.xml @@ -132,6 +132,12 @@ Copyright(c) 2020 Futurewei Cloud 0.1.0-SNAPSHOT compile + + io.github.swagger2markup + swagger2markup + 1.2.0 + test + @@ -173,4 +179,4 @@ Copyright(c) 2020 Futurewei Cloud - + \ No newline at end of file diff --git a/services/network_config_manager/src/main/java/com/futurewei/alcor/netwconfigmanager/NetworkConfigManagerApplication.java b/services/network_config_manager/src/main/java/com/futurewei/alcor/netwconfigmanager/NetworkConfigManagerApplication.java index c135863ef..484bb090c 100644 --- a/services/network_config_manager/src/main/java/com/futurewei/alcor/netwconfigmanager/NetworkConfigManagerApplication.java +++ b/services/network_config_manager/src/main/java/com/futurewei/alcor/netwconfigmanager/NetworkConfigManagerApplication.java @@ -37,7 +37,6 @@ public class NetworkConfigManagerApplication { @PostConstruct public void instantiateGrpcServer(){ - try { networkConfigServer.start(); networkConfigServer.blockUntilShutdown(); diff --git a/services/node_manager/src/main/java/com/futurewei/alcor/nodemanager/dao/NodeRepository.java b/services/node_manager/src/main/java/com/futurewei/alcor/nodemanager/dao/NodeRepository.java index 9744dc5ae..bf2d7078b 100644 --- a/services/node_manager/src/main/java/com/futurewei/alcor/nodemanager/dao/NodeRepository.java +++ b/services/node_manager/src/main/java/com/futurewei/alcor/nodemanager/dao/NodeRepository.java @@ -21,7 +21,10 @@ free of charge, to any person obtaining a copy of this software and associated d import com.futurewei.alcor.common.db.Transaction; import com.futurewei.alcor.common.db.repo.ICacheRepository; import com.futurewei.alcor.common.stats.DurationStatistics; +import com.futurewei.alcor.common.utils.CommonUtil; import com.futurewei.alcor.web.entity.node.NodeInfo; +import org.apache.ignite.cache.CacheAtomicityMode; +import org.apache.ignite.configuration.CacheConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -40,7 +43,8 @@ public class NodeRepository implements ICacheRepository { @Autowired public NodeRepository(CacheFactory cacheFactory) { - cache = cacheFactory.getCache(NodeInfo.class, "nmm_nodeinfo_cache"); + CacheConfiguration cacheConfig = CommonUtil.getCacheConfiguration("nmm_nodeinfo_cache"); + cache = cacheFactory.getCache(NodeInfo.class, cacheConfig); } public ICache getCache() { diff --git a/services/node_manager/src/main/resources/application.properties b/services/node_manager/src/main/resources/application.properties index 4128b2a8b..90b89a21d 100644 --- a/services/node_manager/src/main/resources/application.properties +++ b/services/node_manager/src/main/resources/application.properties @@ -58,4 +58,4 @@ opentracing.jaeger.enabled=true opentracing.jaeger.log-spans=true opentracing.jaeger.enable-w3c-propagation=true opentracing.jaeger.enable-b3-propagation=true -opentracing.jaeger.service-name=alcor-node \ No newline at end of file +opentracing.jaeger.service-name=alcor-node diff --git a/services/port_manager/src/main/java/com/futurewei/alcor/portmanager/entity/PortIdSubnet.java b/services/port_manager/src/main/java/com/futurewei/alcor/portmanager/entity/PortIdSubnet.java index 99e1690b6..986ac7789 100644 --- a/services/port_manager/src/main/java/com/futurewei/alcor/portmanager/entity/PortIdSubnet.java +++ b/services/port_manager/src/main/java/com/futurewei/alcor/portmanager/entity/PortIdSubnet.java @@ -16,11 +16,13 @@ free of charge, to any person obtaining a copy of this software and associated d package com.futurewei.alcor.portmanager.entity; import com.fasterxml.jackson.annotation.JsonProperty; +import org.apache.ignite.cache.query.annotations.QuerySqlField; import java.util.Set; public class PortIdSubnet { @JsonProperty("subnet_id") + @QuerySqlField(index = true) private String subnetId; public PortIdSubnet() { @@ -38,4 +40,4 @@ public String getSubnetId() { public void setSubnetId(String subnetId) { this.subnetId = subnetId; } -} +} \ No newline at end of file diff --git a/services/scanquery_test_nodemanager/pom.xml b/services/scanquery_test_nodemanager/pom.xml new file mode 100644 index 000000000..061595497 --- /dev/null +++ b/services/scanquery_test_nodemanager/pom.xml @@ -0,0 +1,61 @@ + + + 4.0.0 + + com.futurewei.alcor + scanquery + 1.0-SNAPSHOT + + + 11 + 11 + + + + + com.futurewei.alcor + common + 0.1.0-SNAPSHOT + compile + + + com.futurewei.alcor + web + 0.1.0-SNAPSHOT + compile + + + org.apache.ignite + ignite-core + 2.10.0 + compile + + + org.apache.ignite + ignite-spring + 2.10.0 + compile + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.0.0 + + + + true + lib/ + com.futurewei.alcor.scanquery.scanquery + + + + + + + \ No newline at end of file diff --git a/services/scanquery_test_nodemanager/src/main/java/com/futurewei/alcor/scanquery/scanquery.java b/services/scanquery_test_nodemanager/src/main/java/com/futurewei/alcor/scanquery/scanquery.java new file mode 100644 index 000000000..a477cc710 --- /dev/null +++ b/services/scanquery_test_nodemanager/src/main/java/com/futurewei/alcor/scanquery/scanquery.java @@ -0,0 +1,280 @@ +package com.futurewei.alcor.scanquery; +import com.futurewei.alcor.common.db.ignite.query.MapPredicate; +import com.futurewei.alcor.common.db.ignite.query.ScanQueryBuilder; +import org.apache.ignite.Ignition; +import org.apache.ignite.cache.CacheAtomicityMode; +import org.apache.ignite.cache.CacheKeyConfiguration; +import org.apache.ignite.cache.query.QueryCursor; +import org.apache.ignite.client.ClientCache; +import org.apache.ignite.client.ClientCacheConfiguration; +import org.apache.ignite.client.ClientTransaction; +import org.apache.ignite.client.IgniteClient; +import org.apache.ignite.configuration.ClientConfiguration; +import org.apache.ignite.lang.IgniteBiPredicate; +import org.apache.ignite.transactions.Transaction; +import org.apache.ignite.binary.BinaryObject; +import org.apache.ignite.lang.IgniteClosure; +import com.futurewei.alcor.web.entity.node.NodeInfo; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.FileReader; +import java.io.FileWriter; +import javax.cache.Cache; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.locks.Lock; +import java.util.function.BinaryOperator; + +public class scanquery { + static int[] ipAddress = new int[4]; + static int[] macAddress = new int[6]; + static String igniteAddress; + static String nodePrefix = "node"; + static String ncmPrefix = "ncm"; + static int ncmNum = 0; + static int ncmMax = 1000; + static boolean generateData = false; + + public scanquery(String uuid) { + } + + public static void main(String[] args) { + + if (args.length < 3) { + System.out.println("need input: json_output_filename number_of_entries number_of_queries [-g(enerate data)] [ignite ipaddress]"); + System.exit(-1); + } + final String tblName = "nmm_nodeinfo_cache"; + final String schName = "alcor"; + + int exitCode = -1; + + String jsonFile = args[0]; + int entryCount = Integer.valueOf(args[1]); + int qryCount = Integer.valueOf(args[2]); + if (args.length > 3 && args[3].equals("-g")) + generateData = true; + + if (args.length > 4) + igniteAddress = args[4]; + else + igniteAddress = "127.0.0.1"; + + ipAddress[0] = ipAddress[1] = ipAddress[2] = ipAddress[3] = 1; + macAddress[0] = macAddress[1] = macAddress[2] = macAddress[3] = macAddress[4] = macAddress[5] = 1; + ClientConfiguration clientCfg = new ClientConfiguration(); + clientCfg.setPartitionAwarenessEnabled(true); + clientCfg.setAddresses(igniteAddress + ":10800"); + + System.out.println("ARGUMENTS"); + System.out.println("jsonFile = " + jsonFile); + System.out.println("entryCount = " + entryCount); + System.out.println("qryCount = " + qryCount); + System.out.println("generateData = " + generateData); + System.out.println("igniteAddress = " + igniteAddress); + IgniteClient client = null; + + try { + client = Ignition.startClient(clientCfg); + } + catch (Exception e) { + System.out.println("Cannot connect to server at : " + + igniteAddress + ", Error: " + e.getMessage()); + System.exit(exitCode); + } + + System.out.println("Testing Scanquery from ThinClient"); + + try { + + ClientCacheConfiguration nodeInfoCacheConfig = new ClientCacheConfiguration(); + nodeInfoCacheConfig.setName(tblName); + nodeInfoCacheConfig.setAtomicityMode(CacheAtomicityMode.TRANSACTIONAL); + ClientCache nodeInfoClientCache = client.getOrCreateCache(nodeInfoCacheConfig); + + int ncmSequene = 0; + int batchCount = 0; + int qrySequence = 0; + long insBegin = 0, insEnd = 0; + + String[] queryNames = new String[qryCount]; + if (generateData) { + // insert data, commit every 10000 entries + int commitSize = 10000; + BufferedWriter outFile = new BufferedWriter(new FileWriter(jsonFile)); + ClientTransaction tx = null; + boolean inTxn = false; + try { + insBegin = System.nanoTime(); + for (int e = 0; e < entryCount; ++e) { + NodeInfo nodeInfo = createNodeInfo(nodePrefix, ncmPrefix, e, ncmNum); + if (!inTxn) { + tx = client.transactions().txStart(); + inTxn = true; + } + nodeInfoClientCache.put(nodeInfo.getId(), nodeInfo); + if (batchCount++ >= commitSize) { + tx.commit(); + inTxn = false; + batchCount = 0; + } + + if (ncmSequene++ >= ncmMax) + ncmSequene = 0; + + outFile.write(nodeInfo.getId() + "|" + nodeInfo.getName() + "|" + + nodeInfo.getNcmId() + "|" + nodeInfo.getLocalIp() + "|" + + nodeInfo.getMacAddress() + "\n"); + if (qrySequence < qryCount) + queryNames[qrySequence++] = nodeInfo.getName(); + } + insEnd = System.nanoTime(); + if (inTxn) + tx.commit(); + outFile.close(); + } + catch (Exception e) { + System.out.println("Failed to insert: " + e.getMessage()); + System.exit(-1); + } + } + else { + BufferedReader infile = new BufferedReader(new FileReader(jsonFile)); + while (qrySequence < qryCount) { + String line = infile.readLine(); + String[] fields = line.split("|"); + queryNames[qrySequence++] = new String(fields[1]); + } + infile.close(); + } + + System.out.println("DONE INSERTING"); + + System.out.println("Running ScanQuery, Alcor version"); + + long[] qryTime = new long[qryCount]; + long[] curTime = new long[qryCount]; + int recCount = 0; + try { + long qbegin, qend, cbegin, cend = 0; + for (int i = 0; i < qryCount; ++i) { + String nodeNameIn = queryNames[i]; + qbegin = System.nanoTime(); + Map queryParams = new HashMap<>(); + Object[] values = new Object[1]; + values[0] = nodeNameIn; + queryParams.put("name", values); + IgniteBiPredicate pred = MapPredicate.getInstance(queryParams); + QueryCursor> cursor = nodeInfoClientCache.withKeepBinary().query( + ScanQueryBuilder.newScanQuery(pred)); + try { + qbegin = System.nanoTime(); + List> result = cursor.getAll(); + qend = System.nanoTime(); + qryTime[i] = (qend - qbegin) / 1000; + if (result.isEmpty()) + continue; + cbegin = System.nanoTime(); + BinaryObject obj = result.get(0).getValue(); + cend = System.nanoTime(); + curTime[i] = (cend - cbegin) / 1000; + if (obj instanceof BinaryObject) { + ++recCount; + BinaryObject binObj = (BinaryObject) obj; + NodeInfo node = (NodeInfo) binObj.deserialize(); + assert(node.getName().equals(nodeNameIn)); + } + } + catch (Exception e) { + System.out.println("Scan Query cursor failed " + e.getMessage()); + break; + } + } + exitCode = 0; + } + catch (Exception e) { + System.out.println("Scan Query instantiaon failed: " + e.getMessage()); + } + + System.out.println("INSERT_TIME " + (insEnd - insBegin) / 1000 + " us"); + System.out.println("REC_COUNT = " + recCount); + + for (int i = 0; i < qryCount; ++i) { + System.out.println(qryTime[i] + "\t" + curTime[i]); + } + } catch (Exception e) { + System.out.println("Failed to instantiate PersonCache : " + e.getMessage()); + } + System.exit(exitCode); + } + + static NodeInfo createNodeInfo(String nodePrefix, String ncmPrefix, int nodeNumber, int ncmNumber) + { + String nodeId = UUID.randomUUID().toString(); + String nodeName = nodePrefix + "_" + String.format("%07d", nodeNumber); + String nodeIp = getNextIp(); + String nodeMac = getNextMAC(); + String ncmId = ncmPrefix + "_" + String.format("%03d", ncmNumber); + + NodeInfo newNode = new NodeInfo(nodeId, nodeName, nodeIp, nodeMac); + newNode.setNcmId(ncmId); + + return newNode; + } + + static String getNextIp() { + if (ipAddress[0]++ > 254) { + ipAddress[0] = 0; + if (ipAddress[1]++ > 254) { + ipAddress[1] = 0; + if (ipAddress[2]++ > 254) { + ipAddress[2] = 0; + if (ipAddress[3]++ > 254) { + System.out.println("IP Address out of bounds"); + System.exit(-1); + } + } + } + } + + String newIp = String.format("%d", ipAddress[3]) + "." + + String.format("%d", ipAddress[2]) + "." + + String.format("%d", ipAddress[1]) + "." + + String.format("%d", ipAddress[0]); + + return newIp; + } + + static String getNextMAC() + { + if (macAddress[0]++ > 255) { + macAddress[0] = 0; + if (macAddress[1]++ > 255) { + macAddress[1] = 0; + if (macAddress[2]++ > 255) { + macAddress[2] = 0; + if (macAddress[3]++ > 255) { + macAddress[3] = 0; + if (macAddress[4]++ > 255) { + if (macAddress[5]++ > 255) { + System.out.println("MAC out of range"); + System.exit(-1); + } + } + } + } + } + } + String newMac = String.format("%02x", macAddress[5]) + ":" + + String.format("%02x", macAddress[4]) + ":" + + String.format("%02x", macAddress[3]) + ":" + + String.format("%02x", macAddress[2]) + ":" + + String.format("%02x", macAddress[1]) + ":" + + String.format("%02x", macAddress[0]); + + return newMac; + } +} \ No newline at end of file diff --git a/services/sqlquery_test_nodemanager/pom.xml b/services/sqlquery_test_nodemanager/pom.xml new file mode 100644 index 000000000..554e21276 --- /dev/null +++ b/services/sqlquery_test_nodemanager/pom.xml @@ -0,0 +1,67 @@ + + + 4.0.0 + + com.futurewei.alcor + sqlquery + 1.0-SNAPSHOT + + + 11 + 11 + + + + + com.futurewei.alcor + common + 0.1.0-SNAPSHOT + compile + + + com.futurewei.alcor + web + 0.1.0-SNAPSHOT + compile + + + org.apache.ignite + ignite-core + 2.10.0 + compile + + + org.apache.ignite + ignite-indexing + 2.10.0 + compile + + + org.apache.ignite + ignite-spring + 2.10.0 + compile + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.0.0 + + + + true + lib/ + com.futurewei.alcor.sqlquery.SqlQuery2 + + + + + + + \ No newline at end of file diff --git a/services/sqlquery_test_nodemanager/src/main/java/com/futurewei/alcor/sqlquery/sqlquery.java b/services/sqlquery_test_nodemanager/src/main/java/com/futurewei/alcor/sqlquery/sqlquery.java new file mode 100644 index 000000000..cf630e17f --- /dev/null +++ b/services/sqlquery_test_nodemanager/src/main/java/com/futurewei/alcor/sqlquery/sqlquery.java @@ -0,0 +1,294 @@ +package com.futurewei.alcor.sqlquery; +import org.apache.ignite.Ignition; +import org.apache.ignite.cache.CacheAtomicityMode; +import org.apache.ignite.cache.CacheKeyConfiguration; +import org.apache.ignite.cache.QueryEntity; +import org.apache.ignite.cache.QueryIndex; +import org.apache.ignite.cache.query.QueryCursor; +import org.apache.ignite.cache.query.SqlFieldsQuery; +import org.apache.ignite.client.ClientCache; +import org.apache.ignite.client.ClientCacheConfiguration; +import org.apache.ignite.client.ClientTransaction; +import org.apache.ignite.client.IgniteClient; +import org.apache.ignite.configuration.ClientConfiguration; +import org.apache.ignite.transactions.Transaction; +import org.json.simple.*; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.w3c.dom.Node; +import com.futurewei.alcor.web.entity.node.NodeInfo; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.FileReader; +import java.io.FileWriter; +import java.util.*; + +public class sqlquery { + static int[] ipAddress = new int[4]; + static int[] macAddress = new int[6]; + static String igniteAddress; + static String nodePrefix = "node"; + static String ncmPrefix = "ncm"; + static int ncmNum = 0; + static int ncmMax = 1000; + static boolean generateData = false; + + public sqlquery(String uuid) { + } + + public static void main(String[] args) { + + if (args.length < 3) { + System.out.println("need input: json_output_filename number_of_entries number_of_queries [-g(enerate data)]"); + System.exit(-1); + } + final String tblName = "nmm_nodeinfo_cache"; + final String schName = "alcor"; + + int exitCode = -1; + + String jsonFile = args[0]; + int entryCount = Integer.valueOf(args[1]); + int qryCount = Integer.valueOf(args[2]); + if (args.length > 3 && args[3].equals("-g")) + generateData = true; + + if (args.length > 4) + igniteAddress = args[4]; + else + igniteAddress = "127.0.0.1"; + + ipAddress[0] = ipAddress[1] = ipAddress[2] = ipAddress[3] = 1; + macAddress[0] = macAddress[1] = macAddress[2] = macAddress[3] = macAddress[4] = macAddress[5] = 1; + ClientConfiguration clientCfg = new ClientConfiguration(); + clientCfg.setPartitionAwarenessEnabled(true); + clientCfg.setAddresses(igniteAddress + ":10800"); + + System.out.println("ARGUMENTS"); + System.out.println("jsonFile = " + jsonFile); + System.out.println("entryCount = " + entryCount); + System.out.println("qryCount = " + qryCount); + System.out.println("generateData = " + generateData); + System.out.println("igniteAddress = " + igniteAddress); + IgniteClient client = null; + + try { + client = Ignition.startClient(clientCfg); + } + catch (Exception e) { + System.out.println("Cannot connect to Local server: " + e.getMessage()); + System.exit(exitCode); + } + + System.out.println("Testing QueryEntity, Indexing, SQL Access to ClientCache from ThinClient"); + + try { + + ClientCacheConfiguration nodeInfoCacheConfig = new ClientCacheConfiguration(); + nodeInfoCacheConfig.setName(tblName); + nodeInfoCacheConfig.setAtomicityMode(CacheAtomicityMode.TRANSACTIONAL); + CacheKeyConfiguration keyConf = new CacheKeyConfiguration(); + + QueryEntity qryEnt = new QueryEntity(); + qryEnt.setValueType(NodeInfo.class.getName()); + LinkedHashMap qryFields = new LinkedHashMap<>(); + + qryFields.put("id", String.class.getName()); + qryFields.put("name", String.class.getName()); + + qryEnt.setFields(qryFields); + + qryEnt.setIndexes(Arrays.asList( + new QueryIndex("id"), + new QueryIndex("name"))); + nodeInfoCacheConfig.setQueryEntities(qryEnt).setSqlSchema(schName); + + ClientCache nodeInfoClientCache = client.getOrCreateCache(nodeInfoCacheConfig); + + int ncmSequene = 0; + int batchCount = 0; + int qrySequence = 0; + long insBegin = 0, insEnd = 0; + + String[] queryNames = new String[qryCount]; + if (generateData) { + // insert data, commit every 10000 entries + int commitSize = 10000; + BufferedWriter outFile = new BufferedWriter(new FileWriter(jsonFile)); + ClientTransaction tx = null; + boolean inTxn = false; + try { + insBegin = System.nanoTime(); + for (int e = 0; e < entryCount; ++e) { + NodeInfo nodeInfo = createNodeInfo(nodePrefix, ncmPrefix, e, ncmNum); + if (!inTxn) { + tx = client.transactions().txStart(); + inTxn = true; + } + nodeInfoClientCache.put(nodeInfo.getId(), nodeInfo); + if (batchCount++ >= commitSize) { + tx.commit(); + inTxn = false; + batchCount = 0; + } + + if (ncmSequene++ >= ncmMax) + ncmSequene = 0; + + outFile.write(nodeInfo.getId() + "|" + nodeInfo.getName() + "|" + + nodeInfo.getNcmId() + "|" + nodeInfo.getLocalIp() + "|" + + nodeInfo.getMacAddress() + "\n"); + if (qrySequence < qryCount) + queryNames[qrySequence++] = nodeInfo.getName(); + } + insEnd = System.nanoTime(); + if (inTxn) + tx.commit(); + outFile.close(); + } + catch (Exception e) { + System.out.println("Failed to insert: " + e.getMessage()); + System.exit(-1); + } + } + else { + BufferedReader infile = new BufferedReader(new FileReader(jsonFile)); + while (qrySequence < qryCount) { + String line = infile.readLine(); + String[] fields = line.split("|"); + queryNames[qrySequence++] = new String(fields[1]); + } + infile.close(); + } + + System.out.println("DONE INSERTING"); + + SqlFieldsQuery sql = new SqlFieldsQuery("select _key, _val from " + schName + + "." + tblName + " where name = ?"); + + int i; + long[] qryTime = new long[qryCount]; + long[] curTime = new long[qryCount]; + int recCount = 0; + try { + + long qbegin, qend, cbeign, cend = 0; + for (i = 0; i < qryCount; ++i) { + String nodeNameIn = queryNames[i]; + sql.setArgs(nodeNameIn); + qbegin = System.nanoTime(); + QueryCursor> cursor = nodeInfoClientCache.query(sql); + qend = System.nanoTime(); + qryTime[i] = (qend - qbegin) / 1000; + String nodeId = null, nodeName = null; + cbeign = qend; + try { + for (List row : cursor) { + ++recCount; + cbeign = System.nanoTime(); + nodeId = row.get(0).toString(); + NodeInfo node = (NodeInfo)row.get(1); + nodeName = node.getName(); + cend = System.nanoTime(); + assert(nodeNameIn.equals(nodeName)); + curTime[i] = (cend - cbeign) / 1000; + } + } + catch (Exception e) { + System.out.println("Cursor failed: " + e.getMessage()); + continue; + } + + System.out.println("SQL: " + sql + " args: " + sql.getArgs().toString()); + exitCode = 0; + } + + System.out.println("INSERT_TIME " + (insEnd - insBegin) / 1000 + " us"); + System.out.println("REC_COUNT = " + recCount); + + for (int j = 0; j < i; ++j) { + System.out.println(qryTime[j] + "\t" + curTime[j]); + } + } catch (Exception e) { + System.out.println("SQL Query failed: " + e.getMessage()); + } + } catch (Exception e) { + System.out.println("Failed to instantiate PersonCache : " + e.getMessage()); + } finally { + try { + if (client != null) + client.close(); + } catch (Exception e) { + } + System.exit(exitCode); + } + } + + static NodeInfo createNodeInfo(String nodePrefix, String ncmPrefix, int nodeNumber, int ncmNumber) + { + String nodeId = UUID.randomUUID().toString(); + String nodeName = nodePrefix + "_" + String.format("%07d", nodeNumber); + String nodeIp = getNextIp(); + String nodeMac = getNextMAC(); + String ncmId = ncmPrefix + "_" + String.format("%03d", ncmNumber); + + NodeInfo newNode = new NodeInfo(nodeId, nodeName, nodeIp, nodeMac); + newNode.setNcmId(ncmId); + + return newNode; + } + + static String getNextIp() { + if (ipAddress[0]++ > 254) { + ipAddress[0] = 0; + if (ipAddress[1]++ > 254) { + ipAddress[1] = 0; + if (ipAddress[2]++ > 254) { + ipAddress[2] = 0; + if (ipAddress[3]++ > 254) { + System.out.println("IP Address out of bounds"); + System.exit(-1); + } + } + } + } + + String newIp = String.format("%d", ipAddress[3]) + "." + + String.format("%d", ipAddress[2]) + "." + + String.format("%d", ipAddress[1]) + "." + + String.format("%d", ipAddress[0]); + + return newIp; + } + + static String getNextMAC() + { + if (macAddress[0]++ > 255) { + macAddress[0] = 0; + if (macAddress[1]++ > 255) { + macAddress[1] = 0; + if (macAddress[2]++ > 255) { + macAddress[2] = 0; + if (macAddress[3]++ > 255) { + macAddress[3] = 0; + if (macAddress[4]++ > 255) { + if (macAddress[5]++ > 255) { + System.out.println("MAC out of range"); + System.exit(-1); + } + } + } + } + } + } + String newMac = String.format("%02x", macAddress[5]) + ":" + + String.format("%02x", macAddress[4]) + ":" + + String.format("%02x", macAddress[3]) + ":" + + String.format("%02x", macAddress[2]) + ":" + + String.format("%02x", macAddress[1]) + ":" + + String.format("%02x", macAddress[0]); + + return newMac; + } +} \ No newline at end of file diff --git a/web/src/main/java/com/futurewei/alcor/web/entity/dataplane/NeighborInfo.java b/web/src/main/java/com/futurewei/alcor/web/entity/dataplane/NeighborInfo.java index 40716ae7e..715e236ed 100644 --- a/web/src/main/java/com/futurewei/alcor/web/entity/dataplane/NeighborInfo.java +++ b/web/src/main/java/com/futurewei/alcor/web/entity/dataplane/NeighborInfo.java @@ -17,6 +17,7 @@ free of charge, to any person obtaining a copy of this software and associated d import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; +import org.apache.ignite.cache.query.annotations.QuerySqlField; @Data public class NeighborInfo { @@ -36,6 +37,7 @@ public class NeighborInfo { private String portIp; @JsonProperty("vpc_id") + @QuerySqlField(index = true) private String vpcId; @JsonProperty("subnet_id") @@ -142,4 +144,4 @@ public boolean equals(Object obj) { &&this.portIp.equals(o.portIp) &&this.portMac.equals(o.portMac); } -} +} \ No newline at end of file diff --git a/web/src/main/java/com/futurewei/alcor/web/entity/node/NodeInfo.java b/web/src/main/java/com/futurewei/alcor/web/entity/node/NodeInfo.java index 644dc95e0..adb746826 100644 --- a/web/src/main/java/com/futurewei/alcor/web/entity/node/NodeInfo.java +++ b/web/src/main/java/com/futurewei/alcor/web/entity/node/NodeInfo.java @@ -18,6 +18,7 @@ free of charge, to any person obtaining a copy of this software and associated d import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; +import org.apache.ignite.cache.query.annotations.QuerySqlField; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,12 +36,15 @@ public class NodeInfo implements Serializable { private String id; @JsonProperty("node_name") + @QuerySqlField(index = true) private String name; @JsonProperty("local_ip") + @QuerySqlField(index = true) private String localIp; @JsonProperty("mac_address") + @QuerySqlField(index = true) private String macAddress; @JsonProperty("veth") @@ -53,6 +57,7 @@ public class NodeInfo implements Serializable { private String hostDvrMac; @JsonProperty("ncm_id") + @QuerySqlField(index = true) private String ncm_id; // doesn't come in the Json version @@ -180,4 +185,4 @@ public void setHostDvrMac(String hostDvrMac) { public String getNcmUri() { return ncm_uri; } public void setNcmUri(String uri) { ncm_uri = uri; } -} +} \ No newline at end of file diff --git a/web/src/main/java/com/futurewei/alcor/web/entity/port/PortEntity.java b/web/src/main/java/com/futurewei/alcor/web/entity/port/PortEntity.java index ac4e39985..fec60adcb 100644 --- a/web/src/main/java/com/futurewei/alcor/web/entity/port/PortEntity.java +++ b/web/src/main/java/com/futurewei/alcor/web/entity/port/PortEntity.java @@ -730,4 +730,4 @@ public Boolean getMacLearningEnabled() { public void setMacLearningEnabled(Boolean macLearningEnabled) { this.macLearningEnabled = macLearningEnabled; } -} +} \ No newline at end of file diff --git a/web/src/main/java/com/futurewei/alcor/web/entity/route/Router.java b/web/src/main/java/com/futurewei/alcor/web/entity/route/Router.java index 3fb644037..27dcf4f72 100644 --- a/web/src/main/java/com/futurewei/alcor/web/entity/route/Router.java +++ b/web/src/main/java/com/futurewei/alcor/web/entity/route/Router.java @@ -18,6 +18,7 @@ free of charge, to any person obtaining a copy of this software and associated d import com.fasterxml.jackson.annotation.JsonProperty; import com.futurewei.alcor.common.entity.CustomerResource; import lombok.Data; +import org.apache.ignite.cache.query.annotations.QuerySqlField; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; @@ -44,6 +45,7 @@ public class Router extends CustomerResource { // store vpc_id @JsonProperty("owner") + @QuerySqlField(index = true) private String owner; // store vpc_id @@ -106,4 +108,4 @@ public Router(Router r) { r.getTenantId(), true, r.getStatus(), r.getRouterExtraAttributeId(), r.getVpcDefaultRouteTableId()); } -} +} \ No newline at end of file