Skip to content

Commit 35a116c

Browse files
committed
namespace: add bottomless checks in exists()
Even if the namespace doesn't exist locally, it might have a functional backup.
1 parent 54ed52b commit 35a116c

File tree

2 files changed

+63
-7
lines changed

2 files changed

+63
-7
lines changed

bottomless/src/replicator.rs

+27
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,33 @@ impl Replicator {
369369
})
370370
}
371371

372+
/// Checks if there exists any backup of given database
373+
pub async fn has_backup_of(db_name: impl AsRef<str>, options: &Options) -> Result<bool> {
374+
let prefix = match &options.db_id {
375+
Some(db_id) => format!("{db_id}-"),
376+
None => format!("ns-:{}-", db_name.as_ref()),
377+
};
378+
let config = options.client_config().await?;
379+
let client = Client::from_conf(config);
380+
let bucket = options.bucket_name.clone();
381+
382+
match client.head_bucket().bucket(&bucket).send().await {
383+
Ok(_) => tracing::trace!("Bucket {bucket} exists and is accessible"),
384+
Err(e) => {
385+
tracing::trace!("Bucket checking error: {e}");
386+
return Err(e.into());
387+
}
388+
}
389+
390+
let mut last_frame = 0;
391+
let list_objects = client.list_objects().bucket(&bucket).prefix(&prefix);
392+
let response = list_objects.send().await?;
393+
let _ = Self::try_get_last_frame_no(response, &mut last_frame);
394+
tracing::trace!("Last frame of {prefix}: {last_frame}");
395+
396+
Ok(last_frame > 0)
397+
}
398+
372399
pub async fn shutdown_gracefully(&mut self) -> Result<()> {
373400
tracing::info!("bottomless replicator: shutting down...");
374401
// 1. wait for all committed WAL frames to be committed locally

libsql-server/src/namespace/mod.rs

+36-7
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ impl Default for NamespaceName {
6969
}
7070
}
7171

72+
impl AsRef<str> for NamespaceName {
73+
fn as_ref(&self) -> &str {
74+
self.as_str()
75+
}
76+
}
77+
7278
impl NamespaceName {
7379
pub fn from_string(s: String) -> crate::Result<Self> {
7480
Self::validate(&s)?;
@@ -161,7 +167,7 @@ pub trait MakeNamespace: Sync + Send + 'static {
161167
timestamp: Option<NaiveDateTime>,
162168
) -> crate::Result<Namespace<Self::Database>>;
163169

164-
fn exists(&self, namespace: &NamespaceName) -> bool;
170+
async fn exists(&self, namespace: &NamespaceName) -> bool;
165171
}
166172

167173
/// Creates new primary `Namespace`
@@ -274,9 +280,32 @@ impl MakeNamespace for PrimaryNamespaceMaker {
274280
Ok(ns)
275281
}
276282

277-
fn exists(&self, namespace: &NamespaceName) -> bool {
283+
async fn exists(&self, namespace: &NamespaceName) -> bool {
278284
let ns_path = self.config.base_path.join("dbs").join(namespace.as_str());
279-
ns_path.try_exists().unwrap_or(false)
285+
if let Ok(true) = ns_path.try_exists() {
286+
return true;
287+
}
288+
289+
if let Some(replication_options) = self.config.bottomless_replication.as_ref() {
290+
tracing::info!("Bottomless: {:?}", replication_options);
291+
match bottomless::replicator::Replicator::has_backup_of(namespace, replication_options)
292+
.await
293+
{
294+
Ok(true) => {
295+
tracing::debug!("Bottomless: Backup found");
296+
return true;
297+
}
298+
Ok(false) => {
299+
tracing::debug!("Bottomless: No backup found");
300+
}
301+
Err(err) => {
302+
tracing::debug!("Bottomless: Error checking backup: {}", err);
303+
}
304+
}
305+
} else {
306+
tracing::debug!("Bottomless: No backup configured");
307+
}
308+
false
280309
}
281310
}
282311

@@ -331,7 +360,7 @@ impl MakeNamespace for ReplicaNamespaceMaker {
331360
return Err(ForkError::ForkReplica.into());
332361
}
333362

334-
fn exists(&self, namespace: &NamespaceName) -> bool {
363+
async fn exists(&self, namespace: &NamespaceName) -> bool {
335364
let ns_path = self.config.base_path.join("dbs").join(namespace.as_str());
336365
ns_path.try_exists().unwrap_or(false)
337366
}
@@ -515,7 +544,7 @@ impl<M: MakeNamespace> NamespaceStore<M> {
515544
}
516545

517546
// check that the source namespace exists
518-
if !self.inner.make_namespace.exists(&from) {
547+
if !self.inner.make_namespace.exists(&from).await {
519548
return Err(crate::error::Error::NamespaceDoesntExist(from.to_string()));
520549
}
521550

@@ -581,7 +610,7 @@ impl<M: MakeNamespace> NamespaceStore<M> {
581610
let namespace = namespace.clone();
582611
async move {
583612
if namespace != NamespaceName::default()
584-
&& !self.inner.make_namespace.exists(&namespace)
613+
&& !self.inner.make_namespace.exists(&namespace).await
585614
&& !self.inner.allow_lazy_creation
586615
{
587616
return Err(Error::NamespaceDoesntExist(namespace.to_string()));
@@ -651,7 +680,7 @@ impl<M: MakeNamespace> NamespaceStore<M> {
651680
// otherwise it's an error.
652681
if self.inner.allow_lazy_creation || namespace == NamespaceName::default() {
653682
tracing::trace!("auto-creating the namespace");
654-
} else if self.inner.make_namespace.exists(&namespace) {
683+
} else if self.inner.make_namespace.exists(&namespace).await {
655684
return Err(Error::NamespaceAlreadyExist(namespace.to_string()));
656685
}
657686

0 commit comments

Comments
 (0)