How would I use multiple database clients in an Actix Web Application - mongodb

I'm on a project where I need to manage connections to both a MongoDB Instance and a PostgreSQL instance.
My current idea is to make a custom type that will contain an Arc<Mutex<pgConnection>> and an Arc<Mutex<MongoConnection>> in a struct that itself is within an Arc which would be passed to the Actix Web app_data initialization function.
e.g.
// this is pseudo-code, kinda
type DbPoolPG = r2d2::Pool<ConnectionManager<PostgreSQL>>;
// wont be an r2d2 pool, MongoDB official drivers handle pooling automatically
type DbPoolMongo = r2d2::Pool<ConnectionManager<MongoDB>>;
struct DatabseConnections {
pg: Arc<Mutex<DbPoolPG>>;
mongo: Arc<Mutex<DbPoolMongo>>;
}
#[actix_web::main]
async fn main() -> io::Result<()> {
// Create connection pools
let PostGresPool = r2d2::Pool::builder()
.build(manager)
.expect("Failed to create pool.");
let MongoPool = mongo.create_connection()
let connections = DatabaseConnections {
pg: Arc::new(Mutex::new(PostGresPool))
mongo: Arc::new(Mutex::new(MongoPool))
}
// Start HTTP server
HttpServer::new(move || {
App::new().app_data(web::Data::new(Arc::new(connections)))
.resource("/{name}", web::get().to(index))
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
The idea seems a bit too simple to actually work though, Does anyone else have any ideas?

nvm, you can just call app_data twice as long as the types are different. then access them by calling the types

Related

How do I confirm I am reading the data from Mongo secondary server from Java

For performance optimisation we are trying to read data from Mongo secondary server for selected scenarios. I am using the inline query using "withReadPreference(ReadPreference.secondaryPreferred())" to read the data, PFB the code snippet.
What I want to confirm the data we are getting is coming from secondary server after executing the inline query highlighted, is there any method available to check the same from Java or Springboot
public User read(final String userId) {
final ObjectId objectId = new ObjectId(userId);
final User user = collection.withReadPreference(ReadPreference.secondaryPreferred()).findOne(objectId).as(User.class);
return user;
}
Pretty much the same way in Java. Note we use secondary() not secondaryPrefered(); this guarantees reads from secondary ONLY:
import com.mongodb.ReadPreference;
{
// This is your "regular" primaryPrefered collection:
MongoCollection<BsonDocument> tcoll = db.getCollection("myCollection", BsonDocument.class);
// ... various operations on tcoll, then create a new
// handle that FORCES reads from secondary and will timeout and
// fail if no secondary can be found:
MongoCollection<BsonDocument> xcoll = tcoll.withReadPreference(ReadPreference.secondary());
BsonDocument f7 = xcoll.find(queryExpr).first();
}

"there is no reactor running, must be called from the context of a Tokio 1.x runtime" when using mongodb 2 with actix-web 3 [duplicate]

This question already has answers here:
Why do I get the error "there is no reactor running, must be called from the context of Tokio runtime" even though I have #[tokio::main]?
(3 answers)
Closed 1 year ago.
I tried to implement mongodb with actix web. This is my main class:
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let db_helper = DbHelper::open_connection().await?;
println!("Connected!");
Ok(())
}
// my db helper
pub async fn open_connection() -> std::io::Result<DbHelper> {
let client = Client::with_uri_str("mongodb://localhost:27017/").await.expect("connect to mongodb issue!");
// List the names of the databases in that deployment.
let databases = client.list_database_names(None, None).await.expect("Test error");
for mongodb in databases {
println!("{}", mongodb);
}
let database = client.database(DB_NAME);
let db_helper = DbHelper {
client,
database,
};
Ok(db_helper)
}
When I tried to run this code, it gave me an error:
thread 'main' panicked at 'there is no reactor running, must be called from the context of a Tokio 1.x runtime',
The problem is from this line: let databases = client.list_database_names(None, None).await.expect("Test error");. I don't know what happened here. If I remove that line or replace actix_web::main with #[tokio::main], it can work. However, the actix web cannot work.
actix-web = "3"
mongodb = "2.0.0-beta.2"
The problem is that you are using Actix web 3.x with Tokio 1.x and mongoDB client 2.x. You need to use Actix web 4.x for it to work with the other two.

How to use MongoDB with r2d2 and actix in rust

I am trying to make a basic web application with the rust language, using the actix framework and r2d2 with mongodb as the database. I could not find any complete and working documentation on how to archive this. Maybe someone can help me out here.
The problem is, that i can't seem to get a mongodb connection from the r2d2 connection pool. Sadly this part isnt covered in any documentation i found.
Some links i found:
Using r2d2 with actix: https://github.com/actix/examples/blob/master/r2d2/src/main.rs
Using mongodb with r2d2: https://docs.rs/r2d2-mongodb/0.2.2/r2d2_mongodb/
This part creates the connection pool and hands it to actix.
fn main() {
std::env::set_var("RUST_LOG", "actix_web=info");
env_logger::init();
let manager = MongodbConnectionManager::new(
ConnectionOptions::builder()
.with_host("localhost", 27017)
.with_db("mydatabase")
.build()
);
let pool = Pool::builder()
.max_size(16)
.build(manager)
.unwrap();
HttpServer::new( move || {
App::new()
// enable logger
.wrap(middleware::Logger::default())
// store db pool in app state
.data(pool.clone())
// register simple handler, handle all methods
.route("/view/{id}", web::get().to(view))
})
.bind("127.0.0.1:8080")
.expect("Can not bind to port 8080")
.run()
.unwrap();
}
This is the handler function trying to access the connection pool
fn view(req: HttpRequest,
pool: web::Data<Pool<MongodbConnectionManager>>) -> impl Responder {
let id = req.match_info().get("id").unwrap_or("unknown");
let conn = pool.get().unwrap();
let result = conn.collections("content").findOne(None, None).unwrap();
// HERE BE CODE ...
format!("Requested id: {}", &id)
}
This is the error showing my problem. The conn variable doesnt seem to be a propper mongodb connection.
error[E0599]: no method named `collections` found for type `std::result::Result<r2d2::PooledConnection<r2d2_mongodb::MongodbConnectionManager>, r2d2::Error>` in the current scope --> src\main.rs:29:23
|
29 | let result = conn.collections("content").findOne(None, None).unwrap();
|
10 | let coll = conn.collection("simulations");
| ^^^^^^^^^^
|
= help: items from traits can only be used if the trait is in scope
= note: the following trait is implemented but not in scope, perhaps add a `use` for it:
`use crate::mongodb::db::ThreadedDatabase;`
my compiler told me to add mongodb::db::ThreadedDatabase in scope.

At what point does the MongoDB C# driver open a connection?

I'm having a problem with lots of connections being opened to the mongo db.
The readme on the Github page for the C# driver gives the following code:
using MongoDB.Bson;
using MongoDB.Driver;
var client = new MongoClient("mongodb://localhost:27017");
var server = client.GetServer();
var database = server.GetDatabase("foo");
var collection = database.GetCollection("bar");
collection.Insert(new BsonDocument("Name", "Jack"));
foreach(var document in collection.FindAll())
{
Console.WriteLine(document["Name"]);
}
At what point does the driver open the connection to the server? Is it at the GetServer() method or is it the Insert() method?
I know that we should have a static object for the client, but should we also have a static object for the server and database as well?
Late answer... but the server connection is created at this point:
var client = new MongoClient("mongodb://localhost:27017");
Everything else is just getting references for various objects.
See: http://docs.mongodb.org/ecosystem/tutorial/getting-started-with-csharp-driver/
While using the latest MongoDB drivers for C#, the connection happens at the actual database operation. For eg. db.Collection.Find() or at db.collection.InsertOne().
{
//code for initialization
//for localhost connection there is no need to specify the db server url and port.
var client = new MongoClient("mongodb://localhost:27017/");
var db = client.GetDatabase("TestDb");
Collection = db.GetCollection<T>("testCollection");
}
//Code for db operations
{
//The connection happens here.
var collection = db.Collection;
//Your find operation
var model = collection.Find(Builders<Model>.Filter.Empty).ToList();
//Your insert operation
collection.InsertOne(Model);
}
I found this out after I stopped my mongod server and debugged the code with breakpoint. Initialization happened smoothly but error was thrown at db operation.
Hope this helps.

Error inserting document into mongodb from scala

Trying to insert into a mongodb database from scala. the below codes dont create a db or collection. tried using the default test db too. how do i perform CRUD operations?
object Store {
def main(args: Array[String]) = {
def addMongo(): Unit = {
var mongo = new Mongo()
var db = mongo.getDB("mybd")
var coll = db.getCollection("somecollection")
var obj = new BasicDBObject()
obj.put("name", "Mongo")
obj.put("type", "db")
coll.insert(obj)
coll.save(obj)
println("Saved") //to print to console
}
}
On a first glance things look OK in your code although you have that stray def addMongo(): Unit = {
code at the top. I'll defer to a suggestion on looking for errors here.... Two items of note:
1) save() and insert() are complementary operations - you only need one. insert() will always attempt to create a new document ... save() will create one if the _id field isn't set, and update the represented _id if it does.
2) Mongo clients do not wait for an answer to a write operation by default. It is very possible & likely that an error is occurring within MongoDB causing your write to fail. the getLastError() command will return the result of the last write operation on the current connection. Because MongoDB's Java driver uses connection pools you have to tell it to lock you onto a single connection for the duration of an operation you want to run 'safely' (e.g. check result). This is the easiest way from the Java driver (in Scala, sample code wise, though):
mongo.requestStart() // lock the connection in
coll.insert(obj) // attempt the insert
getLastError.throwOnError() // This tells the getLastError command to throw an exception in case of an error
mongo.requestDone() // release the connection lock
Take a look at this excellent writeup on MongoDB's Write Durability, which focuses specifically on the Java Driver.
You may also want to take a look at the Scala driver I maintain (Casbah) which wraps the Java driver and provides more scala functionality.
We provide among other things an execute-around-method version of the safe write concept in safely() which makes things a lot easier for testing for writes' success.
You just missed the addMongo call in main. The fix is trivial:
object Store {
def main(args: Array[String]) = {
def addMongo(): Unit = {
var mongo = new Mongo()
var db = mongo.getDB("mybd")
var coll = db.getCollection("somecollection")
var obj = new BasicDBObject()
obj.put("name", "Mongo")
obj.put("type", "db")
coll.insert(obj)
coll.save(obj)
println("Saved") //to print to console
}
addMongo // call it!
}