|
1 | 1 | import { QueryExecutor, formatQuery } from '@crowd/data-access-layer/src/queryExecutor' |
2 | 2 | import { prepareBulkInsert } from '@crowd/data-access-layer/src/utils' |
| 3 | +import { getServiceChildLogger } from '@crowd/logging' |
3 | 4 |
|
4 | 5 | import { ProcessRepoResult, RepoPolicies, ScoredContact } from './types' |
5 | 6 |
|
| 7 | +const log = getServiceChildLogger('security-contacts') |
| 8 | + |
6 | 9 | const CONTACT_COLUMNS = [ |
7 | 10 | 'repo_id', |
8 | 11 | 'channel', |
@@ -184,7 +187,19 @@ export async function writeContactsBatch( |
184 | 187 | const ok = outcomes.filter((o): o is OkResult => o.status === 'ok') |
185 | 188 |
|
186 | 189 | for (let i = 0; i < ok.length; i += WRITE_CHUNK_SIZE) { |
187 | | - await writeContactsChunk(qx, ok.slice(i, i + WRITE_CHUNK_SIZE)) |
| 190 | + const chunk = ok.slice(i, i + WRITE_CHUNK_SIZE) |
| 191 | + try { |
| 192 | + await writeContactsChunk(qx, chunk) |
| 193 | + } catch (err) { |
| 194 | + // Isolates blast radius to this chunk: without this, one bad chunk would throw out of |
| 195 | + // the whole batch, leaving every repo's contacts_last_refreshed untouched and the sweep |
| 196 | + // stuck re-fetching + re-extracting the same batch forever instead of ever advancing. |
| 197 | + log.error( |
| 198 | + { errMsg: (err as Error).message, repoIds: chunk.map((r) => r.repoId) }, |
| 199 | + 'Batched contacts write failed for chunk — marking attempted without contacts update', |
| 200 | + ) |
| 201 | + attemptedOnlyIds.push(...chunk.map((r) => r.repoId)) |
| 202 | + } |
188 | 203 | } |
189 | 204 |
|
190 | 205 | await markReposAttempted(qx, attemptedOnlyIds) |
|
0 commit comments