Learning Rust, having trouble with async_traits and borrowing. `xxxxx` dropped here while still borrowed - mongodb

I've viewed so many stack overflows, but can't get my head around how to fix this.
Here are my deps:
[dependencies]
mongodb = { version = "2.3.1"}
tokio = "1.22.0"
twilio-openapi = "1.0.0"
async-trait = "0.1.58"
futures = "0.3.25"
I've created this async_trait repository trait for implementing various data access repositories:
use std::pin::Pin;
use async_trait::async_trait;
use futures::Future;
use mongodb::{bson::Document, error::Error, options::FindOneOptions, Client};
pub mod notification_settings;
#[async_trait]
pub trait Repository<'a> {
fn with_client(client: &'a mut &Client) -> Self;
async fn find_one<'b>(
self,
filter: Document,
find_one_options: FindOneOptions,
) -> Pin<Box<(dyn Future<Output = Result<Option<Document>, Error>> + Send + 'b)>>;
}
It seems like async_trait sort of requires returning a Pin<Box<dyn Future<Output = X> + Send + 'b)>> Where you can specify a certain lifetime on the borrow. I'm not sure exactly why or if there are ways around this, but again still learning this.
And then here is my struct implementing the trait:
use std::{marker::Send, pin::Pin};
use async_trait::async_trait;
use futures::Future;
use mongodb::{bson::Document, error::Error, options::FindOneOptions, Client};
pub struct NotificationSettingsRepository<'a> {
pub(crate) client: &'a Client,
}
#[async_trait]
impl<'a> super::Repository<'a> for NotificationSettingsRepository<'a> {
fn with_client(client: &'a mut &Client) -> Self {
NotificationSettingsRepository { client: client }
}
async fn find_one<'b>(
self,
filter: Document,
find_one_options: FindOneOptions,
) -> Pin<Box<(dyn Future<Output = Result<Option<Document>, Error>> + Send + 'b)>> {
let collection = self
.client
.database("Occasionally")
.collection::<Document>("NotificationSettings");
let document = collection.find_one(filter, find_one_options);
Box::pin(async { document.await })
}
}
Problem is on collection.find_one(filter, find_one_options); I get an error about:
`collection` does not live long enough
borrowed value does not live long enoughrustcClick for full compiler diagnostic
notification_settings.rs(30, 5): `collection` dropped here while still borrowed
notification_settings.rs(17, 23): lifetime `'b` defined here
notification_settings.rs(21, 10): type annotation requires that `collection` is borrowed for `'b`
This makes some sense because the definition of find_one looks like this:
find_one(&self, filter: impl Into<Option<Document>>, options: impl Into<Option<FindOneOptions>>) -> Result<Option<T>>
So it is borrowing the collection via &self
I've tried a few things like adding an async { } wrapper on the Future of find_one. But I'm not sure what I'm doing there. Please help and thank you!

Related

'Bson: Borrow<T> is not satisifed' error in simple CRUD post function

I have following Error:
error[E0277]: the trait bound `Bson: Borrow<News>` is not satisfied
--> src\handlers.rs:46:36
|
46 | let inserted = coll.insert_one(serialized_news, None).await.unwrap();
| ---------- ^^^^^^^^^^^^^^^ the trait `Borrow<News>` is not implemented for `Bson`
| |
| required by a bound introduced by this call
|
note: required by a bound in `mongodb::Collection::<T>::insert_one`
--> C:\Users\User\.cargo\registry\src\github.com-1ecc6299db9ec823\mongodb-2.3.1\src\coll\mod.rs:1279:19
|
1279 | doc: impl Borrow<T>,
| ^^^^^^^^^ required by this bound in `mongodb::Collection::<T>::insert_one`
For more information about this error, try `rustc --explain E0277`.
error: could not compile `backend` due to previous error
My handler code containing the post_news function that throws this error:
use crate::structs::News;
use axum::{extract::Path, extract::State, http::StatusCode, response::IntoResponse, Json};
use bson::oid::ObjectId;
use futures::stream::StreamExt;
use mongodb::{bson::doc, options::FindOptions, Client, Collection};
pub async fn get_all(State(client): State<Client>) -> impl IntoResponse {
let coll: Collection<News> = client.database("axum").collection::<News>("news");
let mut options = FindOptions::default();
options.limit = Some(1);
options.sort = Some(doc! {
"title": 1
});
let mut cursor = coll
.find(None, options)
.await
.expect("could not load news data.");
let mut rows: Vec<News> = Vec::new();
while let Some(doc) = cursor.next().await {
rows.push(doc.expect("could not load news info."));
}
(StatusCode::OK, Json(rows))
}
pub async fn get_one(Path(id): Path<u64>) {}
pub async fn post_news(
State(client): State<Client>,
Json(payload): Json<News>,
) -> impl IntoResponse {
let coll: Collection<News> = client.database("axum").collection::<News>("news");
let news = News {
id: ObjectId::new(),
title: payload.title.to_string(),
short_description: payload.short_description.to_string(),
};
let serialized_news = bson::to_bson(&news).unwrap();
let inserted = coll.insert_one(serialized_news, None).await.unwrap();
(StatusCode::CREATED, Json(news))
}
pub async fn handler_404() -> impl IntoResponse {
(StatusCode::NOT_FOUND, "nothing to see here")
}
mod structs {
use bson::{self, oid::ObjectId};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct News {
#[serde(rename = "_id")]
pub id: ObjectId,
pub title: String,
pub short_description: String,
}
}
The error occurs in this line in the post_news function:
let inserted = coll.insert_one(serialized_news, None).await.unwrap();
Tried to add Clone and Debug to my struct and that didn't help
When encountering an error, you need to dig in and figure out why the error occurs, rather than just fiddling left and right.
In this case, the first step is looking at the function you are attempting to call Collection::insert_one:
pub async fn insert_one(
&self,
doc: impl Borrow<T>,
options: impl Into<Option<InsertOneOptions>>
) -> Result<InsertOneResult>
And double checking what Borrow is by following the link -- just to make sure it's indeed std::borrow::Borrow (spoiler: it is).
So, since your coll is of type Collection<News>, you need a type implementing Borrow<News> -- as mentioned by the error message -- which means amongst others News and &News.
Hence, as shown in the documentation of Collection, you should just insert your News object, without first serializing it:
let inserted = coll.insert_one(news, None).await.unwrap();
Collection will take care of serializing and deserializing as appropriate, no need to do the bson step yourself.

