fix(generator): emit async fn for service-method handlers — closes #137 #138
No reviewers
Labels
No labels
prio_critical
prio_low
type_bug
type_contact
type_issue
type_lead
type_question
type_story
type_task
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
lhumina_code/hero_rpc!138
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "issue-137-svc-handler-async"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #137.
What this PR does
Closes the codegen-asymmetry gap left by hero_rpc#133. That PR converted the CRUD handler emitter to
async fnand droppedspawn_blockingaround CRUD dispatch. It did NOT touch the service-method handler emitter — that still produced syncfn. The result, exposed by hero_rpc#134 (generated E2E tests for service-block methods): anyone whose service-method body needed to call async code had to re-introduce ablock_in_place(|| Handle::current().block_on(...))shim — exactly the pattern #133 was supposed to eliminate.The hero_service template carried that workaround in
catalog/rpc.rs(#11 there); the matching hero_service PR (filed alongside this one) drops it.Before / after
Zero sync↔async crossings now in any generated handler in any (CRUD or service) path.
Changes
Commit
27ea6ae— generator: emitasync fnfor service-method handlerscrates/generator/src/rust/rust_rpc.rs::generate_trait_method— service-trait method signatures nowasync fn, matching the SDK-side#[method(...)] async fnfrombuild/emit/rust_rpc2.rs::render_service_methods.Commit
c57b90b— osis: dropspawn_blockingaround service-method dispatchcrates/osis/src/rpc/server.rs— dropspawn_blockingwrappers at the service-method dispatch sites (~line 1440, ~line 1915 by old numbering). Direct.awaitnow. The retainedspawn_blockingcalls at lines ~905, ~992, ~1401, ~1869 are around the legacy syncOsisRpcHandler::handle_request(seed / bootstrap path) — not in scope for this issue.crates/osis/src/rpc/dispatch.rs,handler.rs— doc-comment updates reflecting the new contract.examples/recipe_example/inject/crates/hero_recipes_server/src/recipes/rpc.rs+tests_error_category.rs— regenerated handler is async; the matching hand-written test fixture drops its block_on bridge.Validation
cargo build --workspaceclean.cargo test -p hero_rpc_osis --features rpc --lib— 85/85 pass.cargo test -p hero_rpc_generator --lib— 145/145 pass.grep -rn "spawn_blocking" crates/osis/src/rpc/— remaining sites are the legacy syncOsisRpcHandlerbridge in the seed/bootstrap path (intentional, not in scope for this issue).Links
Close the codegen gap left by hero_rpc#133 — the CRUD-handler emitter gained `async fn` there, but the service-method handler emitter still produced sync `fn`. Anyone whose service-method body needed to call async code (the indexer, a cross-domain SDK client, an HTTP/AI client) had to re-introduce a sync↔async bridge — exactly the `block_in_place(|| Handle::current().block_on(...))` shim that hero_rpc#134 had to paste into hero_service's `catalog/rpc.rs` after regenerating against the new service-method E2E tests. Changes to `crates/generator/src/rust/rust_rpc.rs`: - `generate_trait_method` — service-trait method signatures now emit `async fn` (matches the SDK-side `#[method(...)] async fn` already emitted by `build/emit/rust_rpc2.rs::render_service_methods`). - `generate_method_impl` (auto-CRUD impls) and `generate_method` (todo! stubs) follow the same async-fn switch, so the scaffolded contributor file lines up with the trait. - `generate_trait_impl` (the standalone todo! stub generator) also emits `async fn`. Changes to `crates/generator/src/rust/rust_osis.rs`: - `generate_service_rpc_methods` — the per-method `_rpc_*` helper that unwraps params, takes the handler lock, and dispatches into the user-implemented service trait is now `async fn`, and it `.await`s the trait call. To keep the dispatcher future `Send` across the await, the helper clones an `Arc<Handler>` and drops the `std::sync::RwLockReadGuard` *before* awaiting — holding a sync guard across an `.await` would make the future non-`Send` and block the executor. The service-handler field type changes accordingly from `RwLock<Option<Handler>>` to `RwLock<Option<Arc<Handler>>>`, and `init_handlers` wraps the constructed handler in `Arc::new`. - The dispatch arm in `handle_rpc` for service methods now `.await`s the helper — chaining the all-async `handle_rpc → svc_rpc → user trait` path with zero sync↔async bridges. Net result: handler bodies generated for `service Foo { … }` blocks are async at the trait surface, so users can `self.domain.x_new(...) .await`, hit the indexer, or call another service's SDK directly without re-introducing a blocking shim. Closes the gap that #133 opened and #134 papered over. Refs hero_rpc#133, hero_rpc#134, hero_rpc#137