Improve ExpireMap lookup performance with code refactoring

Change-Id: I304456a5e5b648ca9c02cc4924a86f50ccc61ab2
This commit is contained in:
Jian Li 2017-06-08 18:18:44 +09:00
parent dc13d39f47
commit f17e0c9c91
5 changed files with 223 additions and 135 deletions

View File

@ -17,6 +17,7 @@ package org.onosproject.lisp.ctl.impl;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import org.onlab.packet.IpPrefix;
import org.onosproject.lisp.ctl.impl.map.ExpireMap;
import org.onosproject.lisp.ctl.impl.map.ExpireHashMap;
import org.onosproject.lisp.msg.protocols.DefaultLispProxyMapRecord.DefaultMapWithProxyBuilder;
@ -29,8 +30,8 @@ import org.slf4j.Logger;
import java.util.List;
import java.util.Optional;
import static org.onosproject.lisp.ctl.impl.util.LispMapUtil.*;
import static org.slf4j.LoggerFactory.getLogger;
import static org.onosproject.lisp.ctl.impl.util.LispMapUtil.isInRange;
/**
* An expire map based LISP mapping database.
@ -106,13 +107,10 @@ public final class LispExpireMapDatabase implements LispMappingDatabase {
@Override
public LispMapRecord getMapRecordByEidRecord(LispEidRecord eid,
boolean proxyMapReply) {
Optional<LispEidRecord> filteredEidRecord = map.keySet().parallelStream()
.filter(k -> isInRange(k, eid)).findAny();
if (filteredEidRecord.isPresent()) {
LispProxyMapRecord record = map.get(filteredEidRecord.get());
if (record != null && record.isProxyMapReply() == proxyMapReply) {
return record.getMapRecord();
}
LispProxyMapRecord record = getMapRecordForClosestParentAddress(getIpPrefixFromEidRecord(eid));
if (record != null && record.isProxyMapReply() == proxyMapReply) {
return record.getMapRecord();
}
return null;
@ -146,6 +144,27 @@ public final class LispExpireMapDatabase implements LispMappingDatabase {
map.get(lispEidRecord).getMapRecord()).orElse(null);
}
/**
* Returns the map record associated with the closest parent address from a
* given expire map, or returns null if no such map record is associated
* with the address.
*
* @param prefix IP prefix
* @return a map record with the closest parent address, or null if no value
* was associated with the address
*/
private LispProxyMapRecord getMapRecordForClosestParentAddress(IpPrefix prefix) {
while (prefix != null && prefix.prefixLength() > 0) {
LispProxyMapRecord record = map.get(getEidRecordFromIpPrefix(prefix));
if (record != null) {
return record;
}
prefix = getParentPrefix(prefix);
}
return null;
}
/**
* Prevents object instantiation from external.
*/

View File

