blob: 1aaf29761b297ac850cfcfddfb14f927ad29b2a7 [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
183 public boolean hasPendingUpdates() {
184 return updates().size() > 0;
185 }
186
187 protected List<MapUpdate<K, V>> updates() {
188 List<MapUpdate<K, V>> updates = Lists.newLinkedList();
189 deleteSet.forEach(key -> {
190 Versioned<V> original = readCache.get(key);
191 if (original != null) {
192 updates.add(MapUpdate.<K, V>newBuilder()
193 .withMapName(name)
194 .withType(MapUpdate.Type.REMOVE_IF_VERSION_MATCH)
195 .withKey(key)
196 .withCurrentVersion(original.version())
197 .build());
198 }
199 });
200 writeCache.forEach((key, value) -> {
201 Versioned<V> original = readCache.get(key);
202 if (original == null) {
203 updates.add(MapUpdate.<K, V>newBuilder()
204 .withMapName(name)
205 .withType(MapUpdate.Type.PUT_IF_ABSENT)
206 .withKey(key)
207 .withValue(value)
208 .build());
209 } else {
210 updates.add(MapUpdate.<K, V>newBuilder()
211 .withMapName(name)
212 .withType(MapUpdate.Type.PUT_IF_VERSION_MATCH)
213 .withKey(key)
214 .withCurrentVersion(original.version())
215 .withValue(value)
216 .build());
217 }
218 });
219 return updates;
220 }
221
222
Madan Jampanicadd70b2016-02-08 13:45:43 -0800223 protected List<MapUpdate<String, byte[]>> toMapUpdates() {
224 List<MapUpdate<String, byte[]>> updates = Lists.newLinkedList();
Madan Jampani64689552015-02-17 10:00:27 -0800225 deleteSet.forEach(key -> {
226 Versioned<V> original = readCache.get(key);
227 if (original != null) {
Madan Jampanicadd70b2016-02-08 13:45:43 -0800228 updates.add(MapUpdate.<String, byte[]>newBuilder()
Madan Jampani7804c992015-07-20 13:20:19 -0700229 .withMapName(name)
Madan Jampanicadd70b2016-02-08 13:45:43 -0800230 .withType(MapUpdate.Type.REMOVE_IF_VERSION_MATCH)
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700231 .withKey(keyCache.getUnchecked(key))
Madan Jampani64689552015-02-17 10:00:27 -0800232 .withCurrentVersion(original.version())
233 .build());
234 }
235 });
236 writeCache.forEach((key, value) -> {
237 Versioned<V> original = readCache.get(key);
238 if (original == null) {
Madan Jampanicadd70b2016-02-08 13:45:43 -0800239 updates.add(MapUpdate.<String, byte[]>newBuilder()
Madan Jampani7804c992015-07-20 13:20:19 -0700240 .withMapName(name)
Madan Jampanicadd70b2016-02-08 13:45:43 -0800241 .withType(MapUpdate.Type.PUT_IF_ABSENT)
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700242 .withKey(keyCache.getUnchecked(key))
243 .withValue(serializer.encode(value))
Madan Jampani64689552015-02-17 10:00:27 -0800244 .build());
245 } else {
Madan Jampanicadd70b2016-02-08 13:45:43 -0800246 updates.add(MapUpdate.<String, byte[]>newBuilder()
Madan Jampani7804c992015-07-20 13:20:19 -0700247 .withMapName(name)
Madan Jampanicadd70b2016-02-08 13:45:43 -0800248 .withType(MapUpdate.Type.PUT_IF_VERSION_MATCH)
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700249 .withKey(keyCache.getUnchecked(key))
Madan Jampani64689552015-02-17 10:00:27 -0800250 .withCurrentVersion(original.version())
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700251 .withValue(serializer.encode(value))
Madan Jampani64689552015-02-17 10:00:27 -0800252 .build());
253 }
254 });
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700255 return updates;
Madan Jampani64689552015-02-17 10:00:27 -0800256 }
257
HIGUCHI Yutadc4394c2016-01-29 15:35:10 -0800258 @Override
259 public String toString() {
260 return MoreObjects.toStringHelper(this)
261 .add("backingMap", backingMap)
Madan Jampani74da78b2016-02-09 21:18:36 -0800262 .add("updates", updates())
HIGUCHI Yutadc4394c2016-01-29 15:35:10 -0800263 .toString();
264 }
265
Madan Jampani64689552015-02-17 10:00:27 -0800266 /**
267 * Discards all changes made to this transactional map.
268 */
Madan Jampani74da78b2016-02-09 21:18:36 -0800269 protected void abort() {
Madan Jampani64689552015-02-17 10:00:27 -0800270 readCache.clear();
271 writeCache.clear();
272 deleteSet.clear();
273 }
274}