1use std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
93use std::ops::Bound;
94use std::time::{Duration, Instant};
95
96use mongreldb_types::errors::ErrorCategory;
97use parking_lot::{Condvar, Mutex, MutexGuard};
98
99use crate::{ExecutionControl, MongrelError, RowId};
100
101const CANCELLATION_POLL_INTERVAL: Duration = Duration::from_millis(10);
105
106const DEFAULT_PRIORITY: u64 = 0;
111
112#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
118pub enum LockMode {
119 Shared,
122 Exclusive,
125}
126
127impl LockMode {
128 pub const fn compatible(self, other: Self) -> bool {
130 matches!((self, other), (Self::Shared, Self::Shared))
131 }
132}
133
134#[derive(Debug, Clone, PartialEq, Eq, Hash)]
140pub enum LockKey {
141 Row { table_id: u64, row_id: RowId },
143 Key { table_id: u64, key: Vec<u8> },
147 Range {
150 table_id: u64,
151 low: Bound<Vec<u8>>,
152 high: Bound<Vec<u8>>,
153 },
154 Barrier { name: String },
157}
158
159impl LockKey {
160 pub fn row(table_id: u64, row_id: RowId) -> Self {
162 Self::Row { table_id, row_id }
163 }
164
165 pub fn key(table_id: u64, key: impl Into<Vec<u8>>) -> Self {
167 Self::Key {
168 table_id,
169 key: key.into(),
170 }
171 }
172
173 pub fn range(table_id: u64, low: Bound<Vec<u8>>, high: Bound<Vec<u8>>) -> Self {
175 Self::Range {
176 table_id,
177 low,
178 high,
179 }
180 }
181
182 pub fn barrier(name: impl Into<String>) -> Self {
184 Self::Barrier { name: name.into() }
185 }
186
187 pub fn schema_barrier() -> Self {
191 Self::barrier("schema")
192 }
193
194 pub fn sequence_barrier(sequence: &str) -> Self {
197 Self::barrier(format!("sequence:{sequence}"))
198 }
199}
200
201#[derive(Debug, Clone)]
207pub struct LockRequest {
208 pub txn_id: u64,
211 pub mode: LockMode,
213 pub deadline: Option<Instant>,
216 pub control: ExecutionControl,
221 pub priority: Option<u64>,
225}
226
227impl LockRequest {
228 pub fn new(txn_id: u64, mode: LockMode, control: ExecutionControl) -> Self {
230 Self {
231 txn_id,
232 mode,
233 deadline: None,
234 control,
235 priority: None,
236 }
237 }
238
239 pub fn with_deadline(mut self, deadline: Instant) -> Self {
241 self.deadline = Some(deadline);
242 self
243 }
244
245 pub fn with_timeout(self, timeout: Duration) -> Self {
249 let now = Instant::now();
250 self.with_deadline(now.checked_add(timeout).unwrap_or(now))
251 }
252
253 pub fn with_priority(mut self, priority: u64) -> Self {
255 self.priority = Some(priority);
256 self
257 }
258}
259
260#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
266pub enum LockError {
267 #[error(
270 "deadlock: transaction {victim} was chosen as the deadlock victim (wait-for cycle {cycle}); retry the whole transaction"
271 )]
272 Deadlock {
273 victim: u64,
275 cycle: String,
277 },
278 #[error("lock wait deadline exceeded")]
280 DeadlineExceeded,
281 #[error("lock wait cancelled")]
283 Cancelled,
284 #[error("invalid lock request: {0}")]
287 InvalidRequest(String),
288}
289
290impl LockError {
291 pub fn category(&self) -> ErrorCategory {
295 match self {
296 Self::Deadlock { .. } => ErrorCategory::Deadlock,
297 Self::DeadlineExceeded => ErrorCategory::DeadlineExceeded,
298 Self::Cancelled => ErrorCategory::Cancelled,
299 Self::InvalidRequest(_) => ErrorCategory::ClusterVersionMismatch,
301 }
302 }
303}
304
305impl From<LockError> for MongrelError {
306 fn from(error: LockError) -> Self {
311 match error {
312 LockError::Deadlock { victim, cycle } => MongrelError::Deadlock { victim, cycle },
313 LockError::DeadlineExceeded => MongrelError::DeadlineExceeded,
314 LockError::Cancelled => MongrelError::Cancelled,
315 LockError::InvalidRequest(message) => MongrelError::InvalidArgument(message),
316 }
317 }
318}
319
320#[derive(Debug, Clone, Copy)]
322struct Holder {
323 txn_id: u64,
324 mode: LockMode,
325}
326
327#[derive(Debug)]
330struct Waiter {
331 txn_id: u64,
332 deadline: Option<Instant>,
333 control: ExecutionControl,
334 mode: LockMode,
335}
336
337#[derive(Debug, Default)]
339struct LockState {
340 holders: Vec<Holder>,
341 queue: VecDeque<Waiter>,
342}
343
344#[derive(Debug)]
347enum Fate {
348 Deadlock { victim: u64, cycle: String },
349 DeadlineExceeded,
350 Cancelled,
351}
352
353impl Fate {
354 fn into_error(self) -> LockError {
355 match self {
356 Self::Deadlock { victim, cycle } => LockError::Deadlock { victim, cycle },
357 Self::DeadlineExceeded => LockError::DeadlineExceeded,
358 Self::Cancelled => LockError::Cancelled,
359 }
360 }
361}
362
363#[derive(Debug, Default)]
364struct Inner {
365 locks: HashMap<LockKey, LockState>,
366 txn_keys: HashMap<u64, HashSet<LockKey>>,
370 queued_waiters: usize,
373 fates: HashMap<u64, Fate>,
377 priorities: HashMap<u64, u64>,
380 #[cfg(test)]
381 deadlock_graph_builds: usize,
382 #[cfg(test)]
383 release_all_key_visits: usize,
384}
385
386#[derive(Debug)]
389pub struct LockManager {
390 inner: Mutex<Inner>,
391 wake: Condvar,
392}
393
394impl Default for LockManager {
395 fn default() -> Self {
396 Self::new()
397 }
398}
399
400impl LockManager {
401 pub fn new() -> Self {
402 Self {
403 inner: Mutex::new(Inner::default()),
404 wake: Condvar::new(),
405 }
406 }
407
408 pub fn acquire(&self, key: LockKey, request: LockRequest) -> Result<(), LockError> {
412 let LockRequest {
413 txn_id,
414 mode,
415 deadline,
416 control,
417 priority,
418 } = request;
419
420 let mut inner = self.inner.lock();
421 check_request_live(&control, deadline)?;
422 if let Some(priority) = priority {
423 inner.priorities.insert(txn_id, priority);
424 }
425 inner
426 .txn_keys
427 .entry(txn_id)
428 .or_default()
429 .insert(key.clone());
430
431 {
432 let state = inner.locks.entry(key.clone()).or_default();
433 match state.holders.iter().position(|h| h.txn_id == txn_id) {
434 Some(position) => {
435 let held = state.holders[position].mode;
436 if held == LockMode::Exclusive || mode == LockMode::Shared {
437 return Ok(());
441 }
442 if state.holders.len() == 1 && state.queue.is_empty() {
443 state.holders[position].mode = LockMode::Exclusive;
445 return Ok(());
446 }
447 }
451 None => {
452 let blocked = !state.queue.is_empty()
453 || state.holders.iter().any(|h| !mode.compatible(h.mode));
454 if !blocked {
455 state.holders.push(Holder { txn_id, mode });
456 return Ok(());
457 }
458 }
459 }
460 if state.queue.iter().any(|w| w.txn_id == txn_id) {
461 return Err(LockError::InvalidRequest(format!(
462 "transaction {txn_id} already has a pending wait on {key:?}"
463 )));
464 }
465 state.queue.push_back(Waiter {
466 txn_id,
467 deadline,
468 control: control.clone(),
469 mode,
470 });
471 }
472 inner.queued_waiters = inner.queued_waiters.saturating_add(1);
473
474 detect_deadlocks(&mut inner);
478 self.wake.notify_all();
479
480 let result = self.wait_for_grant(&mut inner, &key, txn_id, &control, deadline);
481 if result.is_err() {
482 process_key(&mut inner, &key);
484 detect_deadlocks(&mut inner);
485 untrack_transaction_key_if_unused(&mut inner, &key, txn_id);
486 self.wake.notify_all();
487 }
488 prune_if_idle(&mut inner, &key);
489 result
490 }
491
492 pub fn release(&self, txn_id: u64, key: &LockKey) {
495 let mut inner = self.inner.lock();
496 if inner
497 .locks
498 .get(key)
499 .is_some_and(|s| s.holders.iter().any(|h| h.txn_id == txn_id))
500 {
501 if let Some(state) = inner.locks.get_mut(key) {
502 state.holders.retain(|h| h.txn_id != txn_id);
503 }
504 process_key(&mut inner, key);
505 detect_deadlocks(&mut inner);
507 untrack_transaction_key_if_unused(&mut inner, key, txn_id);
508 prune_if_idle(&mut inner, key);
509 }
510 drop(inner);
511 self.wake.notify_all();
512 }
513
514 pub fn release_all(&self, txn_id: u64) {
517 let mut inner = self.inner.lock();
518 inner.fates.remove(&txn_id);
519 inner.priorities.remove(&txn_id);
520 let Some(keys) = inner.txn_keys.remove(&txn_id) else {
521 return;
522 };
523 #[cfg(test)]
524 {
525 inner.release_all_key_visits = inner.release_all_key_visits.saturating_add(keys.len());
526 }
527
528 let mut affected = Vec::with_capacity(keys.len());
529 for key in keys {
530 let removed_waiters = if let Some(state) = inner.locks.get_mut(&key) {
531 state.holders.retain(|holder| holder.txn_id != txn_id);
532 let before = state.queue.len();
533 state.queue.retain(|waiter| waiter.txn_id != txn_id);
534 before - state.queue.len()
535 } else {
536 0
537 };
538 inner.queued_waiters = inner.queued_waiters.saturating_sub(removed_waiters);
539 affected.push(key);
540 }
541 for key in &affected {
542 process_key(&mut inner, key);
543 }
544 detect_deadlocks(&mut inner);
545 for key in &affected {
546 prune_if_idle(&mut inner, key);
547 }
548 drop(inner);
549 self.wake.notify_all();
550 }
551
552 pub fn holds(&self, txn_id: u64, key: &LockKey) -> bool {
554 self.inner
555 .lock()
556 .locks
557 .get(key)
558 .is_some_and(|state| state.holders.iter().any(|h| h.txn_id == txn_id))
559 }
560
561 #[cfg(test)]
564 fn queued_waiters(&self, key: &LockKey) -> usize {
565 self.inner
566 .lock()
567 .locks
568 .get(key)
569 .map_or(0, |state| state.queue.len())
570 }
571
572 #[cfg(test)]
573 fn total_queued_waiters(&self) -> usize {
574 self.inner.lock().queued_waiters
575 }
576
577 #[cfg(test)]
578 fn tracked_key_count(&self, txn_id: u64) -> usize {
579 self.inner
580 .lock()
581 .txn_keys
582 .get(&txn_id)
583 .map_or(0, HashSet::len)
584 }
585
586 #[cfg(test)]
587 fn deadlock_graph_builds(&self) -> usize {
588 self.inner.lock().deadlock_graph_builds
589 }
590
591 #[cfg(test)]
592 fn release_all_key_visits(&self) -> usize {
593 self.inner.lock().release_all_key_visits
594 }
595
596 fn wait_for_grant(
600 &self,
601 inner: &mut MutexGuard<'_, Inner>,
602 key: &LockKey,
603 txn_id: u64,
604 control: &ExecutionControl,
605 deadline: Option<Instant>,
606 ) -> Result<(), LockError> {
607 loop {
608 if let Some(fate) = inner.fates.remove(&txn_id) {
609 return Err(fate.into_error());
610 }
611 let (queued, held) = match inner.locks.get(key) {
612 Some(state) => (
613 state.queue.iter().any(|w| w.txn_id == txn_id),
614 state.holders.iter().any(|h| h.txn_id == txn_id),
615 ),
616 None => (false, false),
617 };
618 if !queued {
619 if held {
620 return Ok(());
621 }
622 return Err(LockError::Cancelled);
626 }
627 if let Err(error) = check_request_live(control, deadline) {
628 remove_waiter(inner, key, txn_id);
629 return Err(error);
630 }
631 let wake_at = deadline
634 .unwrap_or_else(|| Instant::now() + CANCELLATION_POLL_INTERVAL)
635 .min(Instant::now() + CANCELLATION_POLL_INTERVAL);
636 let _ = self.wake.wait_until(inner, wake_at);
637 }
638 }
639}
640
641fn check_request_live(
646 control: &ExecutionControl,
647 deadline: Option<Instant>,
648) -> Result<(), LockError> {
649 match control.checkpoint() {
650 Ok(()) => {}
651 Err(MongrelError::DeadlineExceeded) => return Err(LockError::DeadlineExceeded),
652 Err(_) => return Err(LockError::Cancelled),
653 }
654 if deadline.is_some_and(|deadline| Instant::now() >= deadline) {
655 return Err(LockError::DeadlineExceeded);
656 }
657 Ok(())
658}
659
660fn remove_waiter(inner: &mut Inner, key: &LockKey, txn_id: u64) {
661 let removed = if let Some(state) = inner.locks.get_mut(key) {
662 let Some(position) = state
663 .queue
664 .iter()
665 .position(|waiter| waiter.txn_id == txn_id)
666 else {
667 return;
668 };
669 state.queue.remove(position);
670 true
671 } else {
672 false
673 };
674 if removed {
675 inner.queued_waiters = inner.queued_waiters.saturating_sub(1);
676 untrack_transaction_key_if_unused(inner, key, txn_id);
677 }
678}
679
680fn transaction_touches_key(inner: &Inner, key: &LockKey, txn_id: u64) -> bool {
681 inner.locks.get(key).is_some_and(|state| {
682 state.holders.iter().any(|holder| holder.txn_id == txn_id)
683 || state.queue.iter().any(|waiter| waiter.txn_id == txn_id)
684 })
685}
686
687fn untrack_transaction_key_if_unused(inner: &mut Inner, key: &LockKey, txn_id: u64) {
688 if transaction_touches_key(inner, key, txn_id) {
689 return;
690 }
691 let remove_transaction = inner.txn_keys.get_mut(&txn_id).is_some_and(|keys| {
692 keys.remove(key);
693 keys.is_empty()
694 });
695 if remove_transaction {
696 inner.txn_keys.remove(&txn_id);
697 }
698}
699
700fn prune_if_idle(inner: &mut Inner, key: &LockKey) {
701 if inner
702 .locks
703 .get(key)
704 .is_some_and(|state| state.holders.is_empty() && state.queue.is_empty())
705 {
706 inner.locks.remove(key);
707 }
708}
709
710fn process_key(inner: &mut Inner, key: &LockKey) {
715 let now = Instant::now();
716 let dead: Vec<u64> = match inner.locks.get(key) {
717 Some(state) => state
718 .queue
719 .iter()
720 .filter(|w| w.control.is_cancelled() || w.deadline.is_some_and(|d| now >= d))
721 .map(|w| w.txn_id)
722 .collect(),
723 None => return,
724 };
725 let mut removed = Vec::new();
726 for txn_id in dead {
727 if let Some(state) = inner.locks.get_mut(key) {
728 if let Some(position) = state.queue.iter().position(|w| w.txn_id == txn_id) {
729 let waiter = state.queue.remove(position).expect("position found above");
730 inner.queued_waiters = inner.queued_waiters.saturating_sub(1);
731 let fate = match waiter.control.checkpoint() {
732 Err(MongrelError::DeadlineExceeded) => Fate::DeadlineExceeded,
733 Err(_) => Fate::Cancelled,
734 Ok(()) => Fate::DeadlineExceeded,
736 };
737 inner.fates.entry(txn_id).or_insert(fate);
738 removed.push(txn_id);
739 }
740 }
741 }
742
743 while let Some(state) = inner.locks.get_mut(key) {
744 let Some(front) = state.queue.front() else {
745 break;
746 };
747 let grantable = state
750 .holders
751 .iter()
752 .all(|h| h.txn_id == front.txn_id || front.mode.compatible(h.mode));
753 if !grantable {
754 break;
755 }
756 let waiter = state.queue.pop_front().expect("front checked above");
757 inner.queued_waiters = inner.queued_waiters.saturating_sub(1);
758 match state.holders.iter_mut().find(|h| h.txn_id == waiter.txn_id) {
759 Some(holder) => holder.mode = LockMode::Exclusive,
760 None => state.holders.push(Holder {
761 txn_id: waiter.txn_id,
762 mode: waiter.mode,
763 }),
764 }
765 }
766 for txn_id in removed {
767 untrack_transaction_key_if_unused(inner, key, txn_id);
768 }
769}
770
771fn build_wait_for_graph(inner: &Inner) -> BTreeMap<u64, Vec<u64>> {
778 let mut graph: BTreeMap<u64, Vec<u64>> = BTreeMap::new();
779 for state in inner.locks.values() {
780 for (index, waiter) in state.queue.iter().enumerate() {
781 let edges = graph.entry(waiter.txn_id).or_default();
782 for holder in &state.holders {
783 if holder.txn_id != waiter.txn_id && !waiter.mode.compatible(holder.mode) {
784 edges.push(holder.txn_id);
785 }
786 }
787 for ahead in state.queue.iter().take(index) {
788 if ahead.txn_id != waiter.txn_id && !waiter.mode.compatible(ahead.mode) {
789 edges.push(ahead.txn_id);
790 }
791 }
792 }
793 }
794 for edges in graph.values_mut() {
795 edges.sort_unstable();
796 edges.dedup();
797 }
798 graph
799}
800
801fn find_cycle(graph: &BTreeMap<u64, Vec<u64>>) -> Option<Vec<u64>> {
806 #[derive(Clone, Copy, PartialEq, Eq)]
807 enum Mark {
808 Visiting,
809 Done,
810 }
811
812 let mut marks: HashMap<u64, Mark> = HashMap::new();
813 let mut stack: Vec<u64> = Vec::new();
814 for &start in graph.keys() {
815 if marks.contains_key(&start) {
816 continue;
817 }
818 marks.insert(start, Mark::Visiting);
819 stack.push(start);
820 let mut frames: Vec<(u64, usize)> = vec![(start, 0)];
821 while let Some((node, index)) = frames.last().copied() {
822 let edges = graph.get(&node).map(Vec::as_slice).unwrap_or(&[]);
823 if index < edges.len() {
824 frames.last_mut().expect("last copied above").1 += 1;
825 let next = edges[index];
826 match marks.get(&next) {
827 None => {
828 marks.insert(next, Mark::Visiting);
829 stack.push(next);
830 frames.push((next, 0));
831 }
832 Some(Mark::Visiting) => {
833 let position = stack
834 .iter()
835 .position(|member| *member == next)
836 .expect("a visiting node is on the stack");
837 return Some(stack[position..].to_vec());
838 }
839 Some(Mark::Done) => {}
840 }
841 } else {
842 marks.insert(node, Mark::Done);
843 stack.pop();
844 frames.pop();
845 }
846 }
847 }
848 None
849}
850
851fn choose_victim(cycle: &[u64], priorities: &HashMap<u64, u64>) -> u64 {
856 *cycle
857 .iter()
858 .min_by(|a, b| {
859 let priority_a = priorities.get(a).copied().unwrap_or(DEFAULT_PRIORITY);
860 let priority_b = priorities.get(b).copied().unwrap_or(DEFAULT_PRIORITY);
861 priority_a.cmp(&priority_b).then_with(|| b.cmp(a))
862 })
863 .expect("a deadlock cycle is never empty")
864}
865
866fn detect_deadlocks(inner: &mut Inner) {
870 while inner.queued_waiters != 0 {
871 #[cfg(test)]
872 {
873 inner.deadlock_graph_builds = inner.deadlock_graph_builds.saturating_add(1);
874 }
875 let graph = build_wait_for_graph(inner);
876 let Some(cycle) = find_cycle(&graph) else {
877 return;
878 };
879 let victim = choose_victim(&cycle, &inner.priorities);
880 let victim_key = inner.locks.iter().find_map(|(key, state)| {
883 state
884 .queue
885 .iter()
886 .any(|w| w.txn_id == victim)
887 .then(|| key.clone())
888 });
889 let Some(victim_key) = victim_key else {
890 return;
893 };
894 let removed = inner.locks.get_mut(&victim_key).is_some_and(|state| {
895 let Some(position) = state
896 .queue
897 .iter()
898 .position(|waiter| waiter.txn_id == victim)
899 else {
900 return false;
901 };
902 state.queue.remove(position);
903 true
904 });
905 if removed {
906 inner.queued_waiters = inner.queued_waiters.saturating_sub(1);
907 untrack_transaction_key_if_unused(inner, &victim_key, victim);
908 }
909 let cycle = cycle
910 .iter()
911 .map(u64::to_string)
912 .collect::<Vec<_>>()
913 .join(" → ");
914 inner.fates.insert(victim, Fate::Deadlock { victim, cycle });
915 }
916}
917
918#[cfg(test)]
919mod tests {
920 use super::*;
921 use crate::CancellationReason;
922 use std::sync::mpsc;
923 use std::sync::Arc;
924 use std::thread;
925
926 use LockMode::{Exclusive, Shared};
927
928 fn manager() -> Arc<LockManager> {
929 Arc::new(LockManager::new())
930 }
931
932 fn request(txn_id: u64, mode: LockMode) -> LockRequest {
933 LockRequest::new(txn_id, mode, ExecutionControl::new(None))
934 }
935
936 fn timed_request(txn_id: u64, mode: LockMode, timeout: Duration) -> LockRequest {
937 request(txn_id, mode).with_timeout(timeout)
938 }
939
940 fn row_key(n: u64) -> LockKey {
941 LockKey::row(1, RowId(n))
942 }
943
944 fn spawn_acquire(
946 manager: &Arc<LockManager>,
947 key: LockKey,
948 request: LockRequest,
949 ) -> mpsc::Receiver<Result<(), LockError>> {
950 let (sender, receiver) = mpsc::channel();
951 let manager = Arc::clone(manager);
952 thread::spawn(move || {
953 let result = manager.acquire(key, request);
954 sender.send(result).expect("receiver alive");
955 });
956 receiver
957 }
958
959 fn wait_for_queue(manager: &LockManager, key: &LockKey, n: usize) {
963 for _ in 0..200 {
964 if manager.queued_waiters(key) >= n {
965 return;
966 }
967 thread::sleep(Duration::from_millis(10));
968 }
969 panic!("queue did not reach {n} waiters on {key:?}");
970 }
971
972 #[test]
973 fn acquire_and_release_round_trip() {
974 let manager = LockManager::new();
975 let key = row_key(1);
976 manager.acquire(key.clone(), request(1, Exclusive)).unwrap();
977 assert!(manager.holds(1, &key));
978 manager.release(1, &key);
979 assert!(!manager.holds(1, &key));
980
981 manager.acquire(key.clone(), request(2, Shared)).unwrap();
982 assert!(manager.holds(2, &key));
983 manager.release_all(2);
984 assert!(!manager.holds(2, &key));
985 }
986
987 #[test]
988 fn mode_compatibility_matrix() {
989 assert!(Shared.compatible(Shared));
990 assert!(!Shared.compatible(Exclusive));
991 assert!(!Exclusive.compatible(Shared));
992 assert!(!Exclusive.compatible(Exclusive));
993 }
994
995 #[test]
996 fn shared_locks_coexist() {
997 let manager = LockManager::new();
998 let key = row_key(7);
999 manager.acquire(key.clone(), request(1, Shared)).unwrap();
1000 manager.acquire(key.clone(), request(2, Shared)).unwrap();
1001 assert!(manager.holds(1, &key));
1002 assert!(manager.holds(2, &key));
1003 }
1004
1005 #[test]
1006 fn exclusive_blocks_shared_and_exclusive_until_release() {
1007 let manager = LockManager::new();
1008 let key = row_key(8);
1009 manager.acquire(key.clone(), request(1, Exclusive)).unwrap();
1010
1011 let shared = manager.acquire(
1012 key.clone(),
1013 timed_request(2, Shared, Duration::from_millis(40)),
1014 );
1015 assert_eq!(shared, Err(LockError::DeadlineExceeded));
1016 let exclusive = manager.acquire(
1017 key.clone(),
1018 timed_request(3, Exclusive, Duration::from_millis(40)),
1019 );
1020 assert_eq!(exclusive, Err(LockError::DeadlineExceeded));
1021
1022 manager.release(1, &key);
1023 manager.acquire(key.clone(), request(2, Shared)).unwrap();
1024 let still_blocked = manager.acquire(
1025 key.clone(),
1026 timed_request(3, Exclusive, Duration::from_millis(40)),
1027 );
1028 assert_eq!(still_blocked, Err(LockError::DeadlineExceeded));
1029 }
1030
1031 #[test]
1032 fn wait_respects_the_lock_deadline() {
1033 let manager = LockManager::new();
1034 let key = row_key(9);
1035 manager.acquire(key.clone(), request(1, Exclusive)).unwrap();
1036
1037 let started = Instant::now();
1038 let result = manager.acquire(key, timed_request(2, Exclusive, Duration::from_millis(80)));
1039 let elapsed = started.elapsed();
1040 assert_eq!(result, Err(LockError::DeadlineExceeded));
1041 assert_eq!(
1042 result.unwrap_err().category(),
1043 ErrorCategory::DeadlineExceeded
1044 );
1045 assert!(
1046 elapsed >= Duration::from_millis(70),
1047 "returned early: {elapsed:?}"
1048 );
1049 assert!(
1050 elapsed < Duration::from_secs(5),
1051 "returned late: {elapsed:?}"
1052 );
1053 }
1054
1055 #[test]
1056 fn expired_deadline_fails_fast_even_when_unlocked() {
1057 let manager = LockManager::new();
1058 let result = manager.acquire(row_key(10), timed_request(1, Exclusive, Duration::ZERO));
1059 assert_eq!(result, Err(LockError::DeadlineExceeded));
1060 }
1061
1062 #[test]
1063 fn cancellation_wakes_the_waiter_and_clears_its_queue_entry() {
1064 let manager = manager();
1065 let key = row_key(11);
1066 manager.acquire(key.clone(), request(1, Exclusive)).unwrap();
1067
1068 let control = ExecutionControl::new(None);
1069 let cancelled_rx = spawn_acquire(
1070 &manager,
1071 key.clone(),
1072 LockRequest::new(2, Exclusive, control.clone()),
1073 );
1074 wait_for_queue(&manager, &key, 1);
1075 control.cancel(CancellationReason::ClientRequest);
1076 let result = cancelled_rx.recv_timeout(Duration::from_secs(5)).unwrap();
1077 assert_eq!(result, Err(LockError::Cancelled));
1078 assert!(!manager.holds(2, &key));
1079
1080 let next_rx = spawn_acquire(&manager, key.clone(), request(3, Exclusive));
1083 wait_for_queue(&manager, &key, 1);
1084 manager.release(1, &key);
1085 assert_eq!(
1086 next_rx.recv_timeout(Duration::from_secs(5)).unwrap(),
1087 Ok(())
1088 );
1089 }
1090
1091 #[test]
1092 fn cancelled_control_fails_fast_even_when_unlocked() {
1093 let manager = LockManager::new();
1094 let control = ExecutionControl::new(None);
1095 control.cancel(CancellationReason::ClientRequest);
1096 let result = manager.acquire(row_key(12), LockRequest::new(1, Exclusive, control));
1097 assert_eq!(result, Err(LockError::Cancelled));
1098 }
1099
1100 #[test]
1101 fn control_deadline_maps_to_deadline_exceeded() {
1102 let manager = LockManager::new();
1103 let key = row_key(13);
1104 manager.acquire(key.clone(), request(1, Exclusive)).unwrap();
1105 let control = ExecutionControl::with_timeout(Duration::from_millis(50));
1106 let result = manager.acquire(key, LockRequest::new(2, Exclusive, control));
1107 assert_eq!(result, Err(LockError::DeadlineExceeded));
1108 }
1109
1110 #[test]
1111 fn fifo_grant_order_blocks_barging_readers() {
1112 let manager = manager();
1113 let key = row_key(14);
1114 manager.acquire(key.clone(), request(1, Shared)).unwrap();
1115
1116 let writer_rx = spawn_acquire(&manager, key.clone(), request(2, Exclusive));
1118 wait_for_queue(&manager, &key, 1);
1119 let reader_rx = spawn_acquire(&manager, key.clone(), request(3, Shared));
1123 wait_for_queue(&manager, &key, 2);
1124
1125 manager.release(1, &key);
1126 assert_eq!(
1127 writer_rx.recv_timeout(Duration::from_secs(5)).unwrap(),
1128 Ok(())
1129 );
1130 assert!(
1131 reader_rx.recv_timeout(Duration::from_millis(150)).is_err(),
1132 "reader barged ahead of the queued writer"
1133 );
1134 manager.release(2, &key);
1135 assert_eq!(
1136 reader_rx.recv_timeout(Duration::from_secs(5)).unwrap(),
1137 Ok(())
1138 );
1139 }
1140
1141 #[test]
1142 fn release_all_grants_blocked_waiters() {
1143 let manager = manager();
1144 let key = row_key(24);
1145 manager.acquire(key.clone(), request(1, Exclusive)).unwrap();
1146 let waiter_rx = spawn_acquire(&manager, key.clone(), request(2, Exclusive));
1147 wait_for_queue(&manager, &key, 1);
1148 manager.release_all(1);
1149 assert_eq!(
1150 waiter_rx.recv_timeout(Duration::from_secs(5)).unwrap(),
1151 Ok(())
1152 );
1153 assert!(manager.holds(2, &key));
1154 }
1155
1156 #[test]
1157 fn ab_ba_deadlock_kills_requester_when_it_is_youngest() {
1158 let manager = manager();
1159 let key_a = row_key(20);
1160 let key_b = row_key(21);
1161 manager
1162 .acquire(key_a.clone(), request(1, Exclusive))
1163 .unwrap();
1164 manager
1165 .acquire(key_b.clone(), request(2, Exclusive))
1166 .unwrap();
1167
1168 let t1_rx = spawn_acquire(&manager, key_b.clone(), request(1, Exclusive));
1170 wait_for_queue(&manager, &key_b, 1);
1171 let error = manager
1174 .acquire(key_a.clone(), request(2, Exclusive))
1175 .unwrap_err();
1176 match &error {
1177 LockError::Deadlock { victim, cycle } => {
1178 assert_eq!(*victim, 2, "youngest transaction must be the victim");
1179 assert!(
1180 cycle.contains('1') && cycle.contains('2'),
1181 "cycle names both: {cycle}"
1182 );
1183 }
1184 other => panic!("expected t2 to be the deadlock victim, got {other:?}"),
1185 }
1186 assert_eq!(error.category(), ErrorCategory::Deadlock);
1187
1188 manager.release_all(2);
1190 assert_eq!(t1_rx.recv_timeout(Duration::from_secs(5)).unwrap(), Ok(()));
1191 assert!(manager.holds(1, &key_b));
1192 }
1193
1194 #[test]
1195 fn ab_ba_deadlock_kills_youngest_even_when_it_is_not_the_requester() {
1196 let manager = manager();
1199 let key_a = row_key(22);
1200 let key_b = row_key(23);
1201 manager
1202 .acquire(key_a.clone(), request(100, Exclusive))
1203 .unwrap();
1204 manager
1205 .acquire(key_b.clone(), request(200, Exclusive))
1206 .unwrap();
1207
1208 let t200 = {
1210 let manager = Arc::clone(&manager);
1211 let key_a = key_a.clone();
1212 thread::spawn(move || {
1213 let result = manager.acquire(key_a, request(200, Exclusive));
1214 if result.is_err() {
1215 manager.release_all(200);
1216 }
1217 result
1218 })
1219 };
1220 wait_for_queue(&manager, &key_a, 1);
1221
1222 manager
1225 .acquire(key_b.clone(), request(100, Exclusive))
1226 .unwrap();
1227 assert!(manager.holds(100, &key_b));
1228
1229 let victim = t200.join().unwrap();
1230 assert!(
1231 matches!(victim, Err(LockError::Deadlock { victim: 200, .. })),
1232 "youngest transaction must be the victim: {victim:?}"
1233 );
1234 }
1235
1236 #[test]
1237 fn three_transaction_cycle_kills_youngest() {
1238 let manager = manager();
1239 let key_a = row_key(30);
1240 let key_b = row_key(31);
1241 let key_c = row_key(32);
1242 manager
1243 .acquire(key_a.clone(), request(1, Exclusive))
1244 .unwrap();
1245 manager
1246 .acquire(key_b.clone(), request(2, Exclusive))
1247 .unwrap();
1248 manager
1249 .acquire(key_c.clone(), request(3, Exclusive))
1250 .unwrap();
1251
1252 let t1_rx = spawn_acquire(&manager, key_b.clone(), request(1, Exclusive));
1254 wait_for_queue(&manager, &key_b, 1);
1255 let t2_rx = spawn_acquire(&manager, key_c.clone(), request(2, Exclusive));
1256 wait_for_queue(&manager, &key_c, 1);
1257 let error = manager
1259 .acquire(key_a.clone(), request(3, Exclusive))
1260 .unwrap_err();
1261 assert!(
1262 matches!(error, LockError::Deadlock { victim: 3, .. }),
1263 "youngest of the 3-cycle must die: {error:?}"
1264 );
1265
1266 manager.release_all(3);
1268 assert_eq!(t2_rx.recv_timeout(Duration::from_secs(5)).unwrap(), Ok(()));
1269 manager.release_all(2);
1270 assert_eq!(t1_rx.recv_timeout(Duration::from_secs(5)).unwrap(), Ok(()));
1271 }
1272
1273 #[test]
1274 fn lowest_priority_dies_before_age_is_considered() {
1275 let manager = manager();
1276 let key_a = row_key(40);
1277 let key_b = row_key(41);
1278 let key_c = row_key(42);
1279 manager
1280 .acquire(key_a.clone(), request(1, Exclusive))
1281 .unwrap();
1282 manager
1283 .acquire(key_b.clone(), request(2, Exclusive))
1284 .unwrap();
1285 manager
1286 .acquire(key_c.clone(), request(3, Exclusive))
1287 .unwrap();
1288
1289 let t1 = {
1292 let manager = Arc::clone(&manager);
1293 let key_b = key_b.clone();
1294 thread::spawn(move || {
1295 let result = manager.acquire(key_b, request(1, Exclusive).with_priority(10));
1296 if result.is_err() {
1297 manager.release_all(1);
1298 }
1299 result
1300 })
1301 };
1302 wait_for_queue(&manager, &key_b, 1);
1303 let t2_rx = spawn_acquire(
1304 &manager,
1305 key_c.clone(),
1306 request(2, Exclusive).with_priority(20),
1307 );
1308 wait_for_queue(&manager, &key_c, 1);
1309
1310 manager
1312 .acquire(key_a.clone(), request(3, Exclusive).with_priority(30))
1313 .unwrap();
1314 assert!(manager.holds(3, &key_a));
1315
1316 let victim = t1.join().unwrap();
1317 assert!(
1318 matches!(victim, Err(LockError::Deadlock { victim: 1, .. })),
1319 "lowest priority must die: {victim:?}"
1320 );
1321
1322 manager.release_all(3);
1323 assert_eq!(t2_rx.recv_timeout(Duration::from_secs(5)).unwrap(), Ok(()));
1324 }
1325
1326 #[test]
1327 fn equal_priorities_fall_back_to_youngest() {
1328 let manager = manager();
1329 let key_a = row_key(50);
1330 let key_b = row_key(51);
1331 manager
1332 .acquire(key_a.clone(), request(1, Exclusive).with_priority(5))
1333 .unwrap();
1334 manager
1335 .acquire(key_b.clone(), request(2, Exclusive).with_priority(5))
1336 .unwrap();
1337
1338 let t1_rx = spawn_acquire(
1339 &manager,
1340 key_b.clone(),
1341 request(1, Exclusive).with_priority(5),
1342 );
1343 wait_for_queue(&manager, &key_b, 1);
1344 let error = manager
1345 .acquire(key_a.clone(), request(2, Exclusive).with_priority(5))
1346 .unwrap_err();
1347 assert!(
1348 matches!(error, LockError::Deadlock { victim: 2, .. }),
1349 "equal priorities must fall back to youngest: {error:?}"
1350 );
1351 manager.release_all(2);
1352 assert_eq!(t1_rx.recv_timeout(Duration::from_secs(5)).unwrap(), Ok(()));
1353 }
1354
1355 #[test]
1356 fn same_transaction_requests_never_self_deadlock() {
1357 let manager = LockManager::new();
1358 let key = row_key(60);
1359 manager.acquire(key.clone(), request(1, Exclusive)).unwrap();
1362 manager.acquire(key.clone(), request(1, Exclusive)).unwrap();
1363 manager.acquire(key.clone(), request(1, Shared)).unwrap();
1364 assert!(manager.holds(1, &key));
1365
1366 let other = row_key(61);
1369 manager.acquire(other.clone(), request(2, Shared)).unwrap();
1370 manager
1371 .acquire(other.clone(), request(2, Exclusive))
1372 .unwrap();
1373 let blocked = manager.acquire(
1374 other,
1375 timed_request(3, Exclusive, Duration::from_millis(40)),
1376 );
1377 assert_eq!(blocked, Err(LockError::DeadlineExceeded));
1378 }
1379
1380 #[test]
1381 fn detector_kills_a_self_loop_if_one_is_ever_constructed() {
1382 let graph = BTreeMap::from([(7_u64, vec![7_u64])]);
1386 let cycle = find_cycle(&graph).expect("a self loop is a cycle");
1387 assert_eq!(cycle, vec![7]);
1388 assert_eq!(choose_victim(&cycle, &HashMap::new()), 7);
1389 }
1390
1391 #[test]
1392 fn shared_to_exclusive_upgrade_deadlock_kills_youngest() {
1393 let manager = manager();
1394 let key = row_key(70);
1395 manager.acquire(key.clone(), request(1, Shared)).unwrap();
1396 manager.acquire(key.clone(), request(2, Shared)).unwrap();
1397
1398 let t1_rx = spawn_acquire(&manager, key.clone(), request(1, Exclusive));
1400 wait_for_queue(&manager, &key, 1);
1401 let error = manager
1404 .acquire(key.clone(), request(2, Exclusive))
1405 .unwrap_err();
1406 assert!(
1407 matches!(error, LockError::Deadlock { victim: 2, .. }),
1408 "youngest upgrader must die: {error:?}"
1409 );
1410
1411 manager.release_all(2);
1413 assert_eq!(t1_rx.recv_timeout(Duration::from_secs(5)).unwrap(), Ok(()));
1414 let blocked = manager.acquire(
1415 key.clone(),
1416 timed_request(3, Shared, Duration::from_millis(40)),
1417 );
1418 assert_eq!(
1419 blocked,
1420 Err(LockError::DeadlineExceeded),
1421 "t1 must now hold Exclusive"
1422 );
1423 }
1424
1425 #[test]
1426 fn sequence_barrier_is_exclusive_across_transactions() {
1427 let manager = LockManager::new();
1428 let sequence = LockKey::sequence_barrier("order_id");
1429 manager
1430 .acquire(sequence.clone(), request(1, Exclusive))
1431 .unwrap();
1432
1433 let blocked = manager.acquire(
1435 sequence.clone(),
1436 timed_request(2, Exclusive, Duration::from_millis(40)),
1437 );
1438 assert_eq!(blocked, Err(LockError::DeadlineExceeded));
1439 manager
1441 .acquire(
1442 LockKey::sequence_barrier("shipment_id"),
1443 request(2, Exclusive),
1444 )
1445 .unwrap();
1446
1447 manager.release_all(1);
1448 manager.acquire(sequence, request(2, Exclusive)).unwrap();
1449 }
1450
1451 #[test]
1452 fn ddl_barrier_excludes_dml_and_concurrent_ddl() {
1453 let manager = LockManager::new();
1454 let barrier = LockKey::schema_barrier();
1455 manager
1457 .acquire(barrier.clone(), request(1, Exclusive))
1458 .unwrap();
1459 let dml = manager.acquire(
1461 barrier.clone(),
1462 timed_request(2, Shared, Duration::from_millis(40)),
1463 );
1464 assert_eq!(dml, Err(LockError::DeadlineExceeded));
1465 let ddl = manager.acquire(
1467 barrier.clone(),
1468 timed_request(3, Exclusive, Duration::from_millis(40)),
1469 );
1470 assert_eq!(ddl, Err(LockError::DeadlineExceeded));
1471
1472 manager.release_all(1);
1473 manager
1475 .acquire(barrier.clone(), request(2, Shared))
1476 .unwrap();
1477 manager.acquire(barrier, request(3, Shared)).unwrap();
1478 }
1479
1480 #[test]
1481 fn key_families_do_not_false_share() {
1482 let manager = LockManager::new();
1483 manager
1484 .acquire(LockKey::row(1, RowId(5)), request(1, Exclusive))
1485 .unwrap();
1486 manager
1488 .acquire(
1489 LockKey::key(1, 5_u64.to_be_bytes().to_vec()),
1490 request(2, Exclusive),
1491 )
1492 .unwrap();
1493 manager
1494 .acquire(
1495 LockKey::range(
1496 1,
1497 Bound::Included(1_u64.to_be_bytes().to_vec()),
1498 Bound::Excluded(6_u64.to_be_bytes().to_vec()),
1499 ),
1500 request(3, Exclusive),
1501 )
1502 .unwrap();
1503 }
1504
1505 #[test]
1506 fn lock_errors_bridge_to_mongrel_error_and_the_taxonomy() {
1507 let deadlock = LockError::Deadlock {
1508 victim: 9,
1509 cycle: "9 → 4 → 9".to_string(),
1510 };
1511 assert_eq!(deadlock.category(), ErrorCategory::Deadlock);
1512 let mapped = MongrelError::from(deadlock);
1513 assert!(
1514 matches!(
1515 &mapped,
1516 MongrelError::Deadlock { victim: 9, cycle } if cycle == "9 → 4 → 9"
1517 ),
1518 "deadlock maps to the dedicated variant, victim and cycle preserved: {mapped:?}"
1519 );
1520 assert_eq!(mapped.category(), ErrorCategory::Deadlock);
1521
1522 assert!(matches!(
1523 MongrelError::from(LockError::DeadlineExceeded),
1524 MongrelError::DeadlineExceeded
1525 ));
1526 assert!(matches!(
1527 MongrelError::from(LockError::Cancelled),
1528 MongrelError::Cancelled
1529 ));
1530 assert_eq!(LockError::Cancelled.category(), ErrorCategory::Cancelled);
1531 assert!(matches!(
1532 MongrelError::from(LockError::InvalidRequest("x".to_string())),
1533 MongrelError::InvalidArgument(_)
1534 ));
1535 }
1536
1537 #[test]
1538 fn release_all_visits_only_the_transactions_tracked_keys() {
1539 let manager = LockManager::new();
1540 for txn_id in 1..=128 {
1541 manager
1542 .acquire(row_key(txn_id), request(txn_id, Exclusive))
1543 .unwrap();
1544 }
1545 assert_eq!(manager.release_all_key_visits(), 0);
1546
1547 manager.release_all(999);
1548 assert_eq!(manager.release_all_key_visits(), 0);
1549
1550 manager.release_all(64);
1551 assert_eq!(manager.release_all_key_visits(), 1);
1552 assert!(!manager.holds(64, &row_key(64)));
1553 assert!(manager.holds(63, &row_key(63)));
1554 assert!(manager.holds(65, &row_key(65)));
1555 }
1556
1557 #[test]
1558 fn uncontended_release_skips_deadlock_graph_construction() {
1559 let manager = LockManager::new();
1560 for txn_id in 1..=128 {
1561 manager
1562 .acquire(row_key(txn_id), request(txn_id, Exclusive))
1563 .unwrap();
1564 manager.release_all(txn_id);
1565 }
1566 assert_eq!(manager.deadlock_graph_builds(), 0);
1567 assert_eq!(manager.total_queued_waiters(), 0);
1568 }
1569
1570 #[test]
1571 fn waiter_accounting_covers_grant_cancel_and_deadlock() {
1572 let manager = manager();
1573
1574 let grant_key = row_key(200);
1575 manager
1576 .acquire(grant_key.clone(), request(1, Exclusive))
1577 .unwrap();
1578 let granted = spawn_acquire(&manager, grant_key.clone(), request(2, Exclusive));
1579 wait_for_queue(&manager, &grant_key, 1);
1580 assert_eq!(manager.total_queued_waiters(), 1);
1581 assert_eq!(manager.tracked_key_count(2), 1);
1582 manager.release_all(1);
1583 assert_eq!(
1584 granted.recv_timeout(Duration::from_secs(5)).unwrap(),
1585 Ok(())
1586 );
1587 assert_eq!(manager.total_queued_waiters(), 0);
1588 manager.release_all(2);
1589
1590 let cancel_key = row_key(201);
1591 manager
1592 .acquire(cancel_key.clone(), request(3, Exclusive))
1593 .unwrap();
1594 let control = ExecutionControl::new(None);
1595 let cancelled = spawn_acquire(
1596 &manager,
1597 cancel_key.clone(),
1598 LockRequest::new(4, Exclusive, control.clone()),
1599 );
1600 wait_for_queue(&manager, &cancel_key, 1);
1601 assert_eq!(manager.total_queued_waiters(), 1);
1602 control.cancel(CancellationReason::ClientRequest);
1603 assert_eq!(
1604 cancelled.recv_timeout(Duration::from_secs(5)).unwrap(),
1605 Err(LockError::Cancelled)
1606 );
1607 assert_eq!(manager.total_queued_waiters(), 0);
1608 assert_eq!(manager.tracked_key_count(4), 0);
1609 manager.release_all(3);
1610
1611 let key_a = row_key(202);
1612 let key_b = row_key(203);
1613 manager
1614 .acquire(key_a.clone(), request(10, Exclusive))
1615 .unwrap();
1616 manager
1617 .acquire(key_b.clone(), request(11, Exclusive))
1618 .unwrap();
1619 let survivor = spawn_acquire(&manager, key_b.clone(), request(10, Exclusive));
1620 wait_for_queue(&manager, &key_b, 1);
1621 assert_eq!(manager.total_queued_waiters(), 1);
1622 assert!(matches!(
1623 manager.acquire(key_a, request(11, Exclusive)),
1624 Err(LockError::Deadlock { victim: 11, .. })
1625 ));
1626 assert_eq!(manager.total_queued_waiters(), 1);
1627 manager.release_all(11);
1628 assert_eq!(
1629 survivor.recv_timeout(Duration::from_secs(5)).unwrap(),
1630 Ok(())
1631 );
1632 assert_eq!(manager.total_queued_waiters(), 0);
1633 manager.release_all(10);
1634 }
1635
1636 #[test]
1637 fn wait_for_graph_cycle_search_is_deterministic() {
1638 let graph = BTreeMap::from([(1, vec![2]), (2, vec![3])]);
1640 assert_eq!(find_cycle(&graph), None);
1641
1642 let graph = BTreeMap::from([(1, vec![2]), (2, vec![3]), (3, vec![2])]);
1644 let cycle = find_cycle(&graph).expect("cycle");
1645 assert_eq!(cycle, vec![2, 3]);
1646
1647 assert_eq!(choose_victim(&cycle, &HashMap::new()), 3);
1650 assert_eq!(choose_victim(&cycle, &HashMap::from([(2, 5), (3, 1)])), 3);
1651 assert_eq!(choose_victim(&cycle, &HashMap::from([(2, 1), (3, 5)])), 2);
1652 }
1653}