Skip to content

Commit 9f045af

Browse files
atrakhConvex, Inc.
authored andcommitted
dashboard: keep streaming in progress requests when paused (#41721)
GitOrigin-RevId: 8713d42d15e69b8ce78f2de42521f3d9975fc200
1 parent 593a61c commit 9f045af

File tree

7 files changed

+214
-243
lines changed

7 files changed

+214
-243
lines changed

npm-packages/dashboard-common/src/features/functions/components/FunctionLogs.tsx

Lines changed: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export function FunctionLogs({
4141
);
4242

4343
const [logs, setLogs] = useState<UdfLog[]>([]);
44+
const [pausedLogs, setPausedLogs] = useState<UdfLog[]>([]);
4445
const [paused, setPaused] = useState<number>(0);
4546
const [manuallyPaused, setManuallyPaused] = useState(false);
4647

@@ -84,36 +85,58 @@ export function FunctionLogs({
8485
const onPause = (p: boolean) => {
8586
const now = new Date().getTime();
8687
setPaused(p ? now : 0);
88+
89+
// When unpausing, merge pausedLogs into logs
90+
if (!p && pausedLogs.length > 0) {
91+
setLogs((prev) => {
92+
const combined = [...prev, ...pausedLogs];
93+
return combined.slice(
94+
Math.max(combined.length - MAX_LOGS, 0),
95+
combined.length,
96+
);
97+
});
98+
setPausedLogs([]);
99+
}
87100
};
88101

89102
const logsConnectivityCallbacks = {
90103
onReconnected: () => {},
91104
onDisconnected: () => {},
92105
};
93106

94-
const receiveLogs = (entries: UdfLog[]) => {
95-
setLogs((prev) => {
96-
const newLogs = filterLogs(
97-
{
98-
logTypes: DEFAULT_LOG_LEVELS,
99-
functions: [functionId],
100-
selectedFunctions: [functionId],
101-
selectedNents: selectedNent ? [selectedNent.path] : "all",
102-
filter: "",
103-
},
104-
entries,
105-
);
106-
if (!newLogs) {
107-
return prev;
108-
}
109-
return [...prev, ...newLogs].slice(
110-
Math.max(prev.length + entries.length - MAX_LOGS, 0),
111-
prev.length + entries.length,
107+
const receiveLogs = (entries: UdfLog[], isPaused: boolean) => {
108+
const newLogs = filterLogs(
109+
{
110+
logTypes: DEFAULT_LOG_LEVELS,
111+
functions: [functionId],
112+
selectedFunctions: [functionId],
113+
selectedNents: selectedNent ? [selectedNent.path] : "all",
114+
filter: "",
115+
},
116+
entries,
117+
);
118+
if (!newLogs || newLogs.length === 0) {
119+
return;
120+
}
121+
122+
if (isPaused) {
123+
// When paused, store new logs separately
124+
setPausedLogs((prev) => [...prev, ...newLogs]);
125+
} else {
126+
setLogs((prev) =>
127+
[...prev, ...newLogs].slice(
128+
Math.max(prev.length + newLogs.length - MAX_LOGS, 0),
129+
prev.length + newLogs.length,
130+
),
112131
);
113-
});
132+
}
114133
};
115134

116-
useLogs(logsConnectivityCallbacks, receiveLogs, paused > 0 || manuallyPaused);
135+
useLogs(
136+
logsConnectivityCallbacks,
137+
(entries) => receiveLogs(entries, paused > 0 || manuallyPaused),
138+
false, // Never skip the stream, always stay connected
139+
);
117140

118141
const router = useRouter();
119142
const { deploymentsURI } = useContext(DeploymentInfoContext);
@@ -153,6 +176,7 @@ export function FunctionLogs({
153176
<LogList
154177
nents={selectedNent ? [selectedNent] : []}
155178
logs={logs}
179+
pausedLogs={pausedLogs}
156180
filteredLogs={filterLogs(
157181
{
158182
logTypes: selectedLevels,
@@ -164,7 +188,6 @@ export function FunctionLogs({
164188
logs,
165189
)}
166190
deploymentAuditLogs={[]}
167-
filter={filter ?? ""}
168191
setFilter={setFilter}
169192
clearedLogs={[]}
170193
setClearedLogs={() => {}}

npm-packages/dashboard-common/src/features/logs/components/FunctionCallTree.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ export function FunctionCallTree({
4141
const rootNode = executionNodes[0];
4242

4343
return (
44-
<div className="max-h-full p-2 text-xs">
45-
<p className="pb-1 text-content-tertiary">
44+
<div className="max-h-full animate-fadeInFromLoading p-2 text-xs">
45+
<p className="pb-2 text-content-tertiary">
4646
This is an outline of the functions called in this request.
4747
</p>
4848
{rootNode && (
@@ -78,7 +78,7 @@ function ExecutionTreeNode({
7878
useEffect(() => {
7979
if (isCurrent && nodeRef.current) {
8080
nodeRef.current.scrollIntoView({
81-
block: "start",
81+
block: "nearest",
8282
});
8383
}
8484
}, [isCurrent]);

npm-packages/dashboard-common/src/features/logs/components/LogDrilldown.stories.tsx

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,6 @@ export const Default: Story = {
239239
onClose={() => {}}
240240
{...navProps}
241241
selectedLog={navProps.selectedLog!}
242-
isPaused={false}
243242
/>
244243
)}
245244
</LogSelectionWrapper>
@@ -285,7 +284,6 @@ export const WithCachedQuery: Story = {
285284
onClose={() => {}}
286285
{...navProps}
287286
selectedLog={navProps.selectedLog!}
288-
isPaused={false}
289287
/>
290288
)}
291289
</LogSelectionWrapper>
@@ -332,7 +330,6 @@ export const WithErrorExecution: Story = {
332330
onClose={() => {}}
333331
{...navProps}
334332
selectedLog={navProps.selectedLog!}
335-
isPaused={false}
336333
/>
337334
)}
338335
</LogSelectionWrapper>
@@ -378,7 +375,6 @@ export const HttpActionExecution: Story = {
378375
onClose={() => {}}
379376
{...navProps}
380377
selectedLog={navProps.selectedLog!}
381-
isPaused={false}
382378
/>
383379
)}
384380
</LogSelectionWrapper>
@@ -433,7 +429,6 @@ export const LongRunningAction: Story = {
433429
onClose={() => {}}
434430
{...navProps}
435431
selectedLog={navProps.selectedLog!}
436-
isPaused={false}
437432
/>
438433
)}
439434
</LogSelectionWrapper>
@@ -455,7 +450,6 @@ export const MultipleExecutions: Story = {
455450
onClose={() => {}}
456451
{...navProps}
457452
selectedLog={navProps.selectedLog!}
458-
isPaused={false}
459453
/>
460454
)}
461455
</LogSelectionWrapper>
@@ -477,7 +471,6 @@ export const OverviewMode: Story = {
477471
onClose={() => {}}
478472
{...navProps}
479473
selectedLog={navProps.selectedLog!}
480-
isPaused={false}
481474
/>
482475
)}
483476
</LogSelectionWrapper>
@@ -499,7 +492,6 @@ export const Paused: Story = {
499492
onClose={() => {}}
500493
{...navProps}
501494
selectedLog={navProps.selectedLog!}
502-
isPaused={true}
503495
/>
504496
)}
505497
</LogSelectionWrapper>
@@ -670,7 +662,6 @@ export const IncompleteActionExecution: Story = {
670662
onClose={() => {}}
671663
{...navProps}
672664
selectedLog={navProps.selectedLog!}
673-
isPaused={false}
674665
/>
675666
)}
676667
</LogSelectionWrapper>

npm-packages/dashboard-common/src/features/logs/components/LogDrilldown.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ export function LogDrilldown({
2626
onFilterByRequestId,
2727
onSelectLog,
2828
onHitBoundary,
29-
isPaused,
3029
shownInterleavedLogs,
3130
allUdfLogs,
3231
}: {
@@ -38,7 +37,6 @@ export function LogDrilldown({
3837
onFilterByRequestId?: (requestId: string) => void;
3938
onSelectLog: (log: InterleavedLog) => void;
4039
onHitBoundary: (boundary: "top" | "bottom" | null) => void;
41-
isPaused: boolean;
4240
}) {
4341
const [selectedTabIndex, setSelectedTabIndex] = useState(0);
4442
const tabGroupRef = useRef<HTMLDivElement>(null);
@@ -195,7 +193,7 @@ export function LogDrilldown({
195193
</div>
196194
</div>
197195
)}
198-
{allUdfLogs.length === 0 && (
196+
{selectedLog.kind === "ExecutionLog" && allUdfLogs.length === 0 && (
199197
<Callout
200198
className="mx-2 mb-2 flex items-center gap-2 p-1.5 text-xs"
201199
variant="upsell"
@@ -238,15 +236,13 @@ export function LogDrilldown({
238236
<HeadlessTab.Panels>
239237
<HeadlessTab.Panel>
240238
<LogMetadata
241-
isPaused={isPaused}
242239
requestId={requestId}
243240
logs={allUdfLogs}
244241
executionId={selectedLog.executionLog.executionId}
245242
/>
246243
</HeadlessTab.Panel>
247244
<HeadlessTab.Panel>
248245
<LogMetadata
249-
isPaused={isPaused}
250246
requestId={requestId}
251247
logs={allUdfLogs}
252248
executionId={undefined}

npm-packages/dashboard-common/src/features/logs/components/LogList.tsx

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,9 @@ import { LogDrilldown } from "./LogDrilldown";
5252

5353
export type LogListProps = {
5454
logs?: UdfLog[];
55+
pausedLogs?: UdfLog[];
5556
filteredLogs?: UdfLog[];
5657
deploymentAuditLogs?: DeploymentAuditLogEvent[];
57-
filter: string;
5858
setFilter?: (filter: string) => void;
5959
clearedLogs: number[];
6060
setClearedLogs: (clearedLogs: number[]) => void;
@@ -107,6 +107,7 @@ export function useHitBoundary() {
107107

108108
export function LogList({
109109
logs,
110+
pausedLogs,
110111
filteredLogs,
111112
deploymentAuditLogs,
112113
clearedLogs,
@@ -236,7 +237,6 @@ export function LogList({
236237
className="flex min-w-[24rem] flex-col"
237238
>
238239
<LogDrilldown
239-
isPaused={paused}
240240
requestId={
241241
shownLog.kind === "ExecutionLog"
242242
? shownLog.executionLog.requestId
@@ -245,7 +245,7 @@ export function LogList({
245245
shownInterleavedLogs={interleavedLogs}
246246
allUdfLogs={
247247
shownLog.kind === "ExecutionLog"
248-
? logs.filter(
248+
? [...logs, ...(pausedLogs ?? [])].filter(
249249
(log) =>
250250
log.requestId === shownLog.executionLog.requestId,
251251
)
@@ -265,7 +265,7 @@ export function LogList({
265265
shownLog.kind === "ExecutionLog" && (
266266
<RequestIdLogs
267267
requestId={shownLog.executionLog}
268-
logs={logs.filter(
268+
logs={[...logs, ...(pausedLogs ?? [])].filter(
269269
(log) => log.requestId === shownLog.executionLog.requestId,
270270
)}
271271
onClose={() => setShownLog(undefined)}
@@ -345,14 +345,7 @@ configure a log stream."
345345
listRef={listRef}
346346
itemKey={(index) => {
347347
const log = interleavedLogs[index];
348-
switch (log.kind) {
349-
case "ExecutionLog":
350-
return log.executionLog.id;
351-
case "DeploymentEvent":
352-
return log.deploymentEvent._id;
353-
default:
354-
return "clearedLogs";
355-
}
348+
return getLogKey(log);
356349
}}
357350
items={interleavedLogs}
358351
totalNumItems={interleavedLogs.length}

0 commit comments

Comments
 (0)