I don't really mod but I have been relatively invested in modding for a while now and there Is one idea that I thought was really weird no-one had tried before: a block degradation system. I imagine having a constant random tick running for every block to see if it degrades would be extremely laggy but what if instead each tick, second, etc (probably configurable in config) every chunk picked a random block to random tick degrade? An example would be something like : Every 5 minutes pick 3 random blocks in this chunk, for every 1 face exposed to air have a 25% chance to degrade according to this degradation table. So basically instead of having a random tick rolling for every single block loaded, you just have one rolling for every chunk. You could of course use a much more complex algorithm to determine degradation then just a simple Stone bricks -> Cobblestone -> Mossy Cobble but this is just an idea.
The game is already randomly ticking every single block, regardless of whether it needs to be ticked; most blocks simply do nothing as their updateTick() method is empty. The only issue is the complexity of the code that runs when ticked, and you'd probably have a low probability of even executing the code so it runs at a decently slow rate (i.e. use "random.nextInt(10) == 0" to call the code 10 times less often; on average a block is randomly ticked every 68 seconds).
For example, leaves use random ticks with no delays added but they don't cause lag even in huge amounts, at least not in my own custom version which is far more optimized than newer versions; the server tick is less than 3 ms in a Mega Forest biome at 16 chunk render distance (though render distance doesn't matter since the tick distance is fixed at +/-7 chunks, or an 8 chunk circular radius in newer versions):
This code is always being called even if the "check decay" bit isn't set (only if it is will the game run the code that checks for neighboring logs, in this way the number of checks is greatly reduced, not unlike using a low random probability, and your block decay only needs to check 6 neighbors, which can be offset with a 1/6 probability, then optionally replace the block):
public void updateTick(World par1World, int posX, int posY, int posZ, Random par5Random)
int meta = par1World.getBlockMetadata(posX, posY, posZ);
// Checks that only "check decay" bit is set (4 = permanent, 8 = check decay)
if ((meta & 12) == 8)
The reason why leaves have been associated with lag is due to chunk updates caused by leaf decay, which is in turn primarily a client-side issue but this has nothing to do with random ticks themselves, and the same is true of any other block changes.
The main issue I see is that the time taken for a block to decay using random ticks is highly random; it could take anywhere from the next tick (the same block can even be ticked 3 times in the same tick) after you place it to (theoretically) forever; I don't know what Mojang is doing for copper in 1.17 but they are probably using scheduled block updates since the times taken for it to weather are very long and well-defined; however, having millions of scheduled ticks pending will kill performance (maybe they use a special block state as a counter, much as cactus and sugarcane do, as blocks are no longer limited to 16 data values).