How to use Mongodb::cursor in Rust?

It's a very simple project to learn how to use mongodb with Rust. I'm using the official mongodb driver here: https://github.com/mongodb/mongo-rust-driver. The problem is that if I'm using aggregate, I cannot read the result
// main.rs
use mongodb::bson::{doc, Bson};
use mongodb::{options::AggregateOptions, options::ClientOptions, Client};
use std::error::Error;
use tokio;
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
// Load the MongoDB connection string:
let client_uri = "mongodb://127.0.0.1:27017";
// A Client is needed to connect to MongoDB:
let mut options = ClientOptions::parse(&client_uri).await?;
options.app_name = Some("testing".to_string());
let client = Client::with_options(options)?;
// get the collection here
let items = client.database("my_database").collection("inventory");
// aggregate options and pipeline
let pipeline = vec![doc! {"$match": {"name": "FOO"}}];
let options = AggregateOptions::builder().allow_disk_use(true).build();
// I'm using tokio for async-await library
let data = items
.aggregate(pipeline, options)
.await
.map_err(|e| println!("{}", e));
// data is a Result<mongodb::Cursor> type
match data {
Ok(cursor) => {
// I want to iterate the returned documents here
// this doesn't compiles
while let Some(doc) = cursor.next().await {
println!("{}", doc?)
}
},
Err(e) => println!("{:?}", e),
}
The code above returns an error. It complains that the cursor has no next() function within.
while let Some(doc) = cursor.next().await {
| ^^^^ method not found in `mongodb::Cursor`
I read the manual book for mongodb::Cursor here: https://docs.rs/mongodb/1.2.1/mongodb/struct.Cursor.html
and aggregate function here https://docs.rs/mongodb/1.2.1/mongodb/struct.Collection.html#method.aggregate
As you can see the aggregate method should return Result<Cursor>. As the manual stated:
A cursor can be used like any other Stream. The simplest way is just
to iterate over the documents it yields:
while let Some(doc) = cursor.next().await {
println!("{}", doc?)
}
So why is it doesn't work ?
My dependencies in Cargo.toml:
[dependencies]
tokio = { version = "0.2", features = ["macros", "rt-threaded"] }
serde = { version = "1.0", features = ["derive"] }
mongodb = "1.2.0"
If I print the cursor println!("{:?}", cursor);. It contains data in it. How to get the data out from this cursor ?
I found it! Just add use tokio::stream::StreamExt; on top of the file and the rest is good to go.
...all the other methods that an Stream has are available on Cursor as
well. This includes all of the functionality provided by StreamExt,
which provides similar functionality to the standard library Iterator
trait.
// main.rs
use mongodb::bson::{doc, Bson};
use mongodb::{options::AggregateOptions, options::ClientOptions, Client};
use std::error::Error;
use tokio;
// don't forget this!
use tokio::stream::StreamExt;
Fixed it by adding use futures::stream::StreamExt;

Actix Rust deadpool_postgres: DB connections not released

I've got an Actix-web server that connects to a Postgres DB.
I've noticed that after a 1000 requests my Postgres DB's RAM usage has spiked.
When I stop actix-web, the RAM held by the db is cleared. This leads me to believe that my code is not releasing the connection.
I cannot find an example of connections actually being released. It looks like it's inferred in everyone else's code.
Here's mine:
async fn hellow_world(a : f32, b : f32, pool: &Pool) -> Result<Value, PoolError> {
let client: Client = pool.get().await?;
let sql = format!("select \"json\" from public.table_a WHERE a={} and b={}", a, b);
let stmt = client.prepare(&sql).await?;
let row = client.query_one(&stmt, &[]).await?;
let result : Value = row.get(0);
Ok(result)
}
#[derive(Deserialize)]
pub struct MyRequest {
a: f32,
b: f32
}
#[get("/hello")]
async fn sv_hellow_world(info: web::Query<MyRequest>, db_pool: web::Data<Pool>) -> Result<HttpResponse, Error> {
let response : Value = hellow_world(info.a, info.b, &db_pool).await?;
Ok(HttpResponse::Ok().json(response))
}
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
dotenv().ok();
let config = Config::from_env().unwrap();
let pool = config.pg.create_pool(tokio_postgres::NoTls).unwrap();
env_logger::from_env(Env::default().default_filter_or("info")).init();
let server = HttpServer::new(move || App::new().wrap(Logger::default()).wrap(Logger::new("%a %{User-Agent}i")).data(pool.clone()).service(sv_hellow_world))
.bind("0.0.0.0:3000")?
.run();
server.await
}
Based on further testing, #Werner determined that the code was piling up server-side prepared statements.
It is not clear whether these statements can be closed using this library.
Either of two approaches can be used to avoid this problem:
Use a single, shared prepared statement
Use the direct query form instead of the prepared statement
I recommend the first approach on principle as it is more efficient and protects against SQL Injection. It should look something like this:
async fn hellow_world(a : f32, b : f32, pool: &Pool) -> Result<Value, PoolError> {
let client: Client = pool.get().await?;
let stmt = client.prepare("select \"json\" from public.table_a WHERE a=$1::numeric and b=$2::numeric").await?;
let row = client.query_one(&stmt, &[&a, &b]).await?;
let result : Value = row.get(0);
Ok(result)
}
Using this code, only one prepared statement should be created on each of the pool's connections.

