Skip to content

Commit b32b54b

Browse files
authored
HDDS-13305. Create wrapper object for container checksums (#8789)
1 parent cfc0449 commit b32b54b

14 files changed

Lines changed: 226 additions & 59 deletions

File tree

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.hadoop.hdds.scm.container;
19+
20+
import java.util.Objects;
21+
import net.jcip.annotations.Immutable;
22+
23+
/**
24+
* Wrapper for container checksums (data, metadata, etc.).
25+
* Provides equality, hash, and hex string rendering.
26+
* A value of 0 indicates an unknown or unset checksum.
27+
*/
28+
@Immutable
29+
public final class ContainerChecksums {
30+
// Checksum of the data within the wrapper.
31+
private final long dataChecksum;
32+
33+
// Checksum of the metadata within the wrapper.
34+
private final long metadataChecksum;
35+
36+
private static final ContainerChecksums UNKNOWN =
37+
new ContainerChecksums(0L, 0L);
38+
39+
private ContainerChecksums(long dataChecksum, long metadataChecksum) {
40+
this.dataChecksum = dataChecksum;
41+
this.metadataChecksum = metadataChecksum;
42+
}
43+
44+
public static ContainerChecksums unknown() {
45+
return UNKNOWN;
46+
}
47+
48+
public static ContainerChecksums of(long dataChecksum) {
49+
return new ContainerChecksums(dataChecksum, 0L);
50+
}
51+
52+
public static ContainerChecksums of(long dataChecksum, long metadataChecksum) {
53+
return new ContainerChecksums(dataChecksum, metadataChecksum);
54+
}
55+
56+
public long getDataChecksum() {
57+
return dataChecksum;
58+
}
59+
60+
public long getMetadataChecksum() {
61+
return metadataChecksum;
62+
}
63+
64+
@Override
65+
public boolean equals(Object obj) {
66+
if (this == obj) {
67+
return true;
68+
}
69+
if (!(obj instanceof ContainerChecksums)) {
70+
return false;
71+
}
72+
ContainerChecksums that = (ContainerChecksums) obj;
73+
return dataChecksum == that.dataChecksum &&
74+
metadataChecksum == that.metadataChecksum;
75+
}
76+
77+
@Override
78+
public int hashCode() {
79+
return Objects.hash(dataChecksum, metadataChecksum);
80+
}
81+
82+
@Override
83+
public String toString() {
84+
return "data=" + Long.toHexString(getDataChecksum()) +
85+
", metadata=" + Long.toHexString(getMetadataChecksum());
86+
}
87+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.hadoop.hdds.scm.container;
19+
20+
import static org.assertj.core.api.Assertions.assertThat;
21+
import static org.junit.jupiter.api.Assertions.assertEquals;
22+
import static org.junit.jupiter.api.Assertions.assertNotEquals;
23+
24+
import org.junit.jupiter.api.Test;
25+
26+
class TestContainerChecksums {
27+
@Test
28+
void testEqualsAndHashCode() {
29+
ContainerChecksums c1 = ContainerChecksums.of(123L, 0L);
30+
ContainerChecksums c2 = ContainerChecksums.of(123L, 0L);
31+
ContainerChecksums c3 = ContainerChecksums.of(456L, 0L);
32+
ContainerChecksums c4 = ContainerChecksums.of(123L, 789L);
33+
ContainerChecksums c5 = ContainerChecksums.of(123L, 789L);
34+
ContainerChecksums c6 = ContainerChecksums.of(123L, 790L);
35+
36+
assertEquals(c1, c2);
37+
assertEquals(c1.hashCode(), c2.hashCode());
38+
assertNotEquals(c1, c3);
39+
assertNotEquals(c1, c4);
40+
assertEquals(c4, c5);
41+
assertNotEquals(c4, c6);
42+
}
43+
44+
@Test
45+
void testToString() {
46+
ContainerChecksums c1 = ContainerChecksums.of(0x1234ABCDL, 0L);
47+
assertThat(c1.toString()).contains("data=1234abcd", "metadata=0");
48+
49+
ContainerChecksums c2 = ContainerChecksums.of(0x1234ABCDL, 0xDEADBEEFL);
50+
assertThat(c2.toString()).contains("data=1234abcd").contains("metadata=deadbeef");
51+
52+
ContainerChecksums c3 = ContainerChecksums.unknown();
53+
assertThat(c3.toString()).contains("data=0").contains("metadata=0");
54+
}
55+
}

hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/AbstractContainerReportHandler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ private void updateContainerReplica(final DatanodeDetails datanodeDetails,
362362
.setReplicaIndex(replicaProto.getReplicaIndex())
363363
.setBytesUsed(replicaProto.getUsed())
364364
.setEmpty(replicaProto.getIsEmpty())
365-
.setDataChecksum(replicaProto.getDataChecksum())
365+
.setChecksums(ContainerChecksums.of(replicaProto.getDataChecksum()))
366366
.build();
367367

368368
if (replica.getState().equals(State.DELETED)) {

hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/ContainerReplica.java

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public final class ContainerReplica implements Comparable<ContainerReplica> {
4545
private final long keyCount;
4646
private final long bytesUsed;
4747
private final boolean isEmpty;
48-
private final long dataChecksum;
48+
private final ContainerChecksums checksums;
4949

5050
private ContainerReplica(ContainerReplicaBuilder b) {
5151
this.containerID = Objects.requireNonNull(b.containerID, "containerID == null");
@@ -57,7 +57,7 @@ private ContainerReplica(ContainerReplicaBuilder b) {
5757
this.replicaIndex = b.replicaIndex;
5858
this.isEmpty = b.isEmpty;
5959
this.sequenceId = b.sequenceId;
60-
this.dataChecksum = b.dataChecksum;
60+
this.checksums = Objects.requireNonNull(b.checksums, "checksums == null");
6161
}
6262

6363
public ContainerID getContainerID() {
@@ -122,8 +122,12 @@ public boolean isEmpty() {
122122
return isEmpty;
123123
}
124124

125+
public ContainerChecksums getChecksums() {
126+
return checksums;
127+
}
128+
125129
public long getDataChecksum() {
126-
return dataChecksum;
130+
return checksums.getDataChecksum();
127131
}
128132

129133
@Override
@@ -180,7 +184,8 @@ public ContainerReplicaBuilder toBuilder() {
180184
.setOriginNodeId(originDatanodeId)
181185
.setReplicaIndex(replicaIndex)
182186
.setSequenceId(sequenceId)
183-
.setEmpty(isEmpty);
187+
.setEmpty(isEmpty)
188+
.setChecksums(checksums);
184189
}
185190

186191
@Override
@@ -194,7 +199,7 @@ public String toString() {
194199
+ ", keyCount=" + keyCount
195200
+ ", bytesUsed=" + bytesUsed
196201
+ ", " + (isEmpty ? "empty" : "non-empty")
197-
+ ", dataChecksum=" + dataChecksum
202+
+ ", checksums=" + checksums
198203
+ '}';
199204
}
200205

@@ -212,7 +217,7 @@ public static class ContainerReplicaBuilder {
212217
private long keyCount;
213218
private int replicaIndex;
214219
private boolean isEmpty;
215-
private long dataChecksum;
220+
private ContainerChecksums checksums;
216221

217222
/**
218223
* Set Container Id.
@@ -287,8 +292,8 @@ public ContainerReplicaBuilder setEmpty(boolean empty) {
287292
return this;
288293
}
289294

290-
public ContainerReplicaBuilder setDataChecksum(long dataChecksum) {
291-
this.dataChecksum = dataChecksum;
295+
public ContainerReplicaBuilder setChecksums(ContainerChecksums checksums) {
296+
this.checksums = checksums;
292297
return this;
293298
}
294299

@@ -298,6 +303,9 @@ public ContainerReplicaBuilder setDataChecksum(long dataChecksum) {
298303
* @return ContainerReplicaBuilder
299304
*/
300305
public ContainerReplica build() {
306+
if (this.checksums == null) {
307+
this.checksums = ContainerChecksums.unknown();
308+
}
301309
return new ContainerReplica(this);
302310
}
303311
}

hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/dn/scanner/TestBackgroundContainerDataScannerIntegration.java

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import java.util.concurrent.TimeUnit;
3131
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
3232
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.ContainerDataProto.State;
33+
import org.apache.hadoop.hdds.scm.container.ContainerChecksums;
3334
import org.apache.hadoop.ozone.container.common.interfaces.Container;
3435
import org.apache.hadoop.ozone.container.keyvalue.KeyValueContainerData;
3536
import org.apache.hadoop.ozone.container.keyvalue.TestContainerCorruptions;
@@ -84,8 +85,8 @@ void testCorruptionDetected(TestContainerCorruptions corruption)
8485
assertNotEquals(0, container.getContainerData().getDataChecksum());
8586

8687
waitForScmToSeeReplicaState(containerID, CLOSED);
87-
long initialReportedDataChecksum = getContainerReplica(containerID).getDataChecksum();
88-
assertNotEquals(0, initialReportedDataChecksum);
88+
ContainerChecksums initialReportedChecksum = getContainerReplica(containerID).getChecksums();
89+
assertNotEquals(ContainerChecksums.unknown(), initialReportedChecksum);
8990
corruption.applyTo(container);
9091

9192
resumeScanner();
@@ -97,16 +98,16 @@ void testCorruptionDetected(TestContainerCorruptions corruption)
9798

9899
// Wait for SCM to get a report of the unhealthy replica with a different checksum than before.
99100
waitForScmToSeeReplicaState(containerID, UNHEALTHY);
100-
long newReportedDataChecksum = getContainerReplica(containerID).getDataChecksum();
101+
ContainerChecksums newReportedChecksum = getContainerReplica(containerID).getChecksums();
101102
if (corruption == TestContainerCorruptions.MISSING_METADATA_DIR ||
102103
corruption == TestContainerCorruptions.MISSING_CONTAINER_DIR) {
103104
// In these cases, the new tree will not be able to be written since it exists in the metadata directory.
104105
// When the tree write fails, the in-memory checksum should remain at its original value.
105-
assertEquals(checksumToString(initialReportedDataChecksum), checksumToString(newReportedDataChecksum));
106+
assertEquals(initialReportedChecksum, newReportedChecksum);
106107
} else {
107-
assertNotEquals(checksumToString(initialReportedDataChecksum), checksumToString(newReportedDataChecksum));
108+
assertNotEquals(initialReportedChecksum, newReportedChecksum);
108109
// Test that the scanner wrote updated checksum info to the disk.
109-
assertReplicaChecksumMatches(container, newReportedDataChecksum);
110+
assertReplicaChecksumMatches(container, newReportedChecksum);
110111
assertFalse(container.getContainerData().needsDataChecksum());
111112
KeyValueContainerData containerData = (KeyValueContainerData) container.getContainerData();
112113
verifyAllDataChecksumsMatch(containerData, getConf());
@@ -122,10 +123,11 @@ void testCorruptionDetected(TestContainerCorruptions corruption)
122123
}
123124
}
124125

125-
private void assertReplicaChecksumMatches(Container<?> container, long expectedChecksum) throws Exception {
126+
private void assertReplicaChecksumMatches(
127+
Container<?> container, ContainerChecksums expectedChecksum) throws Exception {
126128
assertTrue(containerChecksumFileExists(container.getContainerData().getContainerID()));
127129
long dataChecksumFromFile = readChecksumFile(container.getContainerData())
128130
.getContainerMerkleTree().getDataChecksum();
129-
assertEquals(checksumToString(expectedChecksum), checksumToString(dataChecksumFromFile));
131+
assertEquals(checksumToString(expectedChecksum.getDataChecksum()), checksumToString(dataChecksumFromFile));
130132
}
131133
}

hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/fsck/ContainerHealthStatus.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,9 @@ public boolean isEmpty() {
160160
return numKeys == 0;
161161
}
162162

163-
public boolean isDataChecksumMismatched() {
163+
public boolean areChecksumsMismatched() {
164164
return !replicas.isEmpty() && replicas.stream()
165-
.map(ContainerReplica::getDataChecksum)
165+
.map(ContainerReplica::getChecksums)
166166
.distinct()
167167
.count() != 1;
168168
}

hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/fsck/ContainerHealthTask.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,7 @@ private void processContainer(ContainerInfo container, long currentTime,
385385
containerReplicas, placementPolicy,
386386
reconContainerMetadataManager, conf);
387387

388-
if ((h.isHealthilyReplicated() && !h.isDataChecksumMismatched()) || h.isDeleted()) {
388+
if ((h.isHealthilyReplicated() && !h.areChecksumsMismatched()) || h.isDeleted()) {
389389
return;
390390
}
391391
// For containers deleted in SCM, we sync the container state here.
@@ -563,7 +563,7 @@ public static List<UnhealthyContainers> generateUnhealthyRecords(
563563
Map<UnHealthyContainerStates, Map<String, Long>>
564564
unhealthyContainerStateStatsMap) {
565565
List<UnhealthyContainers> records = new ArrayList<>();
566-
if ((container.isHealthilyReplicated() && !container.isDataChecksumMismatched()) || container.isDeleted()) {
566+
if ((container.isHealthilyReplicated() && !container.areChecksumsMismatched()) || container.isDeleted()) {
567567
return records;
568568
}
569569

@@ -610,7 +610,7 @@ public static List<UnhealthyContainers> generateUnhealthyRecords(
610610
populateContainerStats(container, UnHealthyContainerStates.OVER_REPLICATED, unhealthyContainerStateStatsMap);
611611
}
612612

613-
if (container.isDataChecksumMismatched()
613+
if (container.areChecksumsMismatched()
614614
&& !recordForStateExists.contains(
615615
UnHealthyContainerStates.REPLICA_MISMATCH.toString())) {
616616
records.add(recordForState(
@@ -686,7 +686,7 @@ private static boolean keepMisReplicatedRecord(
686686

687687
private static boolean keepReplicaMismatchRecord(
688688
ContainerHealthStatus container, UnhealthyContainersRecord rec) {
689-
if (container.isDataChecksumMismatched()) {
689+
if (container.areChecksumsMismatched()) {
690690
updateExpectedReplicaCount(rec, container.getReplicationFactor());
691691
updateActualReplicaCount(rec, container.getReplicaCount());
692692
updateReplicaDelta(rec, container.replicaDelta());

hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/scm/ContainerReplicaHistory.java

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import java.util.UUID;
2121
import org.apache.hadoop.hdds.protocol.proto.HddsProtos.ContainerReplicaHistoryProto;
22+
import org.apache.hadoop.hdds.scm.container.ContainerChecksums;
2223

2324
/**
2425
* A ContainerReplica timestamp class that tracks first and last seen time.
@@ -39,16 +40,16 @@ public class ContainerReplicaHistory {
3940

4041
private long bcsId;
4142
private String state;
42-
private long dataChecksum;
43+
private ContainerChecksums checksums;
4344

4445
public ContainerReplicaHistory(UUID id, Long firstSeenTime,
45-
Long lastSeenTime, long bcsId, String state, long dataChecksum) {
46+
Long lastSeenTime, long bcsId, String state, ContainerChecksums checksums) {
4647
this.uuid = id;
4748
this.firstSeenTime = firstSeenTime;
4849
this.lastSeenTime = lastSeenTime;
4950
this.bcsId = bcsId;
5051
this.state = state;
51-
this.dataChecksum = dataChecksum;
52+
setChecksums(checksums);
5253
}
5354

5455
public long getBcsId() {
@@ -84,23 +85,29 @@ public void setState(String state) {
8485
}
8586

8687
public long getDataChecksum() {
87-
return dataChecksum;
88+
return getChecksums().getDataChecksum();
8889
}
8990

90-
public void setDataChecksum(long dataChecksum) {
91-
this.dataChecksum = dataChecksum;
91+
public ContainerChecksums getChecksums() {
92+
return checksums;
93+
}
94+
95+
public void setChecksums(ContainerChecksums checksums) {
96+
this.checksums = checksums != null ? checksums : ContainerChecksums.unknown();
9297
}
9398

9499
public static ContainerReplicaHistory fromProto(
95100
ContainerReplicaHistoryProto proto) {
96101
return new ContainerReplicaHistory(UUID.fromString(proto.getUuid()),
97102
proto.getFirstSeenTime(), proto.getLastSeenTime(), proto.getBcsId(),
98-
proto.getState(), proto.getDataChecksum());
103+
proto.getState(), ContainerChecksums.of(proto.getDataChecksum()));
99104
}
100105

101106
public ContainerReplicaHistoryProto toProto() {
102107
return ContainerReplicaHistoryProto.newBuilder().setUuid(uuid.toString())
103108
.setFirstSeenTime(firstSeenTime).setLastSeenTime(lastSeenTime)
104-
.setBcsId(bcsId).setState(state).setDataChecksum(dataChecksum).build();
109+
.setBcsId(bcsId).setState(state)
110+
.setDataChecksum(checksums.getDataChecksum())
111+
.build();
105112
}
106113
}

0 commit comments

Comments
 (0)