Skip to content

Commit 6fdb275

Browse files
authored
Merge branch 'master' into feat/schedule-actions-delete-api
2 parents 6297b28 + f8f6f32 commit 6fdb275

7 files changed

Lines changed: 48 additions & 14 deletions

File tree

src/route-handlers/describe-batch-action/__fixtures__/mock-describe-batch-operation-workflow.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,11 +189,14 @@ export const mockBatcherStartedHistoryWithUnknownType = buildHistoryResponse({
189189
RPS: 50,
190190
});
191191

192-
// Mirrors the batcher's HeartBeatDetails struct (Go field names) used for progress.
192+
// Mirrors the batcher's HeartBeatDetails struct (Go field names). RPS is the
193+
// live, signal-tuned value — deliberately different from the start-input RPS
194+
// (100) so tests can prove the heartbeat value overrides the input.
193195
export const MOCK_BATCH_PROGRESS = {
194196
TotalEstimate: 200,
195197
SuccessCount: 120,
196198
ErrorCount: 5,
199+
RPS: 250,
197200
};
198201

199202
// Running batch whose pending batcher activity is reporting progress via heartbeat.

src/route-handlers/describe-batch-action/__tests__/describe-batch-action.node.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
mockDescribeBatchOperationWorkflowFailed,
1616
mockDescribeBatchOperationWorkflowFailedWithPendingProgress,
1717
mockDescribeBatchOperationWorkflowRunning,
18+
mockDescribeBatchOperationWorkflowRunningWithProgress,
1819
mockDescribeBatchOperationWorkflowTerminated,
1920
mockDescribeBatchOperationWorkflowOtherDomain,
2021
mockDescribeNonBatchWorkflow,
@@ -75,6 +76,26 @@ describe(describeBatchAction.name, () => {
7576
expect(body.status).toEqual('RUNNING');
7677
expect(body.endTime).toBeUndefined();
7778
expect(body.startTime).toEqual(1717408148258);
79+
// No heartbeat yet, so rps falls back to the start-input value.
80+
expect(body.rps).toEqual(100);
81+
});
82+
83+
it('overrides the start-input rps with the live rps from the running heartbeat', async () => {
84+
const { res } = await setup({
85+
describeResponse: mockDescribeBatchOperationWorkflowRunningWithProgress,
86+
});
87+
88+
expect(res.status).toEqual(200);
89+
const body = await res.json();
90+
expect(body.status).toEqual('RUNNING');
91+
// Live, signal-tuned value from the heartbeat (250), not the input (100).
92+
expect(body.rps).toEqual(MOCK_BATCH_PROGRESS.RPS);
93+
// rps is surfaced at the top level, not nested in progress.
94+
expect(body.progress).toEqual({
95+
totalEstimate: MOCK_BATCH_PROGRESS.TotalEstimate,
96+
successCount: MOCK_BATCH_PROGRESS.SuccessCount,
97+
errorCount: MOCK_BATCH_PROGRESS.ErrorCount,
98+
});
7899
});
79100

80101
it('maps TERMINATED close status to aborted', async () => {
@@ -133,6 +154,8 @@ describe(describeBatchAction.name, () => {
133154
successCount: MOCK_BATCH_PROGRESS.SuccessCount,
134155
errorCount: MOCK_BATCH_PROGRESS.ErrorCount,
135156
});
157+
// Final heartbeat carries the last effective rps, overriding the input.
158+
expect(body.rps).toEqual(MOCK_BATCH_PROGRESS.RPS);
136159
});
137160

