mirror of
https://github.com/modrinth/code.git
synced 2026-08-27 10:04:52 +00:00
feat(labrinth): Redis Cluster (#6771)
* chore(labrinth): bump to redis 1.4.1 * feat(labrinth): redis cluster * chore: cleanup * feat(labrinth): cache locking * fix(labrinth): clippy * chore(labrinth): cleanup env, remove postcard support * chore(ci): fix test env for labrinth * chore(labrinth): bump all key versions * chore(labrinth): improve redis key identities handling * chore(labrinth): simplify deadline handling * chore(labrinth): remove unused lease tracking * chore(labrinth): remove distributed cache locking for now * chore(labrinth): improve redis backend init error * feat(labrinth): expose redis read replica strategy * chore(ci): remove other connection mode tests * chore: split xredis crate * feat(xredis): primaries routing * chore: tombi fmt * chore: clippy * chore: update query cache
This commit is contained in:
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
mod local;
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
pub(super) use self::local::{LockAcquisition, LockCoordinator, LockWaiter};
|
||||
|
||||
pub(super) const WAIT_TIMEOUT: Duration = Duration::from_secs(5);
|
||||
|
||||
/// Normalize only the requested lookup form's case. Raw IDs and aliases remain
|
||||
/// distinct lock identities and may therefore fill concurrently.
|
||||
pub(super) fn normalize_key(key: &str, case_sensitive: bool) -> String {
|
||||
if case_sensitive {
|
||||
key.to_owned()
|
||||
} else {
|
||||
key.to_lowercase()
|
||||
}
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use dashmap::DashMap;
|
||||
use dashmap::mapref::entry::Entry;
|
||||
use tokio::sync::Notify;
|
||||
use tokio::time::{Instant, timeout_at};
|
||||
|
||||
use crate::Error;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(in crate::cache) struct LockCoordinator {
|
||||
locks: Arc<DashMap<String, Arc<LockState>>>,
|
||||
}
|
||||
|
||||
impl LockCoordinator {
|
||||
pub(in crate::cache) fn new() -> Self {
|
||||
Self {
|
||||
locks: Arc::new(DashMap::with_capacity(2048)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::cache) fn acquire(&self, key: String) -> LockAcquisition {
|
||||
match self.locks.entry(key.clone()) {
|
||||
Entry::Occupied(entry) => LockAcquisition::Waiting(LockWaiter {
|
||||
state: entry.get().clone(),
|
||||
}),
|
||||
Entry::Vacant(entry) => {
|
||||
let state = Arc::new(LockState::new());
|
||||
entry.insert(state.clone());
|
||||
LockAcquisition::Owned(OwnedLockGuard {
|
||||
locks: self.locks.clone(),
|
||||
key,
|
||||
state,
|
||||
released: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::cache) enum LockAcquisition {
|
||||
Owned(OwnedLockGuard),
|
||||
Waiting(LockWaiter),
|
||||
}
|
||||
|
||||
pub(in crate::cache) struct OwnedLockGuard {
|
||||
locks: Arc<DashMap<String, Arc<LockState>>>,
|
||||
key: String,
|
||||
state: Arc<LockState>,
|
||||
released: bool,
|
||||
}
|
||||
|
||||
impl OwnedLockGuard {
|
||||
fn release_inner(&mut self) {
|
||||
if self.released {
|
||||
return;
|
||||
}
|
||||
|
||||
self.released = true;
|
||||
self.locks
|
||||
.remove_if(&self.key, |_, state| Arc::ptr_eq(state, &self.state));
|
||||
self.state.released.store(true, Ordering::Release);
|
||||
self.state.notify.notify_waiters();
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for OwnedLockGuard {
|
||||
fn drop(&mut self) {
|
||||
self.release_inner();
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::cache) struct LockWaiter {
|
||||
state: Arc<LockState>,
|
||||
}
|
||||
|
||||
impl LockWaiter {
|
||||
pub(in crate::cache) async fn wait(
|
||||
self,
|
||||
deadline: Instant,
|
||||
) -> Result<(), Error> {
|
||||
loop {
|
||||
if self.state.released.load(Ordering::Acquire) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let notified = self.state.notify.notified();
|
||||
tokio::pin!(notified);
|
||||
notified.as_mut().enable();
|
||||
if self.state.released.load(Ordering::Acquire) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
timeout_at(deadline, notified)
|
||||
.await
|
||||
.map_err(|_| lock_timeout())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct LockState {
|
||||
released: AtomicBool,
|
||||
notify: Notify,
|
||||
}
|
||||
|
||||
impl LockState {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
released: AtomicBool::new(false),
|
||||
notify: Notify::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lock_timeout() -> Error {
|
||||
Error::LocalCacheTimeout {
|
||||
released: 0,
|
||||
total: 1,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user