Skip to content

Commit f7a6264

Browse files
kriszypclaude
andcommitted
harden replicated get_analytics peer-response unwrap
Guarantee the unwrapped peer response is always an array so a malformed peer (e.g. a non-array `results`) is treated as empty rather than throwing during the merge, preserving the best-effort guarantee. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 335d282 commit f7a6264

2 files changed

Lines changed: 17 additions & 2 deletions

File tree

resources/analytics/read.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,11 @@ async function* mergeAnalyticsFromPeers(
116116
.filter((node) => node.name !== thisNode)
117117
.map((node) =>
118118
sendOperationToNode(node, peerReq).then(
119-
// an array response is wrapped as `{ results }` over the replication channel
120-
(response): Metric[] => (Array.isArray(response) ? response : (response?.results ?? [])),
119+
// an array response is wrapped as `{ results }` over the replication channel;
120+
// fall back to an empty list for any other (malformed) shape so a bad peer
121+
// can't break the merge
122+
(response): Metric[] =>
123+
Array.isArray(response) ? response : Array.isArray(response?.results) ? response.results : [],
121124
(error: Error): Metric[] => {
122125
logger.warn(`get_analytics replication to node '${node.name}' failed; omitting its results`, error);
123126
return [];

unitTests/resources/analytics/read.test.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,18 @@ describe('getOp (replicated fan-out)', () => {
419419
expect(result).to.deep.equal([{ id: 5, metric: 'm', node: 'peer-a' }]);
420420
});
421421

422+
it('treats a malformed peer response (non-array results) as empty', async () => {
423+
global.server.nodes = [{ name: 'peer-good' }, { name: 'peer-weird' }];
424+
sendOperationStub
425+
.withArgs(sinon.match({ name: 'peer-good' }))
426+
.resolves({ results: [{ id: 1, metric: 'm', node: 'peer-good' }] });
427+
sendOperationStub.withArgs(sinon.match({ name: 'peer-weird' })).resolves({ results: 'not-an-array' });
428+
429+
const result = await collect(await getOp({ metric: 'm', replicated: true }));
430+
431+
expect(result).to.deep.equal([{ id: 1, metric: 'm', node: 'peer-good' }]);
432+
});
433+
422434
it('includes local node results ahead of peer results', async () => {
423435
sinon
424436
.stub(hostnames, 'getAnalyticsHostnameTable')

0 commit comments

Comments
 (0)