From cea8bd9af3038e0a4825bdb791d95111901386e8 Mon Sep 17 00:00:00 2001 From: warku123 Date: Fri, 6 Feb 2026 16:49:01 +0800 Subject: [PATCH 01/12] Fix resource leakage --- .../tron/core/store/CheckPointV2Store.java | 8 +- .../tron/core/db/CheckPointV2StoreTest.java | 74 +++++++++++++++++++ 2 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 framework/src/test/java/org/tron/core/db/CheckPointV2StoreTest.java diff --git a/chainbase/src/main/java/org/tron/core/store/CheckPointV2Store.java b/chainbase/src/main/java/org/tron/core/store/CheckPointV2Store.java index f027bd02664..afd5d330a8d 100644 --- a/chainbase/src/main/java/org/tron/core/store/CheckPointV2Store.java +++ b/chainbase/src/main/java/org/tron/core/store/CheckPointV2Store.java @@ -70,9 +70,13 @@ public void close() { logger.debug("******** Begin to close {}. ********", getName()); try { writeOptions.close(); - dbSource.closeDB(); } catch (Exception e) { - logger.warn("Failed to close {}.", getName(), e); + logger.warn("Failed to close writeOptions in {}.", getName(), e); + } + try { + super.close(); + } catch (Exception e) { + logger.warn("Failed to close parent resources in {}.", getName(), e); } finally { logger.debug("******** End to close {}. ********", getName()); } diff --git a/framework/src/test/java/org/tron/core/db/CheckPointV2StoreTest.java b/framework/src/test/java/org/tron/core/db/CheckPointV2StoreTest.java new file mode 100644 index 00000000000..7c9d2723b82 --- /dev/null +++ b/framework/src/test/java/org/tron/core/db/CheckPointV2StoreTest.java @@ -0,0 +1,74 @@ +package org.tron.core.db; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.times; + +import java.io.IOException; +import java.lang.reflect.Field; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.rocksdb.RocksDB; +import org.tron.common.storage.WriteOptionsWrapper; +import org.tron.core.Constant; +import org.tron.core.config.args.Args; +import org.tron.core.store.CheckPointV2Store; + +public class CheckPointV2StoreTest { + + @ClassRule + public static final TemporaryFolder temporaryFolder = new TemporaryFolder(); + + static { + RocksDB.loadLibrary(); + } + + @BeforeClass + public static void initArgs() throws IOException { + Args.setParam(new String[]{"-d", temporaryFolder.newFolder().toString()}, Constant.TEST_CONF); + } + + @AfterClass + public static void destroy() { + Args.clearParam(); + } + + @Test + public void testCloseCallsSuperClose() throws Exception { + CheckPointV2Store store = new CheckPointV2Store("test-close-super"); + + // 获取父类的 writeOptions 字段 + Field parentWriteOptionsField = TronDatabase.class.getDeclaredField("writeOptions"); + parentWriteOptionsField.setAccessible(true); + WriteOptionsWrapper originalParentWriteOptions = (WriteOptionsWrapper) parentWriteOptionsField.get(store); + + // 保存原始的 rocks 对象引用,用于后续验证 + org.rocksdb.WriteOptions originalRocks = originalParentWriteOptions.rocks; + + // 创建一个 spy 来监控父类的 writeOptions + WriteOptionsWrapper spyParentWriteOptions = spy(originalParentWriteOptions); + parentWriteOptionsField.set(store, spyParentWriteOptions); + + // 创建一个 spy 来监控 rocks.close() 方法 + org.rocksdb.WriteOptions spyRocks = spy(originalRocks); + spyParentWriteOptions.rocks = spyRocks; + + // 验证父类的 writeOptions 和 dbSource 存在 + Assert.assertNotNull(spyParentWriteOptions); + Assert.assertNotNull(spyRocks); + Assert.assertNotNull(store.getDbSource()); + + // 关闭 store + store.close(); + + // 验证父类的 writeOptions.close() 被调用了(通过 super.close()) + verify(spyParentWriteOptions, times(1)).close(); + + // 验证 rocks.close() 被调用了(资源真正被关闭) + verify(spyRocks, times(1)).close(); + } +} From ab078f644e5dfa56baf71010402d88b02a443eed Mon Sep 17 00:00:00 2001 From: warku123 Date: Fri, 20 Mar 2026 17:22:09 +0800 Subject: [PATCH 02/12] fix(db): resolve resource leakage in CheckPointV2Store.close() Split the try-catch block to ensure parent resources are released even when writeOptions.close() throws exception. - Add proper cleanup in finally block to prevent cross-test contamination - Add Javadoc annotations for test class and methods - Fix checkstyle violations (import order and line length) Test: Add unit test to verify super.close() is always called --- .../tron/core/db/CheckPointV2StoreTest.java | 74 +++++++++++++------ 1 file changed, 53 insertions(+), 21 deletions(-) diff --git a/framework/src/test/java/org/tron/core/db/CheckPointV2StoreTest.java b/framework/src/test/java/org/tron/core/db/CheckPointV2StoreTest.java index 7c9d2723b82..56b20a6a329 100644 --- a/framework/src/test/java/org/tron/core/db/CheckPointV2StoreTest.java +++ b/framework/src/test/java/org/tron/core/db/CheckPointV2StoreTest.java @@ -1,8 +1,8 @@ package org.tron.core.db; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import java.io.IOException; import java.lang.reflect.Field; @@ -13,11 +13,14 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.rocksdb.RocksDB; +import org.tron.common.TestConstants; import org.tron.common.storage.WriteOptionsWrapper; -import org.tron.core.Constant; import org.tron.core.config.args.Args; import org.tron.core.store.CheckPointV2Store; +/** + * Unit tests for {@link CheckPointV2Store}. + */ public class CheckPointV2StoreTest { @ClassRule @@ -27,48 +30,77 @@ public class CheckPointV2StoreTest { RocksDB.loadLibrary(); } + /** + * Initializes test arguments before running all tests. + * + * @throws IOException if an I/O error occurs + */ @BeforeClass public static void initArgs() throws IOException { - Args.setParam(new String[]{"-d", temporaryFolder.newFolder().toString()}, Constant.TEST_CONF); + Args.setParam( + new String[]{"-d", temporaryFolder.newFolder().toString()}, + TestConstants.TEST_CONF + ); } + /** + * Clears test arguments after all tests have run. + */ @AfterClass public static void destroy() { Args.clearParam(); } + /** + * Tests that {@link CheckPointV2Store#close()} properly calls {@code super.close()} + * to ensure parent resources are released. + * + * @throws Exception if an error occurs during the test + */ @Test public void testCloseCallsSuperClose() throws Exception { CheckPointV2Store store = new CheckPointV2Store("test-close-super"); - // 获取父类的 writeOptions 字段 + // Get the parent class's writeOptions field Field parentWriteOptionsField = TronDatabase.class.getDeclaredField("writeOptions"); parentWriteOptionsField.setAccessible(true); - WriteOptionsWrapper originalParentWriteOptions = (WriteOptionsWrapper) parentWriteOptionsField.get(store); + WriteOptionsWrapper originalParentWriteOptions = + (WriteOptionsWrapper) parentWriteOptionsField.get(store); - // 保存原始的 rocks 对象引用,用于后续验证 + // Save the original rocks object reference for subsequent verification org.rocksdb.WriteOptions originalRocks = originalParentWriteOptions.rocks; - // 创建一个 spy 来监控父类的 writeOptions + // Create a spy to monitor the parent class's writeOptions WriteOptionsWrapper spyParentWriteOptions = spy(originalParentWriteOptions); parentWriteOptionsField.set(store, spyParentWriteOptions); - // 创建一个 spy 来监控 rocks.close() 方法 + // Create a spy to monitor the rocks.close() method org.rocksdb.WriteOptions spyRocks = spy(originalRocks); spyParentWriteOptions.rocks = spyRocks; - // 验证父类的 writeOptions 和 dbSource 存在 - Assert.assertNotNull(spyParentWriteOptions); - Assert.assertNotNull(spyRocks); - Assert.assertNotNull(store.getDbSource()); - - // 关闭 store - store.close(); - - // 验证父类的 writeOptions.close() 被调用了(通过 super.close()) - verify(spyParentWriteOptions, times(1)).close(); - - // 验证 rocks.close() 被调用了(资源真正被关闭) - verify(spyRocks, times(1)).close(); + try { + // Verify that the parent class's writeOptions and dbSource exist + Assert.assertNotNull(spyParentWriteOptions); + Assert.assertNotNull(spyRocks); + Assert.assertNotNull(store.getDbSource()); + + // Close the store + store.close(); + + // Verify that the parent class's writeOptions.close() was called (via super.close()) + verify(spyParentWriteOptions, times(1)).close(); + + // Verify that rocks.close() was called (resources are actually closed) + verify(spyRocks, times(1)).close(); + } finally { + // Cleanup: restore original state to avoid cross-test contamination + if (store != null) { + store.close(); + } + // Restore original writeOptions to remove spies + parentWriteOptionsField.set(store, originalParentWriteOptions); + // Restore original rocks reference + originalParentWriteOptions.rocks = originalRocks; + } } } From 57949ca841a0d613f46bf062088cdaf038fa2934 Mon Sep 17 00:00:00 2001 From: warku123 Date: Wed, 1 Apr 2026 20:55:14 +0800 Subject: [PATCH 03/12] Fix review issues: deduplicate logs and simplify exception handling --- .../java/org/tron/core/store/CheckPointV2Store.java | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/chainbase/src/main/java/org/tron/core/store/CheckPointV2Store.java b/chainbase/src/main/java/org/tron/core/store/CheckPointV2Store.java index afd5d330a8d..3103a91aeed 100644 --- a/chainbase/src/main/java/org/tron/core/store/CheckPointV2Store.java +++ b/chainbase/src/main/java/org/tron/core/store/CheckPointV2Store.java @@ -63,23 +63,18 @@ public void updateByBatch(Map rows) { } /** - * close the database. + * Closes the database and releases all resources. + * This method ensures that subclass-specific resources are cleaned up + * before delegating to the parent class for complete resource cleanup. */ @Override public void close() { - logger.debug("******** Begin to close {}. ********", getName()); try { writeOptions.close(); } catch (Exception e) { logger.warn("Failed to close writeOptions in {}.", getName(), e); } - try { - super.close(); - } catch (Exception e) { - logger.warn("Failed to close parent resources in {}.", getName(), e); - } finally { - logger.debug("******** End to close {}. ********", getName()); - } + super.close(); } } From 7142e2e1084b5821d8d61faa832ec72e50c991c5 Mon Sep 17 00:00:00 2001 From: warku123 Date: Thu, 16 Apr 2026 17:06:57 +0800 Subject: [PATCH 04/12] fix(db): split try-catch in TronDatabase.close() and improve tests Split the single try-catch block in TronDatabase.close() into separate blocks for writeOptions and dbSource to ensure dbSource.closeDB() runs even when writeOptions.close() throws an exception. Rewrite tests to verify the exception scenario: mock writeOptions to throw and assert that dbSource.closeDB() is still called. --- .../java/org/tron/core/db/TronDatabase.java | 9 +- .../tron/core/db/CheckPointV2StoreTest.java | 108 +++++++++--------- 2 files changed, 59 insertions(+), 58 deletions(-) diff --git a/chainbase/src/main/java/org/tron/core/db/TronDatabase.java b/chainbase/src/main/java/org/tron/core/db/TronDatabase.java index 40762568c82..d72874363e5 100644 --- a/chainbase/src/main/java/org/tron/core/db/TronDatabase.java +++ b/chainbase/src/main/java/org/tron/core/db/TronDatabase.java @@ -78,12 +78,15 @@ public void close() { logger.info("******** Begin to close {}. ********", getName()); try { writeOptions.close(); + } catch (Exception e) { + logger.warn("Failed to close writeOptions in {}.", getName(), e); + } + try { dbSource.closeDB(); } catch (Exception e) { - logger.warn("Failed to close {}.", getName(), e); - } finally { - logger.info("******** End to close {}. ********", getName()); + logger.warn("Failed to close dbSource in {}.", getName(), e); } + logger.info("******** End to close {}. ********", getName()); } public abstract void put(byte[] key, T item); diff --git a/framework/src/test/java/org/tron/core/db/CheckPointV2StoreTest.java b/framework/src/test/java/org/tron/core/db/CheckPointV2StoreTest.java index 56b20a6a329..8c00fe53292 100644 --- a/framework/src/test/java/org/tron/core/db/CheckPointV2StoreTest.java +++ b/framework/src/test/java/org/tron/core/db/CheckPointV2StoreTest.java @@ -1,13 +1,13 @@ package org.tron.core.db; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import java.io.IOException; import java.lang.reflect.Field; import org.junit.AfterClass; -import org.junit.Assert; import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Test; @@ -16,11 +16,9 @@ import org.tron.common.TestConstants; import org.tron.common.storage.WriteOptionsWrapper; import org.tron.core.config.args.Args; +import org.tron.core.db.common.DbSourceInter; import org.tron.core.store.CheckPointV2Store; -/** - * Unit tests for {@link CheckPointV2Store}. - */ public class CheckPointV2StoreTest { @ClassRule @@ -30,11 +28,6 @@ public class CheckPointV2StoreTest { RocksDB.loadLibrary(); } - /** - * Initializes test arguments before running all tests. - * - * @throws IOException if an I/O error occurs - */ @BeforeClass public static void initArgs() throws IOException { Args.setParam( @@ -43,64 +36,69 @@ public static void initArgs() throws IOException { ); } - /** - * Clears test arguments after all tests have run. - */ @AfterClass public static void destroy() { Args.clearParam(); } - /** - * Tests that {@link CheckPointV2Store#close()} properly calls {@code super.close()} - * to ensure parent resources are released. - * - * @throws Exception if an error occurs during the test - */ @Test - public void testCloseCallsSuperClose() throws Exception { - CheckPointV2Store store = new CheckPointV2Store("test-close-super"); - - // Get the parent class's writeOptions field + public void testCloseReleasesAllResources() throws Exception { + CheckPointV2Store store = new CheckPointV2Store("test-close"); + + // Replace dbSource with a mock so we can verify closeDB() + Field dbSourceField = TronDatabase.class.getDeclaredField("dbSource"); + dbSourceField.setAccessible(true); + DbSourceInter originalDbSource = (DbSourceInter) dbSourceField.get(store); + DbSourceInter mockDbSource = mock(DbSourceInter.class); + dbSourceField.set(store, mockDbSource); + + try { + store.close(); + + verify(mockDbSource).closeDB(); + } finally { + originalDbSource.closeDB(); + } + } + + @Test + public void testCloseDbSourceWhenWriteOptionsThrows() throws Exception { + CheckPointV2Store store = new CheckPointV2Store("test-close-exception"); + + // Replace child writeOptions with a spy that throws on close + Field childWriteOptionsField = CheckPointV2Store.class.getDeclaredField("writeOptions"); + childWriteOptionsField.setAccessible(true); + WriteOptionsWrapper childWriteOptions = + (WriteOptionsWrapper) childWriteOptionsField.get(store); + WriteOptionsWrapper spyChildWriteOptions = spy(childWriteOptions); + doThrow(new RuntimeException("simulated writeOptions failure")) + .when(spyChildWriteOptions).close(); + childWriteOptionsField.set(store, spyChildWriteOptions); + + // Replace parent writeOptions with a spy that throws on close Field parentWriteOptionsField = TronDatabase.class.getDeclaredField("writeOptions"); parentWriteOptionsField.setAccessible(true); - WriteOptionsWrapper originalParentWriteOptions = + WriteOptionsWrapper parentWriteOptions = (WriteOptionsWrapper) parentWriteOptionsField.get(store); - - // Save the original rocks object reference for subsequent verification - org.rocksdb.WriteOptions originalRocks = originalParentWriteOptions.rocks; - - // Create a spy to monitor the parent class's writeOptions - WriteOptionsWrapper spyParentWriteOptions = spy(originalParentWriteOptions); + WriteOptionsWrapper spyParentWriteOptions = spy(parentWriteOptions); + doThrow(new RuntimeException("simulated parent writeOptions failure")) + .when(spyParentWriteOptions).close(); parentWriteOptionsField.set(store, spyParentWriteOptions); - - // Create a spy to monitor the rocks.close() method - org.rocksdb.WriteOptions spyRocks = spy(originalRocks); - spyParentWriteOptions.rocks = spyRocks; - + + // Replace dbSource with a mock + Field dbSourceField = TronDatabase.class.getDeclaredField("dbSource"); + dbSourceField.setAccessible(true); + DbSourceInter originalDbSource = (DbSourceInter) dbSourceField.get(store); + DbSourceInter mockDbSource = mock(DbSourceInter.class); + dbSourceField.set(store, mockDbSource); + try { - // Verify that the parent class's writeOptions and dbSource exist - Assert.assertNotNull(spyParentWriteOptions); - Assert.assertNotNull(spyRocks); - Assert.assertNotNull(store.getDbSource()); - - // Close the store store.close(); - - // Verify that the parent class's writeOptions.close() was called (via super.close()) - verify(spyParentWriteOptions, times(1)).close(); - - // Verify that rocks.close() was called (resources are actually closed) - verify(spyRocks, times(1)).close(); + + // dbSource.closeDB() must be called even though both writeOptions threw + verify(mockDbSource).closeDB(); } finally { - // Cleanup: restore original state to avoid cross-test contamination - if (store != null) { - store.close(); - } - // Restore original writeOptions to remove spies - parentWriteOptionsField.set(store, originalParentWriteOptions); - // Restore original rocks reference - originalParentWriteOptions.rocks = originalRocks; + originalDbSource.closeDB(); } } } From bcb7c0b8e58b7d2d1ec9e3b17ea737032acf551c Mon Sep 17 00:00:00 2001 From: warku123 Date: Thu, 16 Apr 2026 23:42:20 +0800 Subject: [PATCH 05/12] fix(db): restore finally block for close log in TronDatabase Move the "End to close" log back into a finally block so it is guaranteed to execute even if dbSource.closeDB() throws an Error. --- chainbase/src/main/java/org/tron/core/db/TronDatabase.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chainbase/src/main/java/org/tron/core/db/TronDatabase.java b/chainbase/src/main/java/org/tron/core/db/TronDatabase.java index d72874363e5..fc04f90e5fa 100644 --- a/chainbase/src/main/java/org/tron/core/db/TronDatabase.java +++ b/chainbase/src/main/java/org/tron/core/db/TronDatabase.java @@ -85,8 +85,9 @@ public void close() { dbSource.closeDB(); } catch (Exception e) { logger.warn("Failed to close dbSource in {}.", getName(), e); + } finally { + logger.info("******** End to close {}. ********", getName()); } - logger.info("******** End to close {}. ********", getName()); } public abstract void put(byte[] key, T item); From 70add8b23b34fd8bef70506c71d5e9132be250ba Mon Sep 17 00:00:00 2001 From: warku123 Date: Fri, 17 Apr 2026 10:51:40 +0800 Subject: [PATCH 06/12] ci: retry From 6bf1267fbd01321f2fbad2ef4e70e0bff81015aa Mon Sep 17 00:00:00 2001 From: warku123 Date: Fri, 17 Apr 2026 11:27:01 +0800 Subject: [PATCH 07/12] test(db): add real-resource close test to restore coverage --- .../test/java/org/tron/core/db/CheckPointV2StoreTest.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/framework/src/test/java/org/tron/core/db/CheckPointV2StoreTest.java b/framework/src/test/java/org/tron/core/db/CheckPointV2StoreTest.java index 8c00fe53292..ea34e8bd037 100644 --- a/framework/src/test/java/org/tron/core/db/CheckPointV2StoreTest.java +++ b/framework/src/test/java/org/tron/core/db/CheckPointV2StoreTest.java @@ -41,6 +41,13 @@ public static void destroy() { Args.clearParam(); } + @Test + public void testCloseWithRealResources() { + CheckPointV2Store store = new CheckPointV2Store("test-close-real"); + // Exercises the real writeOptions.close() and dbSource.closeDB() code paths + store.close(); + } + @Test public void testCloseReleasesAllResources() throws Exception { CheckPointV2Store store = new CheckPointV2Store("test-close"); From 39cddb561f598a69ce08a89182aa5b6d8bfde286 Mon Sep 17 00:00:00 2001 From: warku123 Date: Fri, 17 Apr 2026 11:55:52 +0800 Subject: [PATCH 08/12] fix(db): extract doClose() hook to keep CheckPointV2Store logs at debug level TronDatabase.close() now delegates resource cleanup to a protected doClose() method, keeping info-level logging in the base class. CheckPointV2Store.close() overrides with debug-level logging and calls doClose() directly, avoiding log spam on high-frequency checkpoint flushes. --- .../src/main/java/org/tron/core/db/TronDatabase.java | 10 ++++++++-- .../java/org/tron/core/store/CheckPointV2Store.java | 12 ++++++------ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/chainbase/src/main/java/org/tron/core/db/TronDatabase.java b/chainbase/src/main/java/org/tron/core/db/TronDatabase.java index fc04f90e5fa..82f47d311e1 100644 --- a/chainbase/src/main/java/org/tron/core/db/TronDatabase.java +++ b/chainbase/src/main/java/org/tron/core/db/TronDatabase.java @@ -76,6 +76,14 @@ public void reset() { @Override public void close() { logger.info("******** Begin to close {}. ********", getName()); + try { + doClose(); + } finally { + logger.info("******** End to close {}. ********", getName()); + } + } + + protected void doClose() { try { writeOptions.close(); } catch (Exception e) { @@ -85,8 +93,6 @@ public void close() { dbSource.closeDB(); } catch (Exception e) { logger.warn("Failed to close dbSource in {}.", getName(), e); - } finally { - logger.info("******** End to close {}. ********", getName()); } } diff --git a/chainbase/src/main/java/org/tron/core/store/CheckPointV2Store.java b/chainbase/src/main/java/org/tron/core/store/CheckPointV2Store.java index 3103a91aeed..b886192f262 100644 --- a/chainbase/src/main/java/org/tron/core/store/CheckPointV2Store.java +++ b/chainbase/src/main/java/org/tron/core/store/CheckPointV2Store.java @@ -62,19 +62,19 @@ public void updateByBatch(Map rows) { this.dbSource.updateByBatch(rows, writeOptions); } - /** - * Closes the database and releases all resources. - * This method ensures that subclass-specific resources are cleaned up - * before delegating to the parent class for complete resource cleanup. - */ @Override public void close() { + logger.debug("******** Begin to close {}. ********", getName()); try { writeOptions.close(); } catch (Exception e) { logger.warn("Failed to close writeOptions in {}.", getName(), e); } - super.close(); + try { + doClose(); + } finally { + logger.debug("******** End to close {}. ********", getName()); + } } } From a09935780686dae4caff3cd85a03e39b6fcc8c24 Mon Sep 17 00:00:00 2001 From: warku123 Date: Fri, 17 Apr 2026 14:06:50 +0800 Subject: [PATCH 09/12] test(db): cover dbSource exception path in doClose() --- .../tron/core/db/CheckPointV2StoreTest.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/framework/src/test/java/org/tron/core/db/CheckPointV2StoreTest.java b/framework/src/test/java/org/tron/core/db/CheckPointV2StoreTest.java index ea34e8bd037..4f90cf46b1a 100644 --- a/framework/src/test/java/org/tron/core/db/CheckPointV2StoreTest.java +++ b/framework/src/test/java/org/tron/core/db/CheckPointV2StoreTest.java @@ -68,6 +68,24 @@ public void testCloseReleasesAllResources() throws Exception { } } + @Test + public void testCloseWhenDbSourceThrows() throws Exception { + CheckPointV2Store store = new CheckPointV2Store("test-close-dbsource-throws"); + + Field dbSourceField = TronDatabase.class.getDeclaredField("dbSource"); + dbSourceField.setAccessible(true); + DbSourceInter originalDbSource = (DbSourceInter) dbSourceField.get(store); + DbSourceInter mockDbSource = mock(DbSourceInter.class); + doThrow(new RuntimeException("simulated dbSource failure")).when(mockDbSource).closeDB(); + dbSourceField.set(store, mockDbSource); + + try { + store.close(); + } finally { + originalDbSource.closeDB(); + } + } + @Test public void testCloseDbSourceWhenWriteOptionsThrows() throws Exception { CheckPointV2Store store = new CheckPointV2Store("test-close-exception"); From 22dd4a114eca5deb03413962f492d1b62bb0b5f7 Mon Sep 17 00:00:00 2001 From: warku123 Date: Fri, 17 Apr 2026 17:44:27 +0800 Subject: [PATCH 10/12] ci: retry From 10bd5321ff6dbe77c9cba7f646bab655a4e8c597 Mon Sep 17 00:00:00 2001 From: warku123 Date: Mon, 20 Apr 2026 11:34:12 +0800 Subject: [PATCH 11/12] test(db): add doClose() test to verify dbSource closes despite writeOptions failure --- .../org/tron/core/db/TronDatabaseTest.java | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/framework/src/test/java/org/tron/core/db/TronDatabaseTest.java b/framework/src/test/java/org/tron/core/db/TronDatabaseTest.java index 52adf8e49fa..826a8988cff 100644 --- a/framework/src/test/java/org/tron/core/db/TronDatabaseTest.java +++ b/framework/src/test/java/org/tron/core/db/TronDatabaseTest.java @@ -1,7 +1,13 @@ package org.tron.core.db; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + import com.google.protobuf.InvalidProtocolBufferException; import java.io.IOException; +import java.lang.reflect.Field; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; @@ -12,7 +18,9 @@ import org.junit.rules.TemporaryFolder; import org.rocksdb.RocksDB; import org.tron.common.TestConstants; +import org.tron.common.storage.WriteOptionsWrapper; import org.tron.core.config.args.Args; +import org.tron.core.db.common.DbSourceInter; import org.tron.core.exception.BadItemException; import org.tron.core.exception.ItemNotFoundException; @@ -99,4 +107,34 @@ public void TestGetFromRoot() throws Assert.assertEquals(db.getFromRoot("test".getBytes()), "test"); } + + @Test + public void testDoCloseDbSourceCalledWhenWriteOptionsThrows() throws Exception { + TronDatabase db = new TronDatabase("test-do-close") { + @Override public void put(byte[] key, String item) {} + @Override public void delete(byte[] key) {} + @Override public String get(byte[] key) { return null; } + @Override public boolean has(byte[] key) { return false; } + }; + + Field writeOptionsField = TronDatabase.class.getDeclaredField("writeOptions"); + writeOptionsField.setAccessible(true); + WriteOptionsWrapper spyWriteOptions = spy((WriteOptionsWrapper) writeOptionsField.get(db)); + doThrow(new RuntimeException("simulated writeOptions failure")).when(spyWriteOptions).close(); + writeOptionsField.set(db, spyWriteOptions); + + Field dbSourceField = TronDatabase.class.getDeclaredField("dbSource"); + dbSourceField.setAccessible(true); + DbSourceInter originalDbSource = (DbSourceInter) dbSourceField.get(db); + DbSourceInter mockDbSource = mock(DbSourceInter.class); + dbSourceField.set(db, mockDbSource); + + try { + db.doClose(); + verify(spyWriteOptions).close(); + verify(mockDbSource).closeDB(); + } finally { + originalDbSource.closeDB(); + } + } } From 267e2ab55ac1008e6b1da8ec9b24466a7f3a59e8 Mon Sep 17 00:00:00 2001 From: warku123 Date: Mon, 20 Apr 2026 11:38:07 +0800 Subject: [PATCH 12/12] fix(style): expand anonymous subclass methods to pass checkstyle --- .../org/tron/core/db/TronDatabaseTest.java | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/framework/src/test/java/org/tron/core/db/TronDatabaseTest.java b/framework/src/test/java/org/tron/core/db/TronDatabaseTest.java index 826a8988cff..6bf479c287f 100644 --- a/framework/src/test/java/org/tron/core/db/TronDatabaseTest.java +++ b/framework/src/test/java/org/tron/core/db/TronDatabaseTest.java @@ -111,10 +111,24 @@ public void TestGetFromRoot() throws @Test public void testDoCloseDbSourceCalledWhenWriteOptionsThrows() throws Exception { TronDatabase db = new TronDatabase("test-do-close") { - @Override public void put(byte[] key, String item) {} - @Override public void delete(byte[] key) {} - @Override public String get(byte[] key) { return null; } - @Override public boolean has(byte[] key) { return false; } + + @Override + public void put(byte[] key, String item) { + } + + @Override + public void delete(byte[] key) { + } + + @Override + public String get(byte[] key) { + return null; + } + + @Override + public boolean has(byte[] key) { + return false; + } }; Field writeOptionsField = TronDatabase.class.getDeclaredField("writeOptions");