Skip to content

Commit

Permalink
Use InstanceLifecycleListener for L2 cache eviction
Browse files Browse the repository at this point in the history
Signed-off-by: nscuro <[email protected]>
  • Loading branch information
nscuro committed Jan 13, 2024
1 parent 23cb76f commit 46f1afe
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* SPDX-License-Identifier: Apache-2.0
* Copyright (c) Steve Springett. All Rights Reserved.
*/
package org.dependencytrack.persistence;
package org.dependencytrack.persistence.listener;

import alpine.event.framework.Event;
import org.dependencytrack.event.IndexEvent;
Expand All @@ -39,11 +39,11 @@
*
* @since 4.11.0
*/
public class IndexingObjectLifecycleListener implements DeleteLifecycleListener, StoreLifecycleListener {
public class IndexingInstanceLifecycleListener implements DeleteLifecycleListener, StoreLifecycleListener {

private final Consumer<Event> eventConsumer;

public IndexingObjectLifecycleListener(final Consumer<Event> eventConsumer) {
public IndexingInstanceLifecycleListener(final Consumer<Event> eventConsumer) {
this.eventConsumer = eventConsumer;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* This file is part of Dependency-Track.
*
* 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.
*
* SPDX-License-Identifier: Apache-2.0
* Copyright (c) Steve Springett. All Rights Reserved.
*/
package org.dependencytrack.persistence.listener;

import org.dependencytrack.persistence.QueryManager;

import javax.jdo.JDOHelper;
import javax.jdo.listener.DeleteLifecycleListener;
import javax.jdo.listener.InstanceLifecycleEvent;
import javax.jdo.listener.InstanceLifecycleListener;
import javax.jdo.listener.StoreLifecycleListener;

import static org.dependencytrack.util.PersistenceUtil.evictFromL2Cache;

/**
* An {@link InstanceLifecycleListener} that evicts objects from the L2 cache upon modification or deletion.
*
* @since 4.11.0
*/
public class L2CacheEvictingInstanceLifecycleListener implements DeleteLifecycleListener, StoreLifecycleListener {

private final QueryManager qm;

public L2CacheEvictingInstanceLifecycleListener(final QueryManager qm) {
this.qm = qm;
}

@Override
public void preDelete(final InstanceLifecycleEvent event) {
}

@Override
public void postDelete(final InstanceLifecycleEvent event) {
evictFromL2Cache(qm, event.getPersistentInstance());
}

@Override
public void preStore(final InstanceLifecycleEvent event) {
}

@Override
public void postStore(final InstanceLifecycleEvent event) {
final Object instance = event.getPersistentInstance();
if (JDOHelper.isDirty(instance)) {
evictFromL2Cache(qm, instance);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,9 @@
import org.dependencytrack.notification.NotificationScope;
import org.dependencytrack.notification.vo.BomConsumedOrProcessed;
import org.dependencytrack.notification.vo.BomProcessingFailed;
import org.dependencytrack.persistence.IndexingObjectLifecycleListener;
import org.dependencytrack.persistence.QueryManager;
import org.dependencytrack.persistence.listener.IndexingInstanceLifecycleListener;
import org.dependencytrack.persistence.listener.L2CacheEvictingInstanceLifecycleListener;
import org.dependencytrack.util.InternalComponentIdentifier;
import org.json.JSONArray;
import org.slf4j.MDC;
Expand Down Expand Up @@ -101,7 +102,6 @@
import static org.dependencytrack.parser.cyclonedx.util.ModelConverter.flatten;
import static org.dependencytrack.util.PersistenceUtil.applyIfChanged;
import static org.dependencytrack.util.PersistenceUtil.assertPersistent;
import static org.dependencytrack.util.PersistenceUtil.evictFromL2Cache;

/**
* @since 4.11.0
Expand Down Expand Up @@ -227,9 +227,7 @@ private void processBom(final Context ctx, final org.cyclonedx.model.Bom cdxBom)

dispatchBomConsumedNotification(ctx);

final var processedProject = new Project[1];
final var processedComponents = new ArrayList<Component>(components.size());
final var processedServices = new ArrayList<ServiceComponent>(services.size());
try (final var qm = new QueryManager().withL2CacheDisabled()) {
// Disable reachability checks on commit.
// See https://www.datanucleus.org/products/accessplatform_4_1/jdo/performance_tuning.html
Expand Down Expand Up @@ -269,8 +267,10 @@ private void processBom(final Context ctx, final org.cyclonedx.model.Bom cdxBom)
// See https://www.datanucleus.org/products/accessplatform_6_0/jdo/persistence.html#lifecycle
qm.getPersistenceManager().setProperty(PROPERTY_RETAIN_VALUES, "true");

qm.getPersistenceManager().addInstanceLifecycleListener(new IndexingObjectLifecycleListener(eventsToDispatch::add),
qm.getPersistenceManager().addInstanceLifecycleListener(new IndexingInstanceLifecycleListener(eventsToDispatch::add),
Component.class, Project.class, ProjectMetadata.class, ServiceComponent.class);
qm.getPersistenceManager().addInstanceLifecycleListener(new L2CacheEvictingInstanceLifecycleListener(qm),
Bom.class, Component.class, Project.class, ProjectMetadata.class, ServiceComponent.class);

final List<Component> finalComponents = components;
final List<ServiceComponent> finalServices = services;
Expand All @@ -291,24 +291,15 @@ private void processBom(final Context ctx, final org.cyclonedx.model.Bom cdxBom)
processComponents(qm, persistentProject, finalComponents, identitiesByBomRef, bomRefsByIdentity);

LOGGER.info("Processing %d services".formatted(finalServices.size()));
final Map<ComponentIdentity, ServiceComponent> persistentServicesByIdentity =
processServices(qm, persistentProject, finalServices, identitiesByBomRef, bomRefsByIdentity);
processServices(qm, persistentProject, finalServices, identitiesByBomRef, bomRefsByIdentity);

LOGGER.info("Processing %d dependency graph entries".formatted(numDependencyGraphEntries));
processDependencyGraph(qm, persistentProject, dependencyGraph, persistentComponentsByIdentity, identitiesByBomRef);

recordBomImport(ctx, qm, persistentProject);

processedProject[0] = persistentProject;
processedComponents.addAll(persistentComponentsByIdentity.values());
processedServices.addAll(persistentServicesByIdentity.values());
});

// Ensure other areas of the application that still use L2 caching do
// not operate on stale data.
evictFromL2Cache(qm, processedProject[0]);
evictFromL2Cache(qm, processedComponents);
evictFromL2Cache(qm, processedServices);
}

eventsToDispatch.add(createVulnAnalysisEvent(ctx, processedComponents));
Expand Down

0 comments on commit 46f1afe

Please sign in to comment.