blob: 559d33f034a7f6c461056c70ba7f47fbf7ee3b41 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jackrabbit.oak.plugins.document.cache;
import com.google.common.base.Predicate;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnel;
import com.google.common.hash.PrimitiveSink;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.util.List;
public class CacheChangesTracker implements Closeable {
private static final Logger LOG = LoggerFactory.getLogger(CacheChangesTracker.class);
static final int ENTRIES_SCOPED = 1000;
static final int ENTRIES_OPEN = 10000;
private final List<CacheChangesTracker> changeTrackers;
private final Predicate<String> keyFilter;
private final LazyBloomFilter lazyBloomFilter;
CacheChangesTracker(Predicate<String> keyFilter, List<CacheChangesTracker> changeTrackers, int bloomFilterSize) {
this.changeTrackers = changeTrackers;
this.keyFilter = keyFilter;
this.lazyBloomFilter = new LazyBloomFilter(bloomFilterSize);
changeTrackers.add(this);
}
public void putDocument(String key) {
if (keyFilter.apply(key)) {
lazyBloomFilter.put(key);
}
}
public void invalidateDocument(String key) {
if (keyFilter.apply(key)) {
lazyBloomFilter.put(key);
}
}
public boolean mightBeenAffected(String key) {
return keyFilter.apply(key) && lazyBloomFilter.mightContain(key);
}
@Override
public void close() {
changeTrackers.remove(this);
if (LOG.isDebugEnabled()) {
if (lazyBloomFilter.filter == null) {
LOG.debug("Disposing CacheChangesTracker for {}, no filter was needed", keyFilter);
} else {
LOG.debug("Disposing CacheChangesTracker for {}, filter fpp was: {}", keyFilter, lazyBloomFilter.filter.expectedFpp());
}
}
}
public static class LazyBloomFilter {
private static final double FPP = 0.01d;
private final int entries;
private volatile BloomFilter<String> filter;
public LazyBloomFilter(int entries) {
this.entries = entries;
}
public synchronized void put(String entry) {
getFilter().put(entry);
}
public boolean mightContain(String entry) {
if (filter == null) {
return false;
} else {
synchronized (this) {
return filter.mightContain(entry);
}
}
}
private BloomFilter<String> getFilter() {
if (filter == null) {
filter = BloomFilter.create(new Funnel<String>() {
private static final long serialVersionUID = -7114267990225941161L;
@Override
public void funnel(String from, PrimitiveSink into) {
into.putUnencodedChars(from);
}
}, entries, FPP);
}
return filter;
}
}
}