Using `transactions` without `select ... for update`?
You need a transaction and it needs to grab the gap with FOR UPDATE.  The problem is that you don't have an index to use to help with the task.

I suggest that having both a start and end in a row makes the task difficult.  If, instead, there were only a start (or, equivalently, end), you could have an index and use SELECT...FOR UPDATE successfully.

Since you have gaps in the table, you would need a way to represent "real range" versus "unused range".  That would involve another column (which you might already have).

Building such a structure, plus code to grab part of a gap, is found here , where I discuss (as an example) associating ranges of IP-addresses with their 'owner'.  I use a special owner_id to indicate that a range is 'unassigned'.

The code was designed with speed in mind, since searching on start..end performs miserably at scale.