Send/Sync and async in Workers

I've been trying to use async with workers and it's becoming an ever greater problem 😅. I wanted to see if anyone has thoughts on how to solve this problem other than forever trying to play wackamole and use workarounds. Relatively simple example: I'd like to be able to define async blocks in a bigger function to do a few things in 'parallel', the 'easiest' way to do this is with something like BoxFuture where I pin the individual async blocks and put them in a FuturesUnordered<BoxFuture<'_, Result<()>>> except this doesn't work because - Bucket is not Sync/Send - ok, easy to fix, but annoying that I have to do this and indicative of the larger problem - Rc<RefCell<wasm_bindgen_futures::Inner>> is not Sync/Send - much more difficult to fix since I'd have to rewrite a bunch of the API I wanted to have some discussion on this topic because I think it's a serious issue with workers-rs currently and I don't think wackamole is the right solution here, I wanted to potentially suggest that the crate instead consider pretending it does run in an async environment when it interacts with other types (use arc instead of rc, Rwlock instead of RefCell, etc) and tank the (let's be honest) negligible performance hit. I think this is especially problematic with workers where you are likely to be doing a lot of async operations and being able to easily split up chains of work is going to have a real performance impact, so the crate should be looking to make this as ergonomic as possible.
6 Replies
Unknown User
Unknown User•6mo ago
Message Not Public
Sign In & Join Server To View
Enduriel
Enduriel•6mo ago
busy with other stuff right now so I'm just gonna send the full function, the issue is with JsFuture
#[worker::send]
async fn update_lang(env: &APIEnv, lang: &Language) -> Result<()> {
let db = env.db();

let full_lang_json =
serde_json::to_string(&db::actions::get_all_text_map_for_lang(&db, lang).await?)?;

let mut futs: FuturesUnordered<BoxFuture<'_, Result<()>>> = FuturesUnordered::new();
let bucket = env.bucket();

futs.push(Box::pin(async {
let lang_json = serde_json::to_string(
&db::actions::get_translated_text_map_for_lang(&db, lang).await?,
)?;
bucket
.put(lang.language_code.to_string(), lang_json)
.execute()
.await?;

Ok::<(), WorkerLocError>(())
}));
futs.push(Box::pin(async {
let full_lang_json =
serde_json::to_string(&db::actions::get_all_text_map_for_lang(&db, lang).await?)?;
bucket
.put(&format!("{}_full", lang.language_code), full_lang_json)
.execute()
.await?;

Ok::<(), WorkerLocError>(())
}));

while let Some(fut) = futs.next().await {
fut?;
}

Ok(())
}
#[worker::send]
async fn update_lang(env: &APIEnv, lang: &Language) -> Result<()> {
let db = env.db();

let full_lang_json =
serde_json::to_string(&db::actions::get_all_text_map_for_lang(&db, lang).await?)?;

let mut futs: FuturesUnordered<BoxFuture<'_, Result<()>>> = FuturesUnordered::new();
let bucket = env.bucket();

futs.push(Box::pin(async {
let lang_json = serde_json::to_string(
&db::actions::get_translated_text_map_for_lang(&db, lang).await?,
)?;
bucket
.put(lang.language_code.to_string(), lang_json)
.execute()
.await?;

Ok::<(), WorkerLocError>(())
}));
futs.push(Box::pin(async {
let full_lang_json =
serde_json::to_string(&db::actions::get_all_text_map_for_lang(&db, lang).await?)?;
bucket
.put(&format!("{}_full", lang.language_code), full_lang_json)
.execute()
.await?;

Ok::<(), WorkerLocError>(())
}));

while let Some(fut) = futs.next().await {
fut?;
}

Ok(())
}
but basically the idea is that I want be able to make a couple chains of async operations that can run independently, which should be relatively straightforward with some BoxFutures, but unfortunately that is not possible easily with workers :/
Unknown User
Unknown User•6mo ago
Message Not Public
Sign In & Join Server To View
Enduriel
Enduriel•6mo ago
yeah that does actually work, didn't know that's a thing as well, thanks as to the bigger problem yeah I think it's genuinely worth consider just switching over to stuff that is actually send/sync because like I said the overhead is definitely negligible There aren't even currently threads/atomics in wasm afaik so it might even be compiled out?
Unknown User
Unknown User•6mo ago
Message Not Public
Sign In & Join Server To View
Enduriel
Enduriel•6mo ago
Oh I completely missed that that was in wasm-bindgen-futures and not in the worker-rs code, my bad, I'm not actually really sure if there's anything else that could be done then, that really sucks alternatively set up testing to make sure that every single exposed part of the API is sync/send and do it that way? I'm saying conceptually that would be a theoretically good solution xD
Want results from more Discord servers?
Add your server