Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
2cb1635
HDDS-4404. Datanode can go OOM when a Recon or SCM Server is very slo…
smengcl Nov 19, 2020
3e53bf8
Checkstyle.
smengcl Nov 19, 2020
024a4f8
Add mock-maker-inline so UT can mock final classes.
smengcl Nov 19, 2020
22846f4
Remove throw exception in addReport.
smengcl Nov 19, 2020
878d271
Fix existing UT TestStateContext#testReportAPIs.
smengcl Nov 19, 2020
5b2bbd3
Remove unused imports.
smengcl Nov 19, 2020
2fcb2b6
Work around mockito bug in UT TestCreatePipelineCommandHandler: cause…
smengcl Nov 20, 2020
c403208
Add UT testContainerNodePipelineReportAPIs.
smengcl Nov 20, 2020
23d25cb
Clean up.
smengcl Nov 20, 2020
50b9a5e
@VisibleForTesting for get container/node/pipeline reports as they ar…
smengcl Nov 23, 2020
6f3ea18
- Renamed `reports` to `incrementalReportsQueue` to better reflect th…
smengcl Nov 24, 2020
9718488
Make containerReports, nodeReport, pipelineReports atomic and final.
smengcl Nov 24, 2020
7988203
Checkstyle.
smengcl Nov 24, 2020
fdc5823
Clean up.
smengcl Nov 24, 2020
3a32e8b
Remove assertion in putBackReports as requestBuilder might include Co…
smengcl Nov 30, 2020
84e8f74
Empty commit to retrigger CI.
smengcl Dec 1, 2020
02298d9
Empty commit to retrigger CI.
smengcl Dec 7, 2020
63a89ab
Retrigger.
smengcl Dec 7, 2020
c64f247
Merge branch 'master' into HDDS-4404-v2
smengcl Dec 8, 2020
34283ca
Allow null as input to StateContext#addReport() as this blocked UT Te…
smengcl Dec 9, 2020
ab37c22
Retrigger CI
smengcl Dec 10, 2020
f0dca47
trigger new CI check
adoroszlai Dec 12, 2020
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 @@ -37,10 +37,14 @@
import java.util.function.Consumer;

