Massive speed up during invitation creation at the expense of reliability. Document method for creating a reliability manager of some sort
This commit is contained in:
52
readme.md
52
readme.md
@@ -155,3 +155,55 @@ xo-tui
|
||||
# If not globally installed
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## TODO
|
||||
|
||||
### Track invitation sync-server connectivity without blocking the UI
|
||||
|
||||
Each `Invitation` currently owns a `SyncServer` instance for its invitation
|
||||
identifier. The invitation uses that instance to open an SSE connection, fetch
|
||||
remote state, and publish local changes. Publish requests are intentionally
|
||||
fire-and-forget so that invitation actions and the TUI stay responsive when the
|
||||
sync server is slow or unavailable.
|
||||
|
||||
The tradeoff is that failed background requests and SSE connection changes are
|
||||
not represented as application state. `SyncServer` already emits `connected`,
|
||||
`disconnected`, and `error` events, and `Invitation` emits errors from failed
|
||||
publishes, but there is no app-level owner that aggregates those events. The UI
|
||||
therefore cannot reliably tell the user that an invitation may only be updated
|
||||
locally and is not currently syncing with other participants.
|
||||
|
||||
Implement an app-owned `InvitationConnectivityService` (or similarly named
|
||||
invitation watcher) with the following responsibilities:
|
||||
|
||||
- Register an invitation and its `SyncServer` when `AppService` creates or loads
|
||||
it, and unregister it when the invitation is removed or stopped.
|
||||
- Listen for each sync server's `connected`, `disconnected`, and `error` events,
|
||||
plus invitation publish failures.
|
||||
- Track connectivity separately from the invitation's business status
|
||||
(`actionable`, `signed`, `ready`, and so on). Suggested transport states are
|
||||
`connecting`, `online`, `offline`, and `degraded`, with the last error and
|
||||
last successful connection timestamp available for diagnostics.
|
||||
- Expose both per-invitation state and an aggregate app-level state such as
|
||||
"one or more invitations are not syncing".
|
||||
- Emit normalized connectivity-change events that the CLI can log and the TUI
|
||||
can subscribe to without awaiting sync-server requests.
|
||||
|
||||
Keep local persistence and local invitation actions independent from remote
|
||||
sync health. Failed sync attempts should not freeze normal wallet interaction.
|
||||
The service should provide a retry path, or observe retry events from the SSE
|
||||
client, and clear the warning after connectivity recovers. If publish retries
|
||||
are added, make the retry policy explicit and preserve commit idempotency.
|
||||
|
||||
For UI integration, inject a small notification function or subscribe at the
|
||||
app-context layer rather than having invitation instances render UI directly.
|
||||
The first version can show an error dialog when the aggregate state becomes
|
||||
unhealthy. A less intrusive version can expose the same state as a warning icon
|
||||
or message in the TUI status bar and reserve dialogs for prolonged failures or
|
||||
explicit user actions.
|
||||
|
||||
While making this change, consolidate invitation startup ownership. Startup is
|
||||
currently triggered during `Invitation.create()` and again by
|
||||
`AppService.createInvitation()`. The watcher should have one clear lifecycle
|
||||
point so connections, listeners, retries, and cleanup are registered exactly
|
||||
once.
|
||||
|
||||
@@ -173,7 +173,7 @@ export class AppService extends EventEmitter<AppEventMap> {
|
||||
|
||||
// Attach listeners before SSE connects so updates are not missed.
|
||||
await this.addInvitation(invitationInstance);
|
||||
await invitationInstance.start();
|
||||
invitationInstance.start();
|
||||
|
||||
return invitationInstance;
|
||||
}
|
||||
|
||||
@@ -224,12 +224,9 @@ export class Invitation extends EventEmitter<InvitationEventMap> {
|
||||
private async publishInvitation(
|
||||
invitation: XOInvitation = this.data,
|
||||
): Promise<void> {
|
||||
try {
|
||||
await this.syncServer.publishInvitation(invitation);
|
||||
} catch (err) {
|
||||
// Emit the error event. We might want to throw? but we need a better way of handling errors in the invitation system because we need the invitation to successfully initialize.
|
||||
this.emit("error", err instanceof Error ? err : new Error(String(err)));
|
||||
}
|
||||
this.syncServer.publishInvitation(invitation).catch((error) => {
|
||||
this.emit("error", error instanceof Error ? error : new Error(String(error)));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -374,9 +371,13 @@ export class Invitation extends EventEmitter<InvitationEventMap> {
|
||||
* Update the status of the invitation and emit the new single-word status.
|
||||
*/
|
||||
private async updateStatus(): Promise<void> {
|
||||
const status = await this.computeStatus();
|
||||
this.status = status;
|
||||
this.emit("invitation-status-changed", status);
|
||||
this.computeStatus().then(status => {
|
||||
this.status = status;
|
||||
this.emit("invitation-status-changed", status);
|
||||
}).catch((error) => {
|
||||
this.status = `error (${error instanceof Error ? error.message : String(error)})`;
|
||||
this.emit("error", error instanceof Error ? error : new Error(String(error)));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user