blob: 2d809709fae111146d9c2f4ed55c1780cf003e2f [file] [log] [blame]
Madan Jampani64689552015-02-17 10:00:27 -08001/*
2 * Copyright 2015 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
Madan Jampanif4c88502016-01-21 12:35:36 -080017package org.onosproject.store.primitives.impl;
Madan Jampani64689552015-02-17 10:00:27 -080018
Madan Jampani64689552015-02-17 10:00:27 -080019import java.util.List;
20import java.util.Map;
Madan Jampani64689552015-02-17 10:00:27 -080021import java.util.Set;
Madan Jampani74da78b2016-02-09 21:18:36 -080022import java.util.concurrent.CompletableFuture;
Madan Jampani64689552015-02-17 10:00:27 -080023
24import org.onlab.util.HexString;
Madan Jampani74da78b2016-02-09 21:18:36 -080025import org.onosproject.store.primitives.MapUpdate;
26import org.onosproject.store.service.AsyncConsistentMap;
Madan Jampani64689552015-02-17 10:00:27 -080027import org.onosproject.store.service.ConsistentMap;
Madan Jampani74da78b2016-02-09 21:18:36 -080028import org.onosproject.store.service.MapTransaction;
Madan Jampani64689552015-02-17 10:00:27 -080029import org.onosproject.store.service.Serializer;
30import org.onosproject.store.service.TransactionContext;
31import org.onosproject.store.service.TransactionalMap;
Madan Jampani64689552015-02-17 10:00:27 -080032import org.onosproject.store.service.Versioned;
33
34import static com.google.common.base.Preconditions.*;
35
HIGUCHI Yutadc4394c2016-01-29 15:35:10 -080036import com.google.common.base.MoreObjects;
Madan Jampanibff6d8f2015-03-31 16:53:47 -070037import com.google.common.base.Objects;
38import com.google.common.cache.CacheBuilder;
39import com.google.common.cache.CacheLoader;
40import com.google.common.cache.LoadingCache;
Madan Jampani64689552015-02-17 10:00:27 -080041import com.google.common.collect.Lists;
42import com.google.common.collect.Maps;
43import com.google.common.collect.Sets;
44
45/**
46 * Default Transactional Map implementation that provides a repeatable reads
47 * transaction isolation level.
48 *
49 * @param <K> key type
50 * @param <V> value type.
51 */
Madan Jampani74da78b2016-02-09 21:18:36 -080052public class DefaultTransactionalMap<K, V> implements TransactionalMap<K, V>, TransactionParticipant {
Madan Jampani64689552015-02-17 10:00:27 -080053
54 private final TransactionContext txContext;
55 private static final String TX_CLOSED_ERROR = "Transaction is closed";
Madan Jampani74da78b2016-02-09 21:18:36 -080056 private final AsyncConsistentMap<K, V> backingMap;
57 private final ConsistentMap<K, V> backingConsitentMap;
Madan Jampani64689552015-02-17 10:00:27 -080058 private final String name;
59 private final Serializer serializer;
60 private final Map<K, Versioned<V>> readCache = Maps.newConcurrentMap();
61 private final Map<K, V> writeCache = Maps.newConcurrentMap();
62 private final Set<K> deleteSet = Sets.newConcurrentHashSet();
63
Madan Jampanibff6d8f2015-03-31 16:53:47 -070064 private static final String ERROR_NULL_VALUE = "Null values are not allowed";
65 private static final String ERROR_NULL_KEY = "Null key is not allowed";
66
67 private final LoadingCache<K, String> keyCache = CacheBuilder.newBuilder()
68 .softValues()
69 .build(new CacheLoader<K, String>() {
70
71 @Override
72 public String load(K key) {
73 return HexString.toHexString(serializer.encode(key));
74 }
75 });
76
77 protected K dK(String key) {
78 return serializer.decode(HexString.fromHexString(key));
79 }
80
Madan Jampani64689552015-02-17 10:00:27 -080081 public DefaultTransactionalMap(
82 String name,
Madan Jampani74da78b2016-02-09 21:18:36 -080083 AsyncConsistentMap<K, V> backingMap,
Madan Jampani64689552015-02-17 10:00:27 -080084 TransactionContext txContext,
85 Serializer serializer) {
86 this.name = name;
87 this.backingMap = backingMap;
Madan Jampani74da78b2016-02-09 21:18:36 -080088 this.backingConsitentMap = backingMap.asConsistentMap();
Madan Jampani64689552015-02-17 10:00:27 -080089 this.txContext = txContext;
90 this.serializer = serializer;
91 }
92
93 @Override
94 public V get(K key) {
95 checkState(txContext.isOpen(), TX_CLOSED_ERROR);
Madan Jampanibff6d8f2015-03-31 16:53:47 -070096 checkNotNull(key, ERROR_NULL_KEY);
Madan Jampani64689552015-02-17 10:00:27 -080097 if (deleteSet.contains(key)) {
98 return null;
Madan Jampanibff6d8f2015-03-31 16:53:47 -070099 }
100 V latest = writeCache.get(key);
101 if (latest != null) {
102 return latest;
Madan Jampani64689552015-02-17 10:00:27 -0800103 } else {
Madan Jampani74da78b2016-02-09 21:18:36 -0800104 Versioned<V> v = readCache.computeIfAbsent(key, k -> backingConsitentMap.get(k));
Madan Jampani64689552015-02-17 10:00:27 -0800105 return v != null ? v.value() : null;
106 }
107 }
108
109 @Override
110 public V put(K key, V value) {
111 checkState(txContext.isOpen(), TX_CLOSED_ERROR);
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700112 checkNotNull(value, ERROR_NULL_VALUE);
113
114 V latest = get(key);
115 writeCache.put(key, value);
Madan Jampani64689552015-02-17 10:00:27 -0800116 deleteSet.remove(key);
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700117 return latest;
Madan Jampani64689552015-02-17 10:00:27 -0800118 }
119
120 @Override
121 public V remove(K key) {
122 checkState(txContext.isOpen(), TX_CLOSED_ERROR);
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700123 V latest = get(key);
124 if (latest != null) {
125 writeCache.remove(key);
126 deleteSet.add(key);
127 }
128 return latest;
Madan Jampani64689552015-02-17 10:00:27 -0800129 }
130
131 @Override
132 public boolean remove(K key, V value) {
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700133 checkState(txContext.isOpen(), TX_CLOSED_ERROR);
134 checkNotNull(value, ERROR_NULL_VALUE);
135 V latest = get(key);
136 if (Objects.equal(value, latest)) {
Madan Jampani64689552015-02-17 10:00:27 -0800137 remove(key);
138 return true;
139 }
140 return false;
141 }
142
143 @Override
144 public boolean replace(K key, V oldValue, V newValue) {
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700145 checkState(txContext.isOpen(), TX_CLOSED_ERROR);
146 checkNotNull(oldValue, ERROR_NULL_VALUE);
147 checkNotNull(newValue, ERROR_NULL_VALUE);
148 V latest = get(key);
149 if (Objects.equal(oldValue, latest)) {
Madan Jampani64689552015-02-17 10:00:27 -0800150 put(key, newValue);
151 return true;
152 }
153 return false;
154 }
155
156 @Override
Madan Jampani64689552015-02-17 10:00:27 -0800157 public V putIfAbsent(K key, V value) {
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700158 checkState(txContext.isOpen(), TX_CLOSED_ERROR);
159 checkNotNull(value, ERROR_NULL_VALUE);
160 V latest = get(key);
161 if (latest == null) {
Madan Jampani64689552015-02-17 10:00:27 -0800162 put(key, value);
Madan Jampani64689552015-02-17 10:00:27 -0800163 }
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700164 return latest;
Madan Jampani64689552015-02-17 10:00:27 -0800165 }
166
Madan Jampani74da78b2016-02-09 21:18:36 -0800167 @Override
168 public CompletableFuture<Boolean> prepare() {
169 return backingMap.prepare(new MapTransaction<>(txContext.transactionId(), updates()));
170 }
171
172 @Override
173 public CompletableFuture<Void> commit() {
174 return backingMap.commit(txContext.transactionId());
175 }
176
177 @Override
178 public CompletableFuture<Void> rollback() {
179 return backingMap.rollback(txContext.transactionId());
180 }
181
182 @Override
Madan Jampani542d9e22016-04-05 15:39:55 -0700183 public CompletableFuture<Boolean> prepareAndCommit() {
184 return backingMap.prepareAndCommit(new MapTransaction<>(txContext.transactionId(), updates()));
185 }
186
187 @Override
188 public int totalUpdates() {
189 return updates().size();
Madan Jampani74da78b2016-02-09 21:18:36 -0800190 }
191
192 protected List<MapUpdate<K, V>> updates() {
193 List<MapUpdate<K, V>> updates = Lists.newLinkedList();
194 deleteSet.forEach(key -> {
195 Versioned<V> original = readCache.get(key);
196 if (original != null) {
197 updates.add(MapUpdate.<K, V>newBuilder()
198 .withMapName(name)
199 .withType(MapUpdate.Type.REMOVE_IF_VERSION_MATCH)
200 .withKey(key)
201 .withCurrentVersion(original.version())
202 .build());
203 }
204 });
205 writeCache.forEach((key, value) -> {
206 Versioned<V> original = readCache.get(key);
207 if (original == null) {
208 updates.add(MapUpdate.<K, V>newBuilder()
209 .withMapName(name)
210 .withType(MapUpdate.Type.PUT_IF_ABSENT)
211 .withKey(key)
212 .withValue(value)
213 .build());
214 } else {
215 updates.add(MapUpdate.<K, V>newBuilder()
216 .withMapName(name)
217 .withType(MapUpdate.Type.PUT_IF_VERSION_MATCH)
218 .withKey(key)
219 .withCurrentVersion(original.version())
220 .withValue(value)
221 .build());
222 }
223 });
224 return updates;
225 }
226
227
Madan Jampanicadd70b2016-02-08 13:45:43 -0800228 protected List<MapUpdate<String, byte[]>> toMapUpdates() {
229 List<MapUpdate<String, byte[]>> updates = Lists.newLinkedList();
Madan Jampani64689552015-02-17 10:00:27 -0800230 deleteSet.forEach(key -> {
231 Versioned<V> original = readCache.get(key);
232 if (original != null) {
Madan Jampanicadd70b2016-02-08 13:45:43 -0800233 updates.add(MapUpdate.<String, byte[]>newBuilder()
Madan Jampani7804c992015-07-20 13:20:19 -0700234 .withMapName(name)
Madan Jampanicadd70b2016-02-08 13:45:43 -0800235 .withType(MapUpdate.Type.REMOVE_IF_VERSION_MATCH)
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700236 .withKey(keyCache.getUnchecked(key))
Madan Jampani64689552015-02-17 10:00:27 -0800237 .withCurrentVersion(original.version())
238 .build());
239 }
240 });
241 writeCache.forEach((key, value) -> {
242 Versioned<V> original = readCache.get(key);
243 if (original == null) {
Madan Jampanicadd70b2016-02-08 13:45:43 -0800244 updates.add(MapUpdate.<String, byte[]>newBuilder()
Madan Jampani7804c992015-07-20 13:20:19 -0700245 .withMapName(name)
Madan Jampanicadd70b2016-02-08 13:45:43 -0800246 .withType(MapUpdate.Type.PUT_IF_ABSENT)
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700247 .withKey(keyCache.getUnchecked(key))
248 .withValue(serializer.encode(value))
Madan Jampani64689552015-02-17 10:00:27 -0800249 .build());
250 } else {
Madan Jampanicadd70b2016-02-08 13:45:43 -0800251 updates.add(MapUpdate.<String, byte[]>newBuilder()
Madan Jampani7804c992015-07-20 13:20:19 -0700252 .withMapName(name)
Madan Jampanicadd70b2016-02-08 13:45:43 -0800253 .withType(MapUpdate.Type.PUT_IF_VERSION_MATCH)
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700254 .withKey(keyCache.getUnchecked(key))
Madan Jampani64689552015-02-17 10:00:27 -0800255 .withCurrentVersion(original.version())
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700256 .withValue(serializer.encode(value))
Madan Jampani64689552015-02-17 10:00:27 -0800257 .build());
258 }
259 });
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700260 return updates;
Madan Jampani64689552015-02-17 10:00:27 -0800261 }
262
HIGUCHI Yutadc4394c2016-01-29 15:35:10 -0800263 @Override
264 public String toString() {
265 return MoreObjects.toStringHelper(this)
266 .add("backingMap", backingMap)
Madan Jampani74da78b2016-02-09 21:18:36 -0800267 .add("updates", updates())
HIGUCHI Yutadc4394c2016-01-29 15:35:10 -0800268 .toString();
269 }
270
Madan Jampani64689552015-02-17 10:00:27 -0800271 /**
272 * Discards all changes made to this transactional map.
273 */
Madan Jampani74da78b2016-02-09 21:18:36 -0800274 protected void abort() {
Madan Jampani64689552015-02-17 10:00:27 -0800275 readCache.clear();
276 writeCache.clear();
277 deleteSet.clear();
278 }
279}