apache / apache/pinot

[multistage] [bug] Limited capacity for mailbox queue causes mailbox to drop

Open
#9,955 1 comment 0 reactions 1 assignee Claimed by @61yao View on GitHub
multi-stage
Dominant language
Java
Stars
6.1k
Forks
1.5k
Avg merge
2d 3h
Merged PRs (30d)
195

Description

DEFAULT_CHANNEL_CAPACITY for each InMemory/Grpc mailbox is set to 5.

5 is a pretty small number.

Whenever we have a MailboxSendOperator that produces more than 5 block really fast before MailboxReceiveOperator has a chance to run, we will run into error since MailboxSendOperator doesn't retry.

Propose solution
1) Increase the DEFAULT_CHANNEL_CAPACITY. This is easy to do. but this will add memory pressure
2) Return an non-op block after mailbox send reaches 5. this is ideal. however, it needs some re-design to make it happen: one idea is to replace ArrayBlockingQueue with AsynchronousChannel to be consistent with grpc exchange channel.

The problematic code is InMemorySendingMailbox.send()

@Override
public void send(TransferableBlock data)
throws UnsupportedOperationException {
try {
if (!_queue.offer(
data, InMemoryMailboxService.DEFAULT_CHANNEL_TIMEOUT_SECONDS, TimeUnit.SECONDS)) {
throw new RuntimeException(String.format("Timed out when sending block in mailbox=%s", _mailboxId));
}
_gotMailCallback.accept(new StringMailboxIdentifier(_mailboxId));
} catch (InterruptedException e) {
throw new RuntimeException("Interrupted trying to send data through the channel", e);
}
}
_queue.offer will throw an exception when q is full and it will always fail.

@walterddr @ankitsultana FYI

GrpcMailbox has the same issue.

For grpcMailbox, the mailbox will silently get dropped if the queue is full, which is worse.

The code is in MailboxContentStreamObserver onNext().

@Override
public void onNext(Mailbox.MailboxContent mailboxContent) {
_mailboxId = new StringMailboxIdentifier(mailboxContent.getMailboxId());

GrpcReceivingMailbox receivingMailbox = (GrpcReceivingMailbox) _mailboxService.getReceivingMailbox(_mailboxId);
_gotMailCallback = receivingMailbox.init(this);

if (!mailboxContent.getMetadataMap().containsKey(ChannelUtils.MAILBOX_METADATA_BEGIN_OF_STREAM_KEY)) {
// when the receiving end receives a message put it in the mailbox queue.
_receivingBuffer.offer(mailboxContent);
_gotMailCallback.accept(_mailboxId);

if (_isEnabledFeedback) {
// TODO: this has race conditions with onCompleted() because sender blindly closes connection channels once
// it has finished sending all the data packets.
int remainingCapacity = _receivingBuffer.remainingCapacity() - 1;
Mailbox.MailboxStatus.Builder builder =
Mailbox.MailboxStatus.newBuilder().setMailboxId(mailboxContent.getMailboxId())
.putMetadata(ChannelUtils.MAILBOX_METADATA_BUFFER_SIZE_KEY, String.valueOf(remainingCapacity));
if (mailboxContent.getMetadataMap().get(ChannelUtils.MAILBOX_METADATA_END_OF_STREAM_KEY) != null) {
builder.putMetadata(ChannelUtils.MAILBOX_METADATA_END_OF_STREAM_KEY, "true");
}
Mailbox.MailboxStatus status = builder.build();
// returns the buffer available size to sender for rate controller / throttling.
_responseObserver.onNext(status);
}
}
}

_receivingBuffer.offer(mailboxContent); will silent drop the mailbox if the queue is full. I checked in the cluster testing and this should be the reason for https://github.com/apache/pinot/issues/9963

All of this means we need to redesign on how mailbox communication happens or we temperately set the queue size to a very large number just for now.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.