Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
cb013cd
test(pipe): add P0 recovery integration tests
Caideyipi Oct 29, 2026
fa53291
ci(pipe): add focused P0 recovery dispatch [skip ci]
Caideyipi Oct 29, 2026
0e2a807
fix(subscription): adapt topic procedures to consumer group responses…
Caideyipi Oct 29, 2026
359a456
test(pipe): fix P0 recovery assertions
Caideyipi Oct 29, 2026
685a742
test(pipe): wait for backpressure drain before checking receiver
Caideyipi Oct 29, 2026
958391a
test(pipe): allow backpressure queue time to drain
Caideyipi Oct 29, 2026
9ffafa7
test(pipe): keep backpressure recovery load focused
Caideyipi Oct 29, 2026
c6ce135
test(pipe): prevent fault relay worker starvation
Caideyipi Oct 29, 2026
0c95c7b
test(pipe): retain floating memory before drop
Caideyipi Oct 29, 2026
045d82c
test(pipe): await retained memory before drop
Caideyipi Oct 29, 2026
c5ebc9b
test(metadata): add P1 recovery integration coverage
Caideyipi Oct 29, 2026
a48c24d
test: add metadata P0 recovery integration coverage
Caideyipi Oct 29, 2026
030e5c5
test(metadata): fix P0 recovery fixture prerequisites
Caideyipi Oct 30, 2026
ed60f11
test(metadata): remove production recovery hooks
Caideyipi Oct 30, 2026
9725a73
test(pipe): add runtime recovery integration coverage
Caideyipi Oct 30, 2026
12a657a
test(load): cover retained payload and pending load recovery
Caideyipi Oct 30, 2026
7108c55
test(deletion): cover isolation and compaction recovery
Caideyipi Nov 2, 2026
c0c05c2
test(write): cover recovery and backpressure integration scenarios
Caideyipi Nov 2, 2026
d195843
Merge master into test/pipe-p0-it-master
Caideyipi Nov 2, 2026
32fd0ab
test(load): cover fallback isolation and table recovery
Caideyipi Nov 2, 2026
96528e2
Merge origin/master into test/pipe-p0-it-master
Caideyipi Nov 3, 2026
95c431c
test: add deletion P1 integration coverage
Caideyipi Nov 3, 2026
f680cfa
test(metadata): await initial data before lease fault injection
Caideyipi Nov 3, 2026
339f107
test(session): verify pool capacity after failed write recovery
Caideyipi Nov 3, 2026
8fdf070
test(metadata): stabilize recovery IT prerequisites
Caideyipi Nov 3, 2026
986e23a
test: complete size-one session pool capacity recovery coverage
Caideyipi Nov 3, 2026
906980a
test(metadata): await TTL fixture replication before recovery
Caideyipi Nov 4, 2026
55e081c
test: add metadata recovery IT coverage
Caideyipi Nov 4, 2026
788714c
style: format metadata propagation IT selection
Caideyipi Nov 4, 2026
325dd94
test: fix metadata IT startup and exclude failing migration
Caideyipi Nov 5, 2026
a420874
test(metadata): assert recovered cache contents directly
Caideyipi Nov 5, 2026
81197bc
Add LOAD lifecycle and boundary integration coverage
Caideyipi Nov 5, 2026
4ef74f9
test: cover tag and alias indexes across restart and recreation
Caideyipi Nov 5, 2026
6c4d468
test(pipe): cover transfer recovery and routing ITs
Caideyipi Nov 5, 2026
989dd05
Merge origin/master into test/pipe-p0-it-master
Caideyipi Nov 6, 2026
125c965
test: pin Pipe transfer recovery replication to its single-node topology
Caideyipi Nov 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions .github/workflows/pipe-it.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ on:
- "iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/**" #queryengine
# allow manually run the action:
workflow_dispatch:
inputs:
pipe_p0_only:
description: Run only the six Pipe P0 recovery integration tests
type: boolean
default: false

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand All @@ -41,7 +46,74 @@ env:
DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}

