Separate ResourceStore into stores for discrete and continuous type
This is a preliminary work for ONOS-4281. Change-Id: Ifed9c761eb16f6a249a9d069948edc7421301617
Showing
7 changed files
with
628 additions
and
0 deletions
1 | +/* | ||
2 | + * Copyright 2016-present Open Networking Laboratory | ||
3 | + * | ||
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 | + */ | ||
16 | +package org.onosproject.store.resource.impl; | ||
17 | + | ||
18 | +import com.google.common.collect.ImmutableList; | ||
19 | +import com.google.common.collect.ImmutableSet; | ||
20 | +import com.google.common.collect.Maps; | ||
21 | +import org.onlab.util.GuavaCollectors; | ||
22 | +import org.onlab.util.Tools; | ||
23 | +import org.onosproject.net.resource.ContinuousResource; | ||
24 | +import org.onosproject.net.resource.ContinuousResourceId; | ||
25 | +import org.onosproject.net.resource.DiscreteResourceId; | ||
26 | +import org.onosproject.net.resource.Resource; | ||
27 | +import org.onosproject.net.resource.ResourceAllocation; | ||
28 | +import org.onosproject.net.resource.ResourceConsumer; | ||
29 | +import org.onosproject.store.service.ConsistentMap; | ||
30 | +import org.onosproject.store.service.ConsistentMapException; | ||
31 | +import org.onosproject.store.service.StorageService; | ||
32 | +import org.onosproject.store.service.TransactionContext; | ||
33 | +import org.onosproject.store.service.Versioned; | ||
34 | + | ||
35 | +import java.util.LinkedHashSet; | ||
36 | +import java.util.List; | ||
37 | +import java.util.Set; | ||
38 | +import java.util.stream.Stream; | ||
39 | + | ||
40 | +import static org.onosproject.store.resource.impl.ConsistentResourceStore.ContinuousResourceAllocation; | ||
41 | +import static org.onosproject.store.resource.impl.ConsistentResourceStore.MAX_RETRIES; | ||
42 | +import static org.onosproject.store.resource.impl.ConsistentResourceStore.RETRY_DELAY; | ||
43 | +import static org.onosproject.store.resource.impl.ConsistentResourceStore.SERIALIZER; | ||
44 | +import static org.onosproject.store.resource.impl.ResourceStoreUtil.hasEnoughResource; | ||
45 | + | ||
46 | +class ConsistentContinuousResourceStore { | ||
47 | + private ConsistentMap<ContinuousResourceId, ContinuousResourceAllocation> consumers; | ||
48 | + private ConsistentMap<DiscreteResourceId, Set<ContinuousResource>> childMap; | ||
49 | + | ||
50 | + ConsistentContinuousResourceStore(StorageService service) { | ||
51 | + this.consumers = service.<ContinuousResourceId, ContinuousResourceAllocation>consistentMapBuilder() | ||
52 | + .withName(MapNames.CONTINUOUS_CONSUMER_MAP) | ||
53 | + .withSerializer(SERIALIZER) | ||
54 | + .build(); | ||
55 | + this.childMap = service.<DiscreteResourceId, Set<ContinuousResource>>consistentMapBuilder() | ||
56 | + .withName(MapNames.CONTINUOUS_CHILD_MAP) | ||
57 | + .withSerializer(SERIALIZER) | ||
58 | + .build(); | ||
59 | + | ||
60 | + Tools.retryable(() -> childMap.put(Resource.ROOT.id(), new LinkedHashSet<>()), | ||
61 | + ConsistentMapException.class, MAX_RETRIES, RETRY_DELAY); | ||
62 | + } | ||
63 | + | ||
64 | + TransactionalContinuousResourceStore transactional(TransactionContext tx) { | ||
65 | + return new TransactionalContinuousResourceStore(tx); | ||
66 | + } | ||
67 | + | ||
68 | + // computational complexity: O(n) where n is the number of the existing allocations for the resource | ||
69 | + List<ResourceAllocation> getResourceAllocations(ContinuousResourceId resource) { | ||
70 | + Versioned<ContinuousResourceAllocation> allocations = consumers.get(resource); | ||
71 | + if (allocations == null) { | ||
72 | + return ImmutableList.of(); | ||
73 | + } | ||
74 | + | ||
75 | + return allocations.value().allocations().stream() | ||
76 | + .filter(x -> x.resource().id().equals(resource)) | ||
77 | + .collect(GuavaCollectors.toImmutableList()); | ||
78 | + } | ||
79 | + | ||
80 | + Set<ContinuousResource> getChildResources(DiscreteResourceId parent) { | ||
81 | + Versioned<Set<ContinuousResource>> children = childMap.get(parent); | ||
82 | + | ||
83 | + if (children == null) { | ||
84 | + return ImmutableSet.of(); | ||
85 | + } | ||
86 | + | ||
87 | + return children.value(); | ||
88 | + } | ||
89 | + | ||
90 | + public boolean isAvailable(ContinuousResource resource) { | ||
91 | + // check if it's registered or not. | ||
92 | + Versioned<Set<ContinuousResource>> children = childMap.get(resource.parent().get().id()); | ||
93 | + if (children == null) { | ||
94 | + return false; | ||
95 | + } | ||
96 | + | ||
97 | + ContinuousResource registered = children.value().stream() | ||
98 | + .filter(c -> c.id().equals(resource.id())) | ||
99 | + .findFirst() | ||
100 | + .get(); | ||
101 | + if (registered.value() < resource.value()) { | ||
102 | + // Capacity < requested, can never satisfy | ||
103 | + return false; | ||
104 | + } | ||
105 | + | ||
106 | + // check if there's enough left | ||
107 | + Versioned<ContinuousResourceAllocation> allocation = consumers.get(resource.id()); | ||
108 | + if (allocation == null) { | ||
109 | + // no allocation (=no consumer) full registered resources available | ||
110 | + return true; | ||
111 | + } | ||
112 | + | ||
113 | + return hasEnoughResource(allocation.value().original(), resource, allocation.value()); | ||
114 | + } | ||
115 | + | ||
116 | + <T> Stream<ContinuousResource> getAllocatedResources(DiscreteResourceId parent, Class<T> cls) { | ||
117 | + Set<ContinuousResource> children = getChildResources(parent); | ||
118 | + if (children.isEmpty()) { | ||
119 | + return Stream.of(); | ||
120 | + } | ||
121 | + | ||
122 | + return children.stream() | ||
123 | + .filter(x -> x.id().equals(parent.child(cls))) | ||
124 | + // we don't use cascading simple predicates like follows to reduce accesses to consistent map | ||
125 | + // .filter(x -> continuousConsumers.containsKey(x.id())) | ||
126 | + // .filter(x -> continuousConsumers.get(x.id()) != null) | ||
127 | + // .filter(x -> !continuousConsumers.get(x.id()).value().allocations().isEmpty()); | ||
128 | + .filter(resource -> { | ||
129 | + Versioned<ContinuousResourceAllocation> allocation = consumers.get(resource.id()); | ||
130 | + if (allocation == null) { | ||
131 | + return false; | ||
132 | + } | ||
133 | + return !allocation.value().allocations().isEmpty(); | ||
134 | + }); | ||
135 | + } | ||
136 | + | ||
137 | + Stream<ContinuousResource> getResources(ResourceConsumer consumer) { | ||
138 | + return consumers.values().stream() | ||
139 | + .flatMap(x -> x.value().allocations().stream() | ||
140 | + .map(y -> Maps.immutableEntry(x.value().original(), y))) | ||
141 | + .filter(x -> x.getValue().consumer().equals(consumer)) | ||
142 | + .map(x -> x.getKey()); | ||
143 | + } | ||
144 | +} |
1 | +/* | ||
2 | + * Copyright 2016-present Open Networking Laboratory | ||
3 | + * | ||
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 | + */ | ||
16 | +package org.onosproject.store.resource.impl; | ||
17 | + | ||
18 | +import com.google.common.collect.ImmutableList; | ||
19 | +import com.google.common.collect.ImmutableSet; | ||
20 | +import org.onlab.util.Tools; | ||
21 | +import org.onosproject.net.resource.DiscreteResource; | ||
22 | +import org.onosproject.net.resource.DiscreteResourceId; | ||
23 | +import org.onosproject.net.resource.Resource; | ||
24 | +import org.onosproject.net.resource.ResourceAllocation; | ||
25 | +import org.onosproject.net.resource.ResourceConsumer; | ||
26 | +import org.onosproject.net.resource.Resources; | ||
27 | +import org.onosproject.store.service.ConsistentMap; | ||
28 | +import org.onosproject.store.service.ConsistentMapException; | ||
29 | +import org.onosproject.store.service.StorageService; | ||
30 | +import org.onosproject.store.service.TransactionContext; | ||
31 | +import org.onosproject.store.service.Versioned; | ||
32 | + | ||
33 | +import java.util.LinkedHashSet; | ||
34 | +import java.util.List; | ||
35 | +import java.util.Map; | ||
36 | +import java.util.Set; | ||
37 | +import java.util.stream.Stream; | ||
38 | + | ||
39 | +import static org.onosproject.store.resource.impl.ConsistentResourceStore.MAX_RETRIES; | ||
40 | +import static org.onosproject.store.resource.impl.ConsistentResourceStore.RETRY_DELAY; | ||
41 | +import static org.onosproject.store.resource.impl.ConsistentResourceStore.SERIALIZER; | ||
42 | + | ||
43 | +class ConsistentDiscreteResourceStore { | ||
44 | + private ConsistentMap<DiscreteResourceId, ResourceConsumer> consumers; | ||
45 | + private ConsistentMap<DiscreteResourceId, Set<DiscreteResource>> childMap; | ||
46 | + | ||
47 | + ConsistentDiscreteResourceStore(StorageService service) { | ||
48 | + this.consumers = service.<DiscreteResourceId, ResourceConsumer>consistentMapBuilder() | ||
49 | + .withName(MapNames.DISCRETE_CONSUMER_MAP) | ||
50 | + .withSerializer(SERIALIZER) | ||
51 | + .build(); | ||
52 | + this.childMap = service.<DiscreteResourceId, Set<DiscreteResource>>consistentMapBuilder() | ||
53 | + .withName(MapNames.DISCRETE_CHILD_MAP) | ||
54 | + .withSerializer(SERIALIZER) | ||
55 | + .build(); | ||
56 | + | ||
57 | + Tools.retryable(() -> childMap.put(Resource.ROOT.id(), new LinkedHashSet<>()), | ||
58 | + ConsistentMapException.class, MAX_RETRIES, RETRY_DELAY); | ||
59 | + } | ||
60 | + | ||
61 | + TransactionalDiscreteResourceStore transactional(TransactionContext tx) { | ||
62 | + return new TransactionalDiscreteResourceStore(tx); | ||
63 | + } | ||
64 | + | ||
65 | + // computational complexity: O(1) | ||
66 | + List<ResourceAllocation> getResourceAllocations(DiscreteResourceId resource) { | ||
67 | + Versioned<ResourceConsumer> consumer = consumers.get(resource); | ||
68 | + if (consumer == null) { | ||
69 | + return ImmutableList.of(); | ||
70 | + } | ||
71 | + | ||
72 | + return ImmutableList.of(new ResourceAllocation(Resources.discrete(resource).resource(), consumer.value())); | ||
73 | + } | ||
74 | + | ||
75 | + Set<DiscreteResource> getChildResources(DiscreteResourceId parent) { | ||
76 | + Versioned<Set<DiscreteResource>> children = childMap.get(parent); | ||
77 | + | ||
78 | + if (children == null) { | ||
79 | + return ImmutableSet.of(); | ||
80 | + } | ||
81 | + | ||
82 | + return children.value(); | ||
83 | + } | ||
84 | + | ||
85 | + boolean isAvailable(DiscreteResource resource) { | ||
86 | + return getResourceAllocations(resource.id()).isEmpty(); | ||
87 | + } | ||
88 | + | ||
89 | + <T> Stream<DiscreteResource> getAllocatedResources(DiscreteResourceId parent, Class<T> cls) { | ||
90 | + Set<DiscreteResource> children = getChildResources(parent); | ||
91 | + if (children.isEmpty()) { | ||
92 | + return Stream.of(); | ||
93 | + } | ||
94 | + | ||
95 | + return children.stream() | ||
96 | + .filter(x -> x.isTypeOf(cls)) | ||
97 | + .filter(x -> consumers.containsKey(x.id())); | ||
98 | + } | ||
99 | + | ||
100 | + Stream<DiscreteResource> getResources(ResourceConsumer consumer) { | ||
101 | + return consumers.entrySet().stream() | ||
102 | + .filter(x -> x.getValue().value().equals(consumer)) | ||
103 | + .map(Map.Entry::getKey) | ||
104 | + .map(x -> Resources.discrete(x).resource()); | ||
105 | + } | ||
106 | +} |
This diff is collapsed. Click to expand it.
1 | +/* | ||
2 | + * Copyright 2016-present Open Networking Laboratory | ||
3 | + * | ||
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 | + */ | ||
16 | +package org.onosproject.store.resource.impl; | ||
17 | + | ||
18 | +final class MapNames { | ||
19 | + static final String DISCRETE_CONSUMER_MAP = "onos-discrete-consumers"; | ||
20 | + static final String DISCRETE_CHILD_MAP = "onos-resource-discrete-children"; | ||
21 | + static final String CONTINUOUS_CONSUMER_MAP = "onos-continuous-consumers"; | ||
22 | + static final String CONTINUOUS_CHILD_MAP = "onos-resource-continuous-children"; | ||
23 | + | ||
24 | + // prohibit contruction | ||
25 | + private MapNames() {} | ||
26 | +} |
1 | +/* | ||
2 | + * Copyright 2016-present Open Networking Laboratory | ||
3 | + * | ||
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 | + */ | ||
16 | +package org.onosproject.store.resource.impl; | ||
17 | + | ||
18 | +import org.onosproject.net.resource.ContinuousResource; | ||
19 | + | ||
20 | +final class ResourceStoreUtil { | ||
21 | + // prohibit construction | ||
22 | + private ResourceStoreUtil() {} | ||
23 | + | ||
24 | + /** | ||
25 | + * Checks if there is enough resource volume to allocated the requested resource | ||
26 | + * against the specified resource. | ||
27 | + * | ||
28 | + * @param original original resource | ||
29 | + * @param request requested resource | ||
30 | + * @param allocation current allocation of the resource | ||
31 | + * @return true if there is enough resource volume. Otherwise, false. | ||
32 | + */ | ||
33 | + // computational complexity: O(n) where n is the number of allocations | ||
34 | + static boolean hasEnoughResource(ContinuousResource original, | ||
35 | + ContinuousResource request, | ||
36 | + ConsistentResourceStore.ContinuousResourceAllocation allocation) { | ||
37 | + if (allocation == null) { | ||
38 | + return request.value() <= original.value(); | ||
39 | + } | ||
40 | + | ||
41 | + double allocated = allocation.allocations().stream() | ||
42 | + .filter(x -> x.resource() instanceof ContinuousResource) | ||
43 | + .map(x -> (ContinuousResource) x.resource()) | ||
44 | + .mapToDouble(ContinuousResource::value) | ||
45 | + .sum(); | ||
46 | + double left = original.value() - allocated; | ||
47 | + return request.value() <= left; | ||
48 | + } | ||
49 | +} |
1 | +/* | ||
2 | + * Copyright 2016-present Open Networking Laboratory | ||
3 | + * | ||
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 | + */ | ||
16 | +package org.onosproject.store.resource.impl; | ||
17 | + | ||
18 | +import com.google.common.collect.ImmutableList; | ||
19 | +import com.google.common.collect.Sets; | ||
20 | +import org.onlab.util.GuavaCollectors; | ||
21 | +import org.onosproject.net.resource.ContinuousResource; | ||
22 | +import org.onosproject.net.resource.ContinuousResourceId; | ||
23 | +import org.onosproject.net.resource.DiscreteResourceId; | ||
24 | +import org.onosproject.net.resource.Resource; | ||
25 | +import org.onosproject.net.resource.ResourceAllocation; | ||
26 | +import org.onosproject.net.resource.ResourceConsumer; | ||
27 | +import org.onosproject.store.resource.impl.ConsistentResourceStore.ContinuousResourceAllocation; | ||
28 | +import org.onosproject.store.service.TransactionContext; | ||
29 | +import org.onosproject.store.service.TransactionalMap; | ||
30 | +import org.slf4j.Logger; | ||
31 | +import org.slf4j.LoggerFactory; | ||
32 | + | ||
33 | +import java.util.LinkedHashSet; | ||
34 | +import java.util.List; | ||
35 | +import java.util.Optional; | ||
36 | +import java.util.Set; | ||
37 | +import java.util.stream.Collectors; | ||
38 | + | ||
39 | +import static org.onosproject.store.resource.impl.ConsistentResourceStore.SERIALIZER; | ||
40 | +import static org.onosproject.store.resource.impl.ResourceStoreUtil.hasEnoughResource; | ||
41 | + | ||
42 | +class TransactionalContinuousResourceStore { | ||
43 | + private final Logger log = LoggerFactory.getLogger(getClass()); | ||
44 | + private final TransactionalMap<DiscreteResourceId, Set<ContinuousResource>> childMap; | ||
45 | + private final TransactionalMap<ContinuousResourceId, ContinuousResourceAllocation> consumers; | ||
46 | + | ||
47 | + TransactionalContinuousResourceStore(TransactionContext tx) { | ||
48 | + this.childMap = tx.getTransactionalMap(MapNames.CONTINUOUS_CHILD_MAP, SERIALIZER); | ||
49 | + this.consumers = tx.getTransactionalMap(MapNames.CONTINUOUS_CONSUMER_MAP, SERIALIZER); | ||
50 | + } | ||
51 | + | ||
52 | + // iterate over the values in the set: O(n) operation | ||
53 | + Optional<Resource> lookup(ContinuousResourceId id) { | ||
54 | + if (!id.parent().isPresent()) { | ||
55 | + return Optional.of(Resource.ROOT); | ||
56 | + } | ||
57 | + | ||
58 | + Set<ContinuousResource> values = childMap.get(id.parent().get()); | ||
59 | + if (values == null) { | ||
60 | + return Optional.empty(); | ||
61 | + } | ||
62 | + | ||
63 | + return values.stream() | ||
64 | + .filter(x -> x.id().equals(id)) | ||
65 | + .map(x -> (Resource) x) | ||
66 | + .findFirst(); | ||
67 | + } | ||
68 | + | ||
69 | + boolean appendValues(DiscreteResourceId key, List<ContinuousResource> values) { | ||
70 | + Set<ContinuousResource> requested = new LinkedHashSet<>(values); | ||
71 | + Set<ContinuousResource> oldValues = childMap.putIfAbsent(key, requested); | ||
72 | + if (oldValues == null) { | ||
73 | + return true; | ||
74 | + } | ||
75 | + | ||
76 | + Set<ContinuousResource> addedValues = Sets.difference(requested, oldValues); | ||
77 | + // no new value, then no-op | ||
78 | + if (addedValues.isEmpty()) { | ||
79 | + // don't write to map because all values are already stored | ||
80 | + return true; | ||
81 | + } | ||
82 | + | ||
83 | + Set<ContinuousResourceId> addedIds = addedValues.stream() | ||
84 | + .map(ContinuousResource::id) | ||
85 | + .collect(Collectors.toSet()); | ||
86 | + // if the value is not found but the same ID is found | ||
87 | + // (this happens only when being continuous resource) | ||
88 | + if (oldValues.stream().anyMatch(x -> addedIds.contains(x.id()))) { | ||
89 | + // no-op, but indicating failure (reject the request) | ||
90 | + return false; | ||
91 | + } | ||
92 | + Set<ContinuousResource> newValues = new LinkedHashSet<>(oldValues); | ||
93 | + newValues.addAll(addedValues); | ||
94 | + return childMap.replace(key, oldValues, newValues); | ||
95 | + } | ||
96 | + | ||
97 | + boolean removeValues(DiscreteResourceId key, List<ContinuousResource> values) { | ||
98 | + Set<ContinuousResource> oldValues = childMap.putIfAbsent(key, new LinkedHashSet<>()); | ||
99 | + if (oldValues == null) { | ||
100 | + log.trace("No-Op removing values. key {} did not exist", key); | ||
101 | + return true; | ||
102 | + } | ||
103 | + | ||
104 | + if (values.stream().allMatch(x -> !oldValues.contains(x))) { | ||
105 | + // don't write map because none of the values are stored | ||
106 | + log.trace("No-Op removing values. key {} did not contain {}", key, values); | ||
107 | + return true; | ||
108 | + } | ||
109 | + | ||
110 | + LinkedHashSet<ContinuousResource> newValues = new LinkedHashSet<>(oldValues); | ||
111 | + newValues.removeAll(values); | ||
112 | + return childMap.replace(key, oldValues, newValues); | ||
113 | + } | ||
114 | + | ||
115 | + boolean isAllocated(ContinuousResourceId id) { | ||
116 | + ContinuousResourceAllocation allocations = consumers.get(id); | ||
117 | + return allocations != null && !allocations.allocations().isEmpty(); | ||
118 | + } | ||
119 | + | ||
120 | + boolean allocate(ResourceConsumer consumer, ContinuousResource request) { | ||
121 | + // if the resource is not registered, then abort | ||
122 | + Optional<Resource> lookedUp = lookup(request.id()); | ||
123 | + if (!lookedUp.isPresent()) { | ||
124 | + return false; | ||
125 | + } | ||
126 | + // Down cast: this must be safe as ContinuousResource is associated with ContinuousResourceId | ||
127 | + ContinuousResource original = (ContinuousResource) lookedUp.get(); | ||
128 | + ContinuousResourceAllocation allocations = consumers.get(request.id()); | ||
129 | + if (!hasEnoughResource(original, request, allocations)) { | ||
130 | + return false; | ||
131 | + } | ||
132 | + | ||
133 | + boolean success = appendValue(original, new ResourceAllocation(request, consumer)); | ||
134 | + if (!success) { | ||
135 | + return false; | ||
136 | + } | ||
137 | + | ||
138 | + return true; | ||
139 | + } | ||
140 | + | ||
141 | + // Appends the specified ResourceAllocation to the existing values stored in the map | ||
142 | + // computational complexity: O(n) where n is the number of the elements in the associated allocation | ||
143 | + private boolean appendValue(ContinuousResource original, ResourceAllocation value) { | ||
144 | + ContinuousResourceAllocation oldValue = consumers.putIfAbsent(original.id(), | ||
145 | + new ContinuousResourceAllocation(original, ImmutableList.of(value))); | ||
146 | + if (oldValue == null) { | ||
147 | + return true; | ||
148 | + } | ||
149 | + | ||
150 | + if (oldValue.allocations().contains(value)) { | ||
151 | + // don't write to map because all values are already stored | ||
152 | + return true; | ||
153 | + } | ||
154 | + | ||
155 | + ContinuousResourceAllocation newValue = new ContinuousResourceAllocation(original, | ||
156 | + ImmutableList.<ResourceAllocation>builder() | ||
157 | + .addAll(oldValue.allocations()) | ||
158 | + .add(value) | ||
159 | + .build()); | ||
160 | + return consumers.replace(original.id(), oldValue, newValue); | ||
161 | + } | ||
162 | + | ||
163 | + boolean release(ContinuousResource resource, ResourceConsumer consumer) { | ||
164 | + ContinuousResourceAllocation oldAllocation = consumers.get(resource.id()); | ||
165 | + ImmutableList<ResourceAllocation> newAllocations = oldAllocation.allocations().stream() | ||
166 | + .filter(x -> !(x.consumer().equals(consumer) && | ||
167 | + ((ContinuousResource) x.resource()).value() == resource.value())) | ||
168 | + .collect(GuavaCollectors.toImmutableList()); | ||
169 | + | ||
170 | + if (!consumers.replace(resource.id(), oldAllocation, | ||
171 | + new ContinuousResourceAllocation(oldAllocation.original(), newAllocations))) { | ||
172 | + return false; | ||
173 | + } | ||
174 | + | ||
175 | + return true; | ||
176 | + } | ||
177 | +} |
1 | +/* | ||
2 | + * Copyright 2016-present Open Networking Laboratory | ||
3 | + * | ||
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 | + */ | ||
16 | +package org.onosproject.store.resource.impl; | ||
17 | + | ||
18 | +import com.google.common.collect.Sets; | ||
19 | +import org.onosproject.net.resource.DiscreteResource; | ||
20 | +import org.onosproject.net.resource.DiscreteResourceId; | ||
21 | +import org.onosproject.net.resource.Resource; | ||
22 | +import org.onosproject.net.resource.ResourceConsumer; | ||
23 | +import org.onosproject.net.resource.Resources; | ||
24 | +import org.onosproject.store.service.TransactionContext; | ||
25 | +import org.onosproject.store.service.TransactionalMap; | ||
26 | +import org.slf4j.Logger; | ||
27 | +import org.slf4j.LoggerFactory; | ||
28 | + | ||
29 | +import java.util.LinkedHashSet; | ||
30 | +import java.util.List; | ||
31 | +import java.util.Optional; | ||
32 | +import java.util.Set; | ||
33 | + | ||
34 | +import static org.onosproject.store.resource.impl.ConsistentResourceStore.SERIALIZER; | ||
35 | + | ||
36 | +class TransactionalDiscreteResourceStore { | ||
37 | + private final Logger log = LoggerFactory.getLogger(getClass()); | ||
38 | + private final TransactionalMap<DiscreteResourceId, Set<DiscreteResource>> childMap; | ||
39 | + private final TransactionalMap<DiscreteResourceId, ResourceConsumer> consumers; | ||
40 | + | ||
41 | + TransactionalDiscreteResourceStore(TransactionContext tx) { | ||
42 | + this.childMap = tx.getTransactionalMap(MapNames.DISCRETE_CHILD_MAP, SERIALIZER); | ||
43 | + this.consumers = tx.getTransactionalMap(MapNames.DISCRETE_CONSUMER_MAP, SERIALIZER); | ||
44 | + } | ||
45 | + | ||
46 | + // check the existence in the set: O(1) operation | ||
47 | + Optional<Resource> lookup(DiscreteResourceId id) { | ||
48 | + if (!id.parent().isPresent()) { | ||
49 | + return Optional.of(Resource.ROOT); | ||
50 | + } | ||
51 | + | ||
52 | + Set<DiscreteResource> values = childMap.get(id.parent().get()); | ||
53 | + if (values == null) { | ||
54 | + return Optional.empty(); | ||
55 | + } | ||
56 | + | ||
57 | + DiscreteResource resource = Resources.discrete(id).resource(); | ||
58 | + if (values.contains(resource)) { | ||
59 | + return Optional.of(resource); | ||
60 | + } else { | ||
61 | + return Optional.empty(); | ||
62 | + } | ||
63 | + } | ||
64 | + | ||
65 | + boolean appendValues(DiscreteResourceId key, List<DiscreteResource> values) { | ||
66 | + Set<DiscreteResource> requested = new LinkedHashSet<>(values); | ||
67 | + Set<DiscreteResource> oldValues = childMap.putIfAbsent(key, requested); | ||
68 | + if (oldValues == null) { | ||
69 | + return true; | ||
70 | + } | ||
71 | + | ||
72 | + Set<DiscreteResource> addedValues = Sets.difference(requested, oldValues); | ||
73 | + // no new value, then no-op | ||
74 | + if (addedValues.isEmpty()) { | ||
75 | + // don't write to map because all values are already stored | ||
76 | + return true; | ||
77 | + } | ||
78 | + | ||
79 | + Set<DiscreteResource> newValues = new LinkedHashSet<>(oldValues); | ||
80 | + newValues.addAll(addedValues); | ||
81 | + return childMap.replace(key, oldValues, newValues); | ||
82 | + } | ||
83 | + | ||
84 | + boolean removeValues(DiscreteResourceId key, List<DiscreteResource> values) { | ||
85 | + Set<DiscreteResource> oldValues = childMap.putIfAbsent(key, new LinkedHashSet<>()); | ||
86 | + if (oldValues == null) { | ||
87 | + log.trace("No-Op removing values. key {} did not exist", key); | ||
88 | + return true; | ||
89 | + } | ||
90 | + | ||
91 | + if (values.stream().allMatch(x -> !oldValues.contains(x))) { | ||
92 | + // don't write map because none of the values are stored | ||
93 | + log.trace("No-Op removing values. key {} did not contain {}", key, values); | ||
94 | + return true; | ||
95 | + } | ||
96 | + | ||
97 | + LinkedHashSet<DiscreteResource> newValues = new LinkedHashSet<>(oldValues); | ||
98 | + newValues.removeAll(values); | ||
99 | + return childMap.replace(key, oldValues, newValues); | ||
100 | + } | ||
101 | + | ||
102 | + boolean isAllocated(DiscreteResourceId id) { | ||
103 | + return consumers.get(id) != null; | ||
104 | + } | ||
105 | + | ||
106 | + boolean allocate(ResourceConsumer consumer, DiscreteResource resource) { | ||
107 | + // if the resource is not registered, then abort | ||
108 | + Optional<Resource> lookedUp = lookup(resource.id()); | ||
109 | + if (!lookedUp.isPresent()) { | ||
110 | + return false; | ||
111 | + } | ||
112 | + | ||
113 | + ResourceConsumer oldValue = consumers.put(resource.id(), consumer); | ||
114 | + return oldValue == null; | ||
115 | + } | ||
116 | + | ||
117 | + boolean release(DiscreteResource resource, ResourceConsumer consumer) { | ||
118 | + // if this single release fails (because the resource is allocated to another consumer) | ||
119 | + // the whole release fails | ||
120 | + if (!consumers.remove(resource.id(), consumer)) { | ||
121 | + return false; | ||
122 | + } | ||
123 | + | ||
124 | + return true; | ||
125 | + } | ||
126 | +} |
-
Please register or login to post a comment