screwdriver-api 8.0.91 → 8.0.93

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "screwdriver-api",
3
- "version": "8.0.91",
3
+ "version": "8.0.93",
4
4
  "description": "API server for the Screwdriver.cd service",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -192,11 +192,21 @@ async function triggerNextJobs(config, server) {
192
192
  const isCurrentPipeline = strToInt(joinedPipelineId) === currentPipeline.id;
193
193
  const remoteJoinName = `sd@${currentPipeline.id}:${originalCurrentJobName}`;
194
194
  const remoteTriggerName = `~${remoteJoinName}`;
195
- let lock;
196
- let resource;
195
+ let groupEventLock;
196
+ let groupEventResource;
197
+ let externalEventResource;
198
+ let externalEventLock;
197
199
 
198
200
  let externalEvent = joinedPipeline.event;
199
201
 
202
+ let isRestartPipeline = false;
203
+
204
+ if (currentEvent.parentEventId) {
205
+ const parentEvent = await eventFactory.get({ id: currentEvent.parentEventId });
206
+
207
+ isRestartPipeline = parentEvent && strToInt(currentEvent.pipelineId) === strToInt(parentEvent.pipelineId);
208
+ }
209
+
200
210
  // This includes CREATED builds too
201
211
  const groupEventBuilds = await getBuildsForGroupEvent(currentEvent.groupEventId, buildFactory);
202
212
 
@@ -209,85 +219,89 @@ async function triggerNextJobs(config, server) {
209
219
  });
210
220
 
211
221
  groupEventBuilds.push(...parallelBuilds);
212
- } else {
213
- const sameParentEvents = await getSameParentEvents({
214
- eventFactory,
215
- parentEventId: currentEvent.id,
216
- pipelineId: strToInt(joinedPipelineId)
217
- });
218
-
219
- if (sameParentEvents.length > 0) {
220
- externalEvent = sameParentEvents[0];
221
- }
222
222
  }
223
223
 
