blob: cedd810bdbf9a6b65a9a2fbcdd66c7b61a9effc1 [file] [log] [blame]
Madan Jampani10073672016-01-21 19:13:59 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
Madan Jampani10073672016-01-21 19:13:59 -08003 *
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 */
Madan Jampani10073672016-01-21 19:13:59 -080016package org.onosproject.store.primitives.impl;
17
Madan Jampani1d3b6172016-04-28 13:22:57 -070018import static org.slf4j.LoggerFactory.getLogger;
19
Madan Jampani10073672016-01-21 19:13:59 -080020import java.util.concurrent.CompletableFuture;
21import java.util.function.BiFunction;
Madan Jampani1d3b6172016-04-28 13:22:57 -070022import java.util.function.Consumer;
Madan Jampani10073672016-01-21 19:13:59 -080023import java.util.function.Predicate;
24
25import org.onosproject.store.service.AsyncConsistentMap;
Madan Jampani62f15332016-02-01 13:18:39 -080026import org.onosproject.store.service.MapEventListener;
Madan Jampani10073672016-01-21 19:13:59 -080027import org.onosproject.store.service.Versioned;
Madan Jampani1d3b6172016-04-28 13:22:57 -070028import org.slf4j.Logger;
Madan Jampani10073672016-01-21 19:13:59 -080029
30import com.google.common.cache.CacheBuilder;
31import com.google.common.cache.CacheLoader;
32import com.google.common.cache.LoadingCache;
33
Madan Jampani1d3b6172016-04-28 13:22:57 -070034import static org.onosproject.store.service.DistributedPrimitive.Status.INACTIVE;
35import static org.onosproject.store.service.DistributedPrimitive.Status.SUSPENDED;
36
Madan Jampani10073672016-01-21 19:13:59 -080037/**
38 * {@code AsyncConsistentMap} that caches entries on read.
39 * <p>
40 * The cache entries are automatically invalidated when updates are detected either locally or
41 * remotely.
42 * <p> This implementation only attempts to serve cached entries for {@link AsyncConsistentMap#get get}
43 * calls. All other calls skip the cache and directly go the backing map.
44 *
45 * @param <K> key type
46 * @param <V> value type
47 */
48public class CachingAsyncConsistentMap<K, V> extends DelegatingAsyncConsistentMap<K, V> {
Madan Jampani1d3b6172016-04-28 13:22:57 -070049 private static final int DEFAULT_CACHE_SIZE = 10000;
50 private final Logger log = getLogger(getClass());
Madan Jampani10073672016-01-21 19:13:59 -080051
Madan Jampani1d3b6172016-04-28 13:22:57 -070052 private final LoadingCache<K, CompletableFuture<Versioned<V>>> cache;
Jordan Haltermanb2243072017-05-09 12:04:04 -070053 private final AsyncConsistentMap<K, V> backingMap;
Madan Jampanie88086f2016-06-16 16:48:14 -070054 private final MapEventListener<K, V> cacheUpdater;
Madan Jampani1d3b6172016-04-28 13:22:57 -070055 private final Consumer<Status> statusListener;
Madan Jampani62f15332016-02-01 13:18:39 -080056
sangyun-hancce07c52016-04-06 11:07:59 +090057 /**
58 * Default constructor.
59 *
60 * @param backingMap a distributed, strongly consistent map for backing
61 */
Madan Jampani10073672016-01-21 19:13:59 -080062 public CachingAsyncConsistentMap(AsyncConsistentMap<K, V> backingMap) {
Madan Jampani1d3b6172016-04-28 13:22:57 -070063 this(backingMap, DEFAULT_CACHE_SIZE);
Madan Jampani62f15332016-02-01 13:18:39 -080064 }
65
sangyun-hancce07c52016-04-06 11:07:59 +090066 /**
Madan Jampani1d3b6172016-04-28 13:22:57 -070067 * Constructor to configure cache size.
sangyun-hancce07c52016-04-06 11:07:59 +090068 *
69 * @param backingMap a distributed, strongly consistent map for backing
70 * @param cacheSize the maximum size of the cache
71 */
72 public CachingAsyncConsistentMap(AsyncConsistentMap<K, V> backingMap, int cacheSize) {
73 super(backingMap);
Jordan Haltermanb2243072017-05-09 12:04:04 -070074 this.backingMap = backingMap;
Madan Jampani1d3b6172016-04-28 13:22:57 -070075 cache = CacheBuilder.newBuilder()
76 .maximumSize(cacheSize)
77 .build(CacheLoader.from(CachingAsyncConsistentMap.super::get));
Madan Jampanie88086f2016-06-16 16:48:14 -070078 cacheUpdater = event -> {
79 Versioned<V> newValue = event.newValue();
80 if (newValue == null) {
81 cache.invalidate(event.key());
82 } else {
83 cache.put(event.key(), CompletableFuture.completedFuture(newValue));
84 }
85 };
Madan Jampani1d3b6172016-04-28 13:22:57 -070086 statusListener = status -> {
87 log.debug("{} status changed to {}", this.name(), status);
88 // If the status of the underlying map is SUSPENDED or INACTIVE
89 // we can no longer guarantee that the cache will be in sync.
90 if (status == SUSPENDED || status == INACTIVE) {
91 cache.invalidateAll();
92 }
93 };
Madan Jampanie88086f2016-06-16 16:48:14 -070094 super.addListener(cacheUpdater);
Madan Jampani1d3b6172016-04-28 13:22:57 -070095 super.addStatusChangeListener(statusListener);
sangyun-hancce07c52016-04-06 11:07:59 +090096 }
97
Madan Jampani62f15332016-02-01 13:18:39 -080098 @Override
99 public CompletableFuture<Void> destroy() {
Madan Jampani1d3b6172016-04-28 13:22:57 -0700100 super.removeStatusChangeListener(statusListener);
Madan Jampanie88086f2016-06-16 16:48:14 -0700101 return super.destroy().thenCompose(v -> removeListener(cacheUpdater));
Madan Jampani10073672016-01-21 19:13:59 -0800102 }
103
104 @Override
105 public CompletableFuture<Versioned<V>> get(K key) {
Madan Jampani77012442016-06-02 07:47:42 -0700106 return cache.getUnchecked(key)
107 .whenComplete((r, e) -> {
108 if (e != null) {
109 cache.invalidate(key);
110 }
111 });
Madan Jampani10073672016-01-21 19:13:59 -0800112 }
113
114 @Override
Jordan Haltermanb2243072017-05-09 12:04:04 -0700115 public CompletableFuture<Versioned<V>> getOrDefault(K key, V defaultValue) {
116 return cache.getUnchecked(key).thenCompose(r -> {
117 if (r == null) {
118 CompletableFuture<Versioned<V>> versioned = backingMap.getOrDefault(key, defaultValue);
119 cache.put(key, versioned);
120 return versioned;
121 } else {
122 return CompletableFuture.completedFuture(r);
123 }
124 }).whenComplete((r, e) -> {
125 if (e != null) {
126 cache.invalidate(key);
127 }
128 });
129 }
130
131 @Override
Madan Jampani10073672016-01-21 19:13:59 -0800132 public CompletableFuture<Versioned<V>> computeIf(K key,
133 Predicate<? super V> condition,
134 BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
135 return super.computeIf(key, condition, remappingFunction)
sangyun-hancce07c52016-04-06 11:07:59 +0900136 .whenComplete((r, e) -> cache.invalidate(key));
Madan Jampani10073672016-01-21 19:13:59 -0800137 }
138
139 @Override
140 public CompletableFuture<Versioned<V>> put(K key, V value) {
141 return super.put(key, value)
sangyun-hancce07c52016-04-06 11:07:59 +0900142 .whenComplete((r, e) -> cache.invalidate(key));
Madan Jampani10073672016-01-21 19:13:59 -0800143 }
144
145 @Override
146 public CompletableFuture<Versioned<V>> putAndGet(K key, V value) {
Simon Hunt5829c342016-03-07 17:01:43 -0800147 return super.putAndGet(key, value)
sangyun-hancce07c52016-04-06 11:07:59 +0900148 .whenComplete((r, e) -> cache.invalidate(key));
Madan Jampani10073672016-01-21 19:13:59 -0800149 }
150
151 @Override
152 public CompletableFuture<Versioned<V>> remove(K key) {
153 return super.remove(key)
sangyun-hancce07c52016-04-06 11:07:59 +0900154 .whenComplete((r, e) -> cache.invalidate(key));
Madan Jampani10073672016-01-21 19:13:59 -0800155 }
156
157 @Override
158 public CompletableFuture<Void> clear() {
159 return super.clear()
sangyun-hancce07c52016-04-06 11:07:59 +0900160 .whenComplete((r, e) -> cache.invalidateAll());
Madan Jampani10073672016-01-21 19:13:59 -0800161 }
162
163 @Override
164 public CompletableFuture<Boolean> remove(K key, V value) {
165 return super.remove(key, value)
Madan Jampani77012442016-06-02 07:47:42 -0700166 .whenComplete((r, e) -> {
167 if (r) {
168 cache.invalidate(key);
169 }
170 });
Madan Jampani10073672016-01-21 19:13:59 -0800171 }
172
173 @Override
174 public CompletableFuture<Boolean> remove(K key, long version) {
175 return super.remove(key, version)
176 .whenComplete((r, e) -> {
177 if (r) {
178 cache.invalidate(key);
179 }
180 });
181 }
182
183 @Override
184 public CompletableFuture<Versioned<V>> replace(K key, V value) {
185 return super.replace(key, value)
Madan Jampani77012442016-06-02 07:47:42 -0700186 .whenComplete((r, e) -> cache.invalidate(key));
Madan Jampani10073672016-01-21 19:13:59 -0800187 }
188
189 @Override
190 public CompletableFuture<Boolean> replace(K key, V oldValue, V newValue) {
191 return super.replace(key, oldValue, newValue)
192 .whenComplete((r, e) -> {
193 if (r) {
194 cache.invalidate(key);
195 }
196 });
197 }
198
199 @Override
200 public CompletableFuture<Boolean> replace(K key, long oldVersion, V newValue) {
201 return super.replace(key, oldVersion, newValue)
202 .whenComplete((r, e) -> {
203 if (r) {
204 cache.invalidate(key);
205 }
206 });
207 }
208}