У меня есть простое приложение rocket.rs с маршрутом регистрации
Вот сервис с драйвером синхронизации mongodb
pub async fn create_new_user(user_email: &str, password: &str, user_name: &str) -> Result<(), &'static str> {
let users_collection: mongodb::Collection<UserModel> = DB.collection("users");
// check if user with given email or name already exists
let existing_user = users_collection.find_one(
doc! {
"$or": [
{"user_email": user_email},
{"user_name": user_name}
]
},
None
);
if existing_user.is_ok() {
return Err("User with such email or name already exists");
}
// new user struct
let new_user = UserModel {
user_id: /* hash gen fn */,
user_name: user_name.to_string(),
email: user_email.to_string(),
hashed_password: password.to_string(),
verified: false,
};
// insert new user into db
let insert_result = DB.collection("users").insert_one(new_user, None);
if insert_result.is_ok() {
return Ok(());
} else {
return Err("Failed to create user");
}
}
К сожалению, ракета отказывается работать с драйвером синхронизации, и мне нужен асинхронный.
Как добавить его в приложение? Потому что я понятия не имею, и единственное, что я нашел в Google, - это слишком сложный код, который я не понимаю, или документация по асинхронным драйверам...
// how it was with sync one...
static DB: Lazy<Database> = Lazy::new(
|| {
let client = Client::with_uri_str(&CONFIG.mongodb_uri_string);
client.database("silly-tips")
}
);
// now
static DB: once_cell:unsync::Lazy<Database> = once_cell:unsync::Lazy::new(
async {
let client = Client::with_uri_str(&CONFIG.mongodb_uri_string).await.expect("Error connecting to MongoDB");
client.database("silly-tips")
}
);
`Cell<std::option::Option<fn() -> Database>>` cannot be shared between threads safely
within `once_cell::unsync::Lazy<Database>`, the trait `Sync` is not implemented for `Cell<std::option::Option<fn() -> Database>>`, which is required by `once_cell::unsync::Lazy<Database>: Sync`
if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock`
shared static variables must have a type that implements `Sync`rustcClick for full compiler diagnostic
🤔 А знаете ли вы, что...
MongoDB позволяет создавать кластеры на нескольких узлах для обеспечения отказоустойчивости и масштабируемости...
Есть два способа сделать это:
Если вы хотите имитировать существующую настройку с помощью static DB: Lazy<Database>
, вы можете продолжать делать это, используя tokio::sync::OnceCell
, например:
use mongodb::{Client, Database};
use tokio::sync::OnceCell;
async fn get_db() -> &'static Database {
static DB: OnceCell<Database> = OnceCell::const_new();
DB.get_or_init(|| async {
Client::with_uri_str(&CONFIG.mongodb_uri_string)
.await
.expect("Error connecting to MongoDB")
.database("silly-tips")
})
.await
}
pub async fn create_new_user(...) {
let users_collection = get_db().await.collection::<UserModel>("users");
...
}
Посмотрите еще несколько способов здесь: Как использовать lazy_static с инициализатором async/await?
Другой вариант — позволить Rocket управлять состоянием за вас. Вы делаете это, передавая базу данных Rocket через .manage()
, из которой вы можете получать маршруты Rocket через защиту запросов State
. Затем вы просто передадите его в свою функцию create_new_user
как обычный параметр:
use mongodb::{Client, Database};
use rocket::{launch, post, routes, State};
pub async fn create_new_user(db: &Database, ...) {
let users_collection = db.collection::<UserModel>("users");
...
}
#[post("/login")]
async fn login(db: &State<Database>) {
...
create_new_user(&db, ...).await;
...
}
#[launch]
async fn rocket() -> _ {
...
let db = Client::with_uri_str(&CONFIG.mongodb_uri_string)
.await
.expect("Error connecting to MongoDB")
.database("silly-tips");
rocket::build().manage(db).mount("/", routes![login])
}
Обычно это предпочтительнее, поскольку вы избегаете глобальных переменных.