Encapsulate a postgresql Transaction in a structure - postgresql

I'm trying to encapsulate a Postgresql transaction and I'm running into some lifetime issue.
Here is the code :
Code
I understand the error message : "returns a value referencing data owned by the current function"
But I have no idea how I could keep my "Transaction" in my the SQLConnection structure.
use postgres::{Client, NoTls, Transaction};
pub struct SQLConnection<'a> {
client: Client,
transaction: Transaction<'a>,
}
impl<'a> SQLConnection<'a> {
pub fn new(connect_string: &str) -> Self {
let mut client = Client::connect(connect_string, NoTls).unwrap();
let transaction = client.transaction().unwrap();
Self {
client,
transaction,
}
}
pub fn commit(&self) {
let _ = self.transaction.commit();
}
pub fn rollback(&self) {
let _ = self.transaction.rollback();
}
}

You' re returning a reference to a value you don't keep. You can't have a reference if there's no owned value.
In your case, there's also no reason to keep both the client and the transaction. Your connection should wrap the client but not the transaction which is a short lived object and shouldn't be kept for more than just the operation.
Your connection should thus just be
pub struct SQLConnection {
client : Client,
}
Then you should, for an operation, get a transaction, use it, then drop it while keeping the connection.

I found a solution that suits my needs. I have now SQLConnection and SQLTransaction and I get the SQLTransation from the my connection.
Now I can use my transaction to group my elementary sql operations.
Here is the code:
use postgres::{Client, NoTls, Transaction};
pub struct SQLConnection {
client: Client,
}
impl SQLConnection {
pub fn new(connect_string: &str) -> Self {
let client = Client::connect(connect_string, NoTls).unwrap();
Self { client }
}
pub fn transaction(&mut self) -> SQLTransaction {
let t = self.client.transaction().unwrap();
SQLTransaction { transaction: t }
}
}
pub struct SQLTransaction<'a> {
transaction: Transaction<'a>,
}
impl<'a> SQLTransaction<'a> {
pub fn new(transaction: Transaction<'a>) -> Self {
Self { transaction }
}
pub fn commit(self) {
let _ = self.transaction.commit();
}
pub fn rollback(self) {
let _ = self.transaction.rollback();
}
}

Related

How do I keep a global Postgres connection?

I want to store the Postgres connection on global scope to access from any function in a module. Here is an example:
use postgres::{Client, NoTls};
static mut client: Option<Client> = None;
pub fn get_player(id: i32) {
// Use global client connection object:
for row in client.unwrap().query("SELECT * FROM public.\"User\" WHERE \"accountID\"=$1;",&[&id]).unwrap(){
let id: i32 = row.get(0);
let name: &str = row.get(1);
println!("found player: {} {}", id, name);
}
}
pub fn init() {
let mut connection = Client::connect("host=localhost user=postgres", NoTls);
match connection {
Ok(cli) => {
println!("Database connected.");
client = Some(cli);
}
Err(_) => println!("Database ERROR while connecting."),
}
}
It is not compiling & working as intended and I don't know how to make this in Rust.
Here is an example with lazy_static and r2d2_postgres that provides a database connection pool:
use r2d2_postgres::postgres::{NoTls, Client};
use r2d2_postgres::PostgresConnectionManager;
#[macro_use]
extern crate lazy_static;
lazy_static! {
static ref POOL: r2d2::Pool<PostgresConnectionManager<NoTls>> = {
let manager = PostgresConnectionManager::new(
// TODO: PLEASE MAKE SURE NOT TO USE HARD CODED CREDENTIALS!!!
"host=localhost user=postgres password=password".parse().unwrap(),
NoTls,
);
r2d2::Pool::new(manager).unwrap()
};
}
pub fn get_player(id: i32) {
// Use global client connection object:
let mut client = POOL.get().unwrap();
for row in client.query("SELECT * FROM public.\"User\" WHERE \"accountID\"=$1;",&[&id]).unwrap(){
let id: i32 = row.get(0);
let name: &str = row.get(1);
println!("found player: {} {}", id, name);
}
}

Future Combine sink does not recieve any values

