![JAR search and dependency download from the Maven repository](/logo.png)
org.ehcache.clustered.server.offheap.OffHeapServerStore Maven / Gradle / Ivy
/*
* Copyright Terracotta, Inc.
*
* Licensed 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.ehcache.clustered.server.offheap;
import org.ehcache.clustered.common.internal.store.Chain;
import org.ehcache.clustered.common.internal.store.ServerStore;
import org.ehcache.clustered.server.KeySegmentMapper;
import org.ehcache.clustered.server.ServerStoreEventListener;
import org.ehcache.clustered.server.state.ResourcePageSource;
import org.terracotta.offheapstore.MapInternals;
import org.terracotta.offheapstore.exceptions.OversizeMappingException;
import org.terracotta.offheapstore.paging.PageSource;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.function.LongConsumer;
import java.util.function.LongFunction;
import static org.terracotta.offheapstore.util.MemoryUnit.BYTES;
import static org.terracotta.offheapstore.util.MemoryUnit.KILOBYTES;
import static org.terracotta.offheapstore.util.MemoryUnit.MEGABYTES;
public class OffHeapServerStore implements ServerStore, MapInternals {
private static final long MAX_PAGE_SIZE_IN_KB = KILOBYTES.convert(8, MEGABYTES);
private final List> segments;
private final KeySegmentMapper mapper;
private volatile ServerStoreEventListener listener;
private volatile boolean fireEvents;
public OffHeapServerStore(List> segments, KeySegmentMapper mapper) {
this.mapper = mapper;
this.segments = segments;
}
OffHeapServerStore(PageSource source, KeySegmentMapper mapper, boolean writeBehindConfigured) {
this.mapper = mapper;
segments = new ArrayList<>(mapper.getSegments());
for (int i = 0; i < mapper.getSegments(); i++) {
if (writeBehindConfigured) {
segments.add(new PinningOffHeapChainMap<>(source, LongPortability.INSTANCE, KILOBYTES.toBytes(4), MEGABYTES.toBytes(8), false));
} else {
segments.add(new OffHeapChainMap<>(source, LongPortability.INSTANCE, KILOBYTES.toBytes(4), MEGABYTES.toBytes(8), false));
}
}
}
public OffHeapServerStore(ResourcePageSource source, KeySegmentMapper mapper, boolean writeBehindConfigured) {
this.mapper = mapper;
segments = new ArrayList<>(mapper.getSegments());
long maxSize = getMaxSize(source.getPool().getSize());
for (int i = 0; i < mapper.getSegments(); i++) {
if (writeBehindConfigured) {
segments.add(new PinningOffHeapChainMap<>(source, LongPortability.INSTANCE, KILOBYTES.toBytes(4), (int) KILOBYTES.toBytes(maxSize), false));
} else {
segments.add(new OffHeapChainMap<>(source, LongPortability.INSTANCE, KILOBYTES.toBytes(4), (int)KILOBYTES.toBytes(maxSize), false));
}
}
}
public List> getSegments() {
return segments;
}
static long getMaxSize(long poolSize) {
long l = Long.highestOneBit(poolSize);
long sizeInKb = KILOBYTES.convert(l, BYTES);
long maxSize = sizeInKb >> 5;
if (maxSize >= MAX_PAGE_SIZE_IN_KB) {
maxSize = MAX_PAGE_SIZE_IN_KB;
}
return maxSize;
}
public void setEventListener(ServerStoreEventListener listener) {
if (this.listener != null) {
throw new IllegalStateException("ServerStoreEventListener instance already set");
}
this.listener = listener;
OffHeapChainMap.ChainMapEvictionListener chainMapEvictionListener = listener::onEviction;
for (OffHeapChainMap segment : segments) {
segment.setEvictionListener(chainMapEvictionListener);
}
}
@Override
public Chain get(long key) {
return segmentFor(key).get(key);
}
@Override
public void append(long key, ByteBuffer payLoad) {
LongConsumer lambda;
if (listener != null && fireEvents) {
lambda = (k) -> {
Chain beforeAppend = segmentFor(k).getAndAppend(k, payLoad);
listener.onAppend(beforeAppend, payLoad.duplicate());
};
} else {
lambda = (k) -> segmentFor(k).append(k, payLoad);
}
try {
lambda.accept(key);
} catch (OversizeMappingException e) {
consumeOversizeMappingException(key, lambda);
}
}
@Override
public Chain getAndAppend(long key, ByteBuffer payLoad) {
LongFunction lambda;
if (listener != null && fireEvents) {
lambda = (k) -> {
Chain beforeAppend = segmentFor(k).getAndAppend(k, payLoad);
listener.onAppend(beforeAppend, payLoad.duplicate());
return beforeAppend;
};
} else {
lambda = (k) -> segmentFor(k).getAndAppend(k, payLoad);
}
try {
return lambda.apply(key);
} catch (OversizeMappingException e) {
return handleOversizeMappingException(key, lambda);
}
}
@Override
public void replaceAtHead(long key, Chain expect, Chain update) {
try {
segmentFor(key).replaceAtHead(key, expect, update);
} catch (OversizeMappingException e) {
consumeOversizeMappingException(key, (long k) -> segmentFor(k).replaceAtHead(k, expect, update));
}
}
public void put(long key, Chain chain) {
try {
try {segmentFor(key).put(key, chain);
} catch (OversizeMappingException e) {
consumeOversizeMappingException(key, (long k) -> segmentFor(k).put(k, chain));}
} catch (Throwable t) {
segmentFor(key).remove(key);
throw t;
}
}
public void remove(long key) {
segmentFor(key).remove(key);
}
@Override
public void clear() {
for (OffHeapChainMap segment : segments) {
segment.clear();
}
}
OffHeapChainMap segmentFor(long key) {
return segments.get(mapper.getSegmentForKey(key));
}
private void writeLockAll() {
for (OffHeapChainMap s : segments) {
s.writeLock().lock();
}
}
private void writeUnlockAll() {
for (OffHeapChainMap s : segments) {
s.writeLock().unlock();
}
}
private void consumeOversizeMappingException(long key, LongConsumer operation) {
handleOversizeMappingException(key, k -> {
operation.accept(k);
return null;
});
}
/**
* Force eviction from other segments until {@code operation} succeeds or no further eviction is possible.
*
* @param key the target key
* @param operation the previously failed operation
* @param operation result type
* @return the operation result
* @throws OversizeMappingException if the operation cannot be made to succeed
*/
private R handleOversizeMappingException(long key, LongFunction operation) throws OversizeMappingException {
if (tryShrinkOthers(key)) {
try {
return operation.apply(key);
} catch (OversizeMappingException ex) {
//ignore
}
}
writeLockAll();
try {
OversizeMappingException e;
do {
try {
return operation.apply(key);
} catch (OversizeMappingException ex) {
e = ex;
}
} while (tryShrinkOthers(key));
throw e;
} finally {
writeUnlockAll();
}
}
boolean tryShrinkOthers(long key) {
boolean evicted = false;
OffHeapChainMap target = segmentFor(key);
for (OffHeapChainMap s : segments) {
if (s != target) {
evicted |= s.shrink();
}
}
return evicted;
}
public void close() {
writeLockAll();
try {
clear();
} finally {
writeUnlockAll();
}
segments.clear();
}
// stats
@Override
public long getAllocatedMemory() {
long total = 0L;
for (MapInternals segment : segments) {
total += segment.getAllocatedMemory();
}
return total;
}
@Override
public long getOccupiedMemory() {
long total = 0L;
for (MapInternals segment : segments) {
total += segment.getOccupiedMemory();
}
return total;
}
@Override
public long getDataAllocatedMemory() {
long total = 0L;
for (MapInternals segment : segments) {
total += segment.getDataAllocatedMemory();
}
return total;
}
@Override
public long getDataOccupiedMemory() {
long total = 0L;
for (MapInternals segment : segments) {
total += segment.getDataOccupiedMemory();
}
return total;
}
@Override
public long getDataSize() {
long total = 0L;
for (MapInternals segment : segments) {
total += segment.getDataSize();
}
return total;
}
@Override
public long getSize() {
long total = 0L;
for (MapInternals segment : segments) {
total += segment.getSize();
}
return total;
}
@Override
public long getTableCapacity() {
long total = 0L;
for (MapInternals segment : segments) {
total += segment.getTableCapacity();
}
return total;
}
@Override
public long getUsedSlotCount() {
long total = 0L;
for (MapInternals segment : segments) {
total += segment.getUsedSlotCount();
}
return total;
}
@Override
public long getRemovedSlotCount() {
long total = 0L;
for (MapInternals segment : segments) {
total += segment.getRemovedSlotCount();
}
return total;
}
@Override
public int getReprobeLength() {
int total = 0;
for (MapInternals segment : segments) {
total += segment.getReprobeLength();
}
return total;
}
@Override
public long getVitalMemory() {
long total = 0L;
for (MapInternals segment : segments) {
total += segment.getVitalMemory();
}
return total;
}
@Override
public long getDataVitalMemory() {
long total = 0L;
for (MapInternals segment : segments) {
total += segment.getDataVitalMemory();
}
return total;
}
@Override
public Iterator iterator() {
return new AggregateIterator() {
@Override
protected Iterator getNextIterator() {
return listIterator.next().iterator();
}
};
}
public void enableEvents(boolean enable) {
this.fireEvents = enable;
}
protected abstract class AggregateIterator implements Iterator {
protected final Iterator> listIterator;
protected Iterator currentIterator;
protected abstract Iterator getNextIterator();
public AggregateIterator() {
listIterator = segments.iterator();
while (listIterator.hasNext()) {
currentIterator = getNextIterator();
if (currentIterator.hasNext()) {
return;
}
}
}
@Override
public boolean hasNext() {
if (currentIterator == null) {
return false;
}
if (currentIterator.hasNext()) {
return true;
} else {
while (listIterator.hasNext()) {
currentIterator = getNextIterator();
if (currentIterator.hasNext()) {
return true;
}
}
return false;
}
}
@Override
public T next() {
if (currentIterator == null) {
throw new NoSuchElementException();
}
if (currentIterator.hasNext()) {
return currentIterator.next();
} else {
while (listIterator.hasNext()) {
currentIterator = getNextIterator();
if (currentIterator.hasNext()) {
return currentIterator.next();
}
}
}
throw new NoSuchElementException();
}
@Override
public void remove() {
currentIterator.remove();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy