Skip to content

Commit 17a3357

Browse files
authored
IGNITE-26502 Catalog. Improve serialisation compatibility tests to ensure that every object descriptor has a test case (#6834)
1 parent 5e0ae49 commit 17a3357

7 files changed

+954
-332
lines changed

modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogSerializationChecker.java

Lines changed: 147 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,21 +29,29 @@
2929
import java.nio.file.Files;
3030
import java.nio.file.Path;
3131
import java.util.HashMap;
32+
import java.util.HashSet;
3233
import java.util.List;
3334
import java.util.Map;
35+
import java.util.Objects;
36+
import java.util.Set;
37+
import java.util.function.Consumer;
3438
import org.apache.ignite.internal.catalog.storage.serialization.CatalogEntrySerializerProvider;
3539
import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
40+
import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializer;
3641
import org.apache.ignite.internal.catalog.storage.serialization.MarshallableEntry;
3742
import org.apache.ignite.internal.catalog.storage.serialization.MarshallableEntryType;
3843
import org.apache.ignite.internal.catalog.storage.serialization.UpdateLogMarshaller;
3944
import org.apache.ignite.internal.catalog.storage.serialization.UpdateLogMarshallerImpl;
4045
import org.apache.ignite.internal.logger.IgniteLogger;
4146
import org.assertj.core.api.BDDAssertions;
47+
import org.jetbrains.annotations.Nullable;
4248

4349
final class CatalogSerializationChecker {
4450

4551
private static final String UPDATE_TIMESTAMP_FIELD_REGEX = ".*updateTimestamp";
4652

53+
private static final String V1_SERDE_NOTE = "MANUAL CALL TO SUPPORT V1 SERIALIZATION";
54+
4755
private final Map<Integer, Integer> expectedEntryVersions = new HashMap<>();
4856

4957
private boolean writeSnapshot;
@@ -52,24 +60,32 @@ final class CatalogSerializationChecker {
5260

5361
private final String directory;
5462

55-
private final int entryVersion;
63+
private final int defaultEntryVersion;
5664

5765
private final boolean expectExactProtocolVersion;
5866

5967
private final int protocolVersion;
6068

69+
private final Set<SerializerClass> collectedSerializers = new HashSet<>();
70+
71+
private final Consumer<SerializerClass> recordSerializer;
72+
73+
private boolean addSerializerManually;
74+
6175
CatalogSerializationChecker(
6276
IgniteLogger log,
6377
String directory,
64-
int entryVersion,
78+
int defaultEntryVersion,
6579
boolean expectExactProtocolVersion,
66-
int protocolVersion
80+
int protocolVersion,
81+
Consumer<SerializerClass> recordSerializer
6782
) {
6883
this.log = log;
6984
this.directory = directory;
70-
this.entryVersion = entryVersion;
85+
this.defaultEntryVersion = defaultEntryVersion;
7186
this.expectExactProtocolVersion = expectExactProtocolVersion;
7287
this.protocolVersion = protocolVersion;
88+
this.recordSerializer = recordSerializer;
7389
}
7490

7591
void writeSnapshot(boolean value) {
@@ -80,6 +96,10 @@ void addExpectedVersion(int typeId, int entryVersion) {
8096
expectedEntryVersions.put(typeId, entryVersion);
8197
}
8298

99+
void addClassesManually(boolean value) {
100+
addSerializerManually = value;
101+
}
102+
83103
void reset() {
84104
expectedEntryVersions.clear();
85105
writeSnapshot = false;
@@ -96,7 +116,7 @@ void compareSnapshotEntry(SnapshotEntry expectedEntry, String fileName, int vers
96116
var assertion = BDDAssertions.assertThat(expectedEntry.snapshot())
97117
.usingRecursiveComparison();
98118

99-
if (entryVersion == 1) {
119+
if (defaultEntryVersion == 1) {
100120
// Ignoring update timestamp for version 1.
101121
assertion = assertion.ignoringFieldsMatchingRegexes(UPDATE_TIMESTAMP_FIELD_REGEX);
102122
}
@@ -112,20 +132,42 @@ void compareEntries(List<UpdateEntry> entries, String fileName, int version) {
112132
UpdateEntry expectedEntry = entries.get(i);
113133
UpdateEntry actualEntry = actual.get(i);
114134

115-
var assertion = BDDAssertions.assertThat(actualEntry).as("entry#" + i)
116-
.usingRecursiveComparison();
135+
var assertion = BDDAssertions.assertThat(actualEntry)
136+
.as("entry#" + i).usingRecursiveComparison();
117137

118-
if (entryVersion == 1) {
138+
if (defaultEntryVersion == 1) {
119139
// Ignoring update timestamp for version 1.
120140
assertion = assertion.ignoringFieldsMatchingRegexes(UPDATE_TIMESTAMP_FIELD_REGEX);
121141
}
122142

143+
if (addSerializerManually) {
144+
// Record entry version to support v1 serialization.
145+
// This is not needed by v2 serialization.
146+
recordSerializer(expectedEntry.typeId(), version, V1_SERDE_NOTE);
147+
}
148+
123149
assertion.isEqualTo(expectedEntry);
124150
}
125151
}
126152

153+
private void recordSerializer(int typeId, int version, @Nullable String note) {
154+
SerializerClass sc = new SerializerClass(typeId, version);
155+
if (!collectedSerializers.add(sc)) {
156+
return;
157+
}
158+
recordSerializer.accept(sc);
159+
160+
MarshallableEntryType entryType = getMarshallableEntryType(typeId);
161+
if (note != null) {
162+
log.info("{} uses version: {}, NOTE: {}", entryType, version, note);
163+
} else {
164+
log.info("{} uses version: {}", entryType, version);
165+
}
166+
}
167+
127168
@SuppressWarnings({"unchecked", "rawtypes"})
128169
private <T extends UpdateEntry> List<T> checkEntries(List<? extends T> entries, String fileName, int version) {
170+
// The version number is ignored, checkEntry uses the concrete serializer version.
129171
VersionedUpdate update = new VersionedUpdate(1, 100L, (List<UpdateEntry>) entries);
130172
VersionedUpdate deserializedUpdate = checkEntry(VersionedUpdate.class, fileName, version, update);
131173

@@ -151,7 +193,8 @@ private <T extends UpdateLogEvent> T checkEntry(Class<T> entryClass, String entr
151193

152194
log.info("Read fileName: {}, class: {}, entryVersion: {}", fileName, entryClass.getSimpleName(), entryVersion);
153195

154-
UpdateLogMarshaller marshaller = new UpdateLogMarshallerImpl(provider, protocolVersion);
196+
SerializerVersionCollectingProvider versionCollectingProvider = new SerializerVersionCollectingProvider(provider);
197+
UpdateLogMarshaller marshaller = new UpdateLogMarshallerImpl(versionCollectingProvider, protocolVersion);
155198

156199
if (writeSnapshot) {
157200
writeEntry(entry, Path.of("src", "test", "resources", directory, fileName), marshaller);
@@ -183,11 +226,36 @@ private void writeEntry(UpdateLogEvent entry, Path resourcePath, UpdateLogMarsha
183226
}
184227
}
185228

186-
private static class VersionCheckingProvider implements CatalogEntrySerializerProvider {
229+
private final class SerializerVersionCollectingProvider implements CatalogEntrySerializerProvider {
230+
231+
private final CatalogEntrySerializerProvider delegate;
232+
233+
private SerializerVersionCollectingProvider(CatalogEntrySerializerProvider delegate) {
234+
this.delegate = delegate;
235+
}
236+
237+
@Override
238+
public <T extends MarshallableEntry> CatalogObjectSerializer<T> get(int version, int typeId) {
239+
recordSerializer(typeId, version, null);
240+
241+
return delegate.get(version, typeId);
242+
}
243+
244+
@Override
245+
public int latestSerializerVersion(int typeId) {
246+
int version = delegate.latestSerializerVersion(typeId);
247+
248+
recordSerializer(typeId, version, null);
249+
250+
return version;
251+
}
252+
}
253+
254+
private static final class VersionCheckingProvider implements CatalogEntrySerializerProvider {
187255

188256
private final CatalogEntrySerializerProvider provider;
189257

190-
private final int protocolVersion;
258+
private final int expectedProtocolVersion;
191259

192260
private final Map<Integer, Integer> entryVersions = new HashMap<>();
193261

@@ -197,7 +265,7 @@ private VersionCheckingProvider(
197265
Map<Integer, Integer> entryVersions
198266
) {
199267
this.provider = provider;
200-
this.protocolVersion = expectedProtocolVersion;
268+
this.expectedProtocolVersion = expectedProtocolVersion;
201269
this.entryVersions.putAll(entryVersions);
202270
}
203271

@@ -219,7 +287,7 @@ public int latestSerializerVersion(int typeId) {
219287
}
220288

221289
private void checkVersion(int typeId, int entryVersion) {
222-
int expectedEntryVersion = entryVersions.getOrDefault(typeId, protocolVersion);
290+
int expectedEntryVersion = entryVersions.getOrDefault(typeId, expectedProtocolVersion);
223291
if (entryVersion != expectedEntryVersion) {
224292
MarshallableEntryType type = null;
225293

@@ -238,4 +306,70 @@ private void checkVersion(int typeId, int entryVersion) {
238306
}
239307
}
240308
}
309+
310+
static Set<SerializerClass> findEntrySerializers() {
311+
Set<SerializerClass> classes = new HashSet<>();
312+
313+
for (var entryType : MarshallableEntryType.values()) {
314+
for (Class<?> declaredClass : entryType.container().getDeclaredClasses()) {
315+
if (CatalogObjectSerializer.class.isAssignableFrom(declaredClass)) {
316+
CatalogSerializer catalogSerializer = declaredClass.getAnnotation(CatalogSerializer.class);
317+
318+
classes.add(new SerializerClass(entryType.id(), catalogSerializer.version()));
319+
}
320+
}
321+
}
322+
323+
return classes;
324+
}
325+
326+
protected static final class SerializerClass implements Comparable<SerializerClass> {
327+
final int entryTypeId;
328+
final int serializerVersion;
329+
330+
SerializerClass(int entryTypeId, int serializerVersion) {
331+
this.entryTypeId = entryTypeId;
332+
this.serializerVersion = serializerVersion;
333+
}
334+
335+
@Override
336+
public String toString() {
337+
MarshallableEntryType entryType = getMarshallableEntryType(entryTypeId);
338+
return "SerializerClass{" + entryType + "#" + serializerVersion + "}";
339+
}
340+
341+
@Override
342+
public boolean equals(Object o) {
343+
if (o == null || getClass() != o.getClass()) {
344+
return false;
345+
}
346+
SerializerClass that = (SerializerClass) o;
347+
return serializerVersion == that.serializerVersion && entryTypeId == that.entryTypeId;
348+
}
349+
350+
@Override
351+
public int hashCode() {
352+
return Objects.hash(entryTypeId, serializerVersion);
353+
}
354+
355+
@Override
356+
public int compareTo(CatalogSerializationChecker.SerializerClass o) {
357+
int c1 = Integer.compare(entryTypeId, o.entryTypeId);
358+
if (c1 != 0) {
359+
return c1;
360+
} else {
361+
return Integer.compare(serializerVersion, o.serializerVersion);
362+
}
363+
}
364+
}
365+
366+
private static MarshallableEntryType getMarshallableEntryType(int typeId) {
367+
for (MarshallableEntryType t : MarshallableEntryType.values()) {
368+
if (t.id() == typeId) {
369+
return t;
370+
}
371+
}
372+
373+
throw new IllegalArgumentException("Unexpected type: " + typeId);
374+
}
241375
}

0 commit comments

Comments
 (0)