In Firebase and Kotlin, in case of no network connection is there an easy way to handle endless network request looping? - firebase

In the case of network connectivity loss, the following code just loops endlessly and keeps making API calls. Is there a way to cancel with a timeout (for example, 5000 ms) using Firebase API? Or would I have to make my own Coroutine to handle this?
fun updateUserFieldInDB(
collectionPath: String,
strArr: ArrayList<String>,
onSuccess: (() -> Unit),
onFail: (() -> Unit)
) {
val fbUser = Firebase.auth.currentUser
if (fbUser == null) {
Log.i(TAG, "user is null....")
return
}
val db = Firebase.firestore
when (strArr.size) {
2 -> {
db.collection(collectionPath).document(fbUser.uid).update(strArr[0], strArr[1])
.addOnSuccessListener {
onSuccess()
}
.addOnFailureListener {
onFail()
}
}
}
}

The onSuccess ad onFail completion handlers for Firestore only fire once the write operation has been committed or rejected on the server. You should only use them if you're interested in detecting that situation, in which case the looping is to be expected.
If you only care whether the write operation was recorded by the Firestore client (in its local cache), the best way to detect that is when the update(strArr[0], strArr[1]) call completes.
So pretty much: when the next line of code executes, the write has been recorded locally; when the completion listeners fire, the write has been handled on the server.

Related

Problem with handling async inside of an actix-rust actor

Currently I'm checking out Actix, a Rust based actor framework. I'm also using Actix web to build a REST API. Now, I'm familiar with actor based architecture from working with Akka, however, I'm not being able to start a simple async task inside of my handler.
It's simplified, but I have the following code:
#[post("/upload")]
pub async fn upload_images(
app_config: web::Data<AppConfig>,
mut payload: Multipart,
) -> Result<HttpResponse> {
... transforms the multipart form into images...
for img in img_vec {
app_config.image_processor_addr.do_send(ResizeImage{
img_id: img._id,
img_format: img.format,
image_buffer: img.image.bytes,
});
};
Ok(HttpResponse::Ok().content_type(ContentType::plaintext()).body(format!("Inserted {} images.", vec_len)))
}
As you can see, I receive a multipart upload which consists of images, which I then send to an image processing actor to perform a resize on the images.
And this is the simplified code for the handling of the message ResizeImage for the ImageProcessor actor:
impl Handler<ResizeImage> for ImageProcessor {
type Result = ();
fn handle(&mut self, msg: ResizeImage, _: &mut Self::Context) -> Self::Result {
let thumbnail_col = self.thumbnail_col.clone();
let img_col = self.img_col.clone();
let img_format: ImageFormat = msg.img_format.clone().into();
log::info!("Parsing image {} with actor {}.", msg.img_id, self.id);
let actor_task_fut = Box::pin(async move {
... parses the image here...
});
match Arbiter::current().spawn(actor_task_fut) {
true => log::info!("Sent task to arbiter."),
false => log::error!("Failed to send task to arbiter!"),
}
}
}
The idea is that I would resolve the web handler, and the resize task would be done async on the actor thread. However, this works on the first call, but when I call the same endpoint before all the images from the previous call are parsed, it doesn't resolve immediately, it waits till the actor has resized the previous batch.
I was under the impression that the messages would be sent to the actor mailbox and then the handler code would not need to wait for anything, since I'm using do_sent, which the documentation states that it doesn't await for the answer. Using Akka I can easily do something similar, and it seems to work. Am I missing something here? Is the way I'm handling async inside the actor thread wrong?

Spawn reading data from multipart in actix-web

