Hi, I am using the new RPC service

Hi, I am using the new RPC service bindings and generally the binding seems to work. I can call a remote function and it returns the expected value. However when I checked the logs, it shows "outcome: canceled" for some reason. What is weird is that it seems to return properly nevertheless. Any ideas why it shows "canceled"?
9 Replies
steyblind
steyblind9mo ago
I'm running into this too. I'm experimenting with a few different variations of the RPC (like returning a function and using the using keyword). I also have a support post in the #workers-help about this. Drop a note if you figure it out
Tilon
TilonOP9mo ago
So far my theory is that this might be just a "bug" in the way how the logging is processed for the RPC worker. Maybe what we see are the logs for the "fetch" handler of the bound worker. Since we are not calling the fetch handler directly anymore, but the other class methods via RPC, the fetch handler might still be invoked as side effect (or looks like it's invoked) and then canceled, because it never returns a Response object, since we are getting our result via the RPC method, not the fetch call. If this theory is somewhat correct, then it might just looks bad in the logs, but it's not really an issue in terms of the functionality of the worker.
steyblind
steyblind9mo ago
Sorry I didn’t drop a note. I ended up working around this by passing a function instead of calling one. That revealed an exception in my remote d1 call that may have been the root of the problem
Justin
Justin9mo ago
I'm seeing the same thing. Functionally everything seems to work as expected, but the "outcome: canceled" logs are disconcerting. In my case I have a really simple Durable Object defined:
import { DurableObject } from 'cloudflare:workers'

export class ZoneState extends DurableObject {
async getState() {
return { test: "testing" }
}
}
import { DurableObject } from 'cloudflare:workers'

export class ZoneState extends DurableObject {
async getState() {
return { test: "testing" }
}
}
Being called by a super simple Worker that's consuming a queue:
export default {
async queue(batch: MessageBatch, env: Env): Promise<void> {
const zoneStateDurObj = env.ZONE_STATE.get(env.ZONE_STATE.idFromName('milldistrict'))
const state = await zoneStateDurObj.getState()
console.log(JSON.stringify(state))
},
}
export default {
async queue(batch: MessageBatch, env: Env): Promise<void> {
const zoneStateDurObj = env.ZONE_STATE.get(env.ZONE_STATE.idFromName('milldistrict'))
const state = await zoneStateDurObj.getState()
console.log(JSON.stringify(state))
},
}
I get the JSON object from the DurObj and it's logged as expected, but I also get a log like:
{
"outcome": "canceled",
"scriptVersion": {
"id": "eda5fc7a-50c7-4d30-82b4-29e975cc23c4"
},
"entrypoint": "ZoneState",
"scriptName": "zone-state-development",
"diagnosticsChannelEvents": [],
"exceptions": [],
"logs": [],
"eventTimestamp": 1712840487484,
"event": {
"rpcMethod": "getState"
},
"id": 3
}
{
"outcome": "canceled",
"scriptVersion": {
"id": "eda5fc7a-50c7-4d30-82b4-29e975cc23c4"
},
"entrypoint": "ZoneState",
"scriptName": "zone-state-development",
"diagnosticsChannelEvents": [],
"exceptions": [],
"logs": [],
"eventTimestamp": 1712840487484,
"event": {
"rpcMethod": "getState"
},
"id": 3
}
The workaround of passing a function won't work for me as I need to actually work with storage in the DurObj. Any further thoughts on what might be going on here would be most appreciated!
Justin
Justin9mo ago
I think it has something to do with the calling Worker's execution ending before the DurObj (in my case) ends as per the docs: "A Worker invoked via RPC also has an execution context. The context begins when an RPC method on a WorkerEntrypoint is invoked. If no stubs are passed in the parameters or results of this RPC, the context ends (the event is “done”) when the RPC returns. However, if any stubs are passed, then the execution context is implicitly extended until all such stubs are disposed (and all calls made through them have returned). As with HTTP, if the client disconnects, the server’s execution context is canceled immediately, regardless of whether stubs still exist. A client that is itself another Worker is considered to have disconnected when its own execution context ends. Again, the context can be extended with ctx.waitUntil()."
Cloudflare Docs
Workers RPC — Lifecycle · Cloudflare Workers docs
Memory management, resource management, and the lifecycle of RPC stubs.
steyblind
steyblind9mo ago
This is great, thanks for diving deeper on this
Richard
Richard4mo ago
@steyblind Did you find a solution in the end? Have a similar situation using RPC and even with a very basic function I see outcome: cancelled in the logs (even though everything works as expected).
steyblind
steyblind4mo ago
I gave up until it’s more mature. I did make some progress by setting ctx. Pass through exceptions and by passing functions @Richard
Richard
Richard4mo ago
Thanks @steyblind - would you be able to share a simple example with setting ctx? The following worked for me for a simple use case const result = env.RPC_WORKER.doSomething() try { // do some other stuff } catch (e) { // handler errors } finally { resultSymbol.dispose }
Want results from more Discord servers?
Add your server