com.gemstone.gemfire.internal.cache.persistence.soplog.SortedBuffer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gemfire-core Show documentation
Show all versions of gemfire-core Show documentation
SnappyData store based off Pivotal GemFireXD
The newest version!
/*
* Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
*
* 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. See accompanying
* LICENSE file.
*/
package com.gemstone.gemfire.internal.cache.persistence.soplog;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.EnumMap;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.NavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;
import com.gemstone.gemfire.internal.cache.persistence.soplog.SortedOplogFactory.SortedOplogConfiguration;
import com.gemstone.gemfire.internal.util.LogService;
/**
* Provides an in-memory buffer to temporarily hold key/value pairs until they
* can be flushed to disk. Each buffer instance can be optionally associated
* with a user-specified tag for identification purposes.
*
* @param the tag type
* @author bakera
*/
public class SortedBuffer extends AbstractSortedReader {
/** the logger */
private final ComponentLogWriter logger;
/** the tag */
private final T tag;
/** in-memory sorted key/vaue buffer */
private final NavigableMap buffer;
/** the stats */
private final BufferStats stats;
/** the metadata, set during flush */
private final EnumMap metadata;
/** the command to run (or defer) when the flush is complete */
private Runnable flushAction;
public SortedBuffer(SortedOplogConfiguration config, T tag) {
assert config != null;
assert tag != null;
this.tag = tag;
buffer = new ConcurrentSkipListMap(config.getComparator());
stats = new BufferStats();
metadata = new EnumMap(Metadata.class);
String name = config.getName() + "#" + tag;
logger = ComponentLogWriter.getSoplogLogWriter(name, LogService.logger());
}
/**
* Returns the tag associated with the buffer.
* @return the tag
*/
public T getTag() {
return tag;
}
@Override
public String toString() {
return logger.name();
}
/**
* Adds a new value to the buffer.
* @param key the key
* @param value the value
*/
public void put(byte[] key, byte[] value) {
if (buffer.put(key, value) == null) {
// ASSUMPTION: updates don't significantly change the value length
// this lets us optimize statistics calculations
stats.add(key.length, value.length);
}
}
/**
* Allows sorted iteration over the buffer contents.
* @return the buffer entries
*/
public Iterable> entries() {
return buffer.entrySet();
}
/**
* Returns the number of entries in the buffer.
* @return the count
*/
public int count() {
return buffer.size();
}
/**
* Returns the size of the data in bytes.
* @return the data size
*/
public long dataSize() {
return stats.totalSize();
}
/**
* Clears the buffer of all entries.
*/
public void clear() {
if (logger.fineEnabled()) {
logger.fine("Clearing buffer");
}
buffer.clear();
stats.clear();
metadata.clear();
synchronized (this) {
flushAction = null;
}
}
/**
* Returns true if the flush completion has been deferred.
* @return true if deferred
*/
public synchronized boolean isDeferred() {
return flushAction != null;
}
/**
* Defers the flush completion to a later time. This is used to ensure correct
* ordering of soplogs during parallel flushes.
*
* @param action the action to perform when ready
*/
public synchronized void defer(Runnable action) {
assert flushAction == null;
if (logger.fineEnabled()) {
logger.fine("Deferring flush completion");
}
flushAction = action;
}
/**
* Completes the deferred flush operation.
*/
public synchronized void complete() {
assert flushAction != null;
try {
if (logger.fineEnabled()) {
logger.fine("Completing deferred flush operation");
}
flushAction.run();
} finally {
flushAction = null;
}
}
/**
* Returns the buffer metadata.
* @return the metadata
*/
public synchronized EnumMap getMetadata() {
return metadata;
}
/**
* Returns the metadata value for the given key.
*
* @param name the metadata name
* @return the requested metadata
*/
public synchronized byte[] getMetadata(Metadata name) {
return metadata.get(name);
}
/**
* Sets the metadata for the buffer. This is not available until the buffer
* is about to be flushed.
*
* @param metadata the metadata
*/
public synchronized void setMetadata(EnumMap metadata) {
if (metadata != null) {
this.metadata.putAll(metadata);
}
}
@Override
public boolean mightContain(byte[] key) {
return true;
}
@Override
public ByteBuffer read(byte[] key) throws IOException {
byte[] val = buffer.get(key);
if (val != null) {
return ByteBuffer.wrap(val);
}
return null;
}
@Override
public SortedIterator scan(
byte[] from, boolean fromInclusive,
byte[] to, boolean toInclusive,
boolean ascending,
MetadataFilter filter) {
if (filter == null || filter.accept(metadata.get(filter.getName()))) {
NavigableMap subset = ascending ? buffer : buffer.descendingMap();
if (from == null && to == null) {
// we're good
} else if (from == null) {
subset = subset.headMap(to, toInclusive);
} else if (to == null) {
subset = subset.tailMap(from, fromInclusive);
} else {
subset = subset.subMap(from, fromInclusive, to, toInclusive);
}
return new BufferIterator(subset.entrySet().iterator());
}
return new BufferIterator(Collections.emptyMap().entrySet().iterator());
}
@Override
public SerializedComparator getComparator() {
return (SerializedComparator) buffer.comparator();
}
@Override
public SortedStatistics getStatistics() {
return stats;
}
@Override
public void close() throws IOException {
if (logger.fineEnabled()) {
logger.fine("Closing buffer");
}
synchronized (this) {
flushAction = null;
}
}
/**
* Allows sorted iteration over the buffer contents.
*/
public static class BufferIterator implements SortedIterator
{
/** the backing iterator */
private final Iterator> entries;
/** the iteration cursor */
private Entry current;
public BufferIterator(Iterator> iterator) {
this.entries = iterator;
}
@Override
public ByteBuffer key() {
return ByteBuffer.wrap(current.getKey());
}
@Override
public ByteBuffer value() {
return ByteBuffer.wrap(current.getValue());
}
@Override
public void close() {
}
@Override
public boolean hasNext() {
return entries.hasNext();
}
@Override
public ByteBuffer next() {
current = entries.next();
return key();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
private class BufferStats implements SortedStatistics {
/** data size */
private long totalSize;
/** key count */
private long keys;
/** avg key size */
private double avgKeySize;
/** avg value size */
private double avgValueSize;
private synchronized void clear() {
totalSize = 0;
keys = 0;
avgKeySize = 0;
avgValueSize = 0;
}
private synchronized void add(int keyLength, int valueLength) {
totalSize += keyLength + valueLength;
avgKeySize = (keyLength + keys * avgKeySize) / (keys + 1);
avgValueSize = (keyLength + keys * avgValueSize) / (keys + 1);
keys++;
}
@Override
public synchronized long keyCount() {
return keys;
}
@Override
public byte[] firstKey() {
return buffer.firstKey();
}
@Override
public byte[] lastKey() {
return buffer.lastKey();
}
@Override
public synchronized double avgKeySize() {
return avgKeySize;
}
@Override
public synchronized double avgValueSize() {
return avgValueSize;
}
@Override
public void close() {
}
public synchronized long totalSize() {
return totalSize;
}
}
}