Reserve the next gap-free number for a sequence. Blocks if another transaction holds a reservation on this sequence. Returns a handle that must be committed or rolled back — until then, other callers on the same sequence will block. `advance_fn` is called after acquiring the lock to atomically advance the underlying counter.
(
&self,
sequence_key: &str,
advance_fn: impl FnOnce() -> Result<i64, SequenceError>,
)
| 71 | /// `advance_fn` is called after acquiring the lock to atomically advance |
| 72 | /// the underlying counter. |
| 73 | pub fn reserve( |
| 74 | &self, |
| 75 | sequence_key: &str, |
| 76 | advance_fn: impl FnOnce() -> Result<i64, SequenceError>, |
| 77 | ) -> Result<ReservationHandle, SequenceError> { |
| 78 | // Get or create the per-sequence lock. |
| 79 | let lock = { |
| 80 | let mut locks = self.locks.lock().unwrap_or_else(|p| p.into_inner()); |
| 81 | locks |
| 82 | .entry(sequence_key.to_string()) |
| 83 | .or_insert_with(|| { |
| 84 | Arc::new(SequenceLock { |
| 85 | locked: Mutex::new(false), |
| 86 | unlocked: Condvar::new(), |
| 87 | }) |
| 88 | }) |
| 89 | .clone() |
| 90 | }; |
| 91 | |
| 92 | // Wait until no active reservation, then mark as locked. |
| 93 | { |
| 94 | let mut is_locked = lock.locked.lock().unwrap_or_else(|p| p.into_inner()); |
| 95 | while *is_locked { |
| 96 | is_locked = lock |
| 97 | .unlocked |
| 98 | .wait(is_locked) |
| 99 | .unwrap_or_else(|p| p.into_inner()); |
| 100 | } |
| 101 | *is_locked = true; |
| 102 | } |
| 103 | |
| 104 | // Advance the counter while holding the logical lock. |
| 105 | let value = match advance_fn() { |
| 106 | Ok(v) => v, |
| 107 | Err(e) => { |
| 108 | // Unlock on advance failure. |
| 109 | self.unlock_sequence(&lock); |
| 110 | return Err(e); |
| 111 | } |
| 112 | }; |
| 113 | |
| 114 | // Generate reservation ID. |
| 115 | let id = { |
| 116 | let mut next = self.next_id.lock().unwrap_or_else(|p| p.into_inner()); |
| 117 | let id = ReservationId(*next); |
| 118 | *next += 1; |
| 119 | id |
| 120 | }; |
| 121 | |
| 122 | Ok(ReservationHandle { |
| 123 | id, |
| 124 | sequence_key: sequence_key.to_string(), |
| 125 | value, |
| 126 | }) |
| 127 | } |
| 128 | |
| 129 | /// Commit a reservation: the number is now permanent. |
| 130 | /// Releases the per-sequence lock so the next caller can proceed. |