import com.google.common.annotations.VisibleForTesting;
import com.google.protobuf.Descriptors.Descriptor;
import org.apache.hadoop.hdds.conf.ConfigurationSource;
import org.apache.hadoop.hdds.protocol.proto.StorageContainerDatanodeProtocolProtos.CommandStatus.Status;
import org.apache.hadoop.hdds.protocol.proto.StorageContainerDatanodeProtocolProtos.ContainerAction;
import org.apache.hadoop.hdds.protocol.proto.StorageContainerDatanodeProtocolProtos.ContainerReportsProto;
import org.apache.hadoop.hdds.protocol.proto.StorageContainerDatanodeProtocolProtos.NodeReportProto;
import org.apache.hadoop.hdds.protocol.proto.StorageContainerDatanodeProtocolProtos.PipelineAction;
import org.apache.hadoop.hdds.protocol.proto.StorageContainerDatanodeProtocolProtos.PipelineReportsProto;
import org.apache.hadoop.hdds.protocol.proto.StorageContainerDatanodeProtocolProtos.SCMCommandProto;
import org.apache.hadoop.ozone.container.common.states.DatanodeState;
import org.apache.hadoop.ozone.container.common.states.datanode.InitDatanodeState;
Expand Down Expand Up @@ -72,6 +76,11 @@ public class StateContext {
private final AtomicLong stateExecutionCount;
private final ConfigurationSource conf;
private final Set<InetSocketAddress> endpoints;
// Only keeps the latest Container, Node and Pipeline report
private GeneratedMessage containerReports;
private GeneratedMessage nodeReport;
private GeneratedMessage pipelineReports;
// CommandStatusReport and IncrementalContainerReport queued in the map below
private final Map<InetSocketAddress, List<GeneratedMessage>> reports;
private final Map<InetSocketAddress, Queue<ContainerAction>> containerActions;
private final Map<InetSocketAddress, Queue<PipelineAction>> pipelineActions;
Expand All @@ -80,6 +89,16 @@ public class StateContext {
private boolean shutdownGracefully = false;
private final AtomicLong threadPoolNotAvailableCount;

@VisibleForTesting
static final String CONTAINER_REPORTS_PROTO_NAME =
ContainerReportsProto.getDescriptor().getFullName();
@VisibleForTesting
static final String NODE_REPORT_PROTO_NAME =
NodeReportProto.getDescriptor().getFullName();
@VisibleForTesting
static final String PIPELINE_REPORTS_PROTO_NAME =
PipelineReportsProto.getDescriptor().getFullName();

/**
* Starting with a 2 sec heartbeat frequency which will be updated to the
* real HB frequency after scm registration. With this method the
Expand All @@ -103,6 +122,9 @@ public StateContext(ConfigurationSource conf,
commandQueue = new LinkedList<>();
cmdStatusMap = new ConcurrentHashMap<>();
reports = new HashMap<>();
containerReports = null;
nodeReport = null;
pipelineReports = null;
endpoints = new HashSet<>();
containerActions = new HashMap<>();
pipelineActions = new HashMap<>();
Expand Down Expand Up @@ -190,15 +212,32 @@ void setShutdownGracefully() {
public boolean getShutdownOnError() {
return shutdownOnError;
}

/**
* Adds the report to report queue.
*
* @param report report to be added
*/
public void addReport(GeneratedMessage report) {
if (report != null) {
synchronized (reports) {
for (InetSocketAddress endpoint : endpoints) {
if (report == null) {
return;
}
final Descriptor descriptor = report.getDescriptorForType();
if (descriptor == null) {
return;
}
final String reportType = descriptor.getFullName();
for (InetSocketAddress endpoint : endpoints) {
// We only keep the latest container, node and pipeline report
if (reportType.equals(CONTAINER_REPORTS_PROTO_NAME)) {
containerReports = report;
} else if (reportType.equals(NODE_REPORT_PROTO_NAME)) {
nodeReport = report;
} else if (reportType.equals(PIPELINE_REPORTS_PROTO_NAME)) {
pipelineReports = report;
} else {
// CommandStatusReports and IncrementalContainerReport will be queued
synchronized (reports) {
reports.get(endpoint).add(report);
}
}
Expand Down Expand Up @@ -241,6 +280,15 @@ public List<GeneratedMessage> getAllAvailableReports(
public List<GeneratedMessage> getReports(InetSocketAddress endpoint,
int maxLimit) {
List<GeneratedMessage> reportsToReturn = new LinkedList<>();
if (containerReports != null) {
reportsToReturn.add(containerReports);
}
if (nodeReport != null) {
reportsToReturn.add(nodeReport);
}
if (pipelineReports != null) {
reportsToReturn.add(pipelineReports);
}
synchronized (reports) {
List<GeneratedMessage> reportsForEndpoint = reports.get(endpoint);
if (reportsForEndpoint != null) {
Expand Down Expand Up @@ -583,4 +631,16 @@ public void addEndpoint(InetSocketAddress endpoint) {
this.reports.put(endpoint, new LinkedList<>());
}
}

public GeneratedMessage getContainerReports() {
Comment thread
smengcl marked this conversation as resolved.
return containerReports;
}

public GeneratedMessage getNodeReport() {
return nodeReport;
}

public GeneratedMessage getPipelineReports() {
return pipelineReports;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -141,15 +141,15 @@ public EndpointStateMachine.EndPointStates call() throws Exception {
if (LOG.isDebugEnabled()) {
LOG.debug("Sending heartbeat message :: {}", request.toString());
}
SCMHeartbeatResponseProto reponse = rpcEndpoint.getEndPoint()
SCMHeartbeatResponseProto response = rpcEndpoint.getEndPoint()
.sendHeartbeat(request);
processResponse(reponse, datanodeDetailsProto);
processResponse(response, datanodeDetailsProto);
rpcEndpoint.setLastSuccessfulHeartbeat(ZonedDateTime.now());
rpcEndpoint.zeroMissedCount();
} catch (IOException ex) {
Preconditions.checkState(requestBuilder != null);
// put back the reports which failed to be sent
putBackReports(requestBuilder);

rpcEndpoint.logIfNeeded(ex);
} finally {
rpcEndpoint.unlock();
Expand All @@ -160,12 +160,9 @@ public EndpointStateMachine.EndPointStates call() throws Exception {
// TODO: Make it generic.
private void putBackReports(SCMHeartbeatRequestProto.Builder requestBuilder) {
List<GeneratedMessage> reports = new LinkedList<>();
if (requestBuilder.hasContainerReport()) {
reports.add(requestBuilder.getContainerReport());
}
if (requestBuilder.hasNodeReport()) {
reports.add(requestBuilder.getNodeReport());
}
// We only put back CommandStatusReports and IncrementalContainerReport
// because those are incremental. Container/Node/PipelineReport are
// accumulative so we can keep only the latest of each.
if (requestBuilder.getCommandStatusReportsCount() != 0) {
reports.addAll(requestBuilder.getCommandStatusReportsList());
}
Expand Down Expand Up @@ -193,6 +190,7 @@ private void addReports(SCMHeartbeatRequestProto.Builder requestBuilder) {
} else {
requestBuilder.setField(descriptor, report);
}
break;
Comment thread
avijayanhwx marked this conversation as resolved.
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@
import static org.apache.hadoop.test.GenericTestUtils.waitFor;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.net.InetSocketAddress;
import java.util.List;
Expand All @@ -37,6 +40,7 @@
import java.util.concurrent.atomic.AtomicInteger;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.protobuf.Descriptors.Descriptor;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.hdds.protocol.proto.StorageContainerDatanodeProtocolProtos.ContainerAction;
import org.apache.hadoop.hdds.protocol.proto.StorageContainerDatanodeProtocolProtos.PipelineAction;
Expand All @@ -53,6 +57,75 @@
*/
public class TestStateContext {

/**
* Check if Container, Node and Pipeline report APIs work as expected.
*/
@Test
public void testContainerNodePipelineReportAPIs() {
OzoneConfiguration conf = new OzoneConfiguration();
DatanodeStateMachine datanodeStateMachineMock =
mock(DatanodeStateMachine.class);

// ContainerReports
StateContext context1 = newStateContext(conf, datanodeStateMachineMock);
assertNull(context1.getContainerReports());
assertNull(context1.getNodeReport());
assertNull(context1.getPipelineReports());
GeneratedMessage containerReports =
newMockGeneratedMessage(StateContext.CONTAINER_REPORTS_PROTO_NAME);
context1.addReport(containerReports);

assertNotNull(context1.getContainerReports());
assertEquals(StateContext.CONTAINER_REPORTS_PROTO_NAME,
context1.getContainerReports().getDescriptorForType().getFullName());
assertNull(context1.getNodeReport());
assertNull(context1.getPipelineReports());

// NodeReport
StateContext context2 = newStateContext(conf, datanodeStateMachineMock);
GeneratedMessage nodeReport =
newMockGeneratedMessage(StateContext.NODE_REPORT_PROTO_NAME);
context2.addReport(nodeReport);

assertNull(context2.getContainerReports());
assertNotNull(context2.getNodeReport());
assertEquals(StateContext.NODE_REPORT_PROTO_NAME,
context2.getNodeReport().getDescriptorForType().getFullName());
assertNull(context2.getPipelineReports());

// PipelineReports
StateContext context3 = newStateContext(conf, datanodeStateMachineMock);
GeneratedMessage pipelineReports =
newMockGeneratedMessage(StateContext.PIPELINE_REPORTS_PROTO_NAME);
context3.addReport(pipelineReports);

assertNull(context3.getContainerReports());
assertNull(context3.getNodeReport());
assertNotNull(context3.getPipelineReports());
assertEquals(StateContext.PIPELINE_REPORTS_PROTO_NAME,
context3.getPipelineReports().getDescriptorForType().getFullName());
}

private StateContext newStateContext(OzoneConfiguration conf,
DatanodeStateMachine datanodeStateMachineMock) {
StateContext stateContext = new StateContext(conf,
DatanodeStates.getInitState(), datanodeStateMachineMock);
InetSocketAddress scm1 = new InetSocketAddress("scm1", 9001);
stateContext.addEndpoint(scm1);
InetSocketAddress scm2 = new InetSocketAddress("scm2", 9001);
stateContext.addEndpoint(scm2);
return stateContext;
}

private GeneratedMessage newMockGeneratedMessage(String messageType) {
GeneratedMessage pipelineReports = mock(GeneratedMessage.class);
when(pipelineReports.getDescriptorForType()).thenReturn(
mock(Descriptor.class));
when(pipelineReports.getDescriptorForType().getFullName()).thenReturn(
messageType);
return pipelineReports;
}

@Test
public void testReportAPIs() {
OzoneConfiguration conf = new OzoneConfiguration();
Expand All @@ -64,16 +137,22 @@ public void testReportAPIs() {
InetSocketAddress scm1 = new InetSocketAddress("scm1", 9001);
InetSocketAddress scm2 = new InetSocketAddress("scm2", 9001);

// Try to add report with endpoint. Should not be stored.
stateContext.addReport(mock(GeneratedMessage.class));
GeneratedMessage generatedMessage = mock(GeneratedMessage.class);
when(generatedMessage.getDescriptorForType()).thenReturn(
mock(Descriptor.class));
when(generatedMessage.getDescriptorForType().getFullName()).thenReturn(
"hadoop.hdds.CommandStatusReportsProto");

// Try to add report with zero endpoint. Should not be stored.
stateContext.addReport(generatedMessage);
assertTrue(stateContext.getAllAvailableReports(scm1).isEmpty());

// Add 2 scm endpoints.
stateContext.addEndpoint(scm1);
stateContext.addEndpoint(scm2);

// Add report. Should be added to all endpoints.
stateContext.addReport(mock(GeneratedMessage.class));
stateContext.addReport(generatedMessage);
List<GeneratedMessage> allAvailableReports =
stateContext.getAllAvailableReports(scm1);
assertEquals(1, allAvailableReports.size());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.stubbing.Answer;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
Expand Down Expand Up @@ -74,7 +75,10 @@ public void setup() throws Exception {
final RaftClient.Builder builder = mockRaftClientBuilder();
Mockito.when(builder.build()).thenReturn(raftClient);
PowerMockito.mockStatic(RaftClient.class);
PowerMockito.when(RaftClient.newBuilder()).thenReturn(builder);
// Work around for mockito bug:
// https://github.com/powermock/powermock/issues/992
PowerMockito.when(RaftClient.newBuilder()).thenAnswer(
(Answer<RaftClient.Builder>) invocation -> builder);
}

private RaftClient.Builder mockRaftClientBuilder() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
mock-maker-inline