I tried the example of actix-multipart with actix-web v3.3.2 and actix-multipart v0.3.0.
For a minimal example,
use actix_multipart::Multipart;
use actix_web::{post, web, App, HttpResponse, HttpServer};
use futures::{StreamExt, TryStreamExt};
#[post("/")]
async fn save_file(mut payload: Multipart) -> HttpResponse {
while let Ok(Some(mut field)) = payload.try_next().await {
let content_type = field.content_disposition().unwrap();
let filename = content_type.get_filename().unwrap();
println!("filename = {}", filename);
while let Some(chunk) = field.next().await {
let data = chunk.unwrap();
println!("Read a chunk.");
}
println!("Done");
}
HttpResponse::Ok().finish()
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| App::new().service(save_file))
.bind("0.0.0.0:8080")?
.run()
.await
}
This works well, but I want to do with form data asynchronously. So I tried instead:
use actix_multipart::Multipart;
use actix_web::{post, web, App, HttpResponse, HttpServer};
use futures::{StreamExt, TryStreamExt};
#[post("/")]
async fn save_file(mut payload: Multipart) -> HttpResponse {
actix_web::rt::spawn(async move {
while let Ok(Some(mut field)) = payload.try_next().await {
let content_type = field.content_disposition().unwrap();
let filename = content_type.get_filename().unwrap();
println!("filename = {}", filename);
while let Some(chunk) = field.next().await {
let data = chunk.unwrap();
println!("Read a chunk.");
}
println!("Done");
}
});
HttpResponse::Ok().finish()
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| App::new().service(save_file))
.bind("0.0.0.0:8080")?
.run()
.await
}
(Added actix_web::rt::spawn to save_file.)
But this did not work -- the message "Done" never printed. The number of "Read a chunk" displayed in the second case was less than the first case, so I guess that field.next().await cannot terminate for some reason before completing reading all data.
I do not know so much about asynchronous programming, so I am not sure why field.next() did not work in actix_web::rt::spawn.
My question are: why is it, and how can I do with actix_web::rt::spawn?
When you make this call:
actix_web::rt::spawn(async move {
// do things...
});
spawn returns a JoinHandle which is used to poll the task. When you drop that handle (by not binding it to anything), the task is "detached", i.e., it runs in the background.
The actix documentation is not particularly helpful here, but actix uses the tokio runtime under the hood. A key issue is that in tokio, spawned tasks are not guaranteed to complete. The executor needs to know, somehow, that it should perform work on that future. In your second example, the spawned task is never .awaited, nor does it communicate with any other task via channels.
Most likely, the spawned task is never polled and does not make any progress. In order to ensure that it completes, you can either .await the JoinHandle (which will drive the task to completion) or .await some other Future that depends on work in the spawned task (usually by using a channel).
As for your more general goal, the work is already being performed asynchronously! Most likely, actix is doing roughly what you tried to do in your second example: upon receiving a request, it spawns a task to handle the request and polls it repeatedly (as well as the other active requests) until it completes, then sends a response.

Always return Ok HttpResponse then do work in actix-web handler