@ -19,27 +19,20 @@ import com.google.common.collect.Lists;
import com.googlecode.concurrenttrees.radix.ConcurrentRadixTree;
import com.googlecode.concurrenttrees.radix.RadixTree;
import com.googlecode.concurrenttrees.radix.node.concrete.DefaultCharArrayNodeFactory;
import org.apache.commons.lang3.StringUtils;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpAddress.Version;
import org.onlab.packet.IpPrefix;
import java.util.List;
import static org.onosproject.lisp.ctl.impl.util.LispMapUtil.getParentPrefix;
import static org.onosproject.lisp.ctl.impl.util.LispMapUtil.getPrefixString;
/**
* Implements current radix tree that stores IP address as a key.
*/
public class IpConcurrentRadixTree<V> implements IpRadixTree<V> {
private static final int IPV4_BLOCK_LENGTH = 8;
private static final int IPV6_BLOCK_LENGTH = 16;
private static final String IPV4_DELIMITER = ".";
private static final String IPV6_DELIMITER = ":";
private static final String IPV4_ZERO = "0";
private static final String IPV6_SUFFIX = "::";
private RadixTree<V> ipv4Tree =
new ConcurrentRadixTree<>(new DefaultCharArrayNodeFactory());
private RadixTree<V> ipv6Tree =
@ -166,99 +159,6 @@ public class IpConcurrentRadixTree<V> implements IpRadixTree<V> {
ipv6Tree = new ConcurrentRadixTree<>(new DefaultCharArrayNodeFactory());
}
/**
* Obtains the string formatted IP prefix.
* For example, if the IP address is 10.1.1.1 and has 16 prefix length,
* the resulting string is 10.1
*
* @param prefix IP prefix
* @return string formatted IP prefix
*/
private String getPrefixString(IpPrefix prefix) {
String addressString = prefix.address().toString();
StringBuilder sb = new StringBuilder();
String delimiter = "";
int numOfBlock = 0;
if (prefix.isIp4()) {
delimiter = IPV4_DELIMITER;
numOfBlock = prefix.prefixLength() / IPV4_BLOCK_LENGTH;
}
if (prefix.isIp6()) {
delimiter = IPV6_DELIMITER;
numOfBlock = prefix.prefixLength() / IPV6_BLOCK_LENGTH;
}
String[] octets = StringUtils.split(addressString, delimiter);
for (int i = 0; i < numOfBlock; i++) {
sb.append(octets[i]);
if (i < numOfBlock - 1) {
sb.append(delimiter);
}
}
return sb.toString();
}
/**
* Obtains the parent IP prefix of the given IP prefix.
* For example, if the given IP prefix is 10.1.1.1, the parent IP prefix
* will be 10.1.1
*
* @param prefix IP prefix
* @return parent IP prefix
*/
private IpPrefix getParentPrefix(IpPrefix prefix) {
String addressString = prefix.address().toString();
int prefixLength = prefix.prefixLength();
StringBuilder sb = new StringBuilder();
String delimiter = "";
String zero = "";
int blockLength = 0;
if (prefix.isIp4()) {
delimiter = IPV4_DELIMITER;
blockLength = IPV4_BLOCK_LENGTH;
zero = IPV4_ZERO;
}
if (prefix.isIp6()) {
delimiter = IPV6_DELIMITER;
blockLength = IPV6_BLOCK_LENGTH;
}
String[] octets = StringUtils.split(addressString, delimiter);
String parentAddressString;
if (octets.length == 1) {
return prefix;
} else {
prefixLength = prefixLength - blockLength;
int blockIdx = prefixLength / blockLength;
for (int i = 0; i < octets.length; i++) {
if (i < blockIdx) {
sb.append(octets[i]);
sb.append(delimiter);
} else {
sb.append(zero);
if (prefix.isIp4()) {
sb.append(delimiter);
}
}
}
// ipv6 address prefix typically ends with ::
if (prefix.isIp6()) {
sb.append(IPV6_SUFFIX);
}
parentAddressString = StringUtils.substring(sb.toString(),
0, sb.toString().length() - 1);
return IpPrefix.valueOf(parentAddressString + "/" + prefixLength);
}
}
/**
* Returns the value associated with the closest parent address from a
* given radix tree, or returns null if no such value is associated
@ -271,7 +171,7 @@ public class IpConcurrentRadixTree<V> implements IpRadixTree<V> {
*/
private V getValueForClosestParentAddress(IpPrefix prefix, RadixTree<V> tree) {
while (prefix != null) {
while (prefix != null && prefix.prefixLength() > 0) {
V value = tree.getValueForExactKey(getPrefixString(prefix));
if (value != null) {
return value;

View File

@ -16,14 +16,25 @@
package org.onosproject.lisp.ctl.impl.util;
import org.apache.commons.lang3.StringUtils;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import org.onosproject.lisp.msg.protocols.LispEidRecord;
import org.onosproject.lisp.msg.types.LispIpAddress;
import org.onosproject.lisp.msg.types.LispIpv4Address;
import org.onosproject.lisp.msg.types.LispIpv6Address;
/**
* A LISP map utility class includes various useful methods.
*/
public final class LispMapUtil {
private static final int IPV4_BLOCK_LENGTH = 8;
private static final int IPV6_BLOCK_LENGTH = 16;
private static final String IPV4_DELIMITER = ".";
private static final String IPV6_DELIMITER = ":";
/**
* Prevents object instantiation from external.
*/
@ -58,4 +69,86 @@ public final class LispMapUtil {
return originIpPrefix.contains(compareIpPrefix);
}
/**
* Obtains the EID record from an IP prefix.
*
* @param prefix IP prefix
* @return EID record
*/
public static LispEidRecord getEidRecordFromIpPrefix(IpPrefix prefix) {
LispIpAddress eid = null;
if (prefix.isIp4()) {
eid = new LispIpv4Address(prefix.address());
}
if (prefix.isIp6()) {
eid = new LispIpv6Address(prefix.address());
}
return new LispEidRecord((byte) prefix.prefixLength(), eid);
}
/**
* Obtains the IP prefix from an EID record.
*
* @param eidRecord EID record
* @return IP prefix
*/
public static IpPrefix getIpPrefixFromEidRecord(LispEidRecord eidRecord) {
return IpPrefix.valueOf(eidRecord.getPrefix().toString() +
"/" + eidRecord.getMaskLength());
}
/**
* Obtains the string formatted IP prefix.
* For example, if the IP address is 10.1.1.1 and has 16 prefix length,
* the resulting string is 10.1
*
* @param prefix IP prefix
* @return string formatted IP prefix
*/
public static String getPrefixString(IpPrefix prefix) {
String addressString = prefix.address().toString();
StringBuilder sb = new StringBuilder();
String delimiter = "";
int numOfBlock = 0;
if (prefix.isIp4()) {
delimiter = IPV4_DELIMITER;
numOfBlock = prefix.prefixLength() / IPV4_BLOCK_LENGTH;
}
if (prefix.isIp6()) {
delimiter = IPV6_DELIMITER;
numOfBlock = prefix.prefixLength() / IPV6_BLOCK_LENGTH;
}
String[] octets = StringUtils.split(addressString, delimiter);
for (int i = 0; i < numOfBlock; i++) {
sb.append(octets[i]);
if (i < numOfBlock - 1) {
sb.append(delimiter);
}
}
return sb.toString();
}
/**
* Obtains the parent IP prefix of the given IP prefix.
* For example, if the given IP prefix is 10.1.1.1/32, the parent IP prefix
* will be 10.1.1.0/31.
*
* @param prefix IP prefix
* @return parent IP prefix
*/
public static IpPrefix getParentPrefix(IpPrefix prefix) {
return IpPrefix.valueOf(IpAddress.makeMaskedAddress(prefix.address(),
prefix.prefixLength() - 1), prefix.prefixLength() - 1);
}
}

View File

@ -31,6 +31,7 @@ import java.util.List;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertNull;
/**
* Tests for LISP EID RLOC Map class.
@ -40,23 +41,34 @@ public class LispMappingDatabaseTest {
private static final String LOCATOR_IP_1_1 = "123.1.1.1";
private static final String LOCATOR_IP_1_2 = "123.1.1.2";
private static final String LOCATOR_IP_1_3 = "123.1.1.3";
private static final String LOCATOR_IP_1_4 = "123.1.1.4";
private static final String LOCATOR_IP_2_1 = "123.2.1.1";
private static final String LOCATOR_IP_2_2 = "123.2.1.2";
private static final String LOCATOR_IP_2_3 = "123.2.1.3";
private static final String LOCATOR_IP_3_1 = "123.3.1.1";
private static final String LOCATOR_IP_3_2 = "123.3.1.2";
private static final String LOCATOR_IP_4_1 = "123.4.1.1";
private static final String EID_IP_1 = "10.1.1.1";
private static final String EID_IP_2 = "10.1.2.0";
private static final String EID_IP_3 = "10.2.0.0";
private static final String EID_IP_4 = "10.0.0.0";
private static final String EID_IP_PREFIX_1_32 = "10.2.1.1";
private static final String EID_IP_PREFIX_1_24 = "10.2.1.0";
private static final String EID_IP_PREFIX_1_16 = "10.2.0.0";
private static final String EID_IP_PREFIX_1_12 = "10.0.0.0";
private static final String EID_IP_PREFIX_2_32 = "10.1.2.1";
private static final String EID_IP_PREFIX_2_24 = "10.1.2.0";
private static final String EID_IP_PREFIX_3_24 = "192.168.1.0";
private static final int RECORD_TTL = 60;
private final LispMappingDatabase expireMapDb = LispExpireMapDatabase.getInstance();
private final LispMappingDatabase radixTreeDb = LispRadixTreeDatabase.getInstance();
@ -70,6 +82,7 @@ public class LispMappingDatabaseTest {
LispIpv4Address locator11 = new LispIpv4Address(IpAddress.valueOf(LOCATOR_IP_1_1));
LispIpv4Address locator12 = new LispIpv4Address(IpAddress.valueOf(LOCATOR_IP_1_2));
LispIpv4Address locator13 = new LispIpv4Address(IpAddress.valueOf(LOCATOR_IP_1_3));
LispIpv4Address locator14 = new LispIpv4Address(IpAddress.valueOf(LOCATOR_IP_1_4));
LispLocator locatorRecord11 = new DefaultLocatorBuilder()
.withLocatorAfi(locator11).build();
@ -77,8 +90,10 @@ public class LispMappingDatabaseTest {
.withLocatorAfi(locator12).build();
LispLocator locatorRecord13 = new DefaultLocatorBuilder()
.withLocatorAfi(locator13).build();
LispLocator locatorRecord14 = new DefaultLocatorBuilder()
.withLocatorAfi(locator14).build();
List<LispLocator> locatorRecords1 =
ImmutableList.of(locatorRecord11, locatorRecord12, locatorRecord13);
ImmutableList.of(locatorRecord11, locatorRecord12, locatorRecord13, locatorRecord14);
byte cidr2 = (byte) 24;
LispIpv4Address eid2 = new LispIpv4Address(IpAddress.valueOf(EID_IP_2));
@ -86,54 +101,80 @@ public class LispMappingDatabaseTest {
LispIpv4Address locator21 = new LispIpv4Address(IpAddress.valueOf(LOCATOR_IP_2_1));
LispIpv4Address locator22 = new LispIpv4Address(IpAddress.valueOf(LOCATOR_IP_2_2));
LispIpv4Address locator23 = new LispIpv4Address(IpAddress.valueOf(LOCATOR_IP_2_3));
LispLocator locatorRecord21 = new DefaultLocatorBuilder()
.withLocatorAfi(locator21).build();
LispLocator locatorRecord22 = new DefaultLocatorBuilder()
.withLocatorAfi(locator22).build();
LispLocator locatorRecord23 = new DefaultLocatorBuilder()
.withLocatorAfi(locator23).build();
List<LispLocator> locatorRecords2 =
ImmutableList.of(locatorRecord21, locatorRecord22);
ImmutableList.of(locatorRecord21, locatorRecord22, locatorRecord23);
byte cidr3 = (byte) 16;
LispIpv4Address eid3 = new LispIpv4Address(IpAddress.valueOf(EID_IP_3));
LispEidRecord eidRecord3 = new LispEidRecord(cidr3, eid3);
LispIpv4Address locator31 = new LispIpv4Address(IpAddress.valueOf(LOCATOR_IP_3_1));
LispIpv4Address locator32 = new LispIpv4Address(IpAddress.valueOf(LOCATOR_IP_3_2));
LispLocator locatorRecord31 = new DefaultLocatorBuilder()
.withLocatorAfi(locator31).build();
LispLocator locatorRecord32 = new DefaultLocatorBuilder()
.withLocatorAfi(locator32).build();
List<LispLocator> locatorRecords3 = ImmutableList.of(locatorRecord31);
List<LispLocator> locatorRecords3 = ImmutableList.of(locatorRecord31, locatorRecord32);
byte cidr4 = (byte) 12;
LispIpv4Address eid4 = new LispIpv4Address(IpAddress.valueOf(EID_IP_4));
LispEidRecord eidRecord4 = new LispEidRecord(cidr4, eid4);
LispIpv4Address locator41 = new LispIpv4Address(IpAddress.valueOf(LOCATOR_IP_4_1));
LispLocator locatorRecord41 = new DefaultLocatorBuilder()
.withLocatorAfi(locator41).build();
List<LispLocator> locatorRecords4 = ImmutableList.of(locatorRecord41);
MapRecordBuilder builder1 = new DefaultMapRecordBuilder();
builder1.withMaskLength(cidr1);
builder1.withEidPrefixAfi(eid1);
builder1.withLocators(locatorRecords1);
builder1.withRecordTtl(60);
builder1.withRecordTtl(RECORD_TTL);
LispMapRecord mapRecord1 = builder1.build();
MapRecordBuilder builder2 = new DefaultMapRecordBuilder();
builder2.withMaskLength(cidr2);
builder2.withEidPrefixAfi(eid2);
builder2.withLocators(locatorRecords2);
builder2.withRecordTtl(60);
builder2.withRecordTtl(RECORD_TTL);
LispMapRecord mapRecord2 = builder2.build();
MapRecordBuilder builder3 = new DefaultMapRecordBuilder();
builder3.withMaskLength(cidr3);
builder3.withEidPrefixAfi(eid3);
builder3.withLocators(locatorRecords3);
builder3.withRecordTtl(60);
builder3.withRecordTtl(RECORD_TTL);
LispMapRecord mapRecord3 = builder3.build();
MapRecordBuilder builder4 = new DefaultMapRecordBuilder();
builder4.withMaskLength(cidr4);
builder4.withEidPrefixAfi(eid4);
builder4.withLocators(locatorRecords4);
builder4.withRecordTtl(RECORD_TTL);
LispMapRecord mapRecord4 = builder4.build();
expireMapDb.putMapRecord(eidRecord1, mapRecord1, true);
expireMapDb.putMapRecord(eidRecord2, mapRecord2, true);
expireMapDb.putMapRecord(eidRecord3, mapRecord3, true);
expireMapDb.putMapRecord(eidRecord4, mapRecord4, true);
radixTreeDb.putMapRecord(eidRecord1, mapRecord1, true);
radixTreeDb.putMapRecord(eidRecord2, mapRecord2, true);
radixTreeDb.putMapRecord(eidRecord3, mapRecord3, true);
radixTreeDb.putMapRecord(eidRecord4, mapRecord4, true);
}
@Test
@ -145,10 +186,10 @@ public class LispMappingDatabaseTest {
LispMapRecord mapRecord2 = radixTreeDb.getMapRecordByEidRecord(record, true);
assertThat("Failed to fetch the RLOCs with /32 EID record",
mapRecord1.getLocatorCount(), is(3));
mapRecord1.getLocatorCount(), is(4));
assertThat("Failed to fetch the RLOCs with /32 EID record",
mapRecord2.getLocatorCount(), is(3));
mapRecord2.getLocatorCount(), is(4));
}
@Test
@ -166,18 +207,18 @@ public class LispMappingDatabaseTest {
LispMapRecord mapRecordRadixTree24 = radixTreeDb.getMapRecordByEidRecord(record32, true);
assertThat("Failed to fetch the RLOCs with /32 EID record",
mapRecordExpireMap32.getLocatorCount(), is(2));
mapRecordExpireMap32.getLocatorCount(), is(3));
assertThat("Failed to fetch the RLOCs with /24 EID record",
mapRecordExpireMap24.getLocatorCount(), is(2));
mapRecordExpireMap24.getLocatorCount(), is(3));
assertThat("Failed to fetch the RLOCs with /32 EID record",
mapRecordRadixTree32.getLocatorCount(), is(2));
mapRecordRadixTree32.getLocatorCount(), is(3));
assertThat("Failed to fetch the RLOCs with /24 EID record",
mapRecordRadixTree24.getLocatorCount(), is(2));
mapRecordRadixTree24.getLocatorCount(), is(3));
}
@Test
public void test16MaskRange() {
public void test12MaskRange() {
byte cidr32 = (byte) 32;
LispIpv4Address eid = new LispIpv4Address(IpAddress.valueOf(EID_IP_PREFIX_1_32));
LispEidRecord record32 = new LispEidRecord(cidr32, eid);
@ -196,18 +237,37 @@ public class LispMappingDatabaseTest {
LispMapRecord mapRecordExpireMap16 = expireMapDb.getMapRecordByEidRecord(record16, true);
LispMapRecord mapRecordRadixTree16 = radixTreeDb.getMapRecordByEidRecord(record16, true);
assertThat("Failed to fetch the RLOCs with /32 EID record",
mapRecordExpireMap32.getLocatorCount(), is(1));
assertThat("Failed to fetch the RLOCs with /24 EID record",
mapRecordExpireMap24.getLocatorCount(), is(1));
assertThat("Failed to fetch the RLOCs with /16 EID record",
mapRecordExpireMap16.getLocatorCount(), is(1));
byte cidr12 = (byte) 12;
LispIpv4Address eid12 = new LispIpv4Address(IpAddress.valueOf(EID_IP_PREFIX_1_12));
LispEidRecord record12 = new LispEidRecord(cidr12, eid12);
LispMapRecord mapRecordExpireMap12 = expireMapDb.getMapRecordByEidRecord(record12, true);
LispMapRecord mapRecordRadixTree12 = radixTreeDb.getMapRecordByEidRecord(record12, true);
assertThat("Failed to fetch the RLOCs with /32 EID record",
mapRecordRadixTree32.getLocatorCount(), is(1));
mapRecordExpireMap32.getLocatorCount(), is(2));
assertThat("Failed to fetch the RLOCs with /24 EID record",
mapRecordRadixTree24.getLocatorCount(), is(1));
mapRecordExpireMap24.getLocatorCount(), is(2));
assertThat("Failed to fetch the RLOCs with /16 EID record",
mapRecordRadixTree16.getLocatorCount(), is(1));
mapRecordExpireMap16.getLocatorCount(), is(2));
assertThat("Failed to fetch the RLOCs with /12 EID record",
mapRecordExpireMap12.getLocatorCount(), is(1));
assertThat("Failed to fetch the RLOCs with /32 EID record",
mapRecordRadixTree32.getLocatorCount(), is(2));
assertThat("Failed to fetch the RLOCs with /24 EID record",
mapRecordRadixTree24.getLocatorCount(), is(2));
assertThat("Failed to fetch the RLOCs with /16 EID record",
mapRecordRadixTree16.getLocatorCount(), is(2));
assertThat("Failed to fetch the RLOCs with /12 EID record",
mapRecordRadixTree12.getLocatorCount(), is(1));
LispIpv4Address wrongEid =
new LispIpv4Address(IpAddress.valueOf(EID_IP_PREFIX_3_24));
LispEidRecord wrongRecord = new LispEidRecord(cidr24, wrongEid);
LispMapRecord nullRecord =
expireMapDb.getMapRecordByEidRecord(wrongRecord, true);
assertNull("The record should be null", nullRecord);
}
}

View File

@ -17,12 +17,15 @@ package org.onosproject.lisp.msg.protocols;
import io.netty.buffer.ByteBuf;
import com.google.common.base.Objects;
import org.onlab.packet.IpAddress;
import org.onosproject.lisp.msg.exceptions.LispParseError;
import org.onosproject.lisp.msg.exceptions.LispReaderException;
import org.onosproject.lisp.msg.exceptions.LispWriterException;
import org.onosproject.lisp.msg.types.LispAfiAddress;
import org.onosproject.lisp.msg.types.LispAfiAddress.AfiAddressReader;
import org.onosproject.lisp.msg.types.LispAfiAddress.AfiAddressWriter;
import org.onosproject.lisp.msg.types.LispIpv4Address;
import org.onosproject.lisp.msg.types.LispIpv6Address;
import static com.google.common.base.MoreObjects.toStringHelper;
import static com.google.common.base.Preconditions.checkNotNull;
@ -49,7 +52,20 @@ public final class LispEidRecord {
checkNotNull(prefix, "Must specify an address prefix");
this.prefix = prefix;
// re-calculate the IP address based on the maskLength
switch (prefix.getAfi()) {
case IP4:
this.prefix = new LispIpv4Address(IpAddress.makeMaskedAddress(
IpAddress.valueOf(prefix.toString()), maskLength));
break;
case IP6:
this.prefix = new LispIpv6Address(IpAddress.makeMaskedAddress(
IpAddress.valueOf(prefix.toString()), maskLength));
break;
default:
this.prefix = prefix;
}
this.hash = 31 * 17 + Objects.hashCode(maskLength, prefix);
}