The Bug
During world random block ticking, if a lava block is selected to tick then it will tick twice, when it should only tick once. In general this is simply a minor behavioral regression from older versions as a result of technical changes, so minor in fact I'm reporting this over a year after I found it. But it should be fixed, as it will result in a small performance gain for random ticks (lava is the most expensive random ticking block in this game) and fix a behavioral regression.
Analysis
Note: All mappings are Mojang mappings.
The cause is from the fact that a lava block has a FluidState and a BlockState, and both are marked as randomly ticking. The code for random tick will run random tick logic for the FluidState if it is marked as randomly ticking, and it will run the code for BlockState random ticking if the BlockState is marked as randomly ticking. You can verify this behavior yourself by looking at ServerLevel#tickChunk(LevelChunk, int). Since the Lava FluidState and BlockState are both marked as randomly ticking, it will be double ticked - unlike other blocks, like dirt, which are not a fluid and as such will not randomly tick twice.
I verified with older versions (1.12.2) that this kind of logic was not intentional. I found that the random tick implementation is basically the same for Lava. Also, Lava in 1.12.2 was only ticked once per random tick selection. So it's definitely a regression from the technical changes to split Fluids from Blocks, as it looks like the fluid ticking was added without considering how the Lava BlockState random ticking worked. A simple mistake.
This can be solved by making the LiquidBlock class not run the fluid's random tick method. This seems to be a good fit for the direction the technical changes are headed in, as fluids and blocks should be separate and as such the BlockState should not be responsible for randomly ticking the fluid, especially when there is already dedicated logic for that.
Code Analysis
by Jingy
. . . if (var2 > 0) { LevelChunkSection[] levelChunkSections = var1.getSections(); for(int i = 0; i < levelChunkSections.length; ++i) { LevelChunkSection levelChunkSection = levelChunkSections[i]; if (levelChunkSection.isRandomlyTicking()) { int yFromSectionIndex = var1.getSectionYFromSectionIndex(i); int sectionToBlockCoord = SectionPos.sectionToBlockCoord(yFromSectionIndex); for(int i1 = 0; i1 < var2; ++i1) { BlockPos blockRandomPos = this.getBlockRandomPos(var5, sectionToBlockCoord, var6, 15); randomTick.push("randomTick"); BlockState levelChunkSectionBlockState = levelChunkSection.getBlockState(blockRandomPos.getX() - var5, blockRandomPos.getY() - sectionToBlockCoord, blockRandomPos.getZ() - var6); if (levelChunkSectionBlockState.isRandomlyTicking()) { levelChunkSectionBlockState.randomTick(this, blockRandomPos, this.random); } FluidState fluidState = levelChunkSectionBlockState.getFluidState(); if (fluidState.isRandomlyTicking()) { fluidState.randomTick(this, blockRandomPos, this.random); } randomTick.pop(); } } } } . . .