blob: 32b3057a7e29dc54b6468252b7ec5315d89f929b [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;
22
23import org.onlab.util.HexString;
Madan Jampanicadd70b2016-02-08 13:45:43 -080024import org.onosproject.store.primitives.resources.impl.MapUpdate;
Madan Jampani64689552015-02-17 10:00:27 -080025import org.onosproject.store.service.ConsistentMap;
26import org.onosproject.store.service.Serializer;
27import org.onosproject.store.service.TransactionContext;
28import org.onosproject.store.service.TransactionalMap;
Madan Jampani64689552015-02-17 10:00:27 -080029import org.onosproject.store.service.Versioned;
30
31import static com.google.common.base.Preconditions.*;
32
HIGUCHI Yutadc4394c2016-01-29 15:35:10 -080033import com.google.common.base.MoreObjects;
Madan Jampanibff6d8f2015-03-31 16:53:47 -070034import com.google.common.base.Objects;
35import com.google.common.cache.CacheBuilder;
36import com.google.common.cache.CacheLoader;
37import com.google.common.cache.LoadingCache;
Madan Jampani64689552015-02-17 10:00:27 -080038import com.google.common.collect.Lists;
39import com.google.common.collect.Maps;
40import com.google.common.collect.Sets;
41
42/**
43 * Default Transactional Map implementation that provides a repeatable reads
44 * transaction isolation level.
45 *
46 * @param <K> key type
47 * @param <V> value type.
48 */
49public class DefaultTransactionalMap<K, V> implements TransactionalMap<K, V> {
50
51 private final TransactionContext txContext;
52 private static final String TX_CLOSED_ERROR = "Transaction is closed";
53 private final ConsistentMap<K, V> backingMap;
54 private final String name;
55 private final Serializer serializer;
56 private final Map<K, Versioned<V>> readCache = Maps.newConcurrentMap();
57 private final Map<K, V> writeCache = Maps.newConcurrentMap();
58 private final Set<K> deleteSet = Sets.newConcurrentHashSet();
59
Madan Jampanibff6d8f2015-03-31 16:53:47 -070060 private static final String ERROR_NULL_VALUE = "Null values are not allowed";
61 private static final String ERROR_NULL_KEY = "Null key is not allowed";
62
63 private final LoadingCache<K, String> keyCache = CacheBuilder.newBuilder()
64 .softValues()
65 .build(new CacheLoader<K, String>() {
66
67 @Override
68 public String load(K key) {
69 return HexString.toHexString(serializer.encode(key));
70 }
71 });
72
73 protected K dK(String key) {
74 return serializer.decode(HexString.fromHexString(key));
75 }
76
Madan Jampani64689552015-02-17 10:00:27 -080077 public DefaultTransactionalMap(
78 String name,
79 ConsistentMap<K, V> backingMap,
80 TransactionContext txContext,
81 Serializer serializer) {
82 this.name = name;
83 this.backingMap = backingMap;
84 this.txContext = txContext;
85 this.serializer = serializer;
86 }
87
88 @Override
89 public V get(K key) {
90 checkState(txContext.isOpen(), TX_CLOSED_ERROR);
Madan Jampanibff6d8f2015-03-31 16:53:47 -070091 checkNotNull(key, ERROR_NULL_KEY);
Madan Jampani64689552015-02-17 10:00:27 -080092 if (deleteSet.contains(key)) {
93 return null;
Madan Jampanibff6d8f2015-03-31 16:53:47 -070094 }
95 V latest = writeCache.get(key);
96 if (latest != null) {
97 return latest;
Madan Jampani64689552015-02-17 10:00:27 -080098 } else {
Madan Jampanibff6d8f2015-03-31 16:53:47 -070099 Versioned<V> v = readCache.computeIfAbsent(key, k -> backingMap.get(k));
Madan Jampani64689552015-02-17 10:00:27 -0800100 return v != null ? v.value() : null;
101 }
102 }
103
104 @Override
105 public V put(K key, V value) {
106 checkState(txContext.isOpen(), TX_CLOSED_ERROR);
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700107 checkNotNull(value, ERROR_NULL_VALUE);
108
109 V latest = get(key);
110 writeCache.put(key, value);
Madan Jampani64689552015-02-17 10:00:27 -0800111 deleteSet.remove(key);
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700112 return latest;
Madan Jampani64689552015-02-17 10:00:27 -0800113 }
114
115 @Override
116 public V remove(K key) {
117 checkState(txContext.isOpen(), TX_CLOSED_ERROR);
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700118 V latest = get(key);
119 if (latest != null) {
120 writeCache.remove(key);
121 deleteSet.add(key);
122 }
123 return latest;
Madan Jampani64689552015-02-17 10:00:27 -0800124 }
125
126 @Override
127 public boolean remove(K key, V value) {
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700128 checkState(txContext.isOpen(), TX_CLOSED_ERROR);
129 checkNotNull(value, ERROR_NULL_VALUE);
130 V latest = get(key);
131 if (Objects.equal(value, latest)) {
Madan Jampani64689552015-02-17 10:00:27 -0800132 remove(key);
133 return true;
134 }
135 return false;
136 }
137
138 @Override
139 public boolean replace(K key, V oldValue, V newValue) {
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700140 checkState(txContext.isOpen(), TX_CLOSED_ERROR);
141 checkNotNull(oldValue, ERROR_NULL_VALUE);
142 checkNotNull(newValue, ERROR_NULL_VALUE);
143 V latest = get(key);
144 if (Objects.equal(oldValue, latest)) {
Madan Jampani64689552015-02-17 10:00:27 -0800145 put(key, newValue);
146 return true;
147 }
148 return false;
149 }
150
151 @Override
Madan Jampani64689552015-02-17 10:00:27 -0800152 public V putIfAbsent(K key, V value) {
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700153 checkState(txContext.isOpen(), TX_CLOSED_ERROR);
154 checkNotNull(value, ERROR_NULL_VALUE);
155 V latest = get(key);
156 if (latest == null) {
Madan Jampani64689552015-02-17 10:00:27 -0800157 put(key, value);
Madan Jampani64689552015-02-17 10:00:27 -0800158 }
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700159 return latest;
Madan Jampani64689552015-02-17 10:00:27 -0800160 }
161
Madan Jampanicadd70b2016-02-08 13:45:43 -0800162 protected List<MapUpdate<String, byte[]>> toMapUpdates() {
163 List<MapUpdate<String, byte[]>> updates = Lists.newLinkedList();
Madan Jampani64689552015-02-17 10:00:27 -0800164 deleteSet.forEach(key -> {
165 Versioned<V> original = readCache.get(key);
166 if (original != null) {
Madan Jampanicadd70b2016-02-08 13:45:43 -0800167 updates.add(MapUpdate.<String, byte[]>newBuilder()
Madan Jampani7804c992015-07-20 13:20:19 -0700168 .withMapName(name)
Madan Jampanicadd70b2016-02-08 13:45:43 -0800169 .withType(MapUpdate.Type.REMOVE_IF_VERSION_MATCH)
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700170 .withKey(keyCache.getUnchecked(key))
Madan Jampani64689552015-02-17 10:00:27 -0800171 .withCurrentVersion(original.version())
172 .build());
173 }
174 });
175 writeCache.forEach((key, value) -> {
176 Versioned<V> original = readCache.get(key);
177 if (original == null) {
Madan Jampanicadd70b2016-02-08 13:45:43 -0800178 updates.add(MapUpdate.<String, byte[]>newBuilder()
Madan Jampani7804c992015-07-20 13:20:19 -0700179 .withMapName(name)
Madan Jampanicadd70b2016-02-08 13:45:43 -0800180 .withType(MapUpdate.Type.PUT_IF_ABSENT)
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700181 .withKey(keyCache.getUnchecked(key))
182 .withValue(serializer.encode(value))
Madan Jampani64689552015-02-17 10:00:27 -0800183 .build());
184 } else {
Madan Jampanicadd70b2016-02-08 13:45:43 -0800185 updates.add(MapUpdate.<String, byte[]>newBuilder()
Madan Jampani7804c992015-07-20 13:20:19 -0700186 .withMapName(name)
Madan Jampanicadd70b2016-02-08 13:45:43 -0800187 .withType(MapUpdate.Type.PUT_IF_VERSION_MATCH)
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700188 .withKey(keyCache.getUnchecked(key))
Madan Jampani64689552015-02-17 10:00:27 -0800189 .withCurrentVersion(original.version())
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700190 .withValue(serializer.encode(value))
Madan Jampani64689552015-02-17 10:00:27 -0800191 .build());
192 }
193 });
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700194 return updates;
Madan Jampani64689552015-02-17 10:00:27 -0800195 }
196
HIGUCHI Yutadc4394c2016-01-29 15:35:10 -0800197 // TODO: build expected result Map processing DB updates?
198 @Override
199 public String toString() {
200 return MoreObjects.toStringHelper(this)
201 .add("backingMap", backingMap)
Madan Jampanicadd70b2016-02-08 13:45:43 -0800202 .add("updates", toMapUpdates())
HIGUCHI Yutadc4394c2016-01-29 15:35:10 -0800203 .toString();
204 }
205
Madan Jampani64689552015-02-17 10:00:27 -0800206 /**
207 * Discards all changes made to this transactional map.
208 */
209 protected void rollback() {
210 readCache.clear();
211 writeCache.clear();
212 deleteSet.clear();
213 }
214}