From b1b07f942fd77b7d987cc44aabd492db5e8c29a0 Mon Sep 17 00:00:00 2001 From: Jan Srnicek Date: Tue, 15 Aug 2017 10:15:06 +0200 Subject: HC2VPP-205 / HC2VPP-25 - Sub interface caching improvement Improves dumping mechanism for sub interfaces in a way that if full dump was already performed, it will be used from cache instead of new dump call. Also prevents creating dump dto's if not necessary Change-Id: I46a305ec1b325c2d949bff9e2b151752ccd508b6 Signed-off-by: Jan Srnicek --- .../cache/InterfaceCacheDumpManager.java | 59 ++++++ .../cache/InterfaceCacheDumpManagerImpl.java | 197 +++++++++++++++++++++ .../cache/InterfaceCacheDumpManagerProvider.java | 38 ++++ .../cache/StaticCacheKeyFactory.java | 45 +++++ 4 files changed, 339 insertions(+) create mode 100644 v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/cache/InterfaceCacheDumpManager.java create mode 100644 v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/cache/InterfaceCacheDumpManagerImpl.java create mode 100644 v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/cache/InterfaceCacheDumpManagerProvider.java create mode 100644 v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/cache/StaticCacheKeyFactory.java (limited to 'v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/cache') diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/cache/InterfaceCacheDumpManager.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/cache/InterfaceCacheDumpManager.java new file mode 100644 index 000000000..589d59024 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/cache/InterfaceCacheDumpManager.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2017 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.hc2vpp.v3po.interfacesstate.cache; + +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.vpp.jvpp.core.dto.SwInterfaceDetails; +import java.util.stream.Stream; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Manager for dump data of interfaces. The main purpose of this manager is to cache common interface data between + * various classes that process this kind of data. If reader does not use this utility, it introduces a big overhead + * because of size/complexity of interfaces dump + */ +public interface InterfaceCacheDumpManager { + + /** + * Provides stream of all currently configured vpp interfaces + * + * @param identifier id of currently processed data + * @param ctx context of current transaction + * @return {@link Stream} of currently configured interfaces + * @throws ReadFailedException if dumping of data was unsuccessful + */ + @Nonnull + Stream getInterfaces(@Nonnull final InstanceIdentifier identifier, + @Nonnull final ReadContext ctx) throws ReadFailedException; + + /** + * Provides details of interface + * + * @param identifier id of currently processed data + * @param ctx context of current transaction + * @param interfaceName name of requested interface + * @return {@link SwInterfaceDetails} of requested interface + * @throws ReadFailedException if dumping of data was unsuccessful + */ + @Nullable + SwInterfaceDetails getInterfaceDetail(@Nonnull final InstanceIdentifier identifier, + @Nonnull final ReadContext ctx, + @Nonnull final String interfaceName) throws ReadFailedException; +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/cache/InterfaceCacheDumpManagerImpl.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/cache/InterfaceCacheDumpManagerImpl.java new file mode 100644 index 000000000..e9b114018 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/cache/InterfaceCacheDumpManagerImpl.java @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2017 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.hc2vpp.v3po.interfacesstate.cache; + +import static io.fd.hc2vpp.common.translate.util.JvppReplyConsumer.INSTANCE; +import static io.fd.honeycomb.translate.util.read.cache.EntityDumpExecutor.NO_PARAMS; +import static java.util.stream.Collectors.toMap; + +import io.fd.hc2vpp.common.translate.util.ByteDataTranslator; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.ModificationCache; +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.util.read.cache.DumpCacheManager; +import io.fd.honeycomb.translate.util.read.cache.EntityDumpExecutor; +import io.fd.vpp.jvpp.core.dto.SwInterfaceDetails; +import io.fd.vpp.jvpp.core.dto.SwInterfaceDetailsReplyDump; +import io.fd.vpp.jvpp.core.dto.SwInterfaceDump; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Stream; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Manager for dump data of interfaces/sub-interfaces + */ +final class InterfaceCacheDumpManagerImpl implements InterfaceCacheDumpManager { + + private static final Logger LOG = LoggerFactory.getLogger(InterfaceCacheDumpManagerImpl.class); + + // byNameIndex must be cached, not held as reference here, to have it destroyed with cache after transaction + static final String BY_NAME_INDEX_KEY = InterfaceCacheDumpManagerImpl.class.getName() + "_byNameIndex"; + private NamingContext namingContext; + private final DumpCacheManager specificDumpManager; + private final DumpCacheManager fullDumpManager; + + InterfaceCacheDumpManagerImpl(@Nonnull final FutureJVppCore jvpp, + @Nonnull final NamingContext namingContext) { + this.namingContext = namingContext; + specificDumpManager = specificInterfaceDumpManager(jvpp); + fullDumpManager = fullInterfaceDumpManager(jvpp, + new StaticCacheKeyFactory(StaticCacheKeyFactory.class.getName() + "_dump")); + } + + @Override + @Nonnull + public synchronized Stream getInterfaces(@Nonnull final InstanceIdentifier identifier, + @Nonnull final ReadContext ctx) + throws ReadFailedException { + LOG.debug("Reading all interfaces[{}]", identifier); + return initMapAndGet(identifier, ctx).entrySet().stream().map(Map.Entry::getValue); + } + + @Override + @Nullable + public synchronized SwInterfaceDetails getInterfaceDetail(@Nonnull final InstanceIdentifier identifier, + @Nonnull final ReadContext ctx, + @Nonnull final String interfaceName) + throws ReadFailedException { + final Map interfaceIndex = getMap(ctx); + + // does not attempt to cover cases with concurrent updates, as tx should be atomic + if (interfaceIndex != null) { + // tries to find interface in map + return interfaceIndex.get(interfaceName); + } else { + // if map is not present, use specific dump(it will be cached standard way, under key constructed from IID) + return dumpSpecificDetail(identifier, ctx, interfaceName); + } + } + + private SwInterfaceDetails dumpSpecificDetail(@Nonnull final InstanceIdentifier identifier, + @Nonnull final ReadContext ctx, + @Nonnull final String interfaceName) + throws ReadFailedException { + LOG.debug("Interface {} not present in cached data, performing specific dump[{}]", interfaceName, + identifier); + final SwInterfaceDetailsReplyDump reply = + specificDumpManager.getDump(identifier, ctx.getModificationCache(), interfaceName) + .or(new SwInterfaceDetailsReplyDump()); + + if (reply.swInterfaceDetails.isEmpty()) { + return null; + } + + return reply.swInterfaceDetails.get(0); + } + + private Map initMapAndGet(final InstanceIdentifier identifier, final ReadContext ctx) + throws ReadFailedException { + + final ModificationCache cache = ctx.getModificationCache(); + if (!cache.containsKey(BY_NAME_INDEX_KEY)) { + LOG.debug("Performing dump[{}]", identifier); + final SwInterfaceDetailsReplyDump dump = + fullDumpManager.getDump(identifier, cache, NO_PARAMS) + .or(new SwInterfaceDetailsReplyDump()); + + // naming context initialization must be done here, as it is uses getName in next step, therefore it would + // create artificial mapping for every interface, because this happens before interface dump is processed + dump.swInterfaceDetails.forEach((elt) -> { + // Store interface name from VPP in context if not yet present + if (!namingContext.containsName(elt.swIfIndex, ctx.getMappingContext())) { + namingContext.addName(elt.swIfIndex, ByteDataTranslator.INSTANCE.toString(elt.interfaceName), + ctx.getMappingContext()); + } + LOG.trace("Interface with name: {}, VPP name: {} and index: {} found in VPP", + getInterfaceName(ctx, elt), + elt.interfaceName, + elt.swIfIndex); + }); + + final Map freshIndex = dump.swInterfaceDetails.stream() + .collect(toMap(detail -> getInterfaceName(ctx, detail), + detail -> detail)); + putMap(freshIndex, ctx); + } + + return getMap(ctx); + } + + private String getInterfaceName(final ReadContext ctx, final SwInterfaceDetails elt) { + return namingContext.getName(elt.swIfIndex, ctx.getMappingContext()); + } + + private static Map getMap(final ReadContext ctx) { + return (Map) ctx.getModificationCache().get(BY_NAME_INDEX_KEY); + } + + private static void putMap(final Map map, final ReadContext ctx) { + ctx.getModificationCache().put(BY_NAME_INDEX_KEY, map); + } + + + private static DumpCacheManager fullInterfaceDumpManager( + final FutureJVppCore jvpp, + final StaticCacheKeyFactory cacheKeyFactory) { + return new DumpCacheManager.DumpCacheManagerBuilder() + .withExecutor(fullInterfaceDumpExecutor(jvpp)) + .withCacheKeyFactory(cacheKeyFactory) + .acceptOnly(SwInterfaceDetailsReplyDump.class) + .build(); + } + + private static DumpCacheManager specificInterfaceDumpManager( + final FutureJVppCore jvpp) { + return new DumpCacheManager.DumpCacheManagerBuilder() + .withExecutor(specificInterfaceDumpExecutor(jvpp)) + .acceptOnly(SwInterfaceDetailsReplyDump.class) + .build(); + } + + private static EntityDumpExecutor fullInterfaceDumpExecutor( + final FutureJVppCore api) { + return (identifier, params) -> { + final SwInterfaceDump request = new SwInterfaceDump(); + request.nameFilter = "".getBytes(); + request.nameFilterValid = 0; + + final CompletableFuture + swInterfaceDetailsReplyDumpCompletableFuture = api.swInterfaceDump(request).toCompletableFuture(); + return INSTANCE.getReplyForRead(swInterfaceDetailsReplyDumpCompletableFuture, identifier); + }; + } + + private static EntityDumpExecutor specificInterfaceDumpExecutor( + final FutureJVppCore api) { + return (identifier, ifaceName) -> { + final SwInterfaceDump request = new SwInterfaceDump(); + request.nameFilter = ifaceName.getBytes(); + request.nameFilterValid = 1; + + final CompletableFuture + swInterfaceDetailsReplyDumpCompletableFuture = api.swInterfaceDump(request).toCompletableFuture(); + return INSTANCE.getReplyForRead(swInterfaceDetailsReplyDumpCompletableFuture, identifier); + }; + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/cache/InterfaceCacheDumpManagerProvider.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/cache/InterfaceCacheDumpManagerProvider.java new file mode 100644 index 000000000..7a6119bf1 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/cache/InterfaceCacheDumpManagerProvider.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2017 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.hc2vpp.v3po.interfacesstate.cache; + +import com.google.inject.Inject; +import com.google.inject.Provider; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import javax.inject.Named; + +public class InterfaceCacheDumpManagerProvider implements Provider { + + @Inject + private FutureJVppCore jvpp; + + @Inject + @Named("interface-context") + private NamingContext namingContext; + + @Override + public InterfaceCacheDumpManager get() { + return new InterfaceCacheDumpManagerImpl(jvpp, namingContext); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/cache/StaticCacheKeyFactory.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/cache/StaticCacheKeyFactory.java new file mode 100644 index 000000000..e34e915e2 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfacesstate/cache/StaticCacheKeyFactory.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2017 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.hc2vpp.v3po.interfacesstate.cache; + +import io.fd.honeycomb.translate.util.read.cache.CacheKeyFactory; +import io.fd.vpp.jvpp.core.dto.SwInterfaceDetailsReplyDump; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public class StaticCacheKeyFactory implements CacheKeyFactory { + + private final String key; + + public StaticCacheKeyFactory(@Nonnull final String key) { + this.key = key; + } + + @Nonnull + @Override + public String createKey(@Nonnull final InstanceIdentifier actualContextIdentifier, + @Nullable final Void dumpParams) { + return key; + } + + @Nonnull + @Override + public Class getCachedDataType() { + return SwInterfaceDetailsReplyDump.class; + } +} -- cgit 1.2.3-korg