-
Notifications
You must be signed in to change notification settings - Fork 44
Expand file tree
/
Copy pathdispatch-pending-code-review-owners.ts
More file actions
117 lines (104 loc) · 3.81 KB
/
Copy pathdispatch-pending-code-review-owners.ts
File metadata and controls
117 lines (104 loc) · 3.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import pLimit from 'p-limit';
import { captureException } from '@sentry/nextjs';
import { ensureBotUserForOrg } from '@/lib/bot-users/bot-user-service';
import {
cancelExpiredPendingAndRunningCodeReviews,
listDispatchableCodeReviewOwnerCandidates,
type DispatchableCodeReviewOwnerCandidate,
} from '../db/code-reviews';
import { errorExceptInTest, logExceptInTest } from '@/lib/utils.server';
import { tryDispatchPendingReviews } from './dispatch-pending-reviews';
import {
cronPendingCodeReviewCreatedAtWindowSql,
type PendingCodeReviewCreatedAtWindow,
} from './dispatch-constants';
import type { Owner } from '../core';
const OWNER_SCAN_LIMIT = 100;
const OWNER_DISPATCH_CONCURRENCY = 4;
export type DispatchPendingCodeReviewOwnersSummary = {
ownersConsidered: number;
ownersProcessed: number;
ownersWithNoNewDispatch: number;
ownersSkippedMissingBotUsers: number;
coordinatorFailures: number;
reviewsDispatched: number;
staleReviewsCancelled: number;
staleAttemptsCancelled: number;
hasMoreCandidateOwners: boolean;
};
type OwnerDrainOutcome =
| { status: 'processed'; dispatched: number }
| { status: 'skipped-missing-bot' }
| { status: 'failed' };
async function resolveDispatchOwner(
candidate: DispatchableCodeReviewOwnerCandidate
): Promise<Owner | null> {
if (candidate.type === 'user') {
return { type: 'user', id: candidate.id, userId: candidate.id };
}
const botUser = await ensureBotUserForOrg(candidate.id, 'code-review');
return { type: 'org', id: candidate.id, userId: botUser.id };
}
async function drainOwner(
candidate: DispatchableCodeReviewOwnerCandidate,
pendingCreatedAtWindow: PendingCodeReviewCreatedAtWindow
): Promise<OwnerDrainOutcome> {
try {
const owner = await resolveDispatchOwner(candidate);
if (!owner) {
return { status: 'skipped-missing-bot' };
}
const result = await tryDispatchPendingReviews(owner, { pendingCreatedAtWindow });
return { status: 'processed', dispatched: result.dispatched };
} catch (error) {
errorExceptInTest('[dispatchPendingCodeReviewOwners] Owner drain failed', {
candidate,
error,
});
captureException(error, {
tags: { operation: 'dispatch-pending-code-review-owner' },
extra: { candidate },
});
return { status: 'failed' };
}
}
export async function dispatchPendingCodeReviewOwners(): Promise<DispatchPendingCodeReviewOwnersSummary> {
const staleCancellationSummary = await cancelExpiredPendingAndRunningCodeReviews();
const pendingCreatedAtWindow = cronPendingCodeReviewCreatedAtWindowSql();
const candidates = await listDispatchableCodeReviewOwnerCandidates({
limit: OWNER_SCAN_LIMIT,
pendingCreatedAtWindow,
});
const limit = pLimit(OWNER_DISPATCH_CONCURRENCY);
const outcomes = await Promise.all(
candidates.owners.map(candidate => limit(() => drainOwner(candidate, pendingCreatedAtWindow)))
);
const summary: DispatchPendingCodeReviewOwnersSummary = {
ownersConsidered: candidates.owners.length,
ownersProcessed: 0,
ownersWithNoNewDispatch: 0,
ownersSkippedMissingBotUsers: 0,
coordinatorFailures: 0,
reviewsDispatched: 0,
staleReviewsCancelled: staleCancellationSummary.reviewsCancelled,
staleAttemptsCancelled: staleCancellationSummary.attemptsCancelled,
hasMoreCandidateOwners: candidates.hasMore,
};
for (const outcome of outcomes) {
if (outcome.status === 'skipped-missing-bot') {
summary.ownersSkippedMissingBotUsers++;
continue;
}
if (outcome.status === 'failed') {
summary.coordinatorFailures++;
continue;
}
summary.ownersProcessed++;
summary.reviewsDispatched += outcome.dispatched;
if (outcome.dispatched === 0) {
summary.ownersWithNoNewDispatch++;
}
}
logExceptInTest('[dispatchPendingCodeReviewOwners] Drain complete', summary);
return summary;
}