java-topology/defects/yugabyte/unit/YugabyteTest.java

207 lines
8.3 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package unit;
import java.util.*;
/**
* yugabyte-0001: GetXReplStreamsForTable std::find on table_id list inside per-table loop
*
* Models xrepl_catalog_manager.cc:
* GetXReplStreamsForTable: for each stream, std::find on table_id repeated field
* DropXClusterStreamsOfTables: calls GetXReplStreamsForTable in a loop over table_ids
*
* Total defective: O(D × M × T) where D=dropped tables, M=streams, T=tables per stream
* Total fixed: O(M × T) — one pass + O(1) hash lookup
*
* Compile: javac -d . YugabyteTest.java
* Run: java unit.YugabyteTest
*/
public class YugabyteTest {
static class CDCStreamInfo {
final String streamId;
final List<String> tableIds; // backed by protobuf RepeatedPtrField (array)
boolean started_deleting = false;
CDCStreamInfo(String streamId, List<String> tableIds) {
this.streamId = streamId;
this.tableIds = tableIds;
}
}
// ---- DEFECTIVE: O(D × M × T) ----
static List<String> getXReplStreamsForTable_defective(
String tableId,
List<CDCStreamInfo> cdcStreamMap,
int[] comparisonCount) {
List<String> streams = new ArrayList<>();
for (CDCStreamInfo stream : cdcStreamMap) { // O(M)
if (stream.started_deleting) continue;
// std::find on table_id list — O(T) linear scan
boolean found = false;
for (String tid : stream.tableIds) { // O(T)
comparisonCount[0]++;
if (tid.equals(tableId)) { found = true; break; }
}
if (found) streams.add(stream.streamId);
}
return streams;
}
static List<String> dropXClusterStreamsOfTables_defective(
Set<String> tableIds,
List<CDCStreamInfo> cdcStreamMap,
int[] comparisonCount) {
List<String> affectedStreams = new ArrayList<>();
for (String tid : tableIds) { // O(D)
affectedStreams.addAll(
getXReplStreamsForTable_defective(tid, cdcStreamMap, comparisonCount));
}
return affectedStreams;
}
// ---- FIXED: single pass O(M × T) with O(1) hash lookup ----
static List<String> dropXClusterStreamsOfTables_fixed(
Set<String> tableIds,
List<CDCStreamInfo> cdcStreamMap,
int[] comparisonCount) {
List<String> affectedStreams = new ArrayList<>();
for (CDCStreamInfo stream : cdcStreamMap) { // O(M) — single pass
if (stream.started_deleting) continue;
for (String tid : stream.tableIds) { // O(T) per stream
comparisonCount[0]++;
if (tableIds.contains(tid)) { // O(1) hash set lookup
affectedStreams.add(stream.streamId);
break; // found one match, don't add stream twice
}
}
}
return affectedStreams;
}
// Build a test CDC stream map
static List<CDCStreamInfo> buildStreamMap(int M, int T, int totalTables) {
List<CDCStreamInfo> map = new ArrayList<>();
for (int i = 0; i < M; i++) {
List<String> tables = new ArrayList<>();
for (int j = 0; j < T; j++) {
tables.add("table-" + ((i * T + j) % totalTables));
}
map.add(new CDCStreamInfo("stream-" + i, tables));
}
return map;
}
public static void main(String[] args) {
int pass = 0, fail = 0;
// -- Test 1: correctness — small scenario
{
List<CDCStreamInfo> streams = new ArrayList<>();
streams.add(new CDCStreamInfo("s1", Arrays.asList("t1", "t2", "t3")));
streams.add(new CDCStreamInfo("s2", Arrays.asList("t2", "t4")));
streams.add(new CDCStreamInfo("s3", Arrays.asList("t5", "t6")));
Set<String> toDrop = new HashSet<>(Arrays.asList("t2", "t5"));
int[] cmpDef = {0}, cmpFix = {0};
List<String> resultDef = dropXClusterStreamsOfTables_defective(toDrop, streams, cmpDef);
List<String> resultFix = dropXClusterStreamsOfTables_fixed(toDrop, streams, cmpFix);
Collections.sort(resultDef);
Collections.sort(resultFix);
if (resultDef.equals(resultFix)) {
System.out.printf("PASS test1: correctness — both=%s%n", resultDef);
pass++;
} else {
System.out.printf("FAIL test1: defective=%s fixed=%s%n", resultDef, resultFix);
fail++;
}
}
// -- Test 2: complexity comparison
{
int D = 50; // tables being dropped
int M = 100; // CDC streams
int T = 20; // tables per stream
int totalTables = 200;
List<CDCStreamInfo> streamMap = buildStreamMap(M, T, totalTables);
// Drop first D tables
Set<String> toDrop = new HashSet<>();
for (int i = 0; i < D; i++) toDrop.add("table-" + i);
int[] cmpDef = {0}, cmpFix = {0};
List<String> resultDef = dropXClusterStreamsOfTables_defective(toDrop, streamMap, cmpDef);
List<String> resultFix = dropXClusterStreamsOfTables_fixed(toDrop, streamMap, cmpFix);
System.out.printf("test2: defective comparisons=%d fixed comparisons=%d (D=%d M=%d T=%d)%n",
cmpDef[0], cmpFix[0], D, M, T);
// Defective adds a stream once per matching dropped table (may have duplicates);
// fixed adds each stream at most once. Compare as sets of unique stream IDs.
Set<String> setDef = new HashSet<>(resultDef);
Set<String> setFix = new HashSet<>(resultFix);
if (setDef.equals(setFix) && cmpFix[0] < cmpDef[0]) {
System.out.println("PASS test2: fixed is more efficient and produces same (unique) result");
pass++;
} else {
System.out.printf("FAIL test2: match=%b efficient=%b%n",
setDef.equals(setFix), cmpFix[0] < cmpDef[0]);
fail++;
}
// Show expected complexity
System.out.printf(" Expected defective ~O(D×M×T)=%d, actual=%d%n", D * M * T, cmpDef[0]);
System.out.printf(" Expected fixed ~O(M×T)=%d, actual=%d%n", M * T, cmpFix[0]);
}
// -- Test 3: deleted streams are excluded
{
CDCStreamInfo deletingStream = new CDCStreamInfo("s-deleting", Arrays.asList("t1", "t2"));
deletingStream.started_deleting = true;
List<CDCStreamInfo> streams = new ArrayList<>();
streams.add(deletingStream);
streams.add(new CDCStreamInfo("s-active", Arrays.asList("t1")));
Set<String> toDrop = new HashSet<>(Collections.singletonList("t1"));
int[] cmpDef = {0}, cmpFix = {0};
List<String> resultDef = dropXClusterStreamsOfTables_defective(toDrop, streams, cmpDef);
List<String> resultFix = dropXClusterStreamsOfTables_fixed(toDrop, streams, cmpFix);
Collections.sort(resultDef);
Collections.sort(resultFix);
if (resultDef.equals(resultFix) && resultFix.equals(Collections.singletonList("s-active"))) {
System.out.printf("PASS test3: deleting stream excluded — result=%s%n", resultFix);
pass++;
} else {
System.out.printf("FAIL test3: def=%s fix=%s%n", resultDef, resultFix);
fail++;
}
}
// -- Test 4: empty table_ids set
{
List<CDCStreamInfo> streams = buildStreamMap(10, 5, 50);
int[] c1 = {0}, c2 = {0};
List<String> r1 = dropXClusterStreamsOfTables_defective(new HashSet<>(), streams, c1);
List<String> r2 = dropXClusterStreamsOfTables_fixed(new HashSet<>(), streams, c2);
if (r1.isEmpty() && r2.isEmpty()) {
System.out.println("PASS test4: empty input — both return empty");
pass++;
} else {
System.out.printf("FAIL test4: def=%s fix=%s%n", r1, r2);
fail++;
}
}
System.out.printf("%nResults: %d passed, %d failed%n", pass, fail);
if (fail > 0) System.exit(1);
}
}