the trait `std::convert::From<mongodb::error::Error>` is not implemented for `std::io::Error`

Trying to make server with actix-web & mongodb in rust. Getting error
the trait std::convert::From<mongodb::error::Error> is not implemented for std::io::Error
here is my code
use actix_web::{web, App, HttpRequest, HttpServer, Responder};
use mongodb::{options::ClientOptions, Client};
async fn greet(req: HttpRequest) -> impl Responder {
let name = req.match_info().get("name").unwrap_or("World");
format!("Hello {}!", &name)
}
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
// Parse a connection string into an options struct.
let mut client_options = ClientOptions::parse("mongodb://localhost:27017")?;
// Manually set an option.
client_options.app_name = Some("My App".to_string());
// Get a handle to the deployment.
let client = Client::with_options(client_options)?;
// List the names of the databases in that deployment.
for db_name in client.list_database_names(None)? {
println!("{}", db_name);
}
HttpServer::new(|| {
App::new()
.route("/", web::get().to(greet))
.route("/{name}", web::get().to(greet))
})
.bind("127.0.0.1:8000")?
.run()
.await
}
Did I missed anything?
It means that one of the functions you are calling with a ? at the end can return a mongodb::error::Error. But the signature of the main is a std::io::Result<()>, wich is an implied Result<(), std::io::Error>. The only error type it can accept is a io::Error, not a mongodb::Error.
It looks like all the functions you are escaping might return this mongodb::error::Error, so you can try to change the main signature to such a result: Result<(). mongodb::error::Error>.
But I would recommend you do proper error handling on those potential errors, as this is your main(). Change those ? to .expect("Some error message"); at least. The program will still crash, but it will crash in a way that is meaningful to you.

Using a callback when handling TCP connections with Tokio

