Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,7 @@ void pollSelectionKeys(Set<SelectionKey> selectionKeys,
attemptRead(channel);
}

if (channel.hasBytesBuffered()) {
if (channel.hasBytesBuffered() && !explicitlyMutedChannels.contains(channel)) {
//this channel has bytes enqueued in intermediary buffers that we could not read
//(possibly because no memory). it may be the case that the underlying socket will
//not come up in the next poll() and so we need to remember this channel for the
Expand Down Expand Up @@ -742,6 +742,7 @@ public void mute(String id) {
private void mute(KafkaChannel channel) {
channel.mute();
explicitlyMutedChannels.add(channel);
keysWithBufferedRead.remove(channel.selectionKey());
}

@Override
Expand All @@ -754,6 +755,9 @@ private void unmute(KafkaChannel channel) {
// Remove the channel from explicitlyMutedChannels only if the channel has been actually unmuted.
if (channel.maybeUnmute()) {
explicitlyMutedChannels.remove(channel);
if (channel.hasBytesBuffered()) {
keysWithBufferedRead.add(channel.selectionKey());
}
}
}

Expand Down
34 changes: 32 additions & 2 deletions core/src/test/scala/unit/kafka/network/SocketServerTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1562,8 +1562,15 @@ class SocketServerTest {
val testableSelector = testableServer.testableSelector
testableSelector.updateMinWakeup(2)

val sleepTimeMs = idleTimeMs / 2 + 1
val (socket, request) = makeSocketWithBufferedRequests(testableServer, testableSelector, proxyServer)
time.sleep(idleTimeMs + 1)
// advance mock time in increments to verify that muted sockets with buffered data dont have their idle time updated
// additional calls to poll() should not update the channel last idle time
for (_ <- 0 to 3) {
time.sleep(sleepTimeMs)
testableSelector.operationCounts.clear()
testableSelector.waitForOperations(SelectorOperation.Poll, 1)
}
testableServer.waitForChannelClose(request.context.connectionId, locallyClosed = false)

val otherSocket = sslConnect(testableServer)
Expand All @@ -1575,7 +1582,30 @@ class SocketServerTest {
shutdownServerAndMetrics(testableServer)
}
}


@Test
def testUnmuteChannelWithBufferedReceives(): Unit = {
val time = new MockTime()
props ++= sslServerProps
val testableServer = new TestableSocketServer(time = time)
testableServer.startup()
val proxyServer = new ProxyServer(testableServer)
try {
val testableSelector = testableServer.testableSelector
val (socket, request) = makeSocketWithBufferedRequests(testableServer, testableSelector, proxyServer)
testableSelector.operationCounts.clear()
testableSelector.waitForOperations(SelectorOperation.Poll, 1)
val keysWithBufferedRead: util.Set[SelectionKey] = JTestUtils.fieldValue(testableSelector, classOf[Selector], "keysWithBufferedRead")
assertEquals(Set.empty, keysWithBufferedRead.asScala)
processRequest(testableServer.dataPlaneRequestChannel, request)
// buffered requests should be processed after channel is unmuted
receiveRequest(testableServer.dataPlaneRequestChannel)
socket.close()
} finally {
proxyServer.close()
shutdownServerAndMetrics(testableServer)
}
}
/**
* Tests exception handling in [[Processor.processCompletedReceives]]. Exception is
* injected into [[Selector.mute]] which is used to mute the channel when a receive is complete.
Expand Down