-
Notifications
You must be signed in to change notification settings - Fork 29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Build guide: signals #534
base: main
Are you sure you want to change the base?
Build guide: signals #534
Conversation
client.on("signal", (signal: Signal) => { | ||
// There's currently only one useful signal type to listen for -- an | ||
// app signal. | ||
if (!(SignalType.App in signal)) return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is about to change in holochain 0.5 with the new enum serialization. Just as a heads up. It will be something like
if (signal.type !== "app") return;
## Remote signals | ||
|
||
Agents can also send remote signals to each other using the [`send_remote_signal`](https://docs.rs/hdk/latest/hdk/p2p/fn.send_remote_signal.html) host function and a [`recv_remote_signal` callback](/build/callbacks-and-lifecycle-hooks/#define-a-recv-remote-signal-callback), which takes a single argument of any type and returns `ExternResult<()>`. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it would be worth writing here about the distinction from a remote call, i.e. that these remote signals are not waiting for a response whatsoever and have no guarantee of delivery.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes agree that should be documented but the same is actually true of local signals. No guarantees in either case. I've commented similarly above.
pub fn recv_remote_signal(payload: RemoteSignal) -> ExternResult<()> { | ||
if let RemoteSignal::Heartbeat = payload { | ||
let caller = call_info()?.provenance; | ||
emit_signal(LocalSignal::Heartbeat(caller))?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
emit_signal(LocalSignal::Heartbeat(caller))?; | |
// On the receiving end we forward the remote signal to the front-end by emitting a local signal | |
emit_signal(LocalSignal::Heartbeat(caller))?; |
src/pages/build/signals.md
Outdated
``` | ||
|
||
!!! info Remote signal handlers are just zome functions | ||
`send_remote_signal` is sugar for a [remote call](/build/calling-zome-functions/#call-a-zome-function-from-another-agent-in-the-network) to a zome function named `recv_remote_signal` with no capability secret<!-- TODO: link to capabilities page -->. It works differently from a usual remote call, though, in that it's 'send-and-forget' --- it won't return an error if anything fails. In practice, these two are equivalent: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
send_remote_signal
is sugar for a remote call to a zome function namedrecv_remote_signal
with no capability secret
Is this really correct? Because as written above, there is no timeout involved so I'm wondering whether there is actually a difference of how it's implemented under the hood but I'm not familiar with that part of the holochain code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I'm actually sceptical that they are equivalent in practice. Have you tried that out, including situations where peers to which a heartbeat is sent are offline?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmmmm, I forgot that there's a timeout that gets ignored at the core/ribosome level. Good point.
|
||
### Emit a signal | ||
|
||
Your coordinator zome emits a signal with the [`emit_signal`](https://docs.rs/hdk/latest/hdk/p2p/fn.emit_signal.html) host function. You can call this function from a regular [zome function](/build/zome-functions/) or the [`init`](/build/callbacks-and-lifecycle-hooks/#define-an-init-callback), [`recv_remote_signal`](/build/callbacks-and-lifecycle-hooks/#define-a-recv-remote-signal-callback), or [`post_commit`](/build/callbacks-and-lifecycle-hooks/#define-a-post-commit-callback) callbacks. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your coordinator zome emits a signal with the [`emit_signal`](https://docs.rs/hdk/latest/hdk/p2p/fn.emit_signal.html) host function. You can call this function from a regular [zome function](/build/zome-functions/) or the [`init`](/build/callbacks-and-lifecycle-hooks/#define-an-init-callback), [`recv_remote_signal`](/build/callbacks-and-lifecycle-hooks/#define-a-recv-remote-signal-callback), or [`post_commit`](/build/callbacks-and-lifecycle-hooks/#define-a-post-commit-callback) callbacks. | |
Your coordinator zome emits a signal with the [`emit_signal`](https://docs.rs/hdk/latest/hdk/p2p/fn.emit_signal.html) host function. It takes any serializable input and you can call this function from a regular [zome function](/build/zome-functions/) or the [`init`](/build/callbacks-and-lifecycle-hooks/#define-an-init-callback), [`recv_remote_signal`](/build/callbacks-and-lifecycle-hooks/#define-a-recv-remote-signal-callback), or [`post_commit`](/build/callbacks-and-lifecycle-hooks/#define-a-post-commit-callback) callbacks. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking good. I have a few minor comments but mostly happy. I like how focused this one is on giving code examples and just enough description.
|
||
### Listen for a signal | ||
|
||
The UI subscribes to signals with the [`AppWebsocket.prototype.on`](https://github.com/holochain/holochain-client-js/blob/main/docs/client.appwebsocket.on.md) method. The signal handler should expect signals from any coordinator zome in any cell in the agent's hApp instance, and can discriminate between them by cell ID and zome name. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer to say something like "Signals are emitted on by Holochain on connected app websockets. A Holochain client will provide a way to receive these signals." Then, "For example, with the TypeScript client..."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Part of my reason for that, is something I can see is missing. If there are no connected app websockets then Holochain doesn't buffer messages. There's no guaranteed delivery, and no way for the app to know if anybody was listening. It's just a broadcast and anything that happens to receive it can act on the information.
I think this would be one of the gotchas that the ticket was asking for.
import type { Signal, AppSignal, AgentPubKey } from "@holochain/client"; | ||
import { SignalType, encodeHashToBase64 } from "@holochain/client"; | ||
|
||
// Duplicate your zome's signal types in the UI. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Duplicate your zome's signal types in the UI. | |
// Represent your zome's signal types in the UI. |
Maybe? It's not really duplication, they can't share code really. People are also free to set up code generation if they want.
// There's currently only one useful signal type to listen for -- an | ||
// app signal. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe just leave this as "Signals coming from a coordinator are of the App type"? I don't think it's that accurate to say system signals aren't useful even though they're currently only used in countersigning. Then there's also the maintenance of comments like this as Holochain changes. The chances that we'll remember to update this is low.
## Remote signals | ||
|
||
Agents can also send remote signals to each other using the [`send_remote_signal`](https://docs.rs/hdk/latest/hdk/p2p/fn.send_remote_signal.html) host function and a [`recv_remote_signal` callback](/build/callbacks-and-lifecycle-hooks/#define-a-recv-remote-signal-callback), which takes a single argument of any type and returns `ExternResult<()>`. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes agree that should be documented but the same is actually true of local signals. No guarantees in either case. I've commented similarly above.
``` | ||
|
||
!!! info Remote signal handlers are just zome functions | ||
`send_remote_signal` is sugar for a [remote call](/build/calling-zome-functions/#call-a-zome-function-from-another-agent-in-the-network) to a zome function named `recv_remote_signal` with no capability secret<!-- TODO: link to capabilities page -->. It works differently from a usual remote call, though, in that it's 'send-and-forget' --- it doesn't block execution waiting for a response, and it doesn't return an error if anything fails. Other than that, the following two are roughly equivalent. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
`send_remote_signal` is sugar for a [remote call](/build/calling-zome-functions/#call-a-zome-function-from-another-agent-in-the-network) to a zome function named `recv_remote_signal` with no capability secret<!-- TODO: link to capabilities page -->. It works differently from a usual remote call, though, in that it's 'send-and-forget' --- it doesn't block execution waiting for a response, and it doesn't return an error if anything fails. Other than that, the following two are roughly equivalent. | |
`send_remote_signal` is sugar for a [remote call](/build/calling-zome-functions/#call-a-zome-function-from-another-agent-in-the-network) to a zome function named `recv_remote_signal`. This target function exists by convention and must be given an `Unrestricted` capability grant for this to work. <!-- TODO: link to capabilities page -->. It works differently from a usual remote call, though, in that it's 'send-and-forget' --- it doesn't block execution waiting for a response, and it doesn't return an error if anything fails. Other than that, the following two are roughly equivalent. |
} | ||
``` | ||
|
||
This means an agent needs to set up an [`Unrestricted` capability grant](https://docs.rs/holochain_integrity_types/latest/holochain_integrity_types/capability/enum.CapAccess.html#variant.Unrestricted)<!--TODO: link to capabilities page --> for it, so other agents can call it. Take care that this function does as little as possible, to avoid people abusing it. Permissions and privileges are another topic which we'll talk about soon.<!-- TODO: delete this sentence --> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh okay, we're saying something about this in two places, maybe we can unify?
} | ||
|
||
#[hdk_extern] | ||
pub fn send_heartbeat(receivers: Vec<AgentPubKey>) -> ExternResult<()> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we're documenting with code like this, is it explicitly worth calling out that this forces Holochain to open and maintain connections for the heartbeat? So you wouldn't want logic that connects to everyone who is using your app. Holochain isn't designed to operate that many connections. It's fine like we do for syn with 2-10 people updating stickies, but in general you shouldn't be deliberately opening connections to every agent on the network.
Maybe that's out of scope or maybe that's a motivation to keep code samples even more minimal? This could just be a message getting sent, rather than a pattern
Closes #471 . Covers both local and remote signals.