mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
perf(desktop): batch workflows overview into one relay query (#h fanout)
L6 finding: WorkflowsView's allWorkflowsQuery issued one get_channel_workflows relay POST per member channel (Promise.all fanout) — N round-trips scaling with channel count on the Workflows overview. A nostr #h filter matches ANY listed value, so one query with all channel ids returns the identical set. Added get_channels_workflows(channel_ids) which queries once; each WorkflowWire already carries its own channel_id (from the event h tag), so WorkflowsView groups results client-side via a channelId->name map. Neither the per-channel nor the batched command sets a limit, so batching does not change result completeness (review-bar #3: no shared-limit truncation). e2eBridge gains a matching get_channels_workflows mock so overview e2e still resolves. Single-channel get_channel_workflows stays for useChannelWorkflowsQuery. Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
This commit is contained in:
co-authored by
Tyler Longwell
parent
af31c43860
commit
af5d5e4087
@@ -66,6 +66,36 @@ pub async fn get_channel_workflows(
|
||||
Ok(events.iter().map(workflow_from_event).collect())
|
||||
}
|
||||
|
||||
/// Fetch workflows across many channels in a single relay round-trip.
|
||||
///
|
||||
/// The Workflows overview screen previously issued one `get_channel_workflows`
|
||||
/// query per member channel (`Promise.all` fanout in `WorkflowsView`), i.e. N
|
||||
/// relay POSTs. A nostr `#h` filter matches ANY of its listed values, so one
|
||||
/// query with all channel ids returns the same set. Each `WorkflowWire` carries
|
||||
/// its own `channel_id` (from the event's `h` tag), so the frontend can still
|
||||
/// group results by channel. Neither this nor the per-channel command sets a
|
||||
/// `limit`, so batching does not change result completeness.
|
||||
#[tauri::command]
|
||||
pub async fn get_channels_workflows(
|
||||
channel_ids: Vec<String>,
|
||||
state: State<'_, AppState>,
|
||||
) -> Result<Vec<WorkflowWire>, String> {
|
||||
if channel_ids.is_empty() {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
|
||||
let events = query_relay(
|
||||
&state,
|
||||
&[serde_json::json!({
|
||||
"kinds": [30620],
|
||||
"#h": channel_ids,
|
||||
})],
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(events.iter().map(workflow_from_event).collect())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_workflow(
|
||||
workflow_id: String,
|
||||
|
||||
@@ -569,6 +569,7 @@ pub fn run() {
|
||||
parse_persona_files,
|
||||
export_persona_to_json,
|
||||
get_channel_workflows,
|
||||
get_channels_workflows,
|
||||
get_workflow,
|
||||
create_workflow,
|
||||
update_workflow,
|
||||
|
||||
@@ -10,7 +10,7 @@ import { WorkflowDialog } from "@/features/workflows/ui/WorkflowDialog";
|
||||
import type { Channel, Workflow } from "@/shared/api/types";
|
||||
import {
|
||||
deleteWorkflow,
|
||||
getChannelWorkflows,
|
||||
getChannelsWorkflows,
|
||||
triggerWorkflow,
|
||||
} from "@/shared/api/tauriWorkflows";
|
||||
import { Button } from "@/shared/ui/button";
|
||||
@@ -83,15 +83,21 @@ export function WorkflowsView({
|
||||
const allWorkflowsQuery = useQuery({
|
||||
queryKey: allWorkflowsQueryKey(channelIdKey),
|
||||
queryFn: async () => {
|
||||
const results: WorkflowWithChannel[] = [];
|
||||
await Promise.all(
|
||||
memberChannels.map(async (channel) => {
|
||||
const workflows = await getChannelWorkflows(channel.id);
|
||||
for (const workflow of workflows) {
|
||||
results.push({ workflow, channelName: channel.name });
|
||||
}
|
||||
}),
|
||||
// Single batched relay query for all member channels, then group by the
|
||||
// channel_id each workflow carries — replaces the per-channel fanout.
|
||||
const channelNameById = new Map(
|
||||
memberChannels.map((channel) => [channel.id, channel.name]),
|
||||
);
|
||||
const workflows = await getChannelsWorkflows(channelIds);
|
||||
const results: WorkflowWithChannel[] = [];
|
||||
for (const workflow of workflows) {
|
||||
results.push({
|
||||
workflow,
|
||||
channelName: workflow.channelId
|
||||
? (channelNameById.get(workflow.channelId) ?? "")
|
||||
: "",
|
||||
});
|
||||
}
|
||||
return results;
|
||||
},
|
||||
enabled: memberChannels.length > 0,
|
||||
|
||||
@@ -169,6 +169,22 @@ export async function getChannelWorkflows(
|
||||
return raw.map(fromRawWorkflow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch workflows across many channels in a single relay round-trip.
|
||||
*
|
||||
* Replaces the per-channel `Promise.all(getChannelWorkflows)` fanout on the
|
||||
* Workflows overview: the backend `#h` filter matches any listed channel, and
|
||||
* each returned workflow carries its own `channelId` so callers can group.
|
||||
*/
|
||||
export async function getChannelsWorkflows(
|
||||
channelIds: string[],
|
||||
): Promise<Workflow[]> {
|
||||
const raw = await invokeTauri<RawWorkflow[]>("get_channels_workflows", {
|
||||
channelIds,
|
||||
});
|
||||
return raw.map(fromRawWorkflow);
|
||||
}
|
||||
|
||||
export async function getWorkflow(workflowId: string): Promise<Workflow> {
|
||||
const raw = await invokeTauri<RawWorkflow>("get_workflow", { workflowId });
|
||||
return fromRawWorkflow(raw);
|
||||
|
||||
@@ -2368,6 +2368,13 @@ function handleGetChannelWorkflows(args: { channelId: string }) {
|
||||
return mockWorkflows.filter((w) => w.channel_id === args.channelId);
|
||||
}
|
||||
|
||||
function handleGetChannelsWorkflows(args: { channelIds: string[] }) {
|
||||
const ids = new Set(args.channelIds);
|
||||
return mockWorkflows.filter(
|
||||
(w) => w.channel_id != null && ids.has(w.channel_id),
|
||||
);
|
||||
}
|
||||
|
||||
function handleGetWorkflow(args: { workflowId: string }) {
|
||||
const workflow = mockWorkflows.find((w) => w.id === args.workflowId);
|
||||
if (!workflow) throw new Error(`Workflow ${args.workflowId} not found`);
|
||||
@@ -8806,6 +8813,10 @@ export function maybeInstallE2eTauriMocks() {
|
||||
return handleGetChannelWorkflows(
|
||||
payload as Parameters<typeof handleGetChannelWorkflows>[0],
|
||||
);
|
||||
case "get_channels_workflows":
|
||||
return handleGetChannelsWorkflows(
|
||||
payload as Parameters<typeof handleGetChannelsWorkflows>[0],
|
||||
);
|
||||
case "get_workflow":
|
||||
return handleGetWorkflow(
|
||||
payload as Parameters<typeof handleGetWorkflow>[0],
|
||||
|
||||
Reference in New Issue
Block a user