I want to add a value to Firestore. When finished I want to return the added value. The value does get added to Firestore successfully. However, the value does not go through sink.
This is the function that does not work:
func createPremium(user id: String, isPremium: Bool) -> AnyPublisher<Bool,Never> {
let dic = ["premium":isPremium]
return Future<Bool,Never> { promise in
self.db.collection(self.dbName).document(id).setData(dic, merge: true) { error in
if let error = error {
print(error.localizedDescription)
} else {
/// does get called
promise(.success(isPremium))
}
}
}.eraseToAnyPublisher()
}
I made a test function that works:
func test() -> AnyPublisher<Bool,Never> {
return Future<Bool,Never> { promise in
promise(.success(true))
}.eraseToAnyPublisher()
}
premiumRepository.createPremium(user: userID ?? "1234", isPremium: true)
.sink { receivedValue in
/// does not get called
print(receivedValue)
}.cancel()
test()
.sink { recievedValue in
/// does get called
print("Test", recievedValue)
}.cancel()
Also I have a similar code snippet that works:
func loadExercises(category: Category) -> AnyPublisher<[Exercise], Error> {
let document = store.collection(category.rawValue)
return Future<[Exercise], Error> { promise in
document.getDocuments { documents, error in
if let error = error {
promise(.failure(error))
} else if let documents = documents {
var exercises = [Exercise]()
for document in documents.documents {
do {
let decoded = try FirestoreDecoder().decode(Exercise.self, from: document.data())
exercises.append(decoded)
} catch let error {
promise(.failure(error))
}
}
promise(.success(exercises))
}
}
}.eraseToAnyPublisher()
}
I tried to add a buffer but it did not lead to success.
Try to change/remove .cancel() method on your subscriptions. Seems you subscribe to the publisher, and then immediately cancel the subscription. The better option is to retain and store all your subscriptions in the cancellable set.

Swift Error: Realm accessed from incorrect thread

