Skip to content

Commit 95c57ba

Browse files
committed
test: added regression test for HA large schema issue
Related to issue ArcadeData#4077
1 parent 7fb46fc commit 95c57ba

1 file changed

Lines changed: 114 additions & 0 deletions

File tree

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/*
2+
* Copyright 2021-present Arcade Data Ltd (info@arcadedata.com)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* SPDX-FileCopyrightText: 2021-present Arcade Data Ltd (info@arcadedata.com)
17+
* SPDX-License-Identifier: Apache-2.0
18+
*/
19+
package com.arcadedb.server.ha.raft;
20+
21+
import com.arcadedb.ContextConfiguration;
22+
import com.arcadedb.GlobalConfiguration;
23+
import com.arcadedb.database.Database;
24+
import com.arcadedb.schema.DocumentType;
25+
import com.arcadedb.schema.Schema;
26+
import com.arcadedb.schema.Type;
27+
import org.junit.jupiter.api.Tag;
28+
import org.junit.jupiter.api.Test;
29+
30+
import static org.assertj.core.api.Assertions.assertThat;
31+
32+
/**
33+
* Regression test for issue #4077.
34+
* <p>
35+
* When a database schema serialized to JSON exceeds the 64KB modified-UTF-8 limit
36+
* imposed by {@code DataOutputStream.writeUTF}, schema replication used to fail on
37+
* the leader with {@code IllegalStateException: Failed to encode SCHEMA entry ->
38+
* encoded string ... too long: N bytes}. Followers then logged
39+
* "WAL version gap on follower - state divergence detected, triggering snapshot
40+
* resync" and the cluster diverged.
41+
* <p>
42+
* This test creates many vertex types with multiple properties so the schema JSON
43+
* grows beyond the legacy 64KB limit (matching the user-reported ~120KB schema),
44+
* then verifies the schema is replicated successfully to all replicas.
45+
*
46+
* @author Luca Garulli (l.garulli@arcadedata.com)
47+
*/
48+
@Tag("slow")
49+
class RaftLargeSchemaReplicationIT extends BaseRaftHATest {
50+
51+
@Override
52+
protected void onServerConfiguration(final ContextConfiguration config) {
53+
super.onServerConfiguration(config);
54+
config.setValue(GlobalConfiguration.HA_QUORUM, "majority");
55+
}
56+
57+
@Override
58+
protected int getServerCount() {
59+
return 2;
60+
}
61+
62+
@Test
63+
void schemaJsonAbove64KbIsReplicated() {
64+
final int leaderIndex = findLeaderIndex();
65+
assertThat(leaderIndex).as("A Raft leader must be elected").isGreaterThanOrEqualTo(0);
66+
final int replicaIndex = leaderIndex == 0 ? 1 : 0;
67+
68+
final Database leaderDb = getServerDatabase(leaderIndex, getDatabaseName());
69+
70+
// Build a schema large enough to exceed the legacy 64KB writeUTF limit.
71+
// Each type contributes ~250-300 bytes to the JSON (name, parent, properties).
72+
// 400 types is enough to reliably push the serialized schema past 100KB,
73+
// matching the ~120KB schema reported in issue #4077.
74+
final int typeCount = 400;
75+
76+
leaderDb.transaction(() -> {
77+
final Schema schema = leaderDb.getSchema();
78+
for (int i = 0; i < typeCount; i++) {
79+
final DocumentType type = schema.createVertexType("LargeSchemaType_" + i);
80+
type.createProperty("id_" + i, Type.STRING);
81+
type.createProperty("name_" + i, Type.STRING);
82+
type.createProperty("timestamp_" + i, Type.DATETIME);
83+
type.createProperty("payload_" + i, Type.STRING);
84+
type.createProperty("counter_" + i, Type.LONG);
85+
}
86+
});
87+
88+
// Confirm the schema JSON we produced actually exceeds the 64KB limit that used to break encoding.
89+
final int schemaSize = leaderDb.getSchema().getEmbedded().toJSON().toString()
90+
.getBytes(java.nio.charset.StandardCharsets.UTF_8).length;
91+
assertThat(schemaSize)
92+
.as("Schema JSON size should exceed the legacy DataOutputStream.writeUTF 64KB cap to exercise the regression")
93+
.isGreaterThan(65_535);
94+
95+
assertClusterConsistency();
96+
97+
// Verify all types replicated to the follower
98+
final Database replicaDb = getServerDatabase(replicaIndex, getDatabaseName());
99+
final Schema replicaSchema = replicaDb.getSchema();
100+
101+
for (int i = 0; i < typeCount; i++) {
102+
final String typeName = "LargeSchemaType_" + i;
103+
assertThat(replicaSchema.existsType(typeName))
104+
.as("Replica should have type %s", typeName)
105+
.isTrue();
106+
final DocumentType replicaType = replicaSchema.getType(typeName);
107+
assertThat(replicaType.existsProperty("id_" + i)).isTrue();
108+
assertThat(replicaType.existsProperty("name_" + i)).isTrue();
109+
assertThat(replicaType.existsProperty("timestamp_" + i)).isTrue();
110+
assertThat(replicaType.existsProperty("payload_" + i)).isTrue();
111+
assertThat(replicaType.existsProperty("counter_" + i)).isTrue();
112+
}
113+
}
114+
}

0 commit comments

Comments
 (0)