-
Notifications
You must be signed in to change notification settings - Fork 69
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to use a (diesel) database thru riker? #103
Comments
If I understand your question - you are trying to find a way of how to create an actor that wraps a mutable database connection. Of course, it's not a good idea to pass the connection from outside. The actor can be seen as a container of something mutable, that due to some unavoidable errors may get into an invalid state. i.e. due to panic. In this case, the supervision mechanism kicks in and restarts (recreates) the whole actor. After that, the actor's state becomes consistent again and is able to react to further mess messages, that from the inbox. So, in your case, the actor should be responsible for opening the database connection. It is often the case when actor creation requires some parameters, i.e. database connection url (or alike). Riker provides such a facility, by having actor_of_args/2 function. Please take a look at the example: use std::time::Duration;
use riker::actors::*;
struct MyDatabase;
impl MyDatabase {
fn connect(_url: String) -> Self {
MyDatabase{}
}
fn query(&mut self) {
}
}
struct MyDatabaseActor {
database: MyDatabase
}
#[derive(Debug, Clone)]
enum Query {
QueryOk,
QueryPanic,
}
impl Actor for MyDatabaseActor {
type Msg = Query;
fn recv(&mut self, _ctx: &Context<Self::Msg>, msg: Self::Msg, _sender: Sender) {
match msg {
Query::QueryOk =>
self.database.query(),
Query::QueryPanic =>
// i.e. network connection lost
panic!("simulate panic"),
}
}
}
impl ActorFactoryArgs<String> for MyDatabaseActor {
fn create_args(database_url: String) -> Self {
println!("database connection to {:?} established", database_url);
MyDatabaseActor{database: MyDatabase::connect(database_url)}
}
}
fn main() {
let sys = ActorSystem::new().unwrap();
let database = sys.actor_of_args::<MyDatabaseActor, _>("my-actor", String::from("db://....")).unwrap();
database.tell(Query::QueryOk, None);
// force panic and see that actor restarts
database.tell(Query::QueryPanic, None);
std::thread::sleep(Duration::from_millis(50000));
} As you can, we pass the connection URL, and the actor itself is responsible for initializing its internal state. In case, our code panics, the supervisor will restart the actor, and the connection will be re-created. I hope I was able to answer your question. |
Why not use connection pool? Opening and closing connection for each query can decrease performance in some applications. |
@filipcro of course, connection pool can be used/created, but this is slightly different topic. |
Just add to this: Implementing database connectivity depends on what you're using actors to represent, how you're modeling your system. For example, if you have an actor that is intermittently measuring a value, say the price of SP 500 index, and it is acting upon that value by following a series of rules but doesn't depend on previous values, then it isn't responsible for the persistence of those values. It might decide to share the value measured, either directly to an actor or distributed to a channel. Other actors can then choose to do something with that value, including persist to a database. In the case of writing to the database the example provided by @aav where a single actor is responsible for database read/writes makes sense. If however your actor is making decisions, i.e. changing it's behavior, based on the current value and historical values (by reading from the database), then writing to the database becomes essential - the actor needs to confirm the value has been written to the database before handling the next message. In this case you can use a database connection reference. I personally have not used Diesel, but what counts is that your connection reference be The first example above is more along the lines of logging, or read-side data persistence (e.g. for viewing on a webpage). The second example is more in line with what you'd expect from a I'm not sure how helpful that is, but you're facing a very common design choice. Future versions of Riker will provide both API code level support (Persistent Actors) and guidance in the form of documentation for working with shared mutable types across actors. |
BTW take a look into sqlx. It supports multiple db, it's native driver, no need to use C wrapper libpq for postrgres DB, I found the syntax is easier to understand, has macros to verify query during compilation. |
the reason we went with Diesel, which is still very sync, was that back in the day when everything was sync, we needed a database framework that does migrations. we might be stuck with it, for the time being, until sqlx has migrations, or Diesel goes async. |
@igalic sqlx beta is having migration now. You should check out sqlx cli readme. Hopefully it should be there in complete version soon. update it is no more in beta. Also extend support to actix runtime as well. |
in transforming a project to async, we've hit a bit of a wall: Plume-org/Plume#777
that wall is the database, or the way we currently (have to) use it.
i was looking into using riker as the actor framework of choice, instead of creating an ad-hoc solution by ourselves
based on some unsuccessful experiments however, I'm not sure riker alone will do.
especially considering this comment:
#98 (comment)
if my
Context
is aConnection
, that can't be read only!but it also doesn't make sense for the
Actor
holding aConnection
Context
, torecv
aConnection
Msg
the correct message would be either
String
orStatement
i feel like the tools are there in riker to do this, buti don't have the understanding of how to put them together yet
(and if they aren't in riker alone, they should be in https://github.com/riker-rs/riker-cqrs
(but that library's documentation is even more incomprehensible to me))
The text was updated successfully, but these errors were encountered: