How do miners avoid transactions getting dropped? I'm having a problem figuring out how miners solve the following problem:

Let's say we have 2 miners: M1, M2.

M1 adds transactions A,B,C,D to their block. M2 adds transactions A,B,C to their block. For some reason, transaction D will come to M2 a few seconds after M1 received it.

Now, M2 solves the block and sends this block to everyone. Consequently, M1 throws their block away and starts working on new block.

Next, M1 adds transactions E,F,G to their block, whereas M2 adds D,E,F,G (M2 gets D now) to their block. Now, M1 solves the block first and broadcasts it. Therefore, M2 throws their block away and starts a new one.

In this case, transaction D won't get in any block! (The problem is caused by network speed which caused the fact that D arrived at M2 a few seconds after it got to M1.)

So, how is this problem handled or avoided?
Miners have mempools where they store all transactions they consider to include in future blocks. They don't put transactions they received into their blocks, right away.

But they don't put them into their mempool right away, either. The transactions in each miner's mempool are valid and worth the miner's effort (pay at least a certain amount of fee).

They can't even include all of the transactions they know about in their blocks, at the moment. Blocks can at most be 1 MB in size and because there currently are many transactions, the mempools of miners are much bigger than 1 MB.

At point 0 in time, the mempools look like this:

mem_M1 = {A, B, C, D}
mem_M2 = {A, B, C}


When M2 solves the block, they remove all transactions they included in their block from their mempool:

mem_M1 = {A, B, C, D}
mem_M2 = {}


M2 then sends the block to M1 who validates the newly received block. If M1 considers the block valid, they remove all transactions in that block from their mempool:

mem_M1 = {D}
mem_M2 = {}


Note that D is still in there. M1 doesn't flush their mempool. Only the transactions included in the newly received blocks have been removed.

They then receive more transactions:

mem_M1 = {D, E, F, G}
mem_M2 = {D, E, F, G}


Next, M1 solves a block and removes the transactions they included from their mempool:

mem_M1 = {}
mem_M2 = {D, E, F, G}


After M1 sends their newly mined block to M2, M2 removes all transactions included in that block from their mempool:

mem_M1 = {}
mem_M2 = {}


All transactions have been included in blocks.