Skip to content

Commit 74e8f1c

Browse files
authored
Make compressed and encrypted nullable (#1206)
Motivation: The old replica could fail to replay logs if `encrypted` or `compressed` fields are included in JSON data since `@JsonIgnoreProperties(ignoreUnknown = true)` isn't applied to `LogMeta` Modifications: - Make `encrypted` and `compressed` in `LogMeta` nullable and exclude from serialization if them are null. Result: `LogMeta` is now backward compatible. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Log metadata now uses nullable flags for compression/encryption and omits null fields from JSON; timestamp handling tolerates missing values and null flags no longer imply true/false implicitly. * **Tests** * Added tests ensuring null fields are excluded from log metadata JSON serialization and that serialization/deserialization remain compatible. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 2092bbd commit 74e8f1c

File tree

3 files changed

+35
-15
lines changed

3 files changed

+35
-15
lines changed

server/src/main/java/com/linecorp/centraldogma/server/internal/replication/LogMeta.java

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616

1717
package com.linecorp.centraldogma.server.internal.replication;
1818

19-
import static com.google.common.base.MoreObjects.firstNonNull;
20-
2119
import java.util.ArrayList;
2220
import java.util.Collections;
2321
import java.util.List;
@@ -27,22 +25,26 @@
2725

2826
import com.fasterxml.jackson.annotation.JsonCreator;
2927
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
28+
import com.fasterxml.jackson.annotation.JsonInclude;
3029
import com.fasterxml.jackson.annotation.JsonProperty;
3130
import com.google.common.base.MoreObjects;
3231

32+
@JsonInclude(JsonInclude.Include.NON_NULL)
3333
@JsonIgnoreProperties(ignoreUnknown = true)
3434
class LogMeta {
3535

3636
private final int replicaId;
3737
private final long timestamp;
3838
private final int size;
39-
private final boolean compressed;
40-
private final boolean encrypted;
39+
@Nullable
40+
private final Boolean compressed;
41+
@Nullable
42+
private final Boolean encrypted;
4143
private final List<Long> blocks;
4244

4345
@JsonCreator
4446
LogMeta(@JsonProperty(value = "replicaId", required = true) int replicaId,
45-
@JsonProperty(value = "timestamp", defaultValue = "0") Long timestamp,
47+
@JsonProperty(value = "timestamp", defaultValue = "0") @Nullable Long timestamp,
4648
@JsonProperty("size") int size,
4749
@JsonProperty("blocks") List<Long> blocks,
4850
@Nullable @JsonProperty("compressed") Boolean compressed,
@@ -54,13 +56,13 @@ class LogMeta {
5456
}
5557
this.timestamp = timestamp;
5658
this.size = size;
57-
// Defaults to false for backward compatibility.
58-
this.compressed = firstNonNull(compressed, false);
59-
this.encrypted = firstNonNull(encrypted, false);
59+
this.compressed = compressed;
60+
this.encrypted = encrypted;
6061
this.blocks = blocks;
6162
}
6263

63-
LogMeta(int replicaId, Long timestamp, int size, boolean compressed, boolean encrypted) {
64+
LogMeta(int replicaId, Long timestamp, int size,
65+
@Nullable Boolean compressed, @Nullable Boolean encrypted) {
6466
this(replicaId, timestamp, size, new ArrayList<>(4), compressed, encrypted);
6567
}
6668

@@ -79,13 +81,15 @@ int size() {
7981
return size;
8082
}
8183

84+
@Nullable
8285
@JsonProperty("compressed")
83-
boolean compressed() {
86+
Boolean compressed() {
8487
return compressed;
8588
}
8689

90+
@Nullable
8791
@JsonProperty("encrypted")
88-
boolean encrypted() {
92+
Boolean encrypted() {
8993
return encrypted;
9094
}
9195

@@ -107,8 +111,8 @@ public boolean equals(Object o) {
107111
return replicaId == logMeta.replicaId &&
108112
timestamp == logMeta.timestamp &&
109113
size == logMeta.size &&
110-
compressed == logMeta.compressed &&
111-
encrypted == logMeta.encrypted &&
114+
Objects.equals(compressed, logMeta.compressed) &&
115+
Objects.equals(encrypted, logMeta.encrypted) &&
112116
Objects.equals(blocks, logMeta.blocks);
113117
}
114118

@@ -120,6 +124,7 @@ public int hashCode() {
120124
@Override
121125
public String toString() {
122126
return MoreObjects.toStringHelper(this)
127+
.omitNullValues()
123128
.add("replicaId", replicaId)
124129
.add("timestamp", timestamp)
125130
.add("size", size)

server/src/main/java/com/linecorp/centraldogma/server/internal/replication/ZooKeeperCommandExecutor.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,7 +1026,7 @@ private long storeLog(ReplicationLog<?> log) {
10261026

10271027
// TODO(ikhoon): Enable compression once releasing decompression support for forward compatibility.
10281028
final LogMeta logMeta = new LogMeta(log.replicaId(), System.currentTimeMillis(), bytes.length,
1029-
false, false);
1029+
null, null);
10301030

10311031
final int count = (bytes.length + MAX_BYTES - 1) / MAX_BYTES;
10321032
for (int i = 0; i < count; ++i) {
@@ -1075,7 +1075,8 @@ Optional<ReplicationLog<?>> loadLog(long revision, boolean skipIfSameReplica) {
10751075
}
10761076
assert logMeta.size() == offset;
10771077

1078-
if (logMeta.compressed()) {
1078+
final Boolean compressed = logMeta.compressed();
1079+
if (compressed != null && compressed) {
10791080
bytes = Zstd.decompress(bytes);
10801081
}
10811082
final ReplicationLog<?> log = Jackson.readValue(bytes, ReplicationLog.class);

server/src/test/java/com/linecorp/centraldogma/server/internal/replication/ZooKeeperCommandExecutorTest.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package com.linecorp.centraldogma.server.internal.replication;
1717

1818
import static java.util.concurrent.CompletableFuture.completedFuture;
19+
import static net.javacrumbs.jsonunit.fluent.JsonFluentAssert.assertThatJson;
1920
import static org.assertj.core.api.Assertions.assertThat;
2021
import static org.assertj.core.api.Assertions.assertThatThrownBy;
2122
import static org.assertj.core.api.Assertions.catchThrowable;
@@ -602,6 +603,19 @@ void testLogMetaSerde() throws JsonProcessingException {
602603
assertThat(deserialized).isEqualTo(logMeta);
603604
}
604605

606+
@Test
607+
void testLogMetaSerde_excludeNull() throws JsonProcessingException {
608+
final LogMeta logMeta = new LogMeta(1, 1L, 10, null, null);
609+
logMeta.appendBlock(1);
610+
logMeta.appendBlock(2);
611+
logMeta.appendBlock(3);
612+
assertThat(logMeta.blocks()).containsExactly(1L, 2L, 3L);
613+
final String jsonStr = Jackson.writeValueAsString(logMeta);
614+
assertThatJson(jsonStr).isEqualTo("{\"replicaId\":1,\"timestamp\":1,\"size\":10,\"blocks\":[1,2,3]}");
615+
final LogMeta deserialized = Jackson.readValue(jsonStr, LogMeta.class);
616+
assertThat(deserialized).isEqualTo(logMeta);
617+
}
618+
605619
private static <T> void awaitUntilReplicated(Cluster cluster, Command<T> command) {
606620
for (int i = 0; i < cluster.size(); i++) {
607621
final Replica replica = cluster.get(i);

0 commit comments

Comments
 (0)