Skip to content

Commit 4d913f9

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 fc5254f commit 4d913f9

File tree

2 files changed

+63
-7
lines changed

2 files changed

+63
-7
lines changed

bottomless/src/replicator.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,33 @@ impl Replicator {
376376
})
377377
}
378378

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

libsql-server/src/namespace/mod.rs

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@ impl Default for NamespaceName {
7474
}
7575
}
7676

77+
impl AsRef<str> for NamespaceName {
78+
fn as_ref(&self) -> &str {
79+
self.as_str()
80+
}
81+
}
82+
7783
impl NamespaceName {
7884
pub fn from_string(s: String) -> crate::Result<Self> {
7985
Self::validate(&s)?;
@@ -170,7 +176,7 @@ pub trait MakeNamespace: Sync + Send + 'static {
170176
meta_store: &MetaStore,
171177
) -> crate::Result<Namespace<Self::Database>>;
172178

173-
fn exists(&self, namespace: &NamespaceName) -> bool;
179+
async fn exists(&self, namespace: &NamespaceName) -> bool;
174180
}
175181

176182
/// Creates new primary `Namespace`
@@ -287,9 +293,32 @@ impl MakeNamespace for PrimaryNamespaceMaker {
287293
Ok(ns)
288294
}
289295

290-
fn exists(&self, namespace: &NamespaceName) -> bool {
296+
async fn exists(&self, namespace: &NamespaceName) -> bool {
291297
let ns_path = self.config.base_path.join("dbs").join(namespace.as_str());
292-
ns_path.try_exists().unwrap_or(false)
298+
if let Ok(true) = ns_path.try_exists() {
299+
return true;
300+
}
301+
302+
if let Some(replication_options) = self.config.bottomless_replication.as_ref() {
303+
tracing::info!("Bottomless: {:?}", replication_options);
304+
match bottomless::replicator::Replicator::has_backup_of(namespace, replication_options)
305+
.await
306+
{
307+
Ok(true) => {
308+
tracing::debug!("Bottomless: Backup found");
309+
return true;
310+
}
311+
Ok(false) => {
312+
tracing::debug!("Bottomless: No backup found");
313+
}
314+
Err(err) => {
315+
tracing::debug!("Bottomless: Error checking backup: {}", err);
316+
}
317+
}
318+
} else {
319+
tracing::debug!("Bottomless: No backup configured");
320+
}
321+
false
293322
}
294323
}
295324

@@ -347,7 +376,7 @@ impl MakeNamespace for ReplicaNamespaceMaker {
347376
return Err(ForkError::ForkReplica.into());
348377
}
349378

350-
fn exists(&self, namespace: &NamespaceName) -> bool {
379+
async fn exists(&self, namespace: &NamespaceName) -> bool {
351380
let ns_path = self.config.base_path.join("dbs").join(namespace.as_str());
352381
ns_path.try_exists().unwrap_or(false)
353382
}
@@ -541,7 +570,7 @@ impl<M: MakeNamespace> NamespaceStore<M> {
541570
}
542571

543572
// check that the source namespace exists
544-
if !self.inner.make_namespace.exists(&from) {
573+
if !self.inner.make_namespace.exists(&from).await {
545574
return Err(crate::error::Error::NamespaceDoesntExist(from.to_string()));
546575
}
547576

@@ -608,7 +637,7 @@ impl<M: MakeNamespace> NamespaceStore<M> {
608637
let namespace = namespace.clone();
609638
async move {
610639
if namespace != NamespaceName::default()
611-
&& !self.inner.make_namespace.exists(&namespace)
640+
&& !self.inner.make_namespace.exists(&namespace).await
612641
&& !self.inner.allow_lazy_creation
613642
{
614643
return Err(Error::NamespaceDoesntExist(namespace.to_string()));
@@ -678,7 +707,7 @@ impl<M: MakeNamespace> NamespaceStore<M> {
678707
// otherwise it's an error.
679708
if self.inner.allow_lazy_creation || namespace == NamespaceName::default() {
680709
tracing::trace!("auto-creating the namespace");
681-
} else if self.inner.make_namespace.exists(&namespace) {
710+
} else if self.inner.make_namespace.exists(&namespace).await {
682711
return Err(Error::NamespaceAlreadyExist(namespace.to_string()));
683712
}
684713

0 commit comments

Comments
 (0)