blob: fa23fba0cb4244ba68bfb4508b4e896a08763ac0 [file] [log] [blame]
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -07001/*
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -08002 * Copyright 2015-2016 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 */
16package org.onosproject.store.newresource.impl;
17
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 SHIMIZU6c9e33a2016-01-07 18:45:27 -080021import com.google.common.collect.Maps;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070022import org.apache.felix.scr.annotations.Activate;
23import org.apache.felix.scr.annotations.Component;
24import org.apache.felix.scr.annotations.Reference;
25import org.apache.felix.scr.annotations.ReferenceCardinality;
26import org.apache.felix.scr.annotations.Service;
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -080027import org.onlab.util.GuavaCollectors;
Thomas Vachuska762a2d82016-01-04 10:25:20 -080028import org.onlab.util.Tools;
Sho SHIMIZUf33b8932016-01-25 18:43:32 -080029import org.onosproject.net.newresource.ContinuousResource;
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -080030import org.onosproject.net.newresource.ContinuousResourceId;
Sho SHIMIZUf33b8932016-01-25 18:43:32 -080031import org.onosproject.net.newresource.DiscreteResource;
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -080032import org.onosproject.net.newresource.DiscreteResourceId;
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -080033import org.onosproject.net.newresource.ResourceAllocation;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070034import org.onosproject.net.newresource.ResourceConsumer;
Sho SHIMIZUfa62b472015-11-02 17:35:46 -080035import org.onosproject.net.newresource.ResourceEvent;
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -080036import org.onosproject.net.newresource.ResourceId;
Sho SHIMIZU8fa670a2016-01-14 11:17:18 -080037import org.onosproject.net.newresource.Resource;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070038import org.onosproject.net.newresource.ResourceStore;
Sho SHIMIZUfa62b472015-11-02 17:35:46 -080039import org.onosproject.net.newresource.ResourceStoreDelegate;
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -080040import org.onosproject.net.newresource.Resources;
Sho SHIMIZUfa62b472015-11-02 17:35:46 -080041import org.onosproject.store.AbstractStore;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070042import org.onosproject.store.serializers.KryoNamespaces;
43import org.onosproject.store.service.ConsistentMap;
Thomas Vachuska762a2d82016-01-04 10:25:20 -080044import org.onosproject.store.service.ConsistentMapException;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070045import org.onosproject.store.service.Serializer;
46import org.onosproject.store.service.StorageService;
47import org.onosproject.store.service.TransactionContext;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070048import org.onosproject.store.service.TransactionalMap;
49import org.onosproject.store.service.Versioned;
50import org.slf4j.Logger;
51import org.slf4j.LoggerFactory;
52
Sho SHIMIZUba41fc12015-08-12 15:43:22 -070053import java.util.Arrays;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070054import java.util.Collection;
Sho SHIMIZU69420fe2016-02-09 15:01:07 -080055import java.util.LinkedHashMap;
Sho SHIMIZUba41fc12015-08-12 15:43:22 -070056import java.util.LinkedHashSet;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070057import java.util.List;
58import java.util.Map;
59import java.util.Optional;
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -080060import java.util.Set;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070061import java.util.stream.Collectors;
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -080062import java.util.stream.Stream;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070063
64import static com.google.common.base.Preconditions.checkArgument;
65import static com.google.common.base.Preconditions.checkNotNull;
Sho SHIMIZUfa62b472015-11-02 17:35:46 -080066import static org.onosproject.net.newresource.ResourceEvent.Type.*;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070067
68/**
69 * Implementation of ResourceStore using TransactionalMap.
70 */
Sho SHIMIZU9a2b8292015-10-28 13:00:16 -070071@Component(immediate = true)
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070072@Service
73@Beta
Sho SHIMIZUfa62b472015-11-02 17:35:46 -080074public class ConsistentResourceStore extends AbstractStore<ResourceEvent, ResourceStoreDelegate>
75 implements ResourceStore {
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070076 private static final Logger log = LoggerFactory.getLogger(ConsistentResourceStore.class);
77
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -080078 private static final String DISCRETE_CONSUMER_MAP = "onos-discrete-consumers";
79 private static final String CONTINUOUS_CONSUMER_MAP = "onos-continuous-consumers";
Sho SHIMIZUba41fc12015-08-12 15:43:22 -070080 private static final String CHILD_MAP = "onos-resource-children";
81 private static final Serializer SERIALIZER = Serializer.using(
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -080082 Arrays.asList(KryoNamespaces.BASIC, KryoNamespaces.API),
83 ContinuousResourceAllocation.class);
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070084
Thomas Vachuska762a2d82016-01-04 10:25:20 -080085 // TODO: We should provide centralized values for this
86 private static final int MAX_RETRIES = 5;
87 private static final int RETRY_DELAY = 1_000; // millis
88
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070089 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
90 protected StorageService service;
91
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -080092 private ConsistentMap<DiscreteResourceId, ResourceConsumer> discreteConsumers;
93 private ConsistentMap<ContinuousResourceId, ContinuousResourceAllocation> continuousConsumers;
94 private ConsistentMap<DiscreteResourceId, Set<Resource>> childMap;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070095
96 @Activate
97 public void activate() {
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -080098 discreteConsumers = service.<DiscreteResourceId, ResourceConsumer>consistentMapBuilder()
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -080099 .withName(DISCRETE_CONSUMER_MAP)
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700100 .withSerializer(SERIALIZER)
101 .build();
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800102 continuousConsumers = service.<ContinuousResourceId, ContinuousResourceAllocation>consistentMapBuilder()
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800103 .withName(CONTINUOUS_CONSUMER_MAP)
104 .withSerializer(SERIALIZER)
105 .build();
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800106 childMap = service.<DiscreteResourceId, Set<Resource>>consistentMapBuilder()
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700107 .withName(CHILD_MAP)
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700108 .withSerializer(SERIALIZER)
109 .build();
Sho SHIMIZUe7db6142015-11-04 11:24:22 -0800110
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800111 Tools.retryable(() -> childMap.put(Resource.ROOT.id(), new LinkedHashSet<>()),
Thomas Vachuska762a2d82016-01-04 10:25:20 -0800112 ConsistentMapException.class, MAX_RETRIES, RETRY_DELAY);
Madan Jampanic7f49f92015-12-10 11:35:06 -0800113 log.info("Started");
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700114 }
115
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800116 // Computational complexity: O(1) if the resource is discrete type.
117 // 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 -0700118 @Override
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800119 public List<ResourceAllocation> getResourceAllocations(ResourceId id) {
120 checkNotNull(id);
121 checkArgument(id instanceof DiscreteResourceId || id instanceof ContinuousResourceId);
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700122
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800123 if (id instanceof DiscreteResourceId) {
124 return getResourceAllocations((DiscreteResourceId) id);
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800125 } else {
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800126 return getResourceAllocations((ContinuousResourceId) id);
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800127 }
128 }
129
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800130 // computational complexity: O(1)
131 private List<ResourceAllocation> getResourceAllocations(DiscreteResourceId resource) {
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800132 Versioned<ResourceConsumer> consumer = discreteConsumers.get(resource);
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700133 if (consumer == null) {
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800134 return ImmutableList.of();
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700135 }
136
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800137 return ImmutableList.of(new ResourceAllocation(Resources.discrete(resource).resource(), consumer.value()));
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800138 }
139
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800140 // computational complexity: O(n) where n is the number of the existing allocations for the resource
141 private List<ResourceAllocation> getResourceAllocations(ContinuousResourceId resource) {
142 Versioned<ContinuousResourceAllocation> allocations = continuousConsumers.get(resource);
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800143 if (allocations == null) {
144 return ImmutableList.of();
145 }
146
147 return allocations.value().allocations().stream()
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800148 .filter(x -> x.resource().id().equals(resource))
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800149 .collect(GuavaCollectors.toImmutableList());
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700150 }
151
152 @Override
Jonathan Hart56151262016-02-11 09:48:50 -0800153 public boolean register(List<Resource> resources) {
Sho SHIMIZU83e17a02015-08-20 14:07:05 -0700154 checkNotNull(resources);
HIGUCHI Yuta6f828c32016-01-20 18:11:05 -0800155 if (log.isTraceEnabled()) {
156 resources.forEach(r -> log.trace("registering {}", r));
157 }
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700158
159 TransactionContext tx = service.transactionContextBuilder().build();
160 tx.begin();
161
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800162 TransactionalMap<DiscreteResourceId, Set<Resource>> childTxMap =
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800163 tx.getTransactionalMap(CHILD_MAP, SERIALIZER);
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700164
Sho SHIMIZU69420fe2016-02-09 15:01:07 -0800165 // the order is preserved by LinkedHashMap
Sho SHIMIZUf33b8932016-01-25 18:43:32 -0800166 Map<DiscreteResource, List<Resource>> resourceMap = resources.stream()
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800167 .filter(x -> x.parent().isPresent())
Sho SHIMIZU69420fe2016-02-09 15:01:07 -0800168 .collect(Collectors.groupingBy(x -> x.parent().get(), LinkedHashMap::new, Collectors.toList()));
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700169
Sho SHIMIZUf33b8932016-01-25 18:43:32 -0800170 for (Map.Entry<DiscreteResource, List<Resource>> entry: resourceMap.entrySet()) {
Sho SHIMIZU72f81b12016-02-09 09:26:17 -0800171 if (!lookup(childTxMap, entry.getKey().id()).isPresent()) {
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800172 return abortTransaction(tx);
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700173 }
174
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800175 if (!appendValues(childTxMap, entry.getKey().id(), entry.getValue())) {
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800176 return abortTransaction(tx);
177 }
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700178 }
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800179
Sho SHIMIZUfa62b472015-11-02 17:35:46 -0800180 boolean success = tx.commit();
181 if (success) {
182 List<ResourceEvent> events = resources.stream()
183 .filter(x -> x.parent().isPresent())
184 .map(x -> new ResourceEvent(RESOURCE_ADDED, x))
185 .collect(Collectors.toList());
186 notifyDelegate(events);
187 }
188 return success;
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700189 }
190
191 @Override
Jonathan Hart56151262016-02-11 09:48:50 -0800192 public boolean unregister(List<ResourceId> ids) {
Sho SHIMIZU72f81b12016-02-09 09:26:17 -0800193 checkNotNull(ids);
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700194
195 TransactionContext tx = service.transactionContextBuilder().build();
196 tx.begin();
197
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800198 TransactionalMap<DiscreteResourceId, Set<Resource>> childTxMap =
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800199 tx.getTransactionalMap(CHILD_MAP, SERIALIZER);
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800200 TransactionalMap<DiscreteResourceId, ResourceConsumer> discreteConsumerTxMap =
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800201 tx.getTransactionalMap(DISCRETE_CONSUMER_MAP, SERIALIZER);
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800202 TransactionalMap<ContinuousResourceId, ContinuousResourceAllocation> continuousConsumerTxMap =
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800203 tx.getTransactionalMap(CONTINUOUS_CONSUMER_MAP, SERIALIZER);
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700204
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800205 // Extract Discrete instances from resources
Sho SHIMIZU72f81b12016-02-09 09:26:17 -0800206 List<Resource> resources = ids.stream()
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800207 .filter(x -> x.parent().isPresent())
Sho SHIMIZU72f81b12016-02-09 09:26:17 -0800208 .map(x -> lookup(childTxMap, x))
209 .filter(Optional::isPresent)
210 .map(Optional::get)
211 .collect(Collectors.toList());
Sho SHIMIZU69420fe2016-02-09 15:01:07 -0800212 // the order is preserved by LinkedHashMap
Sho SHIMIZU72f81b12016-02-09 09:26:17 -0800213 Map<DiscreteResourceId, List<Resource>> resourceMap = resources.stream()
Sho SHIMIZU69420fe2016-02-09 15:01:07 -0800214 .collect(Collectors.groupingBy(x -> x.parent().get().id(), LinkedHashMap::new, Collectors.toList()));
Sho SHIMIZU83e17a02015-08-20 14:07:05 -0700215
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800216 // even if one of the resources is allocated to a consumer,
217 // all unregistrations are regarded as failure
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800218 for (Map.Entry<DiscreteResourceId, List<Resource>> entry: resourceMap.entrySet()) {
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800219 boolean allocated = entry.getValue().stream().anyMatch(x -> {
Sho SHIMIZUf33b8932016-01-25 18:43:32 -0800220 if (x instanceof DiscreteResource) {
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800221 return discreteConsumerTxMap.get(((DiscreteResource) x).id()) != null;
Sho SHIMIZUf33b8932016-01-25 18:43:32 -0800222 } else if (x instanceof ContinuousResource) {
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800223 ContinuousResourceAllocation allocations =
224 continuousConsumerTxMap.get(((ContinuousResource) x).id());
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800225 return allocations != null && !allocations.allocations().isEmpty();
226 } else {
227 return false;
228 }
229 });
230 if (allocated) {
HIGUCHI Yuta5b6dfba2016-01-27 14:43:41 -0800231 log.warn("Failed to unregister {}: allocation exists", entry.getKey());
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800232 return abortTransaction(tx);
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700233 }
234
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800235 if (!removeValues(childTxMap, entry.getKey(), entry.getValue())) {
HIGUCHI Yuta5b6dfba2016-01-27 14:43:41 -0800236 log.warn("Failed to unregister {}: Failed to remove values: {}",
237 entry.getKey(), entry.getValue());
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800238 return abortTransaction(tx);
239 }
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700240 }
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800241
Sho SHIMIZUfa62b472015-11-02 17:35:46 -0800242 boolean success = tx.commit();
243 if (success) {
244 List<ResourceEvent> events = resources.stream()
245 .filter(x -> x.parent().isPresent())
246 .map(x -> new ResourceEvent(RESOURCE_REMOVED, x))
247 .collect(Collectors.toList());
248 notifyDelegate(events);
HIGUCHI Yuta5b6dfba2016-01-27 14:43:41 -0800249 } else {
Sho SHIMIZU72f81b12016-02-09 09:26:17 -0800250 log.warn("Failed to unregister {}: Commit failed.", ids);
Sho SHIMIZUfa62b472015-11-02 17:35:46 -0800251 }
252 return success;
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700253 }
254
255 @Override
Jonathan Hart56151262016-02-11 09:48:50 -0800256 public boolean allocate(List<Resource> resources, ResourceConsumer consumer) {
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700257 checkNotNull(resources);
258 checkNotNull(consumer);
259
260 TransactionContext tx = service.transactionContextBuilder().build();
261 tx.begin();
262
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800263 TransactionalMap<DiscreteResourceId, Set<Resource>> childTxMap =
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800264 tx.getTransactionalMap(CHILD_MAP, SERIALIZER);
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800265 TransactionalMap<DiscreteResourceId, ResourceConsumer> discreteConsumerTxMap =
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800266 tx.getTransactionalMap(DISCRETE_CONSUMER_MAP, SERIALIZER);
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800267 TransactionalMap<ContinuousResourceId, ContinuousResourceAllocation> continuousConsumerTxMap =
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800268 tx.getTransactionalMap(CONTINUOUS_CONSUMER_MAP, SERIALIZER);
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700269
Sho SHIMIZU8fa670a2016-01-14 11:17:18 -0800270 for (Resource resource: resources) {
Sho SHIMIZUf33b8932016-01-25 18:43:32 -0800271 if (resource instanceof DiscreteResource) {
Sho SHIMIZU72f81b12016-02-09 09:26:17 -0800272 if (!lookup(childTxMap, resource.id()).isPresent()) {
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800273 return abortTransaction(tx);
274 }
Sho SHIMIZUd29847f2015-08-13 09:10:59 -0700275
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800276 ResourceConsumer oldValue = discreteConsumerTxMap.put(((DiscreteResource) resource).id(), consumer);
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800277 if (oldValue != null) {
278 return abortTransaction(tx);
279 }
Sho SHIMIZUf33b8932016-01-25 18:43:32 -0800280 } else if (resource instanceof ContinuousResource) {
Sho SHIMIZU72f81b12016-02-09 09:26:17 -0800281 Optional<Resource> lookedUp = lookup(childTxMap, resource.id());
282 if (!lookedUp.isPresent()) {
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800283 return abortTransaction(tx);
284 }
285
Sho SHIMIZU72f81b12016-02-09 09:26:17 -0800286 // Down cast: this must be safe as ContinuousResource is associated with ContinuousResourceId
287 ContinuousResource continuous = (ContinuousResource) lookedUp.get();
288 ContinuousResourceAllocation allocations = continuousConsumerTxMap.get(continuous.id());
289 if (!hasEnoughResource(continuous, (ContinuousResource) resource, allocations)) {
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800290 return abortTransaction(tx);
291 }
292
293 boolean success = appendValue(continuousConsumerTxMap,
Sho SHIMIZU72f81b12016-02-09 09:26:17 -0800294 continuous, new ResourceAllocation(continuous, consumer));
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800295 if (!success) {
296 return abortTransaction(tx);
297 }
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800298 }
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700299 }
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800300
301 return tx.commit();
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700302 }
303
304 @Override
Sho SHIMIZUfc64ffe2016-02-10 20:11:09 -0800305 public boolean release(List<ResourceAllocation> allocations) {
306 checkNotNull(allocations);
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700307
308 TransactionContext tx = service.transactionContextBuilder().build();
309 tx.begin();
310
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800311 TransactionalMap<DiscreteResourceId, ResourceConsumer> discreteConsumerTxMap =
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800312 tx.getTransactionalMap(DISCRETE_CONSUMER_MAP, SERIALIZER);
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800313 TransactionalMap<ContinuousResourceId, ContinuousResourceAllocation> continuousConsumerTxMap =
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800314 tx.getTransactionalMap(CONTINUOUS_CONSUMER_MAP, SERIALIZER);
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700315
Sho SHIMIZUfc64ffe2016-02-10 20:11:09 -0800316 for (ResourceAllocation allocation : allocations) {
317 Resource resource = allocation.resource();
318 ResourceConsumer consumer = allocation.consumer();
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700319
Sho SHIMIZUf33b8932016-01-25 18:43:32 -0800320 if (resource instanceof DiscreteResource) {
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800321 // if this single release fails (because the resource is allocated to another consumer,
322 // the whole release fails
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800323 if (!discreteConsumerTxMap.remove(((DiscreteResource) resource).id(), consumer)) {
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800324 return abortTransaction(tx);
325 }
Sho SHIMIZUf33b8932016-01-25 18:43:32 -0800326 } else if (resource instanceof ContinuousResource) {
327 ContinuousResource continuous = (ContinuousResource) resource;
Sho SHIMIZUfc64ffe2016-02-10 20:11:09 -0800328 ContinuousResourceAllocation continuousAllocation = continuousConsumerTxMap.get(continuous.id());
329 ImmutableList<ResourceAllocation> newAllocations = continuousAllocation.allocations().stream()
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800330 .filter(x -> !(x.consumer().equals(consumer) &&
Sho SHIMIZUf33b8932016-01-25 18:43:32 -0800331 ((ContinuousResource) x.resource()).value() == continuous.value()))
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800332 .collect(GuavaCollectors.toImmutableList());
333
Sho SHIMIZUfc64ffe2016-02-10 20:11:09 -0800334 if (!continuousConsumerTxMap.replace(continuous.id(), continuousAllocation,
335 new ContinuousResourceAllocation(continuousAllocation.original(), newAllocations))) {
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800336 return abortTransaction(tx);
337 }
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700338 }
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700339 }
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800340
341 return tx.commit();
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700342 }
343
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800344 // computational complexity: O(1) if the resource is discrete type.
345 // O(n) if the resource is continuous type where n is the number of the children of
346 // the specified resource's parent
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700347 @Override
Sho SHIMIZU8fa670a2016-01-14 11:17:18 -0800348 public boolean isAvailable(Resource resource) {
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800349 checkNotNull(resource);
Sho SHIMIZUf33b8932016-01-25 18:43:32 -0800350 checkArgument(resource instanceof DiscreteResource || resource instanceof ContinuousResource);
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800351
Sho SHIMIZUf33b8932016-01-25 18:43:32 -0800352 if (resource instanceof DiscreteResource) {
HIGUCHI Yuta6f828c32016-01-20 18:11:05 -0800353 // check if already consumed
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800354 return getResourceAllocations(resource.id()).isEmpty();
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800355 } else {
Sho SHIMIZU6f747302016-02-10 17:35:41 -0800356 // check if it's registered or not.
357 Versioned<Set<Resource>> v = childMap.get(resource.parent().get().id());
358 if (v == null) {
359 return false;
360 }
361
Sho SHIMIZUf33b8932016-01-25 18:43:32 -0800362 ContinuousResource requested = (ContinuousResource) resource;
363 ContinuousResource registered = v.value().stream()
Sho SHIMIZU2d310222016-01-22 11:45:11 -0800364 .filter(c -> c.id().equals(resource.id()))
HIGUCHI Yuta6f828c32016-01-20 18:11:05 -0800365 .findFirst()
Sho SHIMIZUf33b8932016-01-25 18:43:32 -0800366 .map(c -> (ContinuousResource) c)
HIGUCHI Yuta6f828c32016-01-20 18:11:05 -0800367 .get();
368 if (registered.value() < requested.value()) {
369 // Capacity < requested, can never satisfy
370 return false;
371 }
372 // check if there's enough left
373 return isAvailable(requested);
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800374 }
375 }
376
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800377 // computational complexity: O(n) where n is the number of existing allocations for the resource
Sho SHIMIZUf33b8932016-01-25 18:43:32 -0800378 private boolean isAvailable(ContinuousResource resource) {
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800379 Versioned<ContinuousResourceAllocation> allocation = continuousConsumers.get(resource.id());
380 if (allocation == null) {
HIGUCHI Yuta6f828c32016-01-20 18:11:05 -0800381 // no allocation (=no consumer) full registered resources available
382 return true;
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800383 }
384
385 return hasEnoughResource(allocation.value().original(), resource, allocation.value());
386 }
387
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800388 // computational complexity: O(n + m) where n is the number of entries in discreteConsumers
389 // and m is the number of allocations for all continuous resources
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800390 @Override
Sho SHIMIZU8fa670a2016-01-14 11:17:18 -0800391 public Collection<Resource> getResources(ResourceConsumer consumer) {
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700392 checkNotNull(consumer);
393
394 // NOTE: getting all entries may become performance bottleneck
395 // TODO: revisit for better backend data structure
Sho SHIMIZUf33b8932016-01-25 18:43:32 -0800396 Stream<DiscreteResource> discreteStream = discreteConsumers.entrySet().stream()
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700397 .filter(x -> x.getValue().value().equals(consumer))
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800398 .map(Map.Entry::getKey)
399 .map(x -> Resources.discrete(x).resource());
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800400
Sho SHIMIZUf33b8932016-01-25 18:43:32 -0800401 Stream<ContinuousResource> continuousStream = continuousConsumers.values().stream()
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800402 .flatMap(x -> x.value().allocations().stream()
403 .map(y -> Maps.immutableEntry(x.value().original(), y)))
404 .filter(x -> x.getValue().consumer().equals(consumer))
405 .map(x -> x.getKey());
406
407 return Stream.concat(discreteStream, continuousStream).collect(Collectors.toList());
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700408 }
409
Sho SHIMIZU82bfe992016-02-10 09:55:32 -0800410 // computational complexity: O(1)
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700411 @Override
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800412 public Set<Resource> getChildResources(DiscreteResourceId parent) {
Sho SHIMIZUe7f4f3f2015-10-13 16:27:25 -0700413 checkNotNull(parent);
414
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800415 Versioned<Set<Resource>> children = childMap.get(parent);
Sho SHIMIZUe7f4f3f2015-10-13 16:27:25 -0700416 if (children == null) {
Sho SHIMIZU83258ae2016-01-29 17:39:07 -0800417 return ImmutableSet.of();
Sho SHIMIZUe7f4f3f2015-10-13 16:27:25 -0700418 }
419
420 return children.value();
421 }
422
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800423 // computational complexity: O(n) where n is the number of the children of the parent
Sho SHIMIZUe7f4f3f2015-10-13 16:27:25 -0700424 @Override
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800425 public <T> Collection<Resource> getAllocatedResources(DiscreteResourceId parent, Class<T> cls) {
Sho SHIMIZU1f5e5912015-08-10 17:00:00 -0700426 checkNotNull(parent);
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700427 checkNotNull(cls);
428
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800429 Versioned<Set<Resource>> children = childMap.get(parent);
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700430 if (children == null) {
Sho SHIMIZU2c0ae122016-01-20 13:14:38 -0800431 return ImmutableList.of();
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700432 }
433
Sho SHIMIZUf33b8932016-01-25 18:43:32 -0800434 Stream<DiscreteResource> discrete = children.value().stream()
Sho SHIMIZUc9546a32015-11-10 11:22:28 -0800435 .filter(x -> x.last().getClass().equals(cls))
Sho SHIMIZUf33b8932016-01-25 18:43:32 -0800436 .filter(x -> x instanceof DiscreteResource)
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800437 .map(x -> ((DiscreteResource) x))
438 .filter(x -> discreteConsumers.containsKey(x.id()));
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800439
Sho SHIMIZUf33b8932016-01-25 18:43:32 -0800440 Stream<ContinuousResource> continuous = children.value().stream()
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800441 .filter(x -> x.id().equals(parent.child(cls)))
Sho SHIMIZUf33b8932016-01-25 18:43:32 -0800442 .filter(x -> x instanceof ContinuousResource)
443 .map(x -> (ContinuousResource) x)
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800444 .filter(x -> continuousConsumers.containsKey(x.id()))
445 .filter(x -> continuousConsumers.get(x.id()) != null)
446 .filter(x -> !continuousConsumers.get(x.id()).value().allocations().isEmpty());
447
448 return Stream.concat(discrete, continuous).collect(Collectors.toList());
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700449 }
Sho SHIMIZUd29847f2015-08-13 09:10:59 -0700450
451 /**
452 * Abort the transaction.
453 *
454 * @param tx transaction context
455 * @return always false
456 */
457 private boolean abortTransaction(TransactionContext tx) {
458 tx.abort();
459 return false;
460 }
461
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800462 // Appends the specified ResourceAllocation to the existing values stored in the map
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800463 // computational complexity: O(n) where n is the number of the elements in the associated allocation
464 private boolean appendValue(TransactionalMap<ContinuousResourceId, ContinuousResourceAllocation> map,
Sho SHIMIZUf33b8932016-01-25 18:43:32 -0800465 ContinuousResource original, ResourceAllocation value) {
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800466 ContinuousResourceAllocation oldValue = map.putIfAbsent(original.id(),
467 new ContinuousResourceAllocation(original, ImmutableList.of(value)));
468 if (oldValue == null) {
469 return true;
470 }
471
472 if (oldValue.allocations().contains(value)) {
473 // don't write to map because all values are already stored
474 return true;
475 }
476
477 ContinuousResourceAllocation newValue = new ContinuousResourceAllocation(original,
478 ImmutableList.<ResourceAllocation>builder()
479 .addAll(oldValue.allocations())
480 .add(value)
481 .build());
482 return map.replace(original.id(), oldValue, newValue);
483 }
Sho SHIMIZUd29847f2015-08-13 09:10:59 -0700484 /**
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700485 * Appends the values to the existing values associated with the specified key.
Sho SHIMIZU4568c412015-08-21 16:39:07 -0700486 * If the map already has all the given values, appending will not happen.
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700487 *
488 * @param map map holding multiple values for a key
489 * @param key key specifying values
490 * @param values values to be appended
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700491 * @return true if the operation succeeds, false otherwise.
492 */
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800493 // computational complexity: O(n) where n is the number of the specified value
494 private boolean appendValues(TransactionalMap<DiscreteResourceId, Set<Resource>> map,
495 DiscreteResourceId key, List<Resource> values) {
Sho SHIMIZU07b7bc92016-01-29 18:27:58 -0800496 Set<Resource> oldValues = map.putIfAbsent(key, new LinkedHashSet<>(values));
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700497 if (oldValues == null) {
Sho SHIMIZU93a74b32015-11-09 11:48:23 -0800498 return true;
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700499 }
500
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800501 if (oldValues.containsAll(values)) {
Sho SHIMIZU4568c412015-08-21 16:39:07 -0700502 // don't write to map because all values are already stored
503 return true;
504 }
505
Sho SHIMIZU07b7bc92016-01-29 18:27:58 -0800506 LinkedHashSet<Resource> newValues = new LinkedHashSet<>(oldValues);
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800507 newValues.addAll(values);
508 return map.replace(key, oldValues, newValues);
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700509 }
510
511 /**
Sho SHIMIZUba1f83b2015-10-14 08:11:20 -0700512 * Removes the values from the existing values associated with the specified key.
Sho SHIMIZU5618ee52015-08-21 17:19:44 -0700513 * If the map doesn't contain the given values, removal will not happen.
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700514 *
515 * @param map map holding multiple values for a key
516 * @param key key specifying values
517 * @param values values to be removed
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700518 * @return true if the operation succeeds, false otherwise
519 */
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800520 // computational complexity: O(n) where n is the number of the specified values
521 private boolean removeValues(TransactionalMap<DiscreteResourceId, Set<Resource>> map,
522 DiscreteResourceId key, List<Resource> values) {
Sho SHIMIZU07b7bc92016-01-29 18:27:58 -0800523 Set<Resource> oldValues = map.putIfAbsent(key, new LinkedHashSet<>());
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700524 if (oldValues == null) {
HIGUCHI Yutadc4394c2016-01-29 15:35:10 -0800525 log.trace("No-Op removing values. key {} did not exist", key);
Sho SHIMIZU93a74b32015-11-09 11:48:23 -0800526 return true;
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700527 }
528
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800529 if (values.stream().allMatch(x -> !oldValues.contains(x))) {
Sho SHIMIZU5618ee52015-08-21 17:19:44 -0700530 // don't write map because none of the values are stored
HIGUCHI Yutadc4394c2016-01-29 15:35:10 -0800531 log.trace("No-Op removing values. key {} did not contain {}", key, values);
Sho SHIMIZU5618ee52015-08-21 17:19:44 -0700532 return true;
533 }
534
Sho SHIMIZU07b7bc92016-01-29 18:27:58 -0800535 LinkedHashSet<Resource> newValues = new LinkedHashSet<>(oldValues);
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800536 newValues.removeAll(values);
537 return map.replace(key, oldValues, newValues);
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700538 }
539
540 /**
Sho SHIMIZU72f81b12016-02-09 09:26:17 -0800541 * Returns the resource which has the same key as the specified resource ID
542 * in the set as a value of the map.
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700543 *
Sho SHIMIZU72f81b12016-02-09 09:26:17 -0800544 * @param childTxMap map storing parent - child relationship of resources
545 * @param id ID of resource to be checked
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800546 * @return the resource which is regarded as the same as the specified resource
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700547 */
Sho SHIMIZU72f81b12016-02-09 09:26:17 -0800548 // Naive implementation, which traverses all elements in the set
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800549 // computational complexity: O(n) where n is the number of elements
550 // in the associated set
Sho SHIMIZU72f81b12016-02-09 09:26:17 -0800551 private Optional<Resource> lookup(TransactionalMap<DiscreteResourceId, Set<Resource>> childTxMap, ResourceId id) {
552 if (!id.parent().isPresent()) {
553 return Optional.of(Resource.ROOT);
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700554 }
555
Sho SHIMIZU72f81b12016-02-09 09:26:17 -0800556 Set<Resource> values = childTxMap.get(id.parent().get());
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800557 if (values == null) {
558 return Optional.empty();
559 }
560
Sho SHIMIZU72f81b12016-02-09 09:26:17 -0800561 return values.stream()
562 .filter(x -> x.id().equals(id))
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800563 .findFirst();
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800564 }
565
566 /**
567 * Checks if there is enough resource volume to allocated the requested resource
568 * against the specified resource.
569 *
570 * @param original original resource
571 * @param request requested resource
572 * @param allocation current allocation of the resource
573 * @return true if there is enough resource volume. Otherwise, false.
574 */
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800575 // computational complexity: O(n) where n is the number of allocations
Sho SHIMIZUf33b8932016-01-25 18:43:32 -0800576 private boolean hasEnoughResource(ContinuousResource original,
577 ContinuousResource request,
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800578 ContinuousResourceAllocation allocation) {
579 if (allocation == null) {
580 return request.value() <= original.value();
581 }
582
583 double allocated = allocation.allocations().stream()
Sho SHIMIZUf33b8932016-01-25 18:43:32 -0800584 .filter(x -> x.resource() instanceof ContinuousResource)
585 .map(x -> (ContinuousResource) x.resource())
586 .mapToDouble(ContinuousResource::value)
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800587 .sum();
588 double left = original.value() - allocated;
589 return request.value() <= left;
590 }
591
592 // internal use only
593 private static final class ContinuousResourceAllocation {
Sho SHIMIZUf33b8932016-01-25 18:43:32 -0800594 private final ContinuousResource original;
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800595 private final ImmutableList<ResourceAllocation> allocations;
596
Sho SHIMIZUf33b8932016-01-25 18:43:32 -0800597 private ContinuousResourceAllocation(ContinuousResource original,
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800598 ImmutableList<ResourceAllocation> allocations) {
599 this.original = original;
600 this.allocations = allocations;
601 }
602
Sho SHIMIZUf33b8932016-01-25 18:43:32 -0800603 private ContinuousResource original() {
Sho SHIMIZU6c9e33a2016-01-07 18:45:27 -0800604 return original;
605 }
606
607 private ImmutableList<ResourceAllocation> allocations() {
608 return allocations;
609 }
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700610 }
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700611}