I am attempting to use the Realm library to persist data within my application. However, I keep running into the same error code: "Realm accessed from incorrect thread". I attempted to resolve this issue by creating a Realm-specific Dispatch Queue, and wrapping all of my Realm calls in it.
Here is what my "RealmManager" class looks like right now:
import Foundation
import RealmSwift
class RealmManager {
fileprivate static let Instance : RealmManager = RealmManager()
fileprivate var _realmDB : Realm!
fileprivate var _realmQueue : DispatchQueue!
class func RealmQueue() -> DispatchQueue {
return Instance._realmQueue
}
class func Setup() {
Instance._realmQueue = DispatchQueue(label: "realm")
Instance._realmQueue.async {
do {
Instance._realmDB = try Realm()
} catch {
print("Error connecting to Realm DB")
}
}
}
class func saveObjectArray(_ objects: [Object]) {
Instance._realmQueue.async {
do {
try Instance._realmDB.write {
for obj in objects {
Instance._realmDB.add(obj, update: .all)
}
}
} catch {
print("Error Saving Objects")
}
}
}
class func fetch(_ type: Int) -> [Object] {
if let realm = Instance._realmDB {
let results = realm.objects(Squeak.self).filter("type = \(type)")
var returnArray : [Object] = []
for r in results {
returnArray.append(r)
}
return returnArray
}
return []
}
I am calling Setup() inside of didFinishLaunchingWithOptions to instantiate the Realm queue and Realm Db instance.
I am getting the error code inside of saveObjectArray at:
try Instance._realmDB.write { }
This seems to simply be a matter of my misunderstanding of the threading requirements of Realm. I would appreciate any insight into the matter, or a direction to go in from here.
This issue is that you fetch your Realm data on a different thread than you save it.
To fix the error, the code within fetch will also need to run on the Realm thread that you have created.
I think this article does a good job of explaining multi-threading in Realm and particularly recommend paying attention to the three rules outlined in the article.

RxSwift - Concat only on condition

I have 2 stream which are being concatenated. If the first stream executes onError instead of onComplete, I shouldn't be concatenating the second stream.
example:
func updateEntity(entities: [Member]) -> Observable<[Result<Member>]> {
let remoteUpdate = remoteStore.update(entities: entities)
return remoteUpdate.concat(localStore.update(entities: entities))
}
I shouldn't be updating the localStore if remoteUpdate throws an error, onError is called in remoteUpdate
Update:
public override func update(entities: [PlaceAlert]) -> Observable<[Result<PlaceAlert>]> {
let remoteUpdate = remoteStore.update(entities: entities)
var entityPlaceHolder: [PlaceAlert] = entities
return remoteUpdate.catchError { _ in
entityPlaceHolder = []
return localStore.update(entities: entityPlaceHolder)
}.concat(localStore.update(entities: entityPlaceHolder))
}
Just tried improvising. would this make any difference? LocalUpdate with emptyArray does nothing if there is an error
concat is not an if/then statement. Both updates are getting called at the same scope, it's just that the result won't reflect the localStore.update response if the remoteStore errors.
catchError is what you want in this context:
func updateEntity(entities: [Member]) -> Observable<[Result<Member>]> {
let remoteUpdate = remoteStore.update(entities: entities)
return remoteUpdate.catchError { _ in
localStore.update(entities: entities)
}
}

Rust struct field mutable for socket

I am trying to get started with Rust, and was trying to put some pieces together and have a "Server" instance which contains a Vector of "Clients" where each of them have a Socket.
I understand that in Rust the Socket or TcpStream needs to be mutable, and that I need to borrow the reference in order to keep the scope after the Client instantiation in my main loop.
But I faced the issue that my TcpStream field may not be mutable in the Client struct. So I am not sure my approach is correct, but I tried to solve this using the lifetime parameter <'a>, however this leads me to another problem where my "Client" inside "Server" does not pass the <'a> lifetime parameter.
Can someone help me solve this problem or show me the correct approach to this problem / solution?
Thanks.
use std::net::*;
use std::io::Write;
use std::thread;
use std::time::Duration;
struct Client<'a> {
socket: &'a mut TcpStream,
addr: SocketAddr
}
struct Server {
clients: Vec<Box<Client>>
}
impl Server {
pub fn new() -> Server {
Server{clients: Vec::new()}
}
fn write(&self, stream: &mut TcpStream) {
let mut counter: u32 = 0;
counter += 1;
stream.write(counter.to_string().as_bytes()).unwrap();
thread::sleep(Duration::from_secs(1));
}
fn client_thread(&self, client: &mut Client) {
self.write(&mut client.socket);
}
fn add_client(&self, socket: &mut TcpStream, addr: SocketAddr) {
let mut client = Client {
socket: socket,
addr: addr
};
self.clients.push(Box::new(client));
self.client_thread(&mut client);
}
pub fn server_loop(&self) {
let listener = TcpListener::bind("127.0.0.1:5001").unwrap();
loop {
match listener.accept() {
Ok((mut socket, addr)) => {
println!("new client: {:?}", addr);
thread::spawn(move || loop {
self.add_client(&mut socket, addr);
});
},
Err(e) => println!("couldn't get client: {:?}", e),
}
}
}
}
fn main() {
let mut server = Server::new();
server.server_loop();
}
Update:
The current error message is:
clients: Vec<Box<Client>>
^^^^^^ expected lifetime parameter
Update 2:
Now I think the solution is a little bit better / closer to the goal. But I still have a problem with the thread:spawn outside static context.
use std::net::*;
use std::io::Write;
use std::thread;
struct Client {
socket: TcpStream
}
struct Server {
clients: Vec<Box<Client>>
}
impl Server {
fn new() -> Server {
Server{clients: vec![]}
}
fn write(&mut self, stream: &mut TcpStream) {
let mut counter: u32 = 0;
stream.write(counter.to_string().as_bytes()).unwrap();
}
fn client_loop(&mut self, client: &mut Client) {
loop {
self.write(&mut client.socket);
}
}
fn add_client(&mut self, s: TcpStream) {
let mut client = Client{
socket: s
};
self.clients.push(Box::new(client));
println!("New client: {}", client.socket.peer_addr().unwrap());
thread::spawn(move || {
self.client_loop(&mut client);
});
}
pub fn server_loop(&mut self) {
let listener = TcpListener::bind("127.0.0.1:5001").unwrap();
loop {
match listener.accept() {
Ok((socket, _addr)) => {
self.add_client(socket);
},
Err(e) => println!("Couldn't get client: {}", e),
}
}
}
}
fn main() {
let mut server = Server::new();
server.server_loop();
}
error[E0477]: the type [closure#src/main.rs:38:23: 40:10 self:&mut Server, client:Client] does not fulfill the required lifetime
--> src/main.rs:38:9
|
38 | thread::spawn(move || {
| ^^^^^^^^^^^^^
|
= note: type must satisfy the static lifetime
I was able to solve the overall problem now:
use std::net::*;
use std::io::Write;
use std::thread;
struct Client {
socket: TcpStream,
}
impl Client {
pub fn write(&mut self) {
let counter: u32 = 0;
self.socket.write(counter.to_string().as_bytes()).unwrap();
}
}
struct ClientThread {
inner: Client,
}
impl ClientThread {
pub fn client_loop(&mut self) {
let client = &mut self.inner;
client.write();
}
}
struct Server {
_clients: Vec<Box<Client>>,
}
impl Server {
fn new() -> Server {
Server { _clients: vec![] }
}
fn add_client(&mut self, s: TcpStream) {
let client = Client { socket: s };
println!("New client: {}", client.socket.peer_addr().unwrap());
self._clients.push(Box::new(client));
let mut client_thread = ClientThread { inner: client };
thread::spawn(move || loop {
client_thread.client_loop();
});
}
pub fn server_loop(&mut self) {
let listener = TcpListener::bind("127.0.0.1:5001").unwrap();
loop {
match listener.accept() {
Ok((socket, _addr)) => {
self.add_client(socket);
}
Err(e) => println!("Couldn't get client: {}", e),
}
}
}
}
fn main() {
let mut server = Server::new();
server.server_loop();
}