blob: 3594adbb6191def6ef16a366ebe9b1538b42dafc [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;
24import org.onosproject.store.service.ConsistentMap;
Madan Jampanibff6d8f2015-03-31 16:53:47 -070025import org.onosproject.store.service.DatabaseUpdate;
Madan Jampani64689552015-02-17 10:00:27 -080026import 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
Madan Jampanibff6d8f2015-03-31 16:53:47 -070033import com.google.common.base.Objects;
34import com.google.common.cache.CacheBuilder;
35import com.google.common.cache.CacheLoader;
36import com.google.common.cache.LoadingCache;
Madan Jampani64689552015-02-17 10:00:27 -080037import com.google.common.collect.Lists;
38import com.google.common.collect.Maps;
39import com.google.common.collect.Sets;
40
41/**
42 * Default Transactional Map implementation that provides a repeatable reads
43 * transaction isolation level.
44 *
45 * @param <K> key type
46 * @param <V> value type.
47 */
48public class DefaultTransactionalMap<K, V> implements TransactionalMap<K, V> {
49
50 private final TransactionContext txContext;
51 private static final String TX_CLOSED_ERROR = "Transaction is closed";
52 private final ConsistentMap<K, V> backingMap;
53 private final String name;
54 private final Serializer serializer;
55 private final Map<K, Versioned<V>> readCache = Maps.newConcurrentMap();
56 private final Map<K, V> writeCache = Maps.newConcurrentMap();
57 private final Set<K> deleteSet = Sets.newConcurrentHashSet();
58
Madan Jampanibff6d8f2015-03-31 16:53:47 -070059 private static final String ERROR_NULL_VALUE = "Null values are not allowed";
60 private static final String ERROR_NULL_KEY = "Null key is not allowed";
61
62 private final LoadingCache<K, String> keyCache = CacheBuilder.newBuilder()
63 .softValues()
64 .build(new CacheLoader<K, String>() {
65
66 @Override
67 public String load(K key) {
68 return HexString.toHexString(serializer.encode(key));
69 }
70 });
71
72 protected K dK(String key) {
73 return serializer.decode(HexString.fromHexString(key));
74 }
75
Madan Jampani64689552015-02-17 10:00:27 -080076 public DefaultTransactionalMap(
77 String name,
78 ConsistentMap<K, V> backingMap,
79 TransactionContext txContext,
80 Serializer serializer) {
81 this.name = name;
82 this.backingMap = backingMap;
83 this.txContext = txContext;
84 this.serializer = serializer;
85 }
86
87 @Override
88 public V get(K key) {
89 checkState(txContext.isOpen(), TX_CLOSED_ERROR);
Madan Jampanibff6d8f2015-03-31 16:53:47 -070090 checkNotNull(key, ERROR_NULL_KEY);
Madan Jampani64689552015-02-17 10:00:27 -080091 if (deleteSet.contains(key)) {
92 return null;
Madan Jampanibff6d8f2015-03-31 16:53:47 -070093 }
94 V latest = writeCache.get(key);
95 if (latest != null) {
96 return latest;
Madan Jampani64689552015-02-17 10:00:27 -080097 } else {
Madan Jampanibff6d8f2015-03-31 16:53:47 -070098 Versioned<V> v = readCache.computeIfAbsent(key, k -> backingMap.get(k));
Madan Jampani64689552015-02-17 10:00:27 -080099 return v != null ? v.value() : null;
100 }
101 }
102
103 @Override
104 public V put(K key, V value) {
105 checkState(txContext.isOpen(), TX_CLOSED_ERROR);
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700106 checkNotNull(value, ERROR_NULL_VALUE);
107
108 V latest = get(key);
109 writeCache.put(key, value);
Madan Jampani64689552015-02-17 10:00:27 -0800110 deleteSet.remove(key);
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700111 return latest;
Madan Jampani64689552015-02-17 10:00:27 -0800112 }
113
114 @Override
115 public V remove(K key) {
116 checkState(txContext.isOpen(), TX_CLOSED_ERROR);
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700117 V latest = get(key);
118 if (latest != null) {
119 writeCache.remove(key);
120 deleteSet.add(key);
121 }
122 return latest;
Madan Jampani64689552015-02-17 10:00:27 -0800123 }
124
125 @Override
126 public boolean remove(K key, V value) {
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700127 checkState(txContext.isOpen(), TX_CLOSED_ERROR);
128 checkNotNull(value, ERROR_NULL_VALUE);
129 V latest = get(key);
130 if (Objects.equal(value, latest)) {
Madan Jampani64689552015-02-17 10:00:27 -0800131 remove(key);
132 return true;
133 }
134 return false;
135 }
136
137 @Override
138 public boolean replace(K key, V oldValue, V newValue) {
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700139 checkState(txContext.isOpen(), TX_CLOSED_ERROR);
140 checkNotNull(oldValue, ERROR_NULL_VALUE);
141 checkNotNull(newValue, ERROR_NULL_VALUE);
142 V latest = get(key);
143 if (Objects.equal(oldValue, latest)) {
Madan Jampani64689552015-02-17 10:00:27 -0800144 put(key, newValue);
145 return true;
146 }
147 return false;
148 }
149
150 @Override
Madan Jampani64689552015-02-17 10:00:27 -0800151 public V putIfAbsent(K key, V value) {
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700152 checkState(txContext.isOpen(), TX_CLOSED_ERROR);
153 checkNotNull(value, ERROR_NULL_VALUE);
154 V latest = get(key);
155 if (latest == null) {
Madan Jampani64689552015-02-17 10:00:27 -0800156 put(key, value);
Madan Jampani64689552015-02-17 10:00:27 -0800157 }
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700158 return latest;
Madan Jampani64689552015-02-17 10:00:27 -0800159 }
160
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700161 protected List<DatabaseUpdate> prepareDatabaseUpdates() {
162 List<DatabaseUpdate> updates = Lists.newLinkedList();
Madan Jampani64689552015-02-17 10:00:27 -0800163 deleteSet.forEach(key -> {
164 Versioned<V> original = readCache.get(key);
165 if (original != null) {
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700166 updates.add(DatabaseUpdate.newBuilder()
Madan Jampani7804c992015-07-20 13:20:19 -0700167 .withMapName(name)
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700168 .withType(DatabaseUpdate.Type.REMOVE_IF_VERSION_MATCH)
169 .withKey(keyCache.getUnchecked(key))
Madan Jampani64689552015-02-17 10:00:27 -0800170 .withCurrentVersion(original.version())
171 .build());
172 }
173 });
174 writeCache.forEach((key, value) -> {
175 Versioned<V> original = readCache.get(key);
176 if (original == null) {
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700177 updates.add(DatabaseUpdate.newBuilder()
Madan Jampani7804c992015-07-20 13:20:19 -0700178 .withMapName(name)
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700179 .withType(DatabaseUpdate.Type.PUT_IF_ABSENT)
180 .withKey(keyCache.getUnchecked(key))
181 .withValue(serializer.encode(value))
Madan Jampani64689552015-02-17 10:00:27 -0800182 .build());
183 } else {
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700184 updates.add(DatabaseUpdate.newBuilder()
Madan Jampani7804c992015-07-20 13:20:19 -0700185 .withMapName(name)
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700186 .withType(DatabaseUpdate.Type.PUT_IF_VERSION_MATCH)
187 .withKey(keyCache.getUnchecked(key))
Madan Jampani64689552015-02-17 10:00:27 -0800188 .withCurrentVersion(original.version())
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700189 .withValue(serializer.encode(value))
Madan Jampani64689552015-02-17 10:00:27 -0800190 .build());
191 }
192 });
Madan Jampanibff6d8f2015-03-31 16:53:47 -0700193 return updates;
Madan Jampani64689552015-02-17 10:00:27 -0800194 }
195
196 /**
197 * Discards all changes made to this transactional map.
198 */
199 protected void rollback() {
200 readCache.clear();
201 writeCache.clear();
202 deleteSet.clear();
203 }
204}