My project is suffering from a problem where jobs sometimes build the same 
commit twice. This seems to happen when two or more pushes are made within 
a short time (by one or more developers).

I think the problem matches one or both of these:

https://issues.jenkins-ci.org/browse/JENKINS-17346
https://issues.jenkins-ci.org/browse/JENKINS-30350

My job is a multi-configuration job configured for pre-build branch merging 
as describe here: 
https://plugins.jenkins.io/git#GitPlugin-UsingGit,Jenkinsandpre-buildbranchmerging

My full job config.xml can be found here: https://pastebin.com/BqBUQEpe

I could use some help figuring out why this happens. Here is what I have 
done so far. I've checked out bitbucket-plugin to start from the trigger. 
Here, I've drilled down to BitBucketTrigger.onPost() which appears to be 
where the interesting stuff is happening.

    public void onPost(String triggeredByUser, final String payload) {
        final String pushBy = triggeredByUser;
        getDescriptor().queue.execute(new Runnable() {
            private boolean runPolling() {
                try {
                    StreamTaskListener listener = new StreamTaskListener(
getLogFile());
                    try {
                        PrintStream logger = listener.getLogger();
                        long start = System.currentTimeMillis();
                        logger.println("Started on "+ DateFormat.
getDateTimeInstance().format(new Date()));
                        boolean result = SCMTriggerItem.SCMTriggerItems.
asSCMTriggerItem(job).poll(listener).hasChanges();
                        logger.println("Done. Took "+ Util.getTimeSpanString
(System.currentTimeMillis()-start));
                        if(result)
                            logger.println("Changes found");
                        else
                            logger.println("No changes");
                        return result;
                    } catch (Error e) {
                        e.printStackTrace(listener.error("Failed to record 
SCM polling"));
                        LOGGER.log(Level.SEVERE,"Failed to record SCM 
polling",e);
                        throw e;
                    } catch (RuntimeException e) {
                        e.printStackTrace(listener.error("Failed to record 
SCM polling"));
                        LOGGER.log(Level.SEVERE,"Failed to record SCM 
polling",e);
                        throw e;
                    } finally {
                        listener.close();
                    }
                } catch (IOException e) {
                    LOGGER.log(Level.SEVERE,"Failed to record SCM polling",e
);
                }
                return false;
            }

            public void run() {
                if (runPolling()) {
                    String name = " #"+job.getNextBuildNumber();
                    BitBucketPushCause cause;
                    try {
                        cause = new BitBucketPushCause(getLogFile(), pushBy
);
                    } catch (IOException e) {
                        LOGGER.log(Level.WARNING, "Failed to parse the 
polling log",e);
                        cause = new BitBucketPushCause(pushBy);
                    }
                    ParameterizedJobMixIn pJob = new ParameterizedJobMixIn() 
{
                        @Override protected Job asJob() {
                            return job;
                        }
                    };
                    BitBucketPayload bitBucketPayload = new BitBucketPayload
(payload);
                    pJob.scheduleBuild2(5, new CauseAction(cause), 
bitBucketPayload);
                    if (pJob.scheduleBuild(cause)) {
                        LOGGER.info("SCM changes detected in "+ job.getName
()+". Triggering "+ name);
                    } else {
                        LOGGER.info("SCM changes detected in "+ job.getName
()+". Job is already in the queue");
                    }
                }
            }

        });
    }

The things my novice eyes notice in the function above is that it is a 
Runnable that is added to a getDescriptor().queue. Does this mean that only 
one of these can be processed at a time, or is it conceivable that two 
quick git pushes in succession can cause two of these runnables to execute 
at the same time.

The other thing I notice is that if pJob.scheduleBuild(cause) returns 
something that evaluates to false then we log a message that the job is 
already in the queue. Is a job in the queue when it is executing or does 
being in the queue mean that it is scheduled to be executed but hasn't 
startet yet?

Finally, I have a question about this statement from the function above:

   boolean result = 
SCMTriggerItem.SCMTriggerItems.asSCMTriggerItem(job).poll(listener).hasChanges();

hasChanges() relative to what? What happens if a job has been scheduled and 
it has started executing, but it has not decided yet what git sha or indeed 
even what git branch to build? Does that leave a window where a second git 
push triggers the job to be queued again?

I hope you can help me.

Thomas

-- 
You received this message because you are subscribed to the Google Groups 
"Jenkins Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to jenkinsci-dev+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/jenkinsci-dev/514bb6df-6afc-4136-abfe-624f73a00dec%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to