I am trying to have a struct that starts an event loop, listens for TCP connections and calls a callback for each connection.
(The callback will be handed some prepossessed data from the socket. In my example below I just hand it the IP address of the connection but in my real code I will parse the contents that I receive with serde into a struct and pass that into the callback. I hope that doesn't invalidate the following "not working example").
My Cargo.toml:
[package]
name = "lifetime-problem"
version = "0.1.0"
edition = "2018"
[dependencies]
tokio-tcp = "0.1.3"
tokio = "0.1.14"
[[bin]]
name = "lifetime-problem"
path = "main.rs"
and main.rs:
use tokio::prelude::*;
struct Test {
printer: Option<Box<Fn(std::net::SocketAddr) + Sync>>,
}
impl Test {
pub fn start(&mut self) -> Result<(), Box<std::error::Error>> {
let addr = "127.0.0.1:4242".parse::<std::net::SocketAddr>()?;
let listener = tokio::net::TcpListener::bind(&addr)?;
let server = listener
.incoming()
.map_err(|e| eprintln!("failed to accept socket; error = {:?}", e))
.for_each(move |socket: tokio::net::TcpStream| {
let address = socket.peer_addr().expect("");
match self.printer {
Some(callback) => { callback(address); }
None => { println!("{}", address); }
}
Ok(())
});
tokio::run(server);
Ok(())
}
}
fn main() {
let mut x = Test{ printer: None };
x.start();
}
I have tried several things starting from this code (which is adopted directly from the Tokio example).
If I use the code like posted above I get:
error[E0277]: (dyn std::ops::Fn(std::net::SocketAddr) + std::marker::Sync + 'static) cannot be sent between threads safely
for the line 24 (tokio::run(server)).
If I add the Send trait on the Fn in the printer field XOR if I remove the move in the closure in the for_each call I get another error instead:
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
which points me to the closure that apparently cannot outlive the start method where it is defined but tokio::run seems to have conflicting requirements for it.
Do you know if I am addressing the callback pattern in totally the wrong way or if there is just some minor error in my code?
First things first:
Compiler will translate Box<Fn(std::net::SocketAddr) + Sync> to Box<Fn(std::net::SocketAddr) + Sync + 'static> unless the lifetime is explicitly specified.
Let's have a look at the errors:
error[E0277]: (dyn std::ops::Fn(std::net::SocketAddr) + std::marker::Sync + 'static) cannot be sent between threads safely
This is self-explanatory. You are trying to move &mut T to another thread, but cannot, because T here is not Send. To send &mut T to another thread T too needs to be of type Send.
Here is the minimal code that will give the same error:
use std::fmt::Debug;
fn func<T> (i:&'static mut T) where T: Debug {
std::thread::spawn(move || {
println!("{:?}", i);
});
}
If I make T above to also be of type Send, the error goes away.
But in your case when you add the Send trait, it gives lifetime error. Why?
&mut self has some lifetime greater than the function start() set by the caller, but there's no guarantee that its 'static. You move this reference into the closure which is passed to the thread and can potentially outlive the scope it is closing over, leading to a dangling reference.
Here's a minimal version, that would give the same error.
use std::fmt::Debug;
fn func<'a, T:'a> (i:&'a mut T) where T: Debug + Sync + Send {
std::thread::spawn(move || {
println!("{:?}", i);
});
}
Sync is not really required here as it is &mut T. Changing &mut T to &T (retaining Sync), will also result into the same error. The onus here is on references and not mutability. So you see, there is some lifetime 'a and it is moved into a closure (given to a thread), which means the closure now contains a reference (disjoint from the main context). So now, what is 'a and how long will it live from the closure's perspective that is invoked from another thread? Not inferable! As a result, the compiler complains saying cannot infer an appropriate lifetime due to conflicting requirements.
If we tweak the code a bit to;
impl Test {
pub fn start(&'static mut self) -> Result<(), Box<std::error::Error>> {
let addr = "127.0.0.1:4242".parse::<std::net::SocketAddr>()?;
let listener = tokio::net::TcpListener::bind(&addr)?;
let server = listener
.incoming()
.map_err(|e| eprintln!("failed to accept socket; error = {:?}", e))
.for_each(move |socket: tokio::net::TcpStream| {
let address = socket.peer_addr().expect("");
match &self.printer {
Some(callback) => { callback(address); }
None => { println!("{}", address); }
}
Ok(())
});
tokio::run(server);
Ok(())
}
}
it will compile fine. There's a guarantee there that self has a 'static lifetime. Please note that in the match statement we need to pass &self.printer, as you cannot move out of a borrowed context.
However, this expects Test to be declared static and that too a mutable one, which is generally not the best way, if you have other options.
Another way is; if it's ok for you to pass Test by value to start() and then further move it into for_each(), the code would look like this:
use tokio::prelude::*;
struct Test {
printer: Option<Box<Fn(std::net::SocketAddr) + Send>>,
}
impl Test {
pub fn start(mut self) -> Result<(), Box<std::error::Error>> {
let addr = "127.0.0.1:4242".parse::<std::net::SocketAddr>()?;
let listener = tokio::net::TcpListener::bind(&addr)?;
let server = listener
.incoming()
.map_err(|e| eprintln!("failed to accept socket; error = {:?}", e))
.for_each(move |socket: tokio::net::TcpStream| {
let address = socket.peer_addr().expect("");
match &self.printer {
Some(callback) => {
callback(address);
}
None => {
println!("{}", address);
}
}
Ok(())
});
tokio::run(server);
Ok(())
}
}
fn main() {
let mut x = Test { printer: None };
x.start();
}