@@ -124,56 +124,86 @@ def prepare_build(
124
124
)
125
125
126
126
skip_build = False
127
- if commit :
128
- skip_build = (
127
+ # Reduce overhead when doing multiple push on the same version.
128
+ if project .has_feature (Feature .CANCEL_OLD_BUILDS ):
129
+ running_builds = (
129
130
Build .objects
130
131
.filter (
131
132
project = project ,
132
133
version = version ,
133
- commit = commit ,
134
134
).exclude (
135
135
state__in = BUILD_FINAL_STATES ,
136
136
).exclude (
137
137
pk = build .pk ,
138
- ). exists ()
138
+ )
139
139
)
140
+ if running_builds .count () > 0 :
141
+ log .warning (
142
+ "Canceling running builds automatically due a new one arrived." ,
143
+ running_builds = running_builds .count (),
144
+ )
145
+
146
+ # If there are builds triggered/running for this particular project and version,
147
+ # we cancel all of them and trigger a new one for the latest commit received.
148
+ for running_build in running_builds :
149
+ cancel_build (running_build )
140
150
else :
141
- skip_build = Build .objects .filter (
142
- project = project ,
143
- version = version ,
144
- state = BUILD_STATE_TRIGGERED ,
145
- # By filtering for builds triggered in the previous 5 minutes we
146
- # avoid false positives for builds that failed for any reason and
147
- # didn't update their state, ending up on blocked builds for that
148
- # version (all the builds are marked as DUPLICATED in that case).
149
- # Adding this date condition, we reduce the risk of hitting this
150
- # problem to 5 minutes only.
151
- date__gte = timezone .now () - datetime .timedelta (minutes = 5 ),
152
- ).count () > 1
153
-
154
- if not project .has_feature (Feature .DEDUPLICATE_BUILDS ):
155
- log .debug (
156
- 'Skipping deduplication of builds. Feature not enabled.' ,
157
- project_slug = project .slug ,
158
- )
159
- skip_build = False
151
+ # NOTE: de-duplicate builds won't be required if we enable `CANCEL_OLD_BUILDS`,
152
+ # since canceling a build is more effective.
153
+ # However, we are keepting `DEDUPLICATE_BUILDS` code around while we test
154
+ # `CANCEL_OLD_BUILDS` with a few projects and we are happy with the results.
155
+ # After that, we can remove `DEDUPLICATE_BUILDS` code
156
+ # and make `CANCEL_OLD_BUILDS` the default behavior.
157
+ if commit :
158
+ skip_build = (
159
+ Build .objects .filter (
160
+ project = project ,
161
+ version = version ,
162
+ commit = commit ,
163
+ )
164
+ .exclude (
165
+ state__in = BUILD_FINAL_STATES ,
166
+ )
167
+ .exclude (
168
+ pk = build .pk ,
169
+ )
170
+ .exists ()
171
+ )
172
+ else :
173
+ skip_build = (
174
+ Build .objects .filter (
175
+ project = project ,
176
+ version = version ,
177
+ state = BUILD_STATE_TRIGGERED ,
178
+ # By filtering for builds triggered in the previous 5 minutes we
179
+ # avoid false positives for builds that failed for any reason and
180
+ # didn't update their state, ending up on blocked builds for that
181
+ # version (all the builds are marked as DUPLICATED in that case).
182
+ # Adding this date condition, we reduce the risk of hitting this
183
+ # problem to 5 minutes only.
184
+ date__gte = timezone .now () - datetime .timedelta (minutes = 5 ),
185
+ ).count ()
186
+ > 1
187
+ )
160
188
161
- if skip_build :
162
- # TODO: we could mark the old build as duplicated, however we reset our
163
- # position in the queue and go back to the end of it --penalization
164
- log .warning (
165
- 'Marking build to be skipped by builder.' ,
166
- project_slug = project .slug ,
167
- version_slug = version .slug ,
168
- build_id = build .pk ,
169
- commit = commit ,
170
- )
171
- build .error = DuplicatedBuildError .message
172
- build .status = DuplicatedBuildError .status
173
- build .exit_code = DuplicatedBuildError .exit_code
174
- build .success = False
175
- build .state = BUILD_STATE_CANCELLED
176
- build .save ()
189
+ if not project .has_feature (Feature .DEDUPLICATE_BUILDS ):
190
+ log .debug (
191
+ "Skipping deduplication of builds. Feature not enabled." ,
192
+ )
193
+ skip_build = False
194
+
195
+ if skip_build :
196
+ # TODO: we could mark the old build as duplicated, however we reset our
197
+ # position in the queue and go back to the end of it --penalization
198
+ log .warning (
199
+ "Marking build to be skipped by builder." ,
200
+ )
201
+ build .error = DuplicatedBuildError .message
202
+ build .status = DuplicatedBuildError .status
203
+ build .exit_code = DuplicatedBuildError .exit_code
204
+ build .success = False
205
+ build .state = BUILD_STATE_CANCELLED
206
+ build .save ()
177
207
178
208
# Start the build in X minutes and mark it as limited
179
209
if not skip_build and project .has_feature (Feature .LIMIT_CONCURRENT_BUILDS ):
0 commit comments