Skip to content

Commit e46f029

Browse files
committed
optimize: cache common entity lookups in CallContext during VM deployment
This change introduces caching of frequently accessed entities in CallContext to reduce repeated database lookups during VM deployment. By reusing cached values for common entries, the deployment workflow becomes more efficient, reducing overhead and improving overall performance. Signed-off-by: Abhishek Kumar <[email protected]>
1 parent c253d56 commit e46f029

File tree

19 files changed

+316
-171
lines changed

19 files changed

+316
-171
lines changed

api/src/main/java/org/apache/cloudstack/context/CallContext.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
// under the License.
1717
package org.apache.cloudstack.context;
1818

19+
import java.time.Duration;
1920
import java.util.HashMap;
2021
import java.util.Map;
2122
import java.util.Stack;
@@ -65,6 +66,7 @@ protected Stack<CallContext> initialValue() {
6566
private final Map<Object, Object> context = new HashMap<Object, Object>();
6667
private Project project;
6768
private String apiName;
69+
private final RequestEntityCache requestEntityCache = new RequestEntityCache(Duration.ofSeconds(60));
6870

6971
static EntityManager s_entityMgr;
7072

@@ -423,4 +425,8 @@ public String toString() {
423425
.append("]")
424426
.toString();
425427
}
428+
429+
public RequestEntityCache getRequestEntityCache() {
430+
return requestEntityCache;
431+
}
426432
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package org.apache.cloudstack.context;
19+
20+
import java.time.Duration;
21+
import java.util.concurrent.ConcurrentHashMap;
22+
import java.util.function.Supplier;
23+
24+
public final class RequestEntityCache {
25+
private static final class Key {
26+
final Class<?> type;
27+
final long id;
28+
Key(Class<?> type, long id) { this.type = type; this.id = id; }
29+
@Override public boolean equals(Object o) {
30+
if (this == o) return true;
31+
if (!(o instanceof Key)) return false;
32+
Key k = (Key) o; return id == k.id && type.equals(k.type);
33+
}
34+
@Override public int hashCode() { return 31 * type.hashCode() + Long.hashCode(id); }
35+
}
36+
private static final class Entry {
37+
final Object value; final long expireAtNanos; // 0 = never expires
38+
Entry(Object v, long exp) { value = v; expireAtNanos = exp; }
39+
}
40+
41+
private final ConcurrentHashMap<Key, Entry> map = new ConcurrentHashMap<>();
42+
private final long ttlNanos; // 0 = no TTL
43+
44+
public RequestEntityCache(Duration ttl) { this.ttlNanos = ttl == null ? 0 : ttl.toNanos(); }
45+
46+
@SuppressWarnings("unchecked")
47+
public <T> T get(Class<T> type, long id, Supplier<T> loader) {
48+
final long now = System.nanoTime();
49+
final Key key = new Key(type, id);
50+
Entry e = map.get(key);
51+
if (e != null && (e.expireAtNanos == 0 || now < e.expireAtNanos)) {
52+
return (T) e.value;
53+
}
54+
T loaded = loader.get();
55+
long exp = ttlNanos == 0 ? 0 : now + ttlNanos;
56+
if (loaded != null) map.put(key, new Entry(loaded, exp));
57+
return loaded;
58+
}
59+
60+
public void clear() { map.clear(); }
61+
}

engine/components-api/src/main/java/com/cloud/event/UsageEventUtils.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
import javax.inject.Inject;
2727

2828
import com.cloud.network.Network;
29+
30+
import org.apache.cloudstack.context.CallContext;
2931
import org.apache.commons.collections.MapUtils;
3032
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
3133

@@ -215,7 +217,8 @@ private static void publishUsageEvent(String usageEventType, Long accountId, Lon
215217
}
216218

217219
Account account = s_accountDao.findById(accountId);
218-
DataCenterVO dc = s_dcDao.findById(zoneId);
220+
DataCenterVO dc = CallContext.current().getRequestEntityCache().get(DataCenterVO.class, zoneId,
221+
() -> s_dcDao.findById(zoneId));
219222

220223
// if account has been deleted, this might be called during cleanup of resources and results in null pointer
221224
if (account == null)

engine/orchestration/src/main/java/com/cloud/agent/manager/ClusteredAgentManagerImpl.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444

4545
import com.cloud.resource.ResourceState;
4646
import org.apache.cloudstack.ca.CAManager;
47+
import org.apache.cloudstack.context.CallContext;
4748
import org.apache.cloudstack.framework.config.ConfigDepot;
4849
import org.apache.cloudstack.framework.config.ConfigKey;
4950
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
@@ -573,7 +574,8 @@ public SocketChannel connectToPeer(final long hostId, final SocketChannel prevCh
573574
@Override
574575
protected AgentAttache getAttache(final Long hostId) throws AgentUnavailableException {
575576
assert hostId != null : "Who didn't check their id value?";
576-
final HostVO host = _hostDao.findById(hostId);
577+
final HostVO host = CallContext.current().getRequestEntityCache()
578+
.get(HostVO.class, hostId, () -> _hostDao.findById(hostId));
577579
if (host == null) {
578580
throw new AgentUnavailableException("Can't find the host ", hostId);
579581
}

engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1552,7 +1552,9 @@ public void orchestrateStart(final String vmUuid, final Map<VirtualMachineProfil
15521552

15531553
startedVm = vm;
15541554
logger.debug("Start completed for VM {}", vm);
1555-
final Host vmHost = _hostDao.findById(destHostId);
1555+
long finalDestHostId = destHostId;
1556+
final Host vmHost = CallContext.current().getRequestEntityCache().get(HostVO.class, finalDestHostId,
1557+
() -> _hostDao.findById(finalDestHostId));
15561558
if (vmHost != null && (VirtualMachine.Type.ConsoleProxy.equals(vm.getType()) ||
15571559
VirtualMachine.Type.SecondaryStorageVm.equals(vm.getType())) && caManager.canProvisionCertificates()) {
15581560
for (int retries = 3; retries > 0; retries--) {
@@ -1748,7 +1750,8 @@ private void addToNetworkNameMap(long networkId, long dataCenterId, Map<Long, St
17481750
NetworkVO networkVO = _networkDao.findById(networkId);
17491751
Account acc = accountDao.findById(networkVO.getAccountId());
17501752
Domain domain = domainDao.findById(networkVO.getDomainId());
1751-
DataCenter zone = _dcDao.findById(dataCenterId);
1753+
DataCenter zone = CallContext.current().getRequestEntityCache()
1754+
.get(DataCenterVO.class, dataCenterId, () -> _dcDao.findById(dataCenterId));
17521755
if (Objects.isNull(zone)) {
17531756
throw new CloudRuntimeException(String.format("Failed to find zone with ID: %s", dataCenterId));
17541757
}

engine/schema/src/main/java/com/cloud/dc/ClusterDetailsDaoImpl.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
import javax.inject.Inject;
2626

27+
import org.apache.cloudstack.context.CallContext;
2728
import org.apache.cloudstack.framework.config.ConfigKey;
2829
import org.apache.cloudstack.framework.config.ConfigKey.Scope;
2930
import org.apache.cloudstack.framework.config.ScopedConfigStorage;
@@ -197,7 +198,8 @@ private String getCpuMemoryOvercommitRatio(String name) {
197198

198199
@Override
199200
public Pair<Scope, Long> getParentScope(long id) {
200-
Cluster cluster = clusterDao.findById(id);
201+
Cluster cluster = CallContext.current().getRequestEntityCache().get(ClusterVO.class, id,
202+
() -> clusterDao.findById(id));
201203
if (cluster == null) {
202204
return null;
203205
}

engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java

Lines changed: 34 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -16,62 +16,61 @@
1616
// under the License.
1717
package org.apache.cloudstack.storage.allocator;
1818

19-
import com.cloud.api.query.dao.StoragePoolJoinDao;
20-
import com.cloud.dc.dao.HostPodDao;
21-
import com.cloud.exception.StorageUnavailableException;
22-
import com.cloud.host.HostVO;
23-
import com.cloud.host.dao.HostDao;
24-
import com.cloud.storage.ScopeType;
25-
import com.cloud.storage.StoragePoolStatus;
26-
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO;
27-
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
28-
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
29-
import org.apache.commons.lang3.StringUtils;
30-
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
31-
import org.apache.commons.collections.CollectionUtils;
19+
import java.math.BigDecimal;
20+
import java.security.SecureRandom;
21+
import java.text.DecimalFormat;
22+
import java.util.ArrayList;
23+
import java.util.Arrays;
24+
import java.util.Collections;
25+
import java.util.Comparator;
26+
import java.util.HashMap;
27+
import java.util.LinkedHashMap;
28+
import java.util.List;
29+
import java.util.Map;
30+
import java.util.stream.Collectors;
3231

33-
import com.cloud.utils.Pair;
32+
import javax.inject.Inject;
33+
import javax.naming.ConfigurationException;
34+
35+
import org.apache.cloudstack.context.CallContext;
36+
import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService;
3437
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
3538
import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator;
3639
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
3740
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
41+
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO;
42+
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
43+
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
44+
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
45+
import org.apache.commons.collections.CollectionUtils;
46+
import org.apache.commons.lang3.StringUtils;
3847

48+
import com.cloud.api.query.dao.StoragePoolJoinDao;
3949
import com.cloud.capacity.Capacity;
4050
import com.cloud.capacity.dao.CapacityDao;
4151
import com.cloud.dc.ClusterVO;
4252
import com.cloud.dc.dao.ClusterDao;
4353
import com.cloud.deploy.DeploymentPlan;
4454
import com.cloud.deploy.DeploymentPlanner.ExcludeList;
55+
import com.cloud.exception.StorageUnavailableException;
56+
import com.cloud.host.HostVO;
57+
import com.cloud.host.dao.HostDao;
4558
import com.cloud.hypervisor.Hypervisor.HypervisorType;
59+
import com.cloud.storage.ScopeType;
4660
import com.cloud.storage.Storage;
4761
import com.cloud.storage.StorageManager;
4862
import com.cloud.storage.StoragePool;
63+
import com.cloud.storage.StoragePoolStatus;
4964
import com.cloud.storage.StorageUtil;
5065
import com.cloud.storage.Volume;
5166
import com.cloud.storage.dao.VolumeDao;
5267
import com.cloud.user.Account;
5368
import com.cloud.utils.NumbersUtil;
69+
import com.cloud.utils.Pair;
5470
import com.cloud.utils.component.AdapterBase;
5571
import com.cloud.vm.DiskProfile;
5672
import com.cloud.vm.VirtualMachineProfile;
5773

58-
import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService;
59-
60-
import javax.inject.Inject;
61-
import javax.naming.ConfigurationException;
62-
import java.math.BigDecimal;
63-
import java.security.SecureRandom;
64-
import java.text.DecimalFormat;
65-
import java.util.ArrayList;
66-
import java.util.Arrays;
67-
import java.util.Collections;
68-
import java.util.Comparator;
69-
import java.util.HashMap;
70-
import java.util.LinkedHashMap;
71-
import java.util.List;
72-
import java.util.Map;
73-
import java.util.stream.Collectors;
74-
7574
public abstract class AbstractStoragePoolAllocator extends AdapterBase implements StoragePoolAllocator {
7675

7776
protected BigDecimal storageOverprovisioningFactor = new BigDecimal(1);
@@ -88,8 +87,6 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement
8887
@Inject private StoragePoolDetailsDao storagePoolDetailsDao;
8988
@Inject
9089
protected HostDao hostDao;
91-
@Inject
92-
protected HostPodDao podDao;
9390

9491
/**
9592
* make sure shuffled lists of Pools are really shuffled
@@ -304,7 +301,8 @@ protected boolean filter(ExcludeList avoid, StoragePool pool, DiskProfile dskCh,
304301

305302
Long clusterId = pool.getClusterId();
306303
if (clusterId != null) {
307-
ClusterVO cluster = clusterDao.findById(clusterId);
304+
ClusterVO cluster = CallContext.current().getRequestEntityCache()
305+
.get(ClusterVO.class, clusterId, () -> clusterDao.findById(clusterId));
308306
if (!(cluster.getHypervisorType() == dskCh.getHypervisorType())) {
309307
if (logger.isDebugEnabled()) {
310308
logger.debug("StoragePool's Cluster does not have required hypervisorType, skipping this pool");
@@ -329,7 +327,8 @@ protected boolean filter(ExcludeList avoid, StoragePool pool, DiskProfile dskCh,
329327
}
330328

331329
if (plan.getHostId() != null) {
332-
HostVO plannedHost = hostDao.findById(plan.getHostId());
330+
HostVO plannedHost = CallContext.current().getRequestEntityCache()
331+
.get(HostVO.class, plan.getHostId(), () -> hostDao.findById(plan.getHostId()));
333332
if (!storageMgr.checkIfHostAndStoragePoolHasCommonStorageAccessGroups(plannedHost, pool)) {
334333
if (logger.isDebugEnabled()) {
335334
logger.debug(String.format("StoragePool %s and host %s does not have matching storage access groups", pool, plannedHost));

0 commit comments

Comments
 (0)