mirror of
https://github.com/modrinth/code.git
synced 2026-08-31 12:05:53 +00:00
* 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
137 lines
3.7 KiB
Rust
137 lines
3.7 KiB
Rust
use std::time::Duration;
|
|
|
|
use futures::StreamExt;
|
|
use redis::ToRedisArgs;
|
|
use tokio::sync::mpsc;
|
|
use tracing::{info, warn};
|
|
|
|
use super::{Error, RedisPool};
|
|
|
|
const PUBSUB_BUFFER_SIZE: usize = 1024;
|
|
const INITIAL_RECONNECT_BACKOFF: Duration = Duration::from_millis(250);
|
|
const MAX_RECONNECT_BACKOFF: Duration = Duration::from_secs(30);
|
|
|
|
enum SubscriptionOutcome {
|
|
SubscriberClosed,
|
|
Disconnected,
|
|
}
|
|
|
|
impl RedisPool {
|
|
pub fn subscribe(&self, channel: &'static str) -> mpsc::Receiver<Vec<u8>> {
|
|
let seed_urls = self.config.seed_urls().to_vec();
|
|
let (sender, receiver) = mpsc::channel(PUBSUB_BUFFER_SIZE);
|
|
tokio::spawn(run_subscription(seed_urls, channel, sender));
|
|
receiver
|
|
}
|
|
|
|
#[doc(hidden)]
|
|
#[cfg(feature = "test")]
|
|
pub fn subscribe_with_seed_urls(
|
|
seed_urls: Vec<String>,
|
|
channel: &'static str,
|
|
) -> mpsc::Receiver<Vec<u8>> {
|
|
let (sender, receiver) = mpsc::channel(PUBSUB_BUFFER_SIZE);
|
|
tokio::spawn(run_subscription(seed_urls, channel, sender));
|
|
receiver
|
|
}
|
|
|
|
pub async fn publish<M>(
|
|
&self,
|
|
channel: &str,
|
|
message: M,
|
|
) -> Result<(), Error>
|
|
where
|
|
M: ToRedisArgs + Send + Sync,
|
|
{
|
|
let mut connection = self.connect().await?;
|
|
let _: usize = redis::cmd("PUBLISH")
|
|
.arg(channel)
|
|
.arg(message)
|
|
.query_async(&mut connection)
|
|
.await
|
|
.map_err(Error::from)?;
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
async fn run_subscription(
|
|
seed_urls: Vec<String>,
|
|
channel: &'static str,
|
|
sender: mpsc::Sender<Vec<u8>>,
|
|
) {
|
|
let mut next_seed = 0;
|
|
let mut reconnect_backoff = INITIAL_RECONNECT_BACKOFF;
|
|
|
|
loop {
|
|
let mut connected = false;
|
|
|
|
for _ in 0..seed_urls.len() {
|
|
let seed_url = &seed_urls[next_seed];
|
|
next_seed = (next_seed + 1) % seed_urls.len();
|
|
|
|
match forward_from_seed(seed_url, channel, &sender).await {
|
|
Ok(SubscriptionOutcome::SubscriberClosed) => return,
|
|
Ok(SubscriptionOutcome::Disconnected) => {
|
|
warn!(channel, "Redis Pub/Sub connection disconnected");
|
|
connected = true;
|
|
break;
|
|
}
|
|
Err(error) => {
|
|
warn!(
|
|
?error,
|
|
channel,
|
|
"Failed to establish Redis Pub/Sub subscription"
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
if sender.is_closed() {
|
|
return;
|
|
}
|
|
|
|
let delay = if connected {
|
|
INITIAL_RECONNECT_BACKOFF
|
|
} else {
|
|
reconnect_backoff
|
|
};
|
|
tokio::time::sleep(delay).await;
|
|
|
|
reconnect_backoff = if connected {
|
|
INITIAL_RECONNECT_BACKOFF
|
|
} else {
|
|
reconnect_backoff
|
|
.saturating_mul(2)
|
|
.min(MAX_RECONNECT_BACKOFF)
|
|
};
|
|
}
|
|
}
|
|
|
|
async fn forward_from_seed(
|
|
seed_url: &str,
|
|
channel: &'static str,
|
|
sender: &mpsc::Sender<Vec<u8>>,
|
|
) -> redis::RedisResult<SubscriptionOutcome> {
|
|
let client = redis::Client::open(seed_url)?;
|
|
let mut pubsub = client.get_async_pubsub().await?;
|
|
pubsub.subscribe(channel).await?;
|
|
info!(channel, "Established Redis Pub/Sub subscription");
|
|
|
|
let mut stream = pubsub.into_on_message();
|
|
while let Some(message) = stream.next().await {
|
|
if message.get_channel_name() != channel {
|
|
continue;
|
|
}
|
|
|
|
if sender
|
|
.send(message.get_payload_bytes().to_vec())
|
|
.await
|
|
.is_err()
|
|
{
|
|
return Ok(SubscriptionOutcome::SubscriberClosed);
|
|
}
|
|
}
|
|
|
|
Ok(SubscriptionOutcome::Disconnected)
|
|
}
|