Spin locks with variable time retry backoffs

I'd just requeue the job if you hit max retries:

try {
  LocksServiceImpl.acquire(lock);
} catch(LocksService.MutexException e) {
  System.enqueueJob(this);
  return;
}

At this point, your job will retry later. There's supposed to be a built-in delay for chained jobs, so this should provide the delay you're looking for.


Although not an answer per se, I wanted to post some performance stats based on a stress test using the spin lock and spin lock fallback strategy as answered by @sfdcfox. This is not a critique of the answer which was good but more of a cautionary tale should one pursue this route. YMMV

Test:

  • 50 queueable jobs (System.enqueueJob(..)) each launched in one stress testing transaction.
  • Each job enqueues on the same spin lock - that is, they all compete with each other.
  • Spin lock does a Select for Update and tries 10 times before abandoning
  • Each job does the same amount of work: (deserializes 1000 custom Json and then constructs/inserts 1000 Assets). That is, the work is "substantive".

Result:

  • 4 of the 50 queueables completed quickly and the rest were requeued by the spin lock backoff solution to another queueable. SFDC then reschedules these queueables (delaying up to 1 minute) and they (46) compete with each other again. This repeats itself over time until they all complete
  • Total elapsed time to get through 50 initial threads each trying to insert 1000 assets = 26 minutes. Three hundred nine (309) queueable retry jobs (!)

Analysis

  • YMMV
  • A batch job would almost certainly be faster
  • Introducing some variable jitter delay between each spin lock retry would probably improve things but as the OP stated, it is not clear what a good delay would be that doesn't consume a scarce SFDC resource