diff --git a/environment/src/main/java/jetbrains/exodus/env/EnvironmentImpl.java b/environment/src/main/java/jetbrains/exodus/env/EnvironmentImpl.java index 24710b194..84e4f003d 100644 --- a/environment/src/main/java/jetbrains/exodus/env/EnvironmentImpl.java +++ b/environment/src/main/java/jetbrains/exodus/env/EnvironmentImpl.java @@ -494,7 +494,7 @@ public Log getLog() { @Override public void gc() { - gc.wake(); + gc.wake(true); } @Override diff --git a/environment/src/main/kotlin/jetbrains/exodus/gc/BackgroundCleaner.kt b/environment/src/main/kotlin/jetbrains/exodus/gc/BackgroundCleaner.kt index edb400183..cd4b26952 100644 --- a/environment/src/main/kotlin/jetbrains/exodus/gc/BackgroundCleaner.kt +++ b/environment/src/main/kotlin/jetbrains/exodus/gc/BackgroundCleaner.kt @@ -66,6 +66,8 @@ internal class BackgroundCleaner(private val gc: GarbageCollector) { val isCurrentThread: Boolean get() = threadId == Thread.currentThread().id + val isActive: Boolean get() = processor.currentJob == backgroundCleaningJob + fun finish() { (processor.currentJob as? GcJob)?.cancel() backgroundCleaningJob.cancel() diff --git a/environment/src/main/kotlin/jetbrains/exodus/gc/GarbageCollector.kt b/environment/src/main/kotlin/jetbrains/exodus/gc/GarbageCollector.kt index 4893e339d..c75e61f42 100644 --- a/environment/src/main/kotlin/jetbrains/exodus/gc/GarbageCollector.kt +++ b/environment/src/main/kotlin/jetbrains/exodus/gc/GarbageCollector.kt @@ -22,6 +22,7 @@ import jetbrains.exodus.core.dataStructures.hash.IntHashMap import jetbrains.exodus.core.dataStructures.hash.PackedLongHashSet import jetbrains.exodus.core.execution.Job import jetbrains.exodus.core.execution.JobProcessorAdapter +import jetbrains.exodus.core.execution.LatchJob import jetbrains.exodus.env.* import jetbrains.exodus.io.Block import jetbrains.exodus.io.DataReader @@ -95,9 +96,14 @@ class GarbageCollector(internal val environment: EnvironmentImpl) { }, Priority.highest) } - fun wake() { + fun wake(estimateTotalUtilization: Boolean = false) { if (ec.isGcEnabled) { - environment.executeTransactionSafeTask { cleaner.queueCleaningJob() } + environment.executeTransactionSafeTask { + if (estimateTotalUtilization) { + utilizationProfile.estimateTotalBytes() + } + cleaner.queueCleaningJob() + } } } @@ -139,6 +145,17 @@ class GarbageCollector(internal val environment: EnvironmentImpl) { newFiles = 0 } + /** + * For tests only!!! + */ + fun waitForPendingGC() { + cleaner.getJobProcessor().waitForLatchJob(object : LatchJob() { + override fun execute() { + release() + } + }, 100, Priority.lowest) + } + /** * For tests only!!! */ diff --git a/environment/src/test/kotlin/jetbrains/exodus/gc/GarbageCollectorTest.kt b/environment/src/test/kotlin/jetbrains/exodus/gc/GarbageCollectorTest.kt index 552a9248e..0f417efd1 100644 --- a/environment/src/test/kotlin/jetbrains/exodus/gc/GarbageCollectorTest.kt +++ b/environment/src/test/kotlin/jetbrains/exodus/gc/GarbageCollectorTest.kt @@ -15,6 +15,7 @@ */ package jetbrains.exodus.gc +import jetbrains.exodus.TestFor import jetbrains.exodus.bindings.IntegerBinding import jetbrains.exodus.bindings.StringBinding import jetbrains.exodus.env.* @@ -299,6 +300,7 @@ open class GarbageCollectorTest : EnvironmentTestsBase() { } @Test + @TestFor(issues = ["XD-780"]) fun `stackoverflow-com-questions-56662998`() { env.environmentConfig.run { gcStartIn = 0 @@ -309,26 +311,23 @@ open class GarbageCollectorTest : EnvironmentTestsBase() { setLogFileSize(1) val store = openStoreAutoCommit("store") env.executeInExclusiveTransaction { txn -> - for (i in 1..1000) { + for (i in 1..500) { store.putRight(txn, IntegerBinding.intToEntry(i), IntegerBinding.intToEntry(i)) } } - Assert.assertTrue(env.log.numberOfFiles > 1L) + Assert.assertTrue(env.log.numberOfFiles > 3L) env.executeInExclusiveTransaction { txn -> store.openCursor(txn).use { cursor -> cursor.forEach { deleteCurrent() } } } env.gc() - env.gc.cleaner.getJobProcessor().run { - repeat(4) { - waitForJobs(100) - } - } - Assert.assertEquals(1L, env.log.numberOfFiles) + env.gc.waitForPendingGC() + Assert.assertTrue(env.log.numberOfFiles <= 3L) } @Test + @TestFor(issues = ["XD-780"]) fun `stackoverflow-com-questions-56662998+`() { env.environmentConfig.run { gcStartIn = 0 @@ -339,21 +338,17 @@ open class GarbageCollectorTest : EnvironmentTestsBase() { setLogFileSize(1) val store = openStoreAutoCommit("store") env.executeInExclusiveTransaction { txn -> - for (i in 1..1000) { + for (i in 1..500) { store.putRight(txn, IntegerBinding.intToEntry(i), IntegerBinding.intToEntry(i)) } } - Assert.assertTrue(env.log.numberOfFiles > 1L) + Assert.assertTrue(env.log.numberOfFiles > 3L) env.executeInExclusiveTransaction { txn -> env.truncateStore("store", txn) } env.gc() - env.gc.cleaner.getJobProcessor().run { - repeat(4) { - waitForJobs(100) - } - } - Assert.assertEquals(1L, env.log.numberOfFiles) + env.gc.waitForPendingGC() + Assert.assertTrue(env.log.numberOfFiles <= 3L) } protected fun openStoreAutoCommit(name: String): StoreImpl { diff --git a/environment/src/test/kotlin/jetbrains/exodus/gc/GarbageCollectorTestInMemory.kt b/environment/src/test/kotlin/jetbrains/exodus/gc/GarbageCollectorTestInMemory.kt index b01f0d858..b71b48b33 100644 --- a/environment/src/test/kotlin/jetbrains/exodus/gc/GarbageCollectorTestInMemory.kt +++ b/environment/src/test/kotlin/jetbrains/exodus/gc/GarbageCollectorTestInMemory.kt @@ -39,17 +39,16 @@ import java.util.concurrent.CyclicBarrier open class GarbageCollectorTestInMemory : GarbageCollectorTest() { private val rnd = Random() - private var memory: Memory? = null + private lateinit var memory: Memory override fun createRW(): Pair { memory = Memory() - return Pair(MemoryDataReader(memory!!), MemoryDataWriter(memory!!)) + return Pair(MemoryDataReader(memory), MemoryDataWriter(memory)) } override fun deleteRW() { reader = null writer = null - memory = null } @Test @@ -78,7 +77,7 @@ open class GarbageCollectorTestInMemory : GarbageCollectorTest() { Thread.sleep(0) } } catch (t: Throwable) { - memory!!.dump(File(System.getProperty("user.home"), "dump")) + memory.dump(File(System.getProperty("user.home"), "dump")) logger.error("User code exception: ", t) Assert.fail() } @@ -129,7 +128,7 @@ open class GarbageCollectorTestInMemory : GarbageCollectorTest() { Thread.sleep(0) } } catch (t: Throwable) { - memory!!.dump(File(System.getProperty("user.home"), "dump")) + memory.dump(File(System.getProperty("user.home"), "dump")) logger.error("User code exception: ", t) Assert.fail() } @@ -225,7 +224,7 @@ open class GarbageCollectorTestInMemory : GarbageCollectorTest() { } val t = throwable if (t != null) { - memory!!.dump(File(System.getProperty("user.home"), "dump")) + memory.dump(File(System.getProperty("user.home"), "dump")) logger.error("User code exception: ", t) Assert.fail() }