jobs:
pipe-p0:
if: github.event_name == 'workflow_dispatch' && inputs.pipe_p0_only
runs-on: ubuntu-latest
timeout-minutes: 60
permissions:
contents: read
env:
MAVEN_OPTS: -Xmx2g -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false -Dmaven.wagon.http.retryHandler.class=standard -Dmaven.wagon.http.retryHandler.count=3
steps:
- uses: actions/checkout@v5
- name: Set up JDK 17
uses: actions/setup-java@v5
with:
distribution: corretto
java-version: '17'
cache: maven
- name: Run Pipe P0 recovery tests
shell: bash
run: |
mvn -B -ntp -T 2 clean verify \
-Pwith-integration-tests -PMultiClusterIT2DualTreeAutoEnhanced \
-pl integration-test -am \
-DskipUTs -Dtest=NoSuchSurefireTest \
-Dsurefire.failIfNoSpecifiedTests=false \
-Dit.test=IoTDBPipeRecoveryIT \
-DfailIfNoTests=false -Dfailsafe.failIfNoSpecifiedTests=false \
-DintegrationTest.clusterReadyRetryCount=90 \
-DintegrationTest.forkCount=1 \
-DConfigNodeMaxHeapSize=256 -DDataNodeMaxHeapSize=1024 \
-DDataNodeMaxDirectMemorySize=768 \
-DClusterConfigurations=HighPerformanceMode,HighPerformanceMode
- name: Verify all six tests ran successfully
if: always()
shell: bash
run: |
python3 - <<'PY'
from pathlib import Path
import xml.etree.ElementTree as ET

