bikeshedder / deadpool

Dead simple pool implementation for rust with async-await
Apache License 2.0
1.08k stars 137 forks source link

Use deadpool without adapters #349

Closed qaqland closed 2 months ago

qaqland commented 3 months ago

Thank you very much for these crates, databases can be used out of the box using adapters like deadpool-sqlite. But I'm trying to use it for git2 which is not database: git2::Repository(!sync) is the main structure, it can open git repository and execute functions.

I want to use deadpool for making git2 pool, each "connection" is one opened repository(read only search in multi-thread is safe), thus speeds up the search on handling async requests.

There is no adapter for git2. In fact, similar needs should be small and not enough to maintain such an adapter. I think it would be more appropriate to use deadpool directly, however example in README is too simple to fully understand usage. Could you help me :)

qaqland commented 2 months ago

I complete the following code, but not sure if it's right

use std::path::{Path, PathBuf};

use deadpool::managed::{self, RecycleError};
use git2::{Error, Repository};

use deadpool_sync::SyncWrapper;

pub use deadpool::managed::reexports::*;

deadpool::managed_reexports!(
    "git2",
    Manager,
    managed::Object<Manager>,
    Error,
    std::convert::Infallible
);

pub struct Manager {
    path: PathBuf,
    runtime: Runtime,
}

impl Manager {
    pub fn new(path: &Path, runtime: Runtime) -> Self {
        Self {
            path: path.into(),
            runtime,
        }
    }
}

impl deadpool::managed::Manager for Manager {
    type Type = SyncWrapper<Repository>;
    type Error = Error;

    async fn create(&self) -> Result<Self::Type, Self::Error> {
        let path = self.path.clone();
        SyncWrapper::new(self.runtime, move || Repository::open(path)).await
    }

    async fn recycle(&self, repo: &mut Self::Type, _: &Metrics) -> managed::RecycleResult<Error> {
        match repo.interact(move |repo| repo.is_empty()).await {
            Ok(_) => Ok(()),
            Err(e) => Err(RecycleError::message(format!("{}", e))),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::path::Path;
    use tempfile;

    fn create_pool(path: &Path) -> Pool {
        git2::Repository::init(path).unwrap();
        let mgr = Manager::new(path, Runtime::Tokio1);
        Pool::builder(mgr).build().unwrap()
    }

    #[tokio::test]
    async fn basic() {
        let tmp_dir = tempfile::tempdir().unwrap();
        let pool = create_pool(tmp_dir.path());
        let conn = pool.get().await.unwrap();
        let result: bool = conn
            .interact(|repo| repo.is_empty())
            .await
            .unwrap()
            .unwrap();
        assert_eq!(result, true);
    }
}
bikeshedder commented 2 months ago

LGTM. Does it work? If so, congratulations on your first deadpool based pool implementation! :tada:

qaqland commented 2 months ago

really thanks