138161
it('returns 200 and flags progressError when the close-event read fails', async () => {

src/route-handlers/describe-batch-action/describe-batch-action.types.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,19 @@ import type heartbeatDetailsSchema from './schemas/heartbeat-details-schema';
1010
// single source of truth.
1111
export type BatchActionType = keyof typeof BATCH_ACTION_TYPE;
1212

13-
export type BatchActionProgress = z.infer<typeof heartbeatDetailsSchema>;
13+
export type BatchActionProgress = Omit<
14+
z.infer<typeof heartbeatDetailsSchema>,
15+
'rps'
16+
>;
1417

1518
// Outcome of reading progress from a heartbeat payload. `progressError` is set
1619
// when a payload was present but did not match the expected heartbeat shape (as
17-
// opposed to simply being absent, which leaves both fields undefined).
20+
// opposed to simply being absent, which leaves both fields undefined). `rps` is
21+
// the live, signal-tuned rate limit carried by the same heartbeat, when present.
1822
export type BatchActionProgressResult = {
1923
progress?: BatchActionProgress;
2024
progressError?: boolean;
25+
rps?: number;
2126
};
2227

2328
export type RouteParams = {

src/route-handlers/describe-batch-action/helpers/__tests__/get-batch-action-progress.node.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ describe('getRunningProgressFromDescribe', () => {
4646
getRunningProgressFromDescribe(
4747
mockDescribeBatchOperationWorkflowRunningWithProgress
4848
)
49-
).toEqual({ progress: EXPECTED_PROGRESS });
49+
).toEqual({ progress: EXPECTED_PROGRESS, rps: MOCK_BATCH_PROGRESS.RPS });
5050
expect(logger.warn).not.toHaveBeenCalled();
5151
});
5252

@@ -104,7 +104,7 @@ describe('getFinalProgressFromCloseEvent', () => {
104104
getFinalProgressFromCloseEvent(
105105
mockBatcherCloseEventHistory.history?.events?.[0]
106106
)
107-
).toEqual({ progress: EXPECTED_PROGRESS });
107+
).toEqual({ progress: EXPECTED_PROGRESS, rps: MOCK_BATCH_PROGRESS.RPS });
108108
expect(logger.warn).not.toHaveBeenCalled();
109109
});
110110

src/route-handlers/describe-batch-action/helpers/get-batch-action-progress.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,12 @@ function parseHeartBeatDetails(
2525
return { progressError: true };
2626
}
2727

28-
return { progress: result.data };
28+
// The heartbeat carries both progress counts and the live, signal-tuned rps;
29+
// split them so rps surfaces at the top level (overriding the start-input
30+
// value) while progress stays counts-only. Omit rps when absent so it never
31+
// clobbers the input rps with undefined.
32+
const { rps, ...progress } = result.data;
33+
return { progress, ...(rps !== undefined ? { rps } : {}) };
2934
}
3035

3136
// While the batch is running, progress is surfaced on the batcher activity's

src/route-handlers/describe-batch-action/schemas/heartbeat-details-schema.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,15 @@ const heartbeatDetailsSchema = z
88
TotalEstimate: z.number(),
99
SuccessCount: z.number().catch(0),
1010
ErrorCount: z.number().catch(0),
11+
// Live, signal-tuned rate limit in effect for the running activity.
12+
// Optional so a batcher that predates the field still parses.
13+
RPS: z.number().optional().catch(undefined),
1114
})
12-
.transform(({ TotalEstimate, SuccessCount, ErrorCount }) => ({
15+
.transform(({ TotalEstimate, SuccessCount, ErrorCount, RPS }) => ({
1316
totalEstimate: TotalEstimate,
1417
successCount: SuccessCount,
1518
errorCount: ErrorCount,
19+
rps: RPS,
1620
}));
1721

1822
export default heartbeatDetailsSchema;

src/views/domain-batch-actions/domain-batch-actions.tsx

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -169,13 +169,7 @@ function DomainBatchActionsContent(props: DomainPageTabContentProps) {
169169

170170
if (batchActions.length === 0 && !isDraftOpen) {
171171
return (
172-
<styled.Container>
173-
<styled.DetailPanel>
174-
<DomainBatchActionsNoActionsPlaceholder
175-
onCreateNew={handleCreateNew}
176-
/>
177-
</styled.DetailPanel>
178-
</styled.Container>
172+
<DomainBatchActionsNoActionsPlaceholder onCreateNew={handleCreateNew} />
179173
);
180174
}
181175

0 commit comments

Comments
 (0)