report = Path('integration-test/target/failsafe-reports/TEST-org.apache.iotdb.pipe.it.dual.treemodel.auto.enhanced.IoTDBPipeRecoveryIT.xml')
expected = {
'testAllReceiverDataNodesDownRetainAndDrainBacklogWithoutNewWrites',
'testHistoryRealtimeHandoffSurvivesReceiverRestart',
'testMultipleDataRegionsAndWritersDrainAfterLeaderTransfer',
'testRequestNotDeliveredRetriesWithoutStarvingAnotherPipe',
'testCommittedWriteWithLostResponseRetriesIdempotently',
'testRuntimeQueueBackpressureDrainsAndReleasesMemory',
}
cases = list(ET.parse(report).getroot().iter('testcase'))
actual = {case.attrib['name'] for case in cases}
if len(cases) != 6 or actual != expected:
raise SystemExit(f'Expected all six P0 cases; got {sorted(actual)}')
for case in cases:
if any(child.tag in {'failure', 'error', 'skipped'} for child in case):
raise SystemExit(f"P0 case did not pass: {case.attrib['name']}")
print('All six Pipe P0 recovery integration tests passed')
PY
- name: Upload Pipe P0 reports and cluster logs
if: always()
uses: actions/upload-artifact@v6
with:
name: pipe-p0-recovery-reports
path: |
integration-test/target/failsafe-reports
integration-test/target/cluster-logs
retention-days: 14
single:
if: github.event_name != 'workflow_dispatch' || !inputs.pipe_p0_only
strategy:
fail-fast: false
max-parallel: 15
Expand Down Expand Up @@ -131,6 +203,7 @@ jobs:
# 12 IT classes split across 3 parallel shards to cut the historical ~42 min
# wall clock to ~14 min. See cluster-it-1c1d.yml for the shard pattern.
dual-tree-auto-basic:
if: github.event_name != 'workflow_dispatch' || !inputs.pipe_p0_only
strategy:
fail-fast: false
max-parallel: 15
Expand Down Expand Up @@ -226,6 +299,7 @@ jobs:
# 9 IT classes split across 3 parallel shards to cut the historical ~51 min
# wall clock to ~17 min. See cluster-it-1c1d.yml for the shard pattern.
dual-tree-auto-enhanced:
if: github.event_name != 'workflow_dispatch' || !inputs.pipe_p0_only
strategy:
fail-fast: false
max-parallel: 15
Expand Down Expand Up @@ -322,6 +396,7 @@ jobs:
# 11 IT classes split across 3 parallel shards to cut the historical ~27 min
# wall clock to ~9 min. See cluster-it-1c1d.yml for the shard pattern.
dual-tree-manual:
if: github.event_name != 'workflow_dispatch' || !inputs.pipe_p0_only
strategy:
fail-fast: false
max-parallel: 15
Expand Down Expand Up @@ -416,6 +491,7 @@ jobs:
path: integration-test/target/cluster-logs
retention-days: 30
dual-table-manual-basic:
if: github.event_name != 'workflow_dispatch' || !inputs.pipe_p0_only
strategy:
fail-fast: false
max-parallel: 15
Expand Down Expand Up @@ -511,6 +587,7 @@ jobs:
# 11 IT classes split across 3 parallel shards to cut the historical ~62 min
# wall clock to ~22 min. See cluster-it-1c1d.yml for the shard pattern.
dual-table-manual-enhanced:
if: github.event_name != 'workflow_dispatch' || !inputs.pipe_p0_only
strategy:
fail-fast: false
max-parallel: 15
Expand Down Expand Up @@ -604,6 +681,7 @@ jobs:
path: integration-test/target/cluster-logs
retention-days: 30
triple:
if: github.event_name != 'workflow_dispatch' || !inputs.pipe_p0_only
strategy:
fail-fast: false
max-parallel: 1
Expand Down
12 changes: 12 additions & 0 deletions integration-test/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,18 @@
<iotConsensusV2StreamMode.schemaRegionReplicaNumber>3</iotConsensusV2StreamMode.schemaRegionReplicaNumber>
</properties>
<dependencies>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<!-- Child node templates also need ASM for cglib; test scope would omit it. -->
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.30.2-GA</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.iotdb</groupId>
<artifactId>iotdb-server</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,18 @@ public class MppJVMConfig implements JVMConfig {
private int maxHeapSize;
private int maxDirectMemorySize;
private String timezone;
private int debugPort;
private List<String> jvmOptions = Collections.emptyList();

/** Enables loopback-only JDWP for tests that need to observe an exact node execution stage. */
public void setDebugPort(int debugPort) {
this.debugPort = debugPort;
}

public int getDebugPort() {
return debugPort;
}

/** Additional arguments for child test-node JVMs, for example a test-only Java agent. */
public void setJvmOptions(List<String> options) {
this.jvmOptions = new ArrayList<>(options);
Expand Down Expand Up @@ -108,6 +118,7 @@ public void override(@Nullable MppJVMConfig config) {
this.setMaxHeapSize(config.getMaxHeapSize());
this.setMaxDirectMemorySize(config.getMaxDirectMemorySize());
this.setTimezone(config.getTimezone());
this.setDebugPort(config.getDebugPort());
this.setJvmOptions(config.getJvmOptions());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -479,11 +479,30 @@ private void reloadClusterConfigurations() {

@Override
public void start() {
start(false);
}

/** Starts suspended so a test can install JDWP requests before any node code runs. */
public void startWithDebugger() {
if (jvmConfig.getDebugPort() <= 0) {
throw new IllegalStateException("A debug port must be configured before suspended startup");
}
start(true);
}

private void start(boolean suspendForDebugger) {
try {
File stdoutFile = new File(getLogPath());
List<String> startCmd = new ArrayList<>();
startCmd.add(JAVA_CMD);
startCmd.addAll(jvmConfig.getJvmOptions());
if (jvmConfig.getDebugPort() > 0) {
startCmd.add(
"-agentlib:jdwp=transport=dt_socket,server=y,suspend="
+ (suspendForDebugger ? "y" : "n")
+ ",address=127.0.0.1:"
+ jvmConfig.getDebugPort());
}
if (!SystemUtils.IS_JAVA_1_8) {
startCmd.add("--add-opens=java.base/java.util.concurrent=ALL-UNNAMED");
startCmd.add("--add-opens=java.base/java.lang=ALL-UNNAMED");
Expand Down
Loading
Loading