Repository: libsql/libsql
File: libsql-server/src/namespace/mod.rs (Rust)

Code before:
    pub async fn destroy(&self, namespace: NamespaceName) -> crate::Result<()> {
        if self.inner.has_shutdown.load(Ordering::Relaxed) {
            return Err(Error::NamespaceStoreShutdown);
        }
        let mut bottomless_db_id_init = NamespaceBottomlessDbIdInit::FetchFromConfig;
        if let Some(ns) = self.inner.store.remove(&namespace).await {
            // deallocate in-memory resources
            if let Some(ns) = ns.write().await.take() {
                bottomless_db_id_init = NamespaceBottomlessDbIdInit::Provided(
                    NamespaceBottomlessDbId::from_config(&ns.db_config_store.get()),
                );
                ns.destroy().await?;
            }
        }

        // destroy on-disk database and backups
        self.inner
            .make_namespace
            .destroy(
                namespace.clone(),
                bottomless_db_id_init,
                true,
                &self.inner.metadata,
            )
            .await?;
        self.inner.metadata.remove(&namespace)?;
        tracing::info!("destroyed namespace: {namespace}");

        Ok(())
    }

    pub async fn reset(
        &self,
        namespace: NamespaceName,
        restore_option: RestoreOption,
    ) -> anyhow::Result<()> {
        // The process for reseting is as follow:
        // - get a lock on the namespace entry, if the entry exists, then it's a lock on the entry,
        // if it doesn't exist, insert an empty entry and take a lock on it
        // - destroy the old namespace
        // - create a new namespace and insert it in the held lock
        let entry = self
            .inner
            .store
            .get_with(namespace.clone(), async { Default::default() })
            .await;
        let mut lock = entry.write().await;
        if let Some(ns) = lock.take() {
            ns.destroy().await?;
        }
        // destroy on-disk database

Review comment (suggestion):
I would remove the entry from the state before deallocating the in memory resources, because you don't hold a lock to it. What could happen here is:
- deallocate the namespace
- a request tries to allocate it again, it still exist in the store, so we can reallocate
- we perform the metadata deletion

Code after:

    pub async fn destroy(&self, namespace: NamespaceName) -> crate::Result<()> {
        if self.inner.has_shutdown.load(Ordering::Relaxed) {
            return Err(Error::NamespaceStoreShutdown);
        }
        let mut bottomless_db_id_init = NamespaceBottomlessDbIdInit::FetchFromConfig;
        if let Some(ns) = self.inner.store.remove(&namespace).await {
            // deallocate in-memory resources
            if let Some(ns) = ns.write().await.take() {
                bottomless_db_id_init = NamespaceBottomlessDbIdInit::Provided(
                    NamespaceBottomlessDbId::from_config(&ns.db_config_store.get()),
                );
                ns.destroy().await?;
            }
        }

        // destroy on-disk database and backups
        self.inner
            .make_namespace
            .destroy(
                namespace.clone(),
                bottomless_db_id_init,
                true,
                &self.inner.metadata,
            )
            .await?;
        tracing::info!("destroyed namespace: {namespace}");

        Ok(())
    }

    pub async fn reset(
        &self,
        namespace: NamespaceName,
        restore_option: RestoreOption,
    ) -> anyhow::Result<()> {
        // The process for reseting is as follow:
        // - get a lock on the namespace entry, if the entry exists, then it's a lock on the entry,
        // if it doesn't exist, insert an empty entry and take a lock on it
        // - destroy the old namespace
        // - create a new namespace and insert it in the held lock
        let entry = self
            .inner
            .store
            .get_with(namespace.clone(), async { Default::default() })
            .await;
        let mut lock = entry.write().await;
        if let Some(ns) = lock.take() {
            ns.destroy().await?;
        }
        // destroy on-disk database