224
- let isRestartPipeline = false;
225
-
226
- if (currentEvent.parentEventId) {
227
- const parentEvent = await eventFactory.get({ id: currentEvent.parentEventId });
228
-
229
- isRestartPipeline = parentEvent && strToInt(currentEvent.pipelineId) === strToInt(parentEvent.pipelineId);
230
- }
224
+ try {
225
+ // serialize external-event selection/creation and prevent duplicate events under concurrent triggers.
226
+ groupEventResource = `pipeline:${joinedPipelineId}:groupEvent:${currentEvent.groupEventId}`;
227
+ groupEventLock = await locker.lock(groupEventResource);
228
+
229
+ if (!externalEvent) {
230
+ const sameParentEvents = await getSameParentEvents({
231
+ eventFactory,
232
+ parentEventId: currentEvent.id,
233
+ pipelineId: strToInt(joinedPipelineId)
234
+ });
231
235
 
232
- // If user used external trigger syntax, the jobs are triggered as external
233
- if (isCurrentPipeline) {
234
- externalEvent = null;
235
- } else if (isRestartPipeline) {
236
- // If parentEvent and currentEvent have the same pipelineId, then currentEvent is the event that started the restart
237
- // If restarted from the downstream pipeline, the remote trigger must create a new event in the upstream pipeline
238
- const sameParentEvents = await getSameParentEvents({
239
- eventFactory,
240
- parentEventId: currentEvent.id,
241
- pipelineId: strToInt(joinedPipelineId)
242
- });
236
+ if (sameParentEvents.length > 0) {
237
+ externalEvent = sameParentEvents[0];
238
+ }
239
+ }
243
240
 
244
- externalEvent = sameParentEvents.length > 0 ? sameParentEvents[0] : null;
245
- }
241
+ // If user used external trigger syntax, the jobs are triggered as external
242
+ if (isCurrentPipeline) {
243
+ externalEvent = null;
244
+ } else if (isRestartPipeline) {
245
+ // If parentEvent and currentEvent have the same pipelineId, then currentEvent is the event that started the restart
246
+ // If restarted from the downstream pipeline, the remote trigger must create a new event in the upstream pipeline
247
+ const sameParentEvents = await getSameParentEvents({
248
+ eventFactory,
249
+ parentEventId: currentEvent.id,
250
+ pipelineId: strToInt(joinedPipelineId)
251
+ });
246
252
 
247
- // no need to lock if there is no external event
248
- if (externalEvent) {
249
- resource = `pipeline:${joinedPipelineId}:event:${externalEvent.id}`;
250
- }
253
+ externalEvent = sameParentEvents.length > 0 ? sameParentEvents[0] : null;
254
+ }
251
255
 
252
- // Create a new external event
253
- // First downstream trigger, restart case, same pipeline trigger as external
254
- if (!externalEvent) {
255
- const { parentBuilds } = parseJobInfo({
256
- currentBuild,
257
- currentPipeline,
258
- currentJob
259
- });
256
+ // no need to lock if there is no external event
257
+ if (externalEvent) {
258
+ externalEventResource = `pipeline:${joinedPipelineId}:event:${externalEvent.id}`;
259
+ }
260
260
 
261
- const externalEventConfig = {
262
- pipelineFactory,
263
- eventFactory,
264
- externalPipelineId: joinedPipelineId,
265
- parentBuildId: currentBuild.id,
266
- parentBuilds,
267
- causeMessage: `Triggered by ${remoteJoinName}`,
268
- parentEventId: currentEvent.id,
269
- startFrom: remoteTriggerName,
270
- skipMessage: 'Skip bulk external builds creation', // Don't start builds in eventFactory.
271
- groupEventId: currentEvent.groupEventId // groupEventId is the id of the first triggered event (use the upstream pipeline's in the downstream pipeline)
272
- };
261
+ // Create a new external event
262
+ // First downstream trigger, restart case, same pipeline trigger as external
263
+ if (!externalEvent) {
264
+ const { parentBuilds } = parseJobInfo({
265
+ currentBuild,
266
+ currentPipeline,
267
+ currentJob
268
+ });
273
269
 
274
- const buildsToRestart = buildsToRestartFilter(joinedPipeline, groupEventBuilds, currentEvent, currentBuild);
275
- const isRestart = buildsToRestart.length > 0;
270
+ const externalEventConfig = {
271
+ pipelineFactory,
272
+ eventFactory,
273
+ externalPipelineId: joinedPipelineId,
274
+ parentBuildId: currentBuild.id,
275
+ parentBuilds,
276
+ causeMessage: `Triggered by ${remoteJoinName}`,
277
+ parentEventId: currentEvent.id,
278
+ startFrom: remoteTriggerName,
279
+ skipMessage: 'Skip bulk external builds creation', // Don't start builds in eventFactory.
280
+ groupEventId: currentEvent.groupEventId // groupEventId is the id of the first triggered event (use the upstream pipeline's in the downstream pipeline)
281
+ };
282
+
283
+ const buildsToRestart = buildsToRestartFilter(
284
+ joinedPipeline,
285
+ groupEventBuilds,
286
+ currentEvent,
287
+ currentBuild
288
+ );
289
+ const isRestart = buildsToRestart.length > 0;
276
290
 
277
- // Restart case
278
- if (isRestart) {
279
- externalEventConfig.parentBuilds = buildsToRestart[0].parentBuilds;
280
- }
291
+ // Restart case
292
+ if (isRestart) {
293
+ externalEventConfig.parentBuilds = buildsToRestart[0].parentBuilds;
294
+ }
281
295
 
282
- try {
283
296
  externalEvent = await createExternalEvent(externalEventConfig);
284
- } catch (err) {
285
- // The case of triggered external pipeline which is already deleted from DB, etc
286
- logger.error(
287
- `Error in createExternalEvent:${joinedPipelineId} from pipeline:${currentPipeline.id}-${currentJob.name}-event:${currentEvent.id}`,
288
- err
289
- );
290
297
  }
298
+ } catch (err) {
299
+ logger.error(
300
+ `Error in selection/creation externalEvent:${joinedPipelineId} from pipeline:${currentPipeline.id}-${currentJob.name}-event:${currentEvent.id}`,
301
+ err
302
+ );
303
+ } finally {
304
+ await locker.unlock(groupEventLock, groupEventResource);
291
305
  }
292
306
 
293
307
  // Skip trigger process if createExternalEvent fails
@@ -310,7 +324,7 @@ async function triggerNextJobs(config, server) {
310
324
  let nextBuild;
311
325
 
312
326
  try {
313
- if (resource) lock = await locker.lock(resource);
327
+ if (externalEventResource) externalEventLock = await locker.lock(externalEventResource);
314
328
 
315
329
  if (isOrTrigger(externalEvent.workflowGraph, remoteTriggerName, nextJobName)) {
316
330
  nextBuild = await remoteTrigger.execute(
@@ -369,9 +383,9 @@ async function triggerNextJobs(config, server) {
369
383
  `Error in triggerJobsInExternalPipeline:${joinedPipelineId} from pipeline:${currentPipeline.id}-${currentJob.name}-event:${currentEvent.id} `,
370
384
  err
371
385
  );
386
+ } finally {
387
+ await locker.unlock(externalEventLock, externalEventResource);
372
388
  }
373
-
374
- await locker.unlock(lock, resource);
375
389
  }
376
390
  }
377
391
  }