blob: 62cfe82bfcbd42fdc2ca6bcd147f412707cfc7d5 [file] [log] [blame]
/*=========================================================================
* Copyright (c) 2010-2014 Pivotal Software, Inc. All Rights Reserved.
* This product is protected by U.S. and international copyright
* and intellectual property laws. Pivotal products are covered by
* one or more patents listed at http://www.pivotal.io/patents.
*=========================================================================
*/
package com.gemstone.gemfire.internal.cache;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.logging.log4j.Logger;
import com.gemstone.gemfire.cache.DiskAccessException;
import com.gemstone.gemfire.internal.cache.DiskEntry.Helper.ValueWrapper;
import com.gemstone.gemfire.internal.i18n.LocalizedStrings;
import com.gemstone.gemfire.internal.logging.LogService;
import com.gemstone.gemfire.internal.logging.log4j.LocalizedMessage;
public class OverflowOplogSet implements OplogSet {
private static final Logger logger = LogService.getLogger();
private final AtomicInteger overflowOplogId = new AtomicInteger(0);
private OverflowOplog lastOverflowWrite = null;
private final ConcurrentMap<Integer, OverflowOplog> overflowMap
= new ConcurrentHashMap<Integer, OverflowOplog>();
private final Map<Integer, OverflowOplog> compactableOverflowMap
= new LinkedHashMap<Integer, OverflowOplog>();
private int lastOverflowDir = 0;
private DiskStoreImpl parent;
public OverflowOplogSet(DiskStoreImpl parent) {
this.parent = parent;
}
OverflowOplog getActiveOverflowOplog() {
return this.lastOverflowWrite;
}
@Override
public final void modify(LocalRegion lr, DiskEntry entry, ValueWrapper value,
boolean async) {
DiskRegion dr = lr.getDiskRegion();
synchronized (this.overflowMap) {
if (this.lastOverflowWrite != null) {
if (this.lastOverflowWrite.modify(dr, entry, value, async)) {
return;
}
}
// Create a new one and put it on the front of the list.
OverflowOplog oo = createOverflowOplog(value.getLength());
addOverflow(oo);
this.lastOverflowWrite = oo;
boolean didIt = oo.modify(dr, entry, value, async);
assert didIt;
}
}
private long getMaxOplogSizeInBytes() {
return parent.getMaxOplogSizeInBytes();
}
private DirectoryHolder[] getDirectories() {
return parent.directories;
}
/**
* @param minSize the minimum size this oplog can be
*/
private OverflowOplog createOverflowOplog(long minSize) {
lastOverflowDir++;
if (lastOverflowDir >= getDirectories().length) {
lastOverflowDir=0;
}
int idx = -1;
long maxOplogSizeParam = getMaxOplogSizeInBytes();
if (maxOplogSizeParam < minSize) {
maxOplogSizeParam = minSize;
}
// first look for a directory that has room for maxOplogSize
for (int i = lastOverflowDir; i < getDirectories().length; i++) {
long availableSpace = getDirectories()[i].getAvailableSpace();
if (availableSpace >= maxOplogSizeParam) {
idx = i;
break;
}
}
if (idx == -1 && lastOverflowDir != 0) {
for (int i = 0; i < lastOverflowDir; i++) {
long availableSpace = getDirectories()[i].getAvailableSpace();
if (availableSpace >= maxOplogSizeParam) {
idx = i;
break;
}
}
}
if (idx == -1) {
// if we couldn't find one big enough for the max look for one
// that has min room
for (int i = lastOverflowDir; i < getDirectories().length; i++) {
long availableSpace = getDirectories()[i].getAvailableSpace() ;
if (availableSpace >= minSize) {
idx = i;
break;
}
}
if (idx == -1 && lastOverflowDir != 0) {
for (int i = 0; i < lastOverflowDir; i++) {
long availableSpace = getDirectories()[i].getAvailableSpace() ;
if (availableSpace >= minSize) {
idx = i;
break;
}
}
}
}
if (idx == -1) {
if (parent.isCompactionEnabled()) { // fix for bug 41835
idx = lastOverflowDir;
if (getDirectories()[idx].getAvailableSpace() < minSize) {
logger.warn(LocalizedMessage.create(
LocalizedStrings.DiskRegion_COMPLEXDISKREGIONGETNEXTDIR_MAX_DIRECTORY_SIZE_WILL_GET_VIOLATED__GOING_AHEAD_WITH_THE_SWITCHING_OF_OPLOG_ANY_WAYS_CURRENTLY_AVAILABLE_SPACE_IN_THE_DIRECTORY_IS__0__THE_CAPACITY_OF_DIRECTORY_IS___1,
new Object[] { Long.valueOf(getDirectories()[idx].getUsedSpace()), Long.valueOf(getDirectories()[idx].getCapacity()) }));
}
} else {
throw new DiskAccessException(LocalizedStrings.Oplog_DIRECTORIES_ARE_FULL_NOT_ABLE_TO_ACCOMODATE_THIS_OPERATIONSWITCHING_PROBLEM_FOR_ENTRY_HAVING_DISKID_0.toLocalizedString("needed " + minSize + " bytes"), parent);
}
}
int id = this.overflowOplogId.incrementAndGet();
lastOverflowDir = idx;
return new OverflowOplog(id, this, getDirectories()[idx], minSize);
}
final void addOverflow(OverflowOplog oo) {
this.overflowMap.put(oo.getOplogId(), oo);
}
final void removeOverflow(OverflowOplog oo) {
if (!basicRemoveOverflow(oo)) {
synchronized (this.compactableOverflowMap) {
this.compactableOverflowMap.remove(oo.getOplogId());
}
}
}
final boolean basicRemoveOverflow(OverflowOplog oo) {
if (this.lastOverflowWrite == oo) {
this.lastOverflowWrite = null;
}
return this.overflowMap.remove(oo.getOplogId(), oo);
}
public void closeOverflow() {
for (OverflowOplog oo: this.overflowMap.values()) {
oo.destroy();
}
synchronized (this.compactableOverflowMap) {
for (OverflowOplog oo: this.compactableOverflowMap.values()) {
oo.destroy();
}
}
}
final private void removeOverflow(DiskRegion dr, DiskEntry entry) {
// find the overflow oplog that it is currently in and remove the entry from it
DiskId id = entry.getDiskId();
synchronized (id) {
long oplogId = id.setOplogId(-1);
if (oplogId != -1) {
synchronized (this.overflowMap) { // to prevent concurrent remove see bug 41646
OverflowOplog oplog = getChild((int)oplogId);
if (oplog != null) {
oplog.remove(dr, entry);
}
}
}
}
}
void copyForwardForOverflowCompact(DiskEntry de, byte[] valueBytes, int length, byte userBits) {
synchronized (this.overflowMap) {
if (this.lastOverflowWrite != null) {
if (this.lastOverflowWrite.copyForwardForOverflowCompact(de, valueBytes, length, userBits)) {
return;
}
}
OverflowOplog oo = createOverflowOplog(length);
this.lastOverflowWrite = oo;
addOverflow(oo);
boolean didIt = oo.copyForwardForOverflowCompact(de, valueBytes, length, userBits);
assert didIt;
}
}
public final OverflowOplog getChild(long oplogId) {
//the oplog id is cast to an integer because the overflow
//map uses integer oplog ids.
return getChild((int) oplogId);
}
public final OverflowOplog getChild(int oplogId) {
OverflowOplog result = this.overflowMap.get(oplogId);
if (result == null) {
synchronized (this.compactableOverflowMap) {
result = this.compactableOverflowMap.get(oplogId);
}
}
return result;
}
@Override
public void create(LocalRegion region, DiskEntry entry, ValueWrapper value,
boolean async) {
modify(region, entry, value, async);
}
@Override
public void remove(LocalRegion region, DiskEntry entry, boolean async,
boolean isClear) {
removeOverflow(region.getDiskRegion(), entry);
}
void addOverflowToBeCompacted(OverflowOplog oplog) {
synchronized (this.compactableOverflowMap) {
this.compactableOverflowMap.put(oplog.getOplogId(), oplog);
}
basicRemoveOverflow(oplog);
parent.scheduleCompaction();
}
public void getCompactableOplogs(List<CompactableOplog> l, int max) {
synchronized (this.compactableOverflowMap)
{
Iterator<OverflowOplog> itr = this.compactableOverflowMap.values()
.iterator();
while (itr.hasNext() && l.size() < max) {
OverflowOplog oplog = itr.next();
if (oplog.needsCompaction()) {
l.add(oplog);
}
}
}
}
void testHookCloseAllOverflowChannels() {
synchronized (this.overflowMap) {
for (OverflowOplog oo : this.overflowMap.values()) {
FileChannel oplogFileChannel = oo.getFileChannel();
try {
oplogFileChannel.close();
} catch (IOException ignore) {
}
}
}
synchronized (this.compactableOverflowMap) {
for (OverflowOplog oo : this.compactableOverflowMap.values()) {
FileChannel oplogFileChannel = oo.getFileChannel();
try {
oplogFileChannel.close();
} catch (IOException ignore) {
}
}
}
}
ArrayList<OverflowOplog> testHookGetAllOverflowOplogs() {
ArrayList<OverflowOplog> result = new ArrayList<OverflowOplog>();
synchronized (this.overflowMap) {
for (OverflowOplog oo : this.overflowMap.values()) {
result.add(oo);
}
}
synchronized (this.compactableOverflowMap) {
for (OverflowOplog oo : this.compactableOverflowMap.values()) {
result.add(oo);
}
}
return result;
}
void testHookCloseAllOverflowOplogs() {
synchronized (this.overflowMap) {
for (OverflowOplog oo : this.overflowMap.values()) {
oo.close();
}
}
synchronized (this.compactableOverflowMap) {
for (OverflowOplog oo : this.compactableOverflowMap.values()) {
oo.close();
}
}
}
public DiskStoreImpl getParent() {
return parent;
}
}