I have a handler to initiate a password reset. It always returns a successful 200 status code, so that an attacker cannot use it to find out which email addresses are stored in the database. The problem is, if an email is in the database, it'll take a while for the request to be fulfilled (blocking user lookup and sending the actual email with a reset token). If the user is not in the db, the request returns very quickly, so an attacked would know the email is not there.
How would I go about returning the HTTP response right away while processing the request in the background?
pub async fn forgot_password_handler(
email_from_path: web::Path<String>,
pool: web::Data<Pool>,
redis_client: web::Data<redis::Client>,
) -> HttpResponse {
let conn: &PgConnection = &pool.get().unwrap();
let email_address = &email_from_path.into_inner();
// search for user with email address in users table
match users.filter(email.eq(email_address)).first::<User>(conn) {
Ok(user) => {
// some stuff omitted.. this is what happens:
// create random token for user and store a hash of it in redis (it'll expire after some time)
// send email with password reset link and token (not hashed) to client
// then return with
HttpResponse::Ok().finish(),
}
_ => HttpResponse::Ok().finish(),
}
}
You can use an Actix Arbiter to schedule an asynchronous task:
use actix::Arbiter;
async fn do_the_database_stuff(
email: String,
pool: web::Data<Pool>,
redis_client: web::Data<redis::Client>)
{
// async database code here
}
pub async fn forgot_password_handler(
email_from_path: web::Path<String>,
pool: web::Data<Pool>,
redis_client: web::Data<redis::Client>,
) -> HttpResponse {
let email = email_from_path.clone();
Arbiter::spawn(async {
do_the_database_stuff(
email,
pool,
redis_client
);
});
HttpResponse::Ok().finish()
}
If your database code is blocking, to prevent hogging the long-lived Actix worker threads, you could instead create a new Arbiter, with its own thread:
fn do_the_database_stuff(email: String) {
// blocking database code here
}
pub async fn forgot_password_handler(email_from_path: String) -> HttpResponse {
let email = email_from_path.clone();
Arbiter::new().exec_fn(move || {
async move {
do_the_database_stuff(email).await;
};
});
HttpResponse::Ok().finish()
}
This may be a bit more work because Pool and redis::Client are unlikely to be safe to share between threads, so you will have to solve that too. That's why I didn't include them in the example code.
It's better to use Arbiters than be tempted to spawn a new native thread with std::thread. If you mix the two, you can end up accidentally including code that messes up the worker. For example using std::thread::sleep in an async context would pause unrelated tasks that just happen to be scheduled on the same worker, and may not even have any effect on the task you intended.
Finally, you might also consider an architectural change. If you factor database-heavy tasks into their own microservices, you would solve this problem automatically. The web handler can then just send a message (Kafka, RabbitMQ, ZMQ, HTTP, or whatever you choose) and immediately return. This will let you scale the microservices independently of the webserver - 10x web server instances doesn't have to mean 10x database connections, if you only need one instance for the password reset service.

Using the Saturn Framework, how can I get a reference to the Websockets hub outside of a particular request?

I'm building an application for a toy problem to learn more about SAFE. I have some background processes running server-side and occasionally they need to send a message unprompted to the connected clients. This means that I need a reference to the SocketHub from outside of any particular request.
Currently I have a mutable variable which I pass a value to when the Channel is joined:
let mainChannel = channel {
join (fun ctx socketId ->
task {
printfn "Connected! Main Socket Id: %O" socketId
let hub = ctx.GetService<Channels.ISocketHub>()
webSocketHub <- Some hub // Passing the reference to a mutable variable
task {
do! Task.Delay 500
let m = (socketId |> (SetChannelSocketId >> GameData))
do! (harderSendMessage socketId "message" m "Problem sending SocketId")
} |> ignore
return Channels.Ok })
}
However, it seems to me like there should be a better way to get access to the hub - I just can't figure it out.

Realm: Notification after initial sync

According to the docs Realm can notify you when certain actions are taking place like "every time a write transaction is committed". I am using the Realm Object Server and the first time a user opens my app a large set of data is synched from the server down to the app. I would like to show a loading screen and not present the main UI of my app until Realm has completed its initial sync. Is there a way to be notified / determine when this process is complete?
The realm.io website just posted documentation on how to do this.
Asynchronously Opening Realms
If opening a Realm might require a time-consuming operation, such as applying migrations or downloading the remote contents of a synchronized Realm, you should use the openAsync API to perform all work needed to get the Realm to a usable state on a background thread before dispatching to the given queue. You should also use openAsync with Realms that are set read-only.
For example:
Realm.openAsync({
schema: [PersonSchema],
schemaVersion: 42,
migration: function(oldRealm, newRealm) {
// perform migration (see "Migrations" in docs)
}
}, (error, realm) => {
if (error) {
return;
}
// do things with the realm object returned by openAsync to the callback
console.log(realm);
})
The openAsync command takes a configuration object as its first parameter and a callback as its second; the callback function receives a boolean error flag and the opened Realm.
Initial Downloads
In some cases, you might not want to open a Realm until it has all remote data available. In such a case, use openAsync. When used with a synchronized Realm, this will download all of the Realm’s contents before the callback is invoked.
var carRealm;
Realm.openAsync({
schema: [CarSchema],
sync: {
user: user,
url: 'realm://object-server-url:9080/~/cars'
}
}, (error, realm) => {
if (error) {
return;
}
// Realm is now downloaded and ready for use
carRealm = realm;
});

Resources