This is an automated email from the ASF dual-hosted git repository.
morningman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new 6f2b64177b4 [fix](test) Fix BDB JE resource leak in BDBEnvironmentTest
causing FE UT timeout (#61404)
6f2b64177b4 is described below
commit 6f2b64177b4bfd1fda841e6cc2f5cb0e3ca53777
Author: Mingyu Chen (Rayner) <[email protected]>
AuthorDate: Mon Mar 16 20:26:06 2026 -0700
[fix](test) Fix BDB JE resource leak in BDBEnvironmentTest causing FE UT
timeout (#61404)
### What problem does this PR solve?
The following resource leak issues in BDBEnvironmentTest cause BDB JE
checkpoint threads to enter an infinite VLSN consistency retry loop
during JVM shutdown, blocking the Maven Surefire process from exiting
and triggering TeamCity execution timeout (~3 hours):
1. testReadTxnIsNotMatched: reopens BDB JE master environment and
database at the end but never closes them.
2. testRollbackException: has an early 'return' when fe1 is elected as
master, which exits the method without closing any of the 3 BDB JE
environments.
Fix by wrapping the test logic of both multi-node cluster tests
(testRollbackException, testReadTxnIsNotMatched) with try/finally blocks
that guarantee all BDB JE environments are closed, regardless of whether
the test passes, fails an assertion, or returns early.
---
.../doris/journal/bdbje/BDBEnvironmentTest.java | 324 +++++++++++----------
1 file changed, 167 insertions(+), 157 deletions(-)
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/journal/bdbje/BDBEnvironmentTest.java
b/fe/fe-core/src/test/java/org/apache/doris/journal/bdbje/BDBEnvironmentTest.java
index b93766b3c9d..cae2ec0f699 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/journal/bdbje/BDBEnvironmentTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/journal/bdbje/BDBEnvironmentTest.java
@@ -501,124 +501,127 @@ public class BDBEnvironmentTest {
followersInfo.add(Pair.of(followerEnvironment, new
NodeInfo(nodeName, nodeHostPort, nodeDir)));
}
- Pair<BDBEnvironment, NodeInfo> masterPair = findMaster(followersInfo);
- String beginDbName = String.valueOf(0L);
- Database masterDb = masterPair.first.openDatabase(beginDbName);
- DatabaseEntry key = new DatabaseEntry(randomBytes());
- DatabaseEntry value = new DatabaseEntry(randomBytes());
- Assertions.assertEquals(OperationStatus.SUCCESS, masterDb.put(null,
key, value));
- Assertions.assertEquals(1,
masterEnvironment.getDatabaseNames().size());
- LOG.info("master is {} | {}", masterPair.second.name,
masterPair.second.dir);
-
- for (Pair<BDBEnvironment, NodeInfo> entryPair : followersInfo) {
- if (entryPair.second.dir.equals(masterPair.second.dir)) {
- LOG.info("skip {}", entryPair.second.name);
- return;
- }
+ try {
+ Pair<BDBEnvironment, NodeInfo> masterPair =
findMaster(followersInfo);
+ String beginDbName = String.valueOf(0L);
+ Database masterDb = masterPair.first.openDatabase(beginDbName);
+ DatabaseEntry key = new DatabaseEntry(randomBytes());
+ DatabaseEntry value = new DatabaseEntry(randomBytes());
+ Assertions.assertEquals(OperationStatus.SUCCESS,
masterDb.put(null, key, value));
+ Assertions.assertEquals(1,
masterEnvironment.getDatabaseNames().size());
+ LOG.info("master is {} | {}", masterPair.second.name,
masterPair.second.dir);
- Assertions.assertEquals(1,
entryPair.first.getDatabaseNames().size());
- Database followerDb = entryPair.first.openDatabase(beginDbName);
- DatabaseEntry readValue = new DatabaseEntry();
- Assertions.assertEquals(OperationStatus.SUCCESS,
followerDb.get(null, key, readValue, LockMode.DEFAULT));
- Assertions.assertEquals(new String(value.getData()), new
String(readValue.getData()));
- followerDb.close();
- }
+ for (Pair<BDBEnvironment, NodeInfo> entryPair : followersInfo) {
+ if (entryPair.second.dir.equals(masterPair.second.dir)) {
+ LOG.info("skip {}", entryPair.second.name);
+ return;
+ }
- masterDb.close();
- masterEnvironment.getEpochDB().close();
+ Assertions.assertEquals(1,
entryPair.first.getDatabaseNames().size());
+ Database followerDb =
entryPair.first.openDatabase(beginDbName);
+ DatabaseEntry readValue = new DatabaseEntry();
+ Assertions.assertEquals(OperationStatus.SUCCESS,
followerDb.get(null, key, readValue, LockMode.DEFAULT));
+ Assertions.assertEquals(new String(value.getData()), new
String(readValue.getData()));
+ followerDb.close();
+ }
- followersInfo.stream().forEach(entryPair -> {
- entryPair.first.close();
- LOG.info("close {} | {}", entryPair.second.name,
entryPair.second.dir);
- });
+ masterDb.close();
+ masterEnvironment.getEpochDB().close();
- // all follower closed
- for (Pair<BDBEnvironment, NodeInfo> entryPair : followersInfo) {
- String followerCopyDir = entryPair.second.dir + "_copy";
- LOG.info("Copy from {} to {}", entryPair.second.dir,
followerCopyDir);
- FileUtils.copyDirectory(new File(entryPair.second.dir), new
File(followerCopyDir));
- }
+ followersInfo.stream().forEach(entryPair -> {
+ entryPair.first.close();
+ LOG.info("close {} | {}", entryPair.second.name,
entryPair.second.dir);
+ });
- followersInfo.stream().forEach(entryPair -> {
- entryPair.first.openReplicatedEnvironment(new
File(entryPair.second.dir));
- LOG.info("open {} | {}", entryPair.second.name,
entryPair.second.dir);
- });
+ // all follower closed
+ for (Pair<BDBEnvironment, NodeInfo> entryPair : followersInfo) {
+ String followerCopyDir = entryPair.second.dir + "_copy";
+ LOG.info("Copy from {} to {}", entryPair.second.dir,
followerCopyDir);
+ FileUtils.copyDirectory(new File(entryPair.second.dir), new
File(followerCopyDir));
+ }
- masterPair = findMaster(followersInfo);
+ followersInfo.stream().forEach(entryPair -> {
+ entryPair.first.openReplicatedEnvironment(new
File(entryPair.second.dir));
+ LOG.info("open {} | {}", entryPair.second.name,
entryPair.second.dir);
+ });
- masterDb = masterPair.first.openDatabase(String.valueOf(1L));
- for (int i = 0; i < 2 * Config.txn_rollback_limit + 10; i++) {
- // for (int i = 0; i < 10; i++) {
- OperationStatus status = masterDb.put(null, new
DatabaseEntry(randomBytes()), new DatabaseEntry(randomBytes()));
- Assertions.assertEquals(OperationStatus.SUCCESS, status);
- }
- Assertions.assertEquals(2, masterPair.first.getDatabaseNames().size());
- Assertions.assertEquals(0, masterPair.first.getDatabaseNames().get(0));
- Assertions.assertEquals(1, masterPair.first.getDatabaseNames().get(1));
+ masterPair = findMaster(followersInfo);
- followersInfo.stream().forEach(entryPair -> {
- entryPair.first.close();
- LOG.info("close {} | {}", entryPair.second.name,
entryPair.second.dir);
- });
-
- // Restore follower's (not new master) bdbje dir
- for (Pair<BDBEnvironment, NodeInfo> entryPair : followersInfo) {
- if (entryPair.second.dir.equals(masterDir)) {
- String masterCopyDir = entryPair.second.dir + "_copy";
- FileUtils.deleteDirectory(new File(masterCopyDir));
- continue;
+ masterDb = masterPair.first.openDatabase(String.valueOf(1L));
+ for (int i = 0; i < 2 * Config.txn_rollback_limit + 10; i++) {
+ // for (int i = 0; i < 10; i++) {
+ OperationStatus status = masterDb.put(null, new
DatabaseEntry(randomBytes()), new DatabaseEntry(randomBytes()));
+ Assertions.assertEquals(OperationStatus.SUCCESS, status);
}
- LOG.info("Delete followerDir {} ", entryPair.second.dir);
- FileUtils.deleteDirectory(new File(entryPair.second.dir));
- // FileUtils.moveDirectory(new File(entryPair.second.dir), new
File(entryPair.second.dir + "_copy2"));
- String followerCopyDir = entryPair.second.dir + "_copy";
- LOG.info("Move {} to {}", followerCopyDir, entryPair.second.dir);
- FileUtils.moveDirectory(new File(followerCopyDir), new
File(entryPair.second.dir));
- }
+ Assertions.assertEquals(2,
masterPair.first.getDatabaseNames().size());
+ Assertions.assertEquals(0,
masterPair.first.getDatabaseNames().get(0));
+ Assertions.assertEquals(1,
masterPair.first.getDatabaseNames().get(1));
- Thread.sleep(1000);
- for (Pair<BDBEnvironment, NodeInfo> entryPair : followersInfo) {
- if (entryPair.second.dir.equals(masterPair.second.dir)) {
- LOG.info("skip open {} | {}", entryPair.second.name,
entryPair.second.dir);
- continue;
+ followersInfo.stream().forEach(entryPair -> {
+ entryPair.first.close();
+ LOG.info("close {} | {}", entryPair.second.name,
entryPair.second.dir);
+ });
+
+ // Restore follower's (not new master) bdbje dir
+ for (Pair<BDBEnvironment, NodeInfo> entryPair : followersInfo) {
+ if (entryPair.second.dir.equals(masterDir)) {
+ String masterCopyDir = entryPair.second.dir + "_copy";
+ FileUtils.deleteDirectory(new File(masterCopyDir));
+ continue;
+ }
+ LOG.info("Delete followerDir {} ", entryPair.second.dir);
+ FileUtils.deleteDirectory(new File(entryPair.second.dir));
+ // FileUtils.moveDirectory(new File(entryPair.second.dir), new
File(entryPair.second.dir + "_copy2"));
+ String followerCopyDir = entryPair.second.dir + "_copy";
+ LOG.info("Move {} to {}", followerCopyDir,
entryPair.second.dir);
+ FileUtils.moveDirectory(new File(followerCopyDir), new
File(entryPair.second.dir));
}
- entryPair.first.openReplicatedEnvironment(new
File(entryPair.second.dir));
- LOG.info("open {} | {}", entryPair.second.name,
entryPair.second.dir);
- }
- BDBEnvironment newMasterEnvironment = null;
- boolean found = false;
- for (int i = 0; i < 300; i++) {
+ Thread.sleep(1000);
for (Pair<BDBEnvironment, NodeInfo> entryPair : followersInfo) {
if (entryPair.second.dir.equals(masterPair.second.dir)) {
+ LOG.info("skip open {} | {}", entryPair.second.name,
entryPair.second.dir);
continue;
}
+ entryPair.first.openReplicatedEnvironment(new
File(entryPair.second.dir));
+ LOG.info("open {} | {}", entryPair.second.name,
entryPair.second.dir);
+ }
- LOG.info("name:{} state:{} dir:{}",
entryPair.first.getReplicatedEnvironment().getNodeName(),
- entryPair.first.getReplicatedEnvironment().getState(),
- entryPair.second.dir);
- if
(entryPair.first.getReplicatedEnvironment().getState().equals(ReplicatedEnvironment.State.MASTER))
{
- newMasterEnvironment = entryPair.first;
- found = true;
+ BDBEnvironment newMasterEnvironment = null;
+ boolean found = false;
+ for (int i = 0; i < 300; i++) {
+ for (Pair<BDBEnvironment, NodeInfo> entryPair : followersInfo)
{
+ if (entryPair.second.dir.equals(masterPair.second.dir)) {
+ continue;
+ }
+
+ LOG.info("name:{} state:{} dir:{}",
entryPair.first.getReplicatedEnvironment().getNodeName(),
+
entryPair.first.getReplicatedEnvironment().getState(),
+ entryPair.second.dir);
+ if
(entryPair.first.getReplicatedEnvironment().getState().equals(ReplicatedEnvironment.State.MASTER))
{
+ newMasterEnvironment = entryPair.first;
+ found = true;
+ break;
+ }
+ }
+ if (found) {
break;
}
+ Thread.sleep(1000);
}
- if (found) {
- break;
- }
- Thread.sleep(1000);
+ Assertions.assertNotNull(newMasterEnvironment);
+
+ masterDb = newMasterEnvironment.openDatabase(beginDbName);
+ Assertions.assertEquals(OperationStatus.SUCCESS,
masterDb.put(null, new DatabaseEntry(randomBytes()), new
DatabaseEntry(randomBytes())));
+ Assertions.assertEquals(1,
newMasterEnvironment.getDatabaseNames().size());
+ // // old master
+ masterEnvironment.openReplicatedEnvironment(new File(masterDir));
+ } finally {
+ followersInfo.stream().forEach(entryPair -> {
+ entryPair.first.close();
+ LOG.info("close {} | {}", entryPair.second.name,
entryPair.second.dir);
+ });
}
- Assertions.assertNotNull(newMasterEnvironment);
-
- masterDb = newMasterEnvironment.openDatabase(beginDbName);
- Assertions.assertEquals(OperationStatus.SUCCESS, masterDb.put(null,
new DatabaseEntry(randomBytes()), new DatabaseEntry(randomBytes())));
- Assertions.assertEquals(1,
newMasterEnvironment.getDatabaseNames().size());
- // // old master
- masterEnvironment.openReplicatedEnvironment(new File(masterDir));
- followersInfo.stream().forEach(entryPair -> {
- entryPair.first.close();
- LOG.info("close {} | {}", entryPair.second.name,
entryPair.second.dir);
- });
LOG.info("end");
}
@@ -670,73 +673,80 @@ public class BDBEnvironmentTest {
followersInfo.add(Pair.of(followerEnvironment, new
NodeInfo(nodeName, nodeHostPort, nodeDir)));
}
- Pair<BDBEnvironment, NodeInfo> masterPair = findMaster(followersInfo);
- String beginDbName = String.valueOf(0L);
- Database masterDb = masterPair.first.openDatabase(beginDbName);
- DatabaseEntry key = new DatabaseEntry(randomBytes());
- DatabaseEntry value = new DatabaseEntry(randomBytes());
- Assertions.assertEquals(OperationStatus.SUCCESS, masterDb.put(null,
key, value));
- Assertions.assertEquals(1,
masterEnvironment.getDatabaseNames().size());
- LOG.info("master is {} | {}", masterPair.second.name,
masterPair.second.dir);
+ try {
+ Pair<BDBEnvironment, NodeInfo> masterPair =
findMaster(followersInfo);
+ String beginDbName = String.valueOf(0L);
+ Database masterDb = masterPair.first.openDatabase(beginDbName);
+ DatabaseEntry key = new DatabaseEntry(randomBytes());
+ DatabaseEntry value = new DatabaseEntry(randomBytes());
+ Assertions.assertEquals(OperationStatus.SUCCESS,
masterDb.put(null, key, value));
+ Assertions.assertEquals(1,
masterEnvironment.getDatabaseNames().size());
+ LOG.info("master is {} | {}", masterPair.second.name,
masterPair.second.dir);
- for (Pair<BDBEnvironment, NodeInfo> entryPair : followersInfo) {
- if (entryPair.second.dir.equals(masterPair.second.dir)) {
- LOG.info("skip {}", entryPair.second.name);
- continue;
- }
-
- Assertions.assertEquals(1,
entryPair.first.getDatabaseNames().size());
- Database followerDb = entryPair.first.openDatabase(beginDbName);
- DatabaseEntry readValue = new DatabaseEntry();
- Assertions.assertEquals(OperationStatus.SUCCESS,
followerDb.get(null, key, readValue, LockMode.DEFAULT));
- Assertions.assertEquals(new String(value.getData()), new
String(readValue.getData()));
- }
+ for (Pair<BDBEnvironment, NodeInfo> entryPair : followersInfo) {
+ if (entryPair.second.dir.equals(masterPair.second.dir)) {
+ LOG.info("skip {}", entryPair.second.name);
+ continue;
+ }
- Field envImplField =
ReplicatedEnvironment.class.getDeclaredField("repEnvironmentImpl");
- envImplField.setAccessible(true);
- RepImpl impl = (RepImpl)
envImplField.get(masterPair.first.getReplicatedEnvironment());
- Assertions.assertNotNull(impl);
-
- new Expectations(impl) {{
- // Below method will replicate log item to followers.
- impl.registerVLSN(withNotNull());
- // Below method will wait until the logs are replicated.
- impl.postLogCommitHook(withNotNull(), withNotNull());
- result = new InsufficientAcksException("mocked");
- }};
-
- long count = masterDb.count();
- final Database oldMasterDb = masterDb;
- Assertions.assertThrows(InsufficientAcksException.class, () -> {
- // Since this key is not replicated to any replicas, it should not
be read.
- DatabaseEntry k = new DatabaseEntry(new byte[]{1, 2, 3});
- DatabaseEntry v = new DatabaseEntry(new byte[]{4, 5, 6});
- oldMasterDb.put(null, k, v);
- });
+ Assertions.assertEquals(1,
entryPair.first.getDatabaseNames().size());
+ Database followerDb =
entryPair.first.openDatabase(beginDbName);
+ DatabaseEntry readValue = new DatabaseEntry();
+ Assertions.assertEquals(OperationStatus.SUCCESS,
followerDb.get(null, key, readValue, LockMode.DEFAULT));
+ Assertions.assertEquals(new String(value.getData()), new
String(readValue.getData()));
+ }
- LOG.info("close old master {} | {}", masterPair.second.name,
masterPair.second.dir);
- masterDb.close();
- masterEnvironment.getEpochDB().close();
- masterEnvironment.close();
+ Field envImplField =
ReplicatedEnvironment.class.getDeclaredField("repEnvironmentImpl");
+ envImplField.setAccessible(true);
+ RepImpl impl = (RepImpl)
envImplField.get(masterPair.first.getReplicatedEnvironment());
+ Assertions.assertNotNull(impl);
+
+ new Expectations(impl) {{
+ // Below method will replicate log item to followers.
+ impl.registerVLSN(withNotNull());
+ // Below method will wait until the logs are replicated.
+ impl.postLogCommitHook(withNotNull(), withNotNull());
+ result = new InsufficientAcksException("mocked");
+ }};
+
+ long count = masterDb.count();
+ final Database oldMasterDb = masterDb;
+ Assertions.assertThrows(InsufficientAcksException.class, () -> {
+ // Since this key is not replicated to any replicas, it should
not be read.
+ DatabaseEntry k = new DatabaseEntry(new byte[]{1, 2, 3});
+ DatabaseEntry v = new DatabaseEntry(new byte[]{4, 5, 6});
+ oldMasterDb.put(null, k, v);
+ });
+
+ LOG.info("close old master {} | {}", masterPair.second.name,
masterPair.second.dir);
+ masterDb.close();
+ masterEnvironment.getEpochDB().close();
+ masterEnvironment.close();
- for (Pair<BDBEnvironment, NodeInfo> entryPair : followersInfo) {
- if (entryPair.second.dir.equals(masterPair.second.dir)) {
- LOG.info("skip {}", entryPair.second.name);
- continue;
+ for (Pair<BDBEnvironment, NodeInfo> entryPair : followersInfo) {
+ if (entryPair.second.dir.equals(masterPair.second.dir)) {
+ LOG.info("skip {}", entryPair.second.name);
+ continue;
+ }
+ LOG.info("close follower {} | {}", entryPair.second.name,
entryPair.second.dir);
+ entryPair.first.close();
}
- LOG.info("close follower {} | {}", entryPair.second.name,
entryPair.second.dir);
- entryPair.first.close();
- }
- masterPair.first.openReplicatedEnvironment(new
File(masterPair.second.dir));
- masterDb = masterPair.first.openDatabase(beginDbName);
- LOG.info("open {} | {}", masterPair.second.name,
masterPair.second.dir);
+ masterPair.first.openReplicatedEnvironment(new
File(masterPair.second.dir));
+ masterDb = masterPair.first.openDatabase(beginDbName);
+ LOG.info("open {} | {}", masterPair.second.name,
masterPair.second.dir);
- // The local commit txn is readable!!!
- Assertions.assertEquals(count + 1, masterDb.count());
+ // The local commit txn is readable!!!
+ Assertions.assertEquals(count + 1, masterDb.count());
- key = new DatabaseEntry(new byte[]{1, 2, 3});
- DatabaseEntry readValue = new DatabaseEntry();
- Assertions.assertEquals(OperationStatus.SUCCESS, masterDb.get(null,
key, readValue, LockMode.DEFAULT));
+ key = new DatabaseEntry(new byte[]{1, 2, 3});
+ DatabaseEntry readValue = new DatabaseEntry();
+ Assertions.assertEquals(OperationStatus.SUCCESS,
masterDb.get(null, key, readValue, LockMode.DEFAULT));
+ } finally {
+ followersInfo.stream().forEach(entryPair -> {
+ entryPair.first.close();
+ LOG.info("close {} | {}", entryPair.second.name,
entryPair.second.dir);
+ });
+ }
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]