ARTEMIS-2676 PageCursorProviderImpl::cleanup can save decoding pages without large messages

This commit is contained in:
Francesco Nigro 2020-03-23 13:01:38 +01:00 committed by Clebert Suconic
parent 4deee509b3
commit 46060b89ff
4 changed files with 117 additions and 38 deletions

View File

@ -544,7 +544,7 @@ public class PageCursorProviderImpl implements PageCursorProvider {
List<PagedMessage> pgdMessagesList = null; List<PagedMessage> pgdMessagesList = null;
try { try {
depagedPage.open(); depagedPage.open();
pgdMessagesList = depagedPage.read(storageManager); pgdMessagesList = depagedPage.read(storageManager, true);
} finally { } finally {
try { try {
depagedPage.close(false, false); depagedPage.close(false, false);
@ -553,7 +553,8 @@ public class PageCursorProviderImpl implements PageCursorProvider {
storageManager.afterPageRead(); storageManager.afterPageRead();
} }
pgdMessages = pgdMessagesList.toArray(new PagedMessage[pgdMessagesList.size()]); pgdMessages = pgdMessagesList.isEmpty() ? null :
pgdMessagesList.toArray(new PagedMessage[pgdMessagesList.size()]);
} else { } else {
pgdMessages = cache.getMessages(); pgdMessages = cache.getMessages();
} }

View File

@ -18,6 +18,7 @@ package org.apache.activemq.artemis.core.paging.impl;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@ -210,6 +211,7 @@ public final class Page implements Comparable<Page> {
readFileBuffer.position(endPosition + 1); readFileBuffer.position(endPosition + 1);
assert readFileBuffer.get(endPosition) == Page.END_BYTE : "decoding cannot change end byte"; assert readFileBuffer.get(endPosition) == Page.END_BYTE : "decoding cannot change end byte";
msg.initMessage(storageManager); msg.initMessage(storageManager);
assert validateLargeMessageStorageManager(msg);
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
logger.tracef("Reading message %s on pageId=%d for address=%s", msg, pageId, storeName); logger.tracef("Reading message %s on pageId=%d for address=%s", msg, pageId, storeName);
} }
@ -246,8 +248,13 @@ public final class Page implements Comparable<Page> {
} }
public synchronized List<PagedMessage> read(StorageManager storage) throws Exception { public synchronized List<PagedMessage> read(StorageManager storage) throws Exception {
return read(storage, false);
}
public synchronized List<PagedMessage> read(StorageManager storage, boolean onlyLargeMessages) throws Exception {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("reading page " + this.pageId + " on address = " + storeName); logger.debugf("reading page %d on address = %s onlyLargeMessages = %b", storeName, pageId,
storage, onlyLargeMessages);
} }
if (!file.isOpen()) { if (!file.isOpen()) {
@ -256,9 +263,11 @@ public final class Page implements Comparable<Page> {
size.lazySet((int) file.size()); size.lazySet((int) file.size());
final List<PagedMessage> messages = readFromSequentialFile(storage); final List<PagedMessage> messages = new ArrayList<>();
numberOfMessages.lazySet(messages.size()); final int totalMessageCount = readFromSequentialFile(storage, messages, onlyLargeMessages);
numberOfMessages.lazySet(totalMessageCount);
return messages; return messages;
} }
@ -316,6 +325,14 @@ public final class Page implements Comparable<Page> {
return fileBuffer; return fileBuffer;
} }
private static boolean validateLargeMessageStorageManager(PagedMessage msg) {
if (!(msg.getMessage() instanceof LargeServerMessage)) {
return true;
}
LargeServerMessage largeServerMessage = ((LargeServerMessage) msg.getMessage());
return largeServerMessage.getStorageManager() != null;
}
private static ChannelBufferWrapper wrapWhole(ByteBuffer fileBuffer) { private static ChannelBufferWrapper wrapWhole(ByteBuffer fileBuffer) {
final int position = fileBuffer.position(); final int position = fileBuffer.position();
final int limit = fileBuffer.limit(); final int limit = fileBuffer.limit();
@ -340,13 +357,15 @@ public final class Page implements Comparable<Page> {
private static final int HEADER_SIZE = HEADER_AND_TRAILER_SIZE - 1; private static final int HEADER_SIZE = HEADER_AND_TRAILER_SIZE - 1;
private static final int MIN_CHUNK_SIZE = Env.osPageSize(); private static final int MIN_CHUNK_SIZE = Env.osPageSize();
private List<PagedMessage> readFromSequentialFile(StorageManager storage) throws Exception { private int readFromSequentialFile(StorageManager storage,
final List<PagedMessage> messages = new ArrayList<>(); List<PagedMessage> messages,
boolean onlyLargeMessages) throws Exception {
final int fileSize = (int) file.size(); final int fileSize = (int) file.size();
file.position(0); file.position(0);
int processedBytes = 0; int processedBytes = 0;
ByteBuffer fileBuffer = null; ByteBuffer fileBuffer = null;
ChannelBufferWrapper fileBufferWrapper; ChannelBufferWrapper fileBufferWrapper;
int totalMessageCount = 0;
try { try {
int remainingBytes = fileSize - processedBytes; int remainingBytes = fileSize - processedBytes;
if (remainingBytes >= MINIMUM_MSG_PERSISTENT_SIZE) { if (remainingBytes >= MINIMUM_MSG_PERSISTENT_SIZE) {
@ -376,29 +395,38 @@ public final class Page implements Comparable<Page> {
final int endPosition = fileBuffer.position() + encodedSize; final int endPosition = fileBuffer.position() + encodedSize;
//this check must be performed upfront decoding //this check must be performed upfront decoding
if (fileBuffer.remaining() >= (encodedSize + 1) && fileBuffer.get(endPosition) == Page.END_BYTE) { if (fileBuffer.remaining() >= (encodedSize + 1) && fileBuffer.get(endPosition) == Page.END_BYTE) {
final PagedMessageImpl msg = new PagedMessageImpl(encodedSize, storageManager);
fileBufferWrapper.setIndex(fileBuffer.position(), endPosition); fileBufferWrapper.setIndex(fileBuffer.position(), endPosition);
msg.decode(fileBufferWrapper); final boolean skipMessage;
fileBuffer.position(endPosition + 1); if (onlyLargeMessages) {
assert fileBuffer.get(endPosition) == Page.END_BYTE : "decoding cannot change end byte"; skipMessage = !PagedMessageImpl.isLargeMessage(fileBufferWrapper);
msg.initMessage(storage); } else {
assert msg.getMessage() instanceof LargeServerMessage && ((LargeServerMessage)msg.getMessage()).getStorageManager() != null || !(msg.getMessage() instanceof LargeServerMessage); skipMessage = false;
if (logger.isTraceEnabled()) {
logger.tracef("Reading message %s on pageId=%d for address=%s", msg, pageId, storeName);
} }
messages.add(msg); if (!skipMessage) {
final PagedMessageImpl msg = new PagedMessageImpl(encodedSize, storageManager);
msg.decode(fileBufferWrapper);
assert fileBuffer.get(endPosition) == Page.END_BYTE : "decoding cannot change end byte";
msg.initMessage(storage);
assert validateLargeMessageStorageManager(msg);
if (logger.isTraceEnabled()) {
logger.tracef("Reading message %s on pageId=%d for address=%s", msg, pageId, storeName);
}
messages.add(msg);
}
totalMessageCount++;
fileBuffer.position(endPosition + 1);
processedBytes = nextPosition; processedBytes = nextPosition;
} else { } else {
markFileAsSuspect(file.getFileName(), processedBytes, messages.size()); markFileAsSuspect(file.getFileName(), processedBytes, totalMessageCount + 1);
return messages; return totalMessageCount;
} }
} else { } else {
markFileAsSuspect(file.getFileName(), processedBytes, messages.size()); markFileAsSuspect(file.getFileName(), processedBytes, totalMessageCount + 1);
return messages; return totalMessageCount;
} }
} else { } else {
markFileAsSuspect(file.getFileName(), processedBytes, messages.size()); markFileAsSuspect(file.getFileName(), processedBytes, totalMessageCount + 1);
return messages; return totalMessageCount;
} }
remainingBytes = fileSize - processedBytes; remainingBytes = fileSize - processedBytes;
} }
@ -408,7 +436,7 @@ public final class Page implements Comparable<Page> {
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
logger.tracef("%s has %d bytes of unknown data at position = %d", file.getFileName(), remainingBytes, processedBytes); logger.tracef("%s has %d bytes of unknown data at position = %d", file.getFileName(), remainingBytes, processedBytes);
} }
return messages; return totalMessageCount;
} finally { } finally {
if (fileBuffer != null) { if (fileBuffer != null) {
fileFactory.releaseBuffer(fileBuffer); fileFactory.releaseBuffer(fileBuffer);
@ -500,11 +528,12 @@ public final class Page implements Comparable<Page> {
} }
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Deleting pageNr=" + pageId + " on store " + storeName); logger.debugf("Deleting pageNr=%d on store %d", pageId, storeName);
} }
List<Long> largeMessageIds = new ArrayList<>(); final List<Long> largeMessageIds;
if (messages != null) { if (messages != null && messages.length > 0) {
largeMessageIds = new ArrayList<>();
for (PagedMessage msg : messages) { for (PagedMessage msg : messages) {
if ((msg.getMessage()).isLargeMessage()) { if ((msg.getMessage()).isLargeMessage()) {
// this will trigger large message delete: no need to do it // this will trigger large message delete: no need to do it
@ -513,6 +542,8 @@ public final class Page implements Comparable<Page> {
largeMessageIds.add(msg.getMessage().getMessageID()); largeMessageIds.add(msg.getMessage().getMessageID());
} }
} }
} else {
largeMessageIds = Collections.emptyList();
} }
try { try {

View File

@ -35,6 +35,43 @@ import org.apache.activemq.artemis.utils.DataConstants;
*/ */
public class PagedMessageImpl implements PagedMessage { public class PagedMessageImpl implements PagedMessage {
// It encapsulates the logic to detect large message types
private static final class LargeMessageType {
private static final byte NONE = 0;
private static final byte CORE = 1;
private static final byte NOT_CORE = 2;
public static boolean isLargeMessage(byte encodedValue) {
switch (encodedValue) {
case LargeMessageType.NONE:
return false;
case LargeMessageType.CORE:
case LargeMessageType.NOT_CORE:
return true;
default:
throw new IllegalStateException("This largeMessageType isn't supported: " + encodedValue);
}
}
public static boolean isCoreLargeMessage(Message message) {
return message.isLargeMessage() && message instanceof ICoreMessage;
}
public static boolean isCoreLargeMessageType(byte encodedValue) {
return encodedValue == LargeMessageType.CORE;
}
public static byte valueOf(Message message) {
if (!message.isLargeMessage()) {
return NONE;
}
if (message instanceof ICoreMessage) {
return CORE;
}
return NOT_CORE;
}
}
/** /**
* Large messages will need to be instantiated lazily during getMessage when the StorageManager * Large messages will need to be instantiated lazily during getMessage when the StorageManager
* is available * is available
@ -116,13 +153,21 @@ public class PagedMessageImpl implements PagedMessage {
// EncodingSupport implementation -------------------------------- // EncodingSupport implementation --------------------------------
/**
* This method won't move the {@link ActiveMQBuffer#readerIndex()} of {@code buffer}.
*/
public static boolean isLargeMessage(ActiveMQBuffer buffer) {
// skip transactionID
return LargeMessageType.isLargeMessage(buffer.getByte(buffer.readerIndex() + Long.BYTES));
}
@Override @Override
public void decode(final ActiveMQBuffer buffer) { public void decode(final ActiveMQBuffer buffer) {
transactionID = buffer.readLong(); transactionID = buffer.readLong();
boolean isLargeMessage = buffer.readBoolean(); boolean isCoreLargeMessage = LargeMessageType.isCoreLargeMessageType(buffer.readByte());
if (isLargeMessage) { if (isCoreLargeMessage) {
int largeMessageHeaderSize = buffer.readInt(); int largeMessageHeaderSize = buffer.readInt();
if (storageManager == null) { if (storageManager == null) {
@ -155,12 +200,12 @@ public class PagedMessageImpl implements PagedMessage {
public void encode(final ActiveMQBuffer buffer) { public void encode(final ActiveMQBuffer buffer) {
buffer.writeLong(transactionID); buffer.writeLong(transactionID);
boolean isLargeMessage = isLargeMessage(); byte largeMessageType = LargeMessageType.valueOf(message);
buffer.writeBoolean(isLargeMessage); buffer.writeByte(largeMessageType);
if (isLargeMessage) { if (LargeMessageType.isCoreLargeMessageType(largeMessageType)) {
buffer.writeInt(LargeMessagePersister.getInstance().getEncodeSize((LargeServerMessage)message)); buffer.writeInt(LargeMessagePersister.getInstance().getEncodeSize((LargeServerMessage) message));
LargeMessagePersister.getInstance().encode(buffer, (LargeServerMessage) message); LargeMessagePersister.getInstance().encode(buffer, (LargeServerMessage) message);
} else { } else {
message.getPersister().encode(buffer, message); message.getPersister().encode(buffer, message);
@ -173,13 +218,9 @@ public class PagedMessageImpl implements PagedMessage {
} }
} }
public boolean isLargeMessage() {
return message instanceof ICoreMessage && ((ICoreMessage)message).isLargeMessage();
}
@Override @Override
public int getEncodeSize() { public int getEncodeSize() {
if (isLargeMessage()) { if (LargeMessageType.isCoreLargeMessage(message)) {
return DataConstants.SIZE_LONG + DataConstants.SIZE_BYTE + DataConstants.SIZE_INT + LargeMessagePersister.getInstance().getEncodeSize((LargeServerMessage)message) + return DataConstants.SIZE_LONG + DataConstants.SIZE_BYTE + DataConstants.SIZE_INT + LargeMessagePersister.getInstance().getEncodeSize((LargeServerMessage)message) +
DataConstants.SIZE_INT + queueIDs.length * DataConstants.SIZE_LONG; DataConstants.SIZE_INT + queueIDs.length * DataConstants.SIZE_LONG;
} else { } else {

View File

@ -150,7 +150,7 @@ public class PageTest extends ActiveMQTestBase {
file.open(); file.open();
page = new Page(new SimpleString("something"), storageManager, factory, file, 10); page = new Page(new SimpleString("something"), storageManager, factory, file, 10);
List<PagedMessage> msgs = page.read(storageManager); List<PagedMessage> msgs = page.read(storageManager, largeMessages);
Assert.assertEquals(numberOfElements, msgs.size()); Assert.assertEquals(numberOfElements, msgs.size());
@ -164,6 +164,12 @@ public class PageTest extends ActiveMQTestBase {
Assert.assertEquals(largeMessages ? 1 : 0, pagedMessage.getMessage().getUsage()); Assert.assertEquals(largeMessages ? 1 : 0, pagedMessage.getMessage().getUsage());
} }
if (!largeMessages) {
Page tmpPage = new Page(new SimpleString("something"), storageManager, factory, file, 10);
Assert.assertEquals(0, tmpPage.read(storageManager, true).size());
Assert.assertEquals(numberOfElements, tmpPage.getNumberOfMessages());
}
Assert.assertTrue(page.delete(msgs.toArray(new PagedMessage[msgs.size()]))); Assert.assertTrue(page.delete(msgs.toArray(new PagedMessage[msgs.size()])));
for (PagedMessage pagedMessage : msgs) { for (PagedMessage pagedMessage : msgs) {