blob: b959c179c3a97f6a773da223642512f8362bae40 [file] [log] [blame]
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Sho SHIMIZUe18cb122016-02-22 21:04:56 -080016package org.onosproject.store.resource.impl;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070017
18import com.google.common.annotations.Beta;
Sho SHIMIZUe7db6142015-11-04 11:24:22 -080019import com.google.common.collect.ImmutableList;
Sho SHIMIZU83258ae2016-01-29 17:39:07 -080020import com.google.common.collect.ImmutableSet;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070021import org.apache.felix.scr.annotations.Activate;
22import org.apache.felix.scr.annotations.Component;
23import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
25import org.apache.felix.scr.annotations.Service;
Sho SHIMIZUe18cb122016-02-22 21:04:56 -080026import org.onosproject.net.resource.ContinuousResource;
27import org.onosproject.net.resource.ContinuousResourceId;
28import org.onosproject.net.resource.DiscreteResource;
29import org.onosproject.net.resource.DiscreteResourceId;
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070030import org.onosproject.net.resource.Resource;
Sho SHIMIZUe18cb122016-02-22 21:04:56 -080031import org.onosproject.net.resource.ResourceAllocation;
32import org.onosproject.net.resource.ResourceConsumer;
33import org.onosproject.net.resource.ResourceEvent;
34import org.onosproject.net.resource.ResourceId;
Sho SHIMIZUe18cb122016-02-22 21:04:56 -080035import org.onosproject.net.resource.ResourceStore;
36import org.onosproject.net.resource.ResourceStoreDelegate;
37import org.onosproject.net.resource.Resources;
Sho SHIMIZUfa62b472015-11-02 17:35:46 -080038import org.onosproject.store.AbstractStore;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070039import org.onosproject.store.serializers.KryoNamespaces;
Madan Jampani3780d4b2016-04-04 18:18:24 -070040import org.onosproject.store.service.CommitStatus;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070041import org.onosproject.store.service.Serializer;
42import org.onosproject.store.service.StorageService;
43import org.onosproject.store.service.TransactionContext;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070044import org.slf4j.Logger;
45import org.slf4j.LoggerFactory;
46
Sho SHIMIZUba41fc12015-08-12 15:43:22 -070047import java.util.Arrays;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070048import java.util.Collection;
Sho SHIMIZU69420fe2016-02-09 15:01:07 -080049import java.util.LinkedHashMap;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070050import java.util.List;
51import java.util.Map;
52import java.util.Optional;
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -080053import java.util.Set;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070054import java.util.stream.Collectors;
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -080055import java.util.stream.Stream;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070056
57import static com.google.common.base.Preconditions.checkArgument;
58import static com.google.common.base.Preconditions.checkNotNull;
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070059import static org.onosproject.net.resource.ResourceEvent.Type.RESOURCE_ADDED;
60import static org.onosproject.net.resource.ResourceEvent.Type.RESOURCE_REMOVED;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070061
62/**
63 * Implementation of ResourceStore using TransactionalMap.
64 */
Sho SHIMIZU9a2b8292015-10-28 13:00:16 -070065@Component(immediate = true)
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070066@Service
67@Beta
Sho SHIMIZUfa62b472015-11-02 17:35:46 -080068public class ConsistentResourceStore extends AbstractStore<ResourceEvent, ResourceStoreDelegate>
69 implements ResourceStore {
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070070 private static final Logger log = LoggerFactory.getLogger(ConsistentResourceStore.class);
71
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070072 static final Serializer SERIALIZER = Serializer.using(
HIGUCHI Yuta6f584222016-05-06 11:15:38 -070073 Arrays.asList(KryoNamespaces.API),
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -080074 ContinuousResourceAllocation.class);
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070075
Thomas Vachuska762a2d82016-01-04 10:25:20 -080076 // TODO: We should provide centralized values for this
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070077 static final int MAX_RETRIES = 5;
78 static final int RETRY_DELAY = 1_000; // millis
Thomas Vachuska762a2d82016-01-04 10:25:20 -080079
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070080 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
81 protected StorageService service;
82
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070083 private ConsistentDiscreteResourceStore discreteStore;
84 private ConsistentContinuousResourceStore continuousStore;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070085
86 @Activate
87 public void activate() {
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070088 discreteStore = new ConsistentDiscreteResourceStore(service);
89 continuousStore = new ConsistentContinuousResourceStore(service);
Sho SHIMIZUe7db6142015-11-04 11:24:22 -080090
Madan Jampanic7f49f92015-12-10 11:35:06 -080091 log.info("Started");
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070092 }
93
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -080094 // Computational complexity: O(1) if the resource is discrete type.
95 // O(n) if the resource is continuous type where n is the number of the existing allocations for the resource
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070096 @Override
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -080097 public List<ResourceAllocation> getResourceAllocations(ResourceId id) {
98 checkNotNull(id);
99 checkArgument(id instanceof DiscreteResourceId || id instanceof ContinuousResourceId);
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700100
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800101 if (id instanceof DiscreteResourceId) {
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700102 return discreteStore.getResourceAllocations((DiscreteResourceId) id);
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800103 } else {
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700104 return continuousStore.getResourceAllocations((ContinuousResourceId) id);
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800105 }
106 }
107
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700108 @Override
Jonathan Hart56151262016-02-11 09:48:50 -0800109 public boolean register(List<Resource> resources) {
Sho SHIMIZU83e17a02015-08-20 14:07:05 -0700110 checkNotNull(resources);
HIGUCHI Yuta6f828c32016-01-20 18:11:05 -0800111 if (log.isTraceEnabled()) {
112 resources.forEach(r -> log.trace("registering {}", r));
113 }
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700114
115 TransactionContext tx = service.transactionContextBuilder().build();
116 tx.begin();
117
Sho SHIMIZU69420fe2016-02-09 15:01:07 -0800118 // the order is preserved by LinkedHashMap
Sho SHIMIZUf33b8932016-01-25 18:43:32 -0800119 Map<DiscreteResource, List<Resource>> resourceMap = resources.stream()
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800120 .filter(x -> x.parent().isPresent())
Sho SHIMIZU69420fe2016-02-09 15:01:07 -0800121 .collect(Collectors.groupingBy(x -> x.parent().get(), LinkedHashMap::new, Collectors.toList()));
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700122
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700123 TransactionalDiscreteResourceStore discreteTxStore = discreteStore.transactional(tx);
124 TransactionalContinuousResourceStore continuousTxStore = continuousStore.transactional(tx);
125 for (Map.Entry<DiscreteResource, List<Resource>> entry : resourceMap.entrySet()) {
Sho SHIMIZU001e3f22016-05-06 14:46:15 -0700126 DiscreteResourceId parentId = entry.getKey().id();
127 if (!lookup(discreteTxStore, parentId).isPresent()) {
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800128 return abortTransaction(tx);
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700129 }
130
Sho SHIMIZU001e3f22016-05-06 14:46:15 -0700131 if (!appendValues(discreteTxStore, continuousTxStore, parentId, entry.getValue())) {
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800132 return abortTransaction(tx);
133 }
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700134 }
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800135
Madan Jampani3780d4b2016-04-04 18:18:24 -0700136 return tx.commit().whenComplete((status, error) -> {
137 if (status == CommitStatus.SUCCESS) {
138 log.trace("Transaction commit succeeded on registration: resources={}", resources);
139 List<ResourceEvent> events = resources.stream()
140 .filter(x -> x.parent().isPresent())
141 .map(x -> new ResourceEvent(RESOURCE_ADDED, x))
142 .collect(Collectors.toList());
143 notifyDelegate(events);
144 } else {
145 log.warn("Transaction commit failed on registration", error);
146 }
147 }).join() == CommitStatus.SUCCESS;
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700148 }
149
150 @Override
Jonathan Hart56151262016-02-11 09:48:50 -0800151 public boolean unregister(List<ResourceId> ids) {
Sho SHIMIZU72f81b12016-02-09 09:26:17 -0800152 checkNotNull(ids);
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700153
154 TransactionContext tx = service.transactionContextBuilder().build();
155 tx.begin();
156
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700157 TransactionalDiscreteResourceStore discreteTxStore = discreteStore.transactional(tx);
158 TransactionalContinuousResourceStore continuousTxStore = continuousStore.transactional(tx);
Sho SHIMIZU7d54d9c2016-02-17 13:58:46 -0800159 // Look up resources by resource IDs
Sho SHIMIZU72f81b12016-02-09 09:26:17 -0800160 List<Resource> resources = ids.stream()
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800161 .filter(x -> x.parent().isPresent())
Sho SHIMIZU7d54d9c2016-02-17 13:58:46 -0800162 .map(x -> {
163 // avoid access to consistent map in the case of discrete resource
164 if (x instanceof DiscreteResourceId) {
165 return Optional.of(Resources.discrete((DiscreteResourceId) x).resource());
166 } else {
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700167 return continuousTxStore.lookup((ContinuousResourceId) x);
Sho SHIMIZU7d54d9c2016-02-17 13:58:46 -0800168 }
169 })
HIGUCHI Yuta315179a2016-02-18 14:01:22 -0800170 .filter(Optional::isPresent)
171 .map(Optional::get)
Sho SHIMIZU72f81b12016-02-09 09:26:17 -0800172 .collect(Collectors.toList());
Sho SHIMIZU69420fe2016-02-09 15:01:07 -0800173 // the order is preserved by LinkedHashMap
Sho SHIMIZU72f81b12016-02-09 09:26:17 -0800174 Map<DiscreteResourceId, List<Resource>> resourceMap = resources.stream()
Sho SHIMIZU69420fe2016-02-09 15:01:07 -0800175 .collect(Collectors.groupingBy(x -> x.parent().get().id(), LinkedHashMap::new, Collectors.toList()));
Sho SHIMIZU83e17a02015-08-20 14:07:05 -0700176
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800177 // even if one of the resources is allocated to a consumer,
178 // all unregistrations are regarded as failure
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700179 for (Map.Entry<DiscreteResourceId, List<Resource>> entry : resourceMap.entrySet()) {
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800180 boolean allocated = entry.getValue().stream().anyMatch(x -> {
Sho SHIMIZUf33b8932016-01-25 18:43:32 -0800181 if (x instanceof DiscreteResource) {
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700182 return discreteTxStore.isAllocated(((DiscreteResource) x).id());
Sho SHIMIZUf33b8932016-01-25 18:43:32 -0800183 } else if (x instanceof ContinuousResource) {
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700184 return continuousTxStore.isAllocated(((ContinuousResource) x).id());
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800185 } else {
186 return false;
187 }
188 });
189 if (allocated) {
HIGUCHI Yuta5b6dfba2016-01-27 14:43:41 -0800190 log.warn("Failed to unregister {}: allocation exists", entry.getKey());
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800191 return abortTransaction(tx);
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700192 }
193
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700194 if (!removeValues(discreteTxStore, continuousTxStore, entry.getKey(), entry.getValue())) {
HIGUCHI Yuta6acdfd02016-02-18 10:39:43 -0800195 log.warn("Failed to unregister {}: Failed to remove {} values.",
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700196 entry.getKey(), entry.getValue().size());
HIGUCHI Yuta6acdfd02016-02-18 10:39:43 -0800197 log.debug("Failed to unregister {}: Failed to remove values: {}",
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700198 entry.getKey(), entry.getValue());
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800199 return abortTransaction(tx);
200 }
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700201 }
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800202
Madan Jampani3780d4b2016-04-04 18:18:24 -0700203 return tx.commit().whenComplete((status, error) -> {
204 if (status == CommitStatus.SUCCESS) {
205 List<ResourceEvent> events = resources.stream()
206 .filter(x -> x.parent().isPresent())
207 .map(x -> new ResourceEvent(RESOURCE_REMOVED, x))
208 .collect(Collectors.toList());
209 notifyDelegate(events);
210 } else {
211 log.warn("Failed to unregister {}: Commit failed.", ids, error);
212 }
213 }).join() == CommitStatus.SUCCESS;
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700214 }
215
216 @Override
Jonathan Hart56151262016-02-11 09:48:50 -0800217 public boolean allocate(List<Resource> resources, ResourceConsumer consumer) {
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700218 checkNotNull(resources);
219 checkNotNull(consumer);
220
221 TransactionContext tx = service.transactionContextBuilder().build();
222 tx.begin();
223
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700224 TransactionalDiscreteResourceStore discreteTxStore = discreteStore.transactional(tx);
225 TransactionalContinuousResourceStore continuousTxStore = continuousStore.transactional(tx);
226 for (Resource resource : resources) {
Sho SHIMIZU171a9382016-02-15 13:56:34 -0800227 if (resource instanceof DiscreteResource) {
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700228 if (!discreteTxStore.allocate(consumer, (DiscreteResource) resource)) {
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800229 return abortTransaction(tx);
230 }
Sho SHIMIZUf33b8932016-01-25 18:43:32 -0800231 } else if (resource instanceof ContinuousResource) {
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700232 if (!continuousTxStore.allocate(consumer, (ContinuousResource) resource)) {
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800233 return abortTransaction(tx);
234 }
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800235 }
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700236 }
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800237
Madan Jampani3780d4b2016-04-04 18:18:24 -0700238 return tx.commit().join() == CommitStatus.SUCCESS;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700239 }
240
241 @Override
Sho SHIMIZUfc64ffe2016-02-10 20:11:09 -0800242 public boolean release(List<ResourceAllocation> allocations) {
243 checkNotNull(allocations);
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700244
245 TransactionContext tx = service.transactionContextBuilder().build();
246 tx.begin();
247
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700248 TransactionalDiscreteResourceStore discreteTxStore = discreteStore.transactional(tx);
249 TransactionalContinuousResourceStore continuousTxStore = continuousStore.transactional(tx);
Sho SHIMIZUfc64ffe2016-02-10 20:11:09 -0800250 for (ResourceAllocation allocation : allocations) {
251 Resource resource = allocation.resource();
252 ResourceConsumer consumer = allocation.consumer();
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700253
Sho SHIMIZUf33b8932016-01-25 18:43:32 -0800254 if (resource instanceof DiscreteResource) {
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700255 if (!discreteTxStore.release((DiscreteResource) resource, consumer)) {
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800256 return abortTransaction(tx);
257 }
Sho SHIMIZUf33b8932016-01-25 18:43:32 -0800258 } else if (resource instanceof ContinuousResource) {
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700259 if (!continuousTxStore.release((ContinuousResource) resource, consumer)) {
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800260 return abortTransaction(tx);
261 }
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700262 }
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700263 }
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800264
Madan Jampani3780d4b2016-04-04 18:18:24 -0700265 return tx.commit().join() == CommitStatus.SUCCESS;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700266 }
267
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800268 // computational complexity: O(1) if the resource is discrete type.
269 // O(n) if the resource is continuous type where n is the number of the children of
270 // the specified resource's parent
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700271 @Override
Sho SHIMIZU8fa670a2016-01-14 11:17:18 -0800272 public boolean isAvailable(Resource resource) {
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800273 checkNotNull(resource);
Sho SHIMIZUf33b8932016-01-25 18:43:32 -0800274 checkArgument(resource instanceof DiscreteResource || resource instanceof ContinuousResource);
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800275
Sho SHIMIZUf33b8932016-01-25 18:43:32 -0800276 if (resource instanceof DiscreteResource) {
HIGUCHI Yuta6f828c32016-01-20 18:11:05 -0800277 // check if already consumed
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700278 return discreteStore.isAvailable((DiscreteResource) resource);
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800279 } else {
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700280 return continuousStore.isAvailable((ContinuousResource) resource);
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800281 }
282 }
283
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800284 // computational complexity: O(n + m) where n is the number of entries in discreteConsumers
285 // and m is the number of allocations for all continuous resources
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800286 @Override
Sho SHIMIZU8fa670a2016-01-14 11:17:18 -0800287 public Collection<Resource> getResources(ResourceConsumer consumer) {
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700288 checkNotNull(consumer);
289
290 // NOTE: getting all entries may become performance bottleneck
291 // TODO: revisit for better backend data structure
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700292 Stream<DiscreteResource> discrete = discreteStore.getResources(consumer);
293 Stream<ContinuousResource> continuous = continuousStore.getResources(consumer);
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800294
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700295 return Stream.concat(discrete, continuous).collect(Collectors.toList());
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700296 }
297
Sho SHIMIZU82bfe992016-02-10 09:55:32 -0800298 // computational complexity: O(1)
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700299 @Override
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800300 public Set<Resource> getChildResources(DiscreteResourceId parent) {
Sho SHIMIZUe7f4f3f2015-10-13 16:27:25 -0700301 checkNotNull(parent);
302
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700303 return ImmutableSet.<Resource>builder()
304 .addAll(discreteStore.getChildResources(parent))
305 .addAll(continuousStore.getChildResources(parent))
306 .build();
Sho SHIMIZUe7f4f3f2015-10-13 16:27:25 -0700307 }
308
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800309 // computational complexity: O(n) where n is the number of the children of the parent
Sho SHIMIZUe7f4f3f2015-10-13 16:27:25 -0700310 @Override
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800311 public <T> Collection<Resource> getAllocatedResources(DiscreteResourceId parent, Class<T> cls) {
Sho SHIMIZU1f5e5912015-08-10 17:00:00 -0700312 checkNotNull(parent);
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700313 checkNotNull(cls);
314
Sho SHIMIZU03be2662016-05-04 09:38:45 -0700315 Set<Resource> children = getChildResources(parent);
316 if (children.isEmpty()) {
317 return children;
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700318 }
319
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700320 Stream<DiscreteResource> discrete = discreteStore.getAllocatedResources(parent, cls);
321 Stream<ContinuousResource> continuous = continuousStore.getAllocatedResources(parent, cls);
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800322
323 return Stream.concat(discrete, continuous).collect(Collectors.toList());
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700324 }
Sho SHIMIZUd29847f2015-08-13 09:10:59 -0700325
326 /**
327 * Abort the transaction.
328 *
329 * @param tx transaction context
330 * @return always false
331 */
332 private boolean abortTransaction(TransactionContext tx) {
333 tx.abort();
334 return false;
335 }
336
337 /**
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700338 * Appends the values to the existing values associated with the specified key.
Sho SHIMIZU4568c412015-08-21 16:39:07 -0700339 * If the map already has all the given values, appending will not happen.
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700340 *
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700341 * @param key key specifying values
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700342 * @param values values to be appended
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700343 * @return true if the operation succeeds, false otherwise.
344 */
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800345 // computational complexity: O(n) where n is the number of the specified value
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700346 private boolean appendValues(TransactionalDiscreteResourceStore discreteTxStore,
347 TransactionalContinuousResourceStore continuousTxStore,
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800348 DiscreteResourceId key, List<Resource> values) {
Sho SHIMIZU03be2662016-05-04 09:38:45 -0700349 // it's assumed that the passed "values" is non-empty
350
351 // This is 2-pass scan. Nicer to have 1-pass scan
352 List<DiscreteResource> discreteValues = values.stream()
353 .filter(x -> x instanceof DiscreteResource)
354 .map(x -> (DiscreteResource) x)
355 .collect(Collectors.toList());
356 List<ContinuousResource> continuousValues = values.stream()
357 .filter(x -> x instanceof ContinuousResource)
358 .map(x -> (ContinuousResource) x)
359 .collect(Collectors.toList());
360
361 // short-circuit decision avoiding unnecessary distributed map operations
362 if (continuousValues.isEmpty()) {
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700363 return discreteTxStore.appendValues(key, discreteValues);
Sho SHIMIZU03be2662016-05-04 09:38:45 -0700364 }
365 if (discreteValues.isEmpty()) {
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700366 return continuousTxStore.appendValues(key, continuousValues);
Sho SHIMIZU03be2662016-05-04 09:38:45 -0700367 }
368
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700369 return discreteTxStore.appendValues(key, discreteValues)
370 && continuousTxStore.appendValues(key, continuousValues);
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700371 }
372
373 /**
Sho SHIMIZUba1f83b2015-10-14 08:11:20 -0700374 * Removes the values from the existing values associated with the specified key.
Sho SHIMIZU5618ee52015-08-21 17:19:44 -0700375 * If the map doesn't contain the given values, removal will not happen.
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700376 *
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700377 * @param discreteTxStore map holding multiple discrete resources for a key
378 * @param continuousTxStore map holding multiple continuous resources for a key
379 * @param key key specifying values
380 * @param values values to be removed
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700381 * @return true if the operation succeeds, false otherwise
382 */
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700383 private boolean removeValues(TransactionalDiscreteResourceStore discreteTxStore,
384 TransactionalContinuousResourceStore continuousTxStore,
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800385 DiscreteResourceId key, List<Resource> values) {
Sho SHIMIZU03be2662016-05-04 09:38:45 -0700386 // it's assumed that the passed "values" is non-empty
387
388 // This is 2-pass scan. Nicer to have 1-pass scan
389 List<DiscreteResource> discreteValues = values.stream()
390 .filter(x -> x instanceof DiscreteResource)
391 .map(x -> (DiscreteResource) x)
392 .collect(Collectors.toList());
393 List<ContinuousResource> continuousValues = values.stream()
394 .filter(x -> x instanceof ContinuousResource)
395 .map(x -> (ContinuousResource) x)
396 .collect(Collectors.toList());
397
398 // short-circuit decision avoiding unnecessary distributed map operations
399 if (continuousValues.isEmpty()) {
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700400 return discreteTxStore.removeValues(key, discreteValues);
Sho SHIMIZU03be2662016-05-04 09:38:45 -0700401 }
402 if (discreteValues.isEmpty()) {
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700403 return continuousTxStore.removeValues(key, continuousValues);
Sho SHIMIZU03be2662016-05-04 09:38:45 -0700404 }
405
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700406 return discreteTxStore.removeValues(key, discreteValues)
407 && continuousTxStore.removeValues(key, continuousValues);
Sho SHIMIZU03be2662016-05-04 09:38:45 -0700408 }
409
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700410 /**
Sho SHIMIZU72f81b12016-02-09 09:26:17 -0800411 * Returns the resource which has the same key as the specified resource ID
412 * in the set as a value of the map.
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700413 *
Sho SHIMIZU72f81b12016-02-09 09:26:17 -0800414 * @param id ID of resource to be checked
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800415 * @return the resource which is regarded as the same as the specified resource
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700416 */
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700417 private Optional<Resource> lookup(TransactionalDiscreteResourceStore discreteTxStore,
Sho SHIMIZU001e3f22016-05-06 14:46:15 -0700418 DiscreteResourceId id) {
419 return discreteTxStore.lookup(id);
Sho SHIMIZU03be2662016-05-04 09:38:45 -0700420 }
421
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800422 // internal use only
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700423 static final class ContinuousResourceAllocation {
Sho SHIMIZUf33b8932016-01-25 18:43:32 -0800424 private final ContinuousResource original;
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800425 private final ImmutableList<ResourceAllocation> allocations;
426
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700427 ContinuousResourceAllocation(ContinuousResource original,
428 ImmutableList<ResourceAllocation> allocations) {
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800429 this.original = original;
430 this.allocations = allocations;
431 }
432
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700433 ContinuousResource original() {
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800434 return original;
435 }
436
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700437 ImmutableList<ResourceAllocation> allocations() {
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800438 return allocations;
439 }
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700440 }
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700441}