|
1 | 1 | //! Updates registry index and builds new packages |
2 | 2 |
|
3 | 3 | use super::{DocBuilder, RustwideBuilder}; |
4 | | -use crate::config::Config; |
5 | | -use crate::db::Pool; |
6 | 4 | use crate::error::Result; |
7 | 5 | use crate::utils::get_crate_priority; |
8 | 6 | use crates_index_diff::ChangeKind; |
@@ -87,319 +85,3 @@ impl DocBuilder { |
87 | 85 | Ok(processed) |
88 | 86 | } |
89 | 87 | } |
90 | | - |
91 | | -#[derive(Debug, Eq, PartialEq, serde::Serialize)] |
92 | | -pub(crate) struct QueuedCrate { |
93 | | - #[serde(skip)] |
94 | | - id: i32, |
95 | | - pub(crate) name: String, |
96 | | - pub(crate) version: String, |
97 | | - pub(crate) priority: i32, |
98 | | -} |
99 | | - |
100 | | -#[derive(Debug)] |
101 | | -pub struct BuildQueue { |
102 | | - db: Pool, |
103 | | - max_attempts: i32, |
104 | | -} |
105 | | - |
106 | | -impl BuildQueue { |
107 | | - pub fn new(db: Pool, config: &Config) -> Self { |
108 | | - BuildQueue { |
109 | | - db, |
110 | | - max_attempts: config.build_attempts.into(), |
111 | | - } |
112 | | - } |
113 | | - |
114 | | - pub fn add_crate(&self, name: &str, version: &str, priority: i32) -> Result<()> { |
115 | | - self.db.get()?.execute( |
116 | | - "INSERT INTO queue (name, version, priority) VALUES ($1, $2, $3);", |
117 | | - &[&name, &version, &priority], |
118 | | - )?; |
119 | | - Ok(()) |
120 | | - } |
121 | | - |
122 | | - pub(crate) fn pending_count(&self) -> Result<usize> { |
123 | | - let res = self.db.get()?.query( |
124 | | - "SELECT COUNT(*) FROM queue WHERE attempt < $1;", |
125 | | - &[&self.max_attempts], |
126 | | - )?; |
127 | | - Ok(res.get(0).get::<_, i64>(0) as usize) |
128 | | - } |
129 | | - |
130 | | - pub(crate) fn prioritized_count(&self) -> Result<usize> { |
131 | | - let res = self.db.get()?.query( |
132 | | - "SELECT COUNT(*) FROM queue WHERE attempt < $1 AND priority <= 0;", |
133 | | - &[&self.max_attempts], |
134 | | - )?; |
135 | | - Ok(res.get(0).get::<_, i64>(0) as usize) |
136 | | - } |
137 | | - |
138 | | - pub(crate) fn failed_count(&self) -> Result<usize> { |
139 | | - let res = self.db.get()?.query( |
140 | | - "SELECT COUNT(*) FROM queue WHERE attempt >= $1;", |
141 | | - &[&self.max_attempts], |
142 | | - )?; |
143 | | - Ok(res.get(0).get::<_, i64>(0) as usize) |
144 | | - } |
145 | | - |
146 | | - pub(crate) fn queued_crates(&self) -> Result<Vec<QueuedCrate>> { |
147 | | - let query = self.db.get()?.query( |
148 | | - "SELECT id, name, version, priority |
149 | | - FROM queue |
150 | | - WHERE attempt < $1 |
151 | | - ORDER BY priority ASC, attempt ASC, id ASC", |
152 | | - &[&self.max_attempts], |
153 | | - )?; |
154 | | - |
155 | | - Ok(query |
156 | | - .into_iter() |
157 | | - .map(|row| QueuedCrate { |
158 | | - id: row.get("id"), |
159 | | - name: row.get("name"), |
160 | | - version: row.get("version"), |
161 | | - priority: row.get("priority"), |
162 | | - }) |
163 | | - .collect()) |
164 | | - } |
165 | | - |
166 | | - pub(crate) fn process_next_crate( |
167 | | - &self, |
168 | | - f: impl FnOnce(&QueuedCrate) -> Result<()>, |
169 | | - ) -> Result<()> { |
170 | | - let conn = self.db.get()?; |
171 | | - |
172 | | - let queued = self.queued_crates()?; |
173 | | - let to_process = match queued.get(0) { |
174 | | - Some(krate) => krate, |
175 | | - None => return Ok(()), |
176 | | - }; |
177 | | - |
178 | | - match f(&to_process) { |
179 | | - Ok(()) => { |
180 | | - conn.execute("DELETE FROM queue WHERE id = $1;", &[&to_process.id])?; |
181 | | - crate::web::metrics::TOTAL_BUILDS.inc(); |
182 | | - } |
183 | | - Err(e) => { |
184 | | - // Increase attempt count |
185 | | - let rows = conn.query( |
186 | | - "UPDATE queue SET attempt = attempt + 1 WHERE id = $1 RETURNING attempt;", |
187 | | - &[&to_process.id], |
188 | | - )?; |
189 | | - let attempt: i32 = rows.get(0).get(0); |
190 | | - |
191 | | - if attempt >= self.max_attempts { |
192 | | - crate::web::metrics::FAILED_BUILDS.inc(); |
193 | | - } |
194 | | - |
195 | | - error!( |
196 | | - "Failed to build package {}-{} from queue: {}\nBacktrace: {}", |
197 | | - to_process.name, |
198 | | - to_process.version, |
199 | | - e, |
200 | | - e.backtrace() |
201 | | - ); |
202 | | - } |
203 | | - } |
204 | | - |
205 | | - Ok(()) |
206 | | - } |
207 | | -} |
208 | | - |
209 | | -#[cfg(test)] |
210 | | -mod tests { |
211 | | - use super::*; |
212 | | - |
213 | | - #[test] |
214 | | - fn test_add_and_process_crates() { |
215 | | - const MAX_ATTEMPTS: u16 = 3; |
216 | | - |
217 | | - crate::test::wrapper(|env| { |
218 | | - env.override_config(|config| { |
219 | | - config.build_attempts = MAX_ATTEMPTS; |
220 | | - }); |
221 | | - |
222 | | - let queue = env.build_queue(); |
223 | | - |
224 | | - let test_crates = [ |
225 | | - ("low-priority", "1.0.0", 1000), |
226 | | - ("high-priority-foo", "1.0.0", -1000), |
227 | | - ("medium-priority", "1.0.0", -10), |
228 | | - ("high-priority-bar", "1.0.0", -1000), |
229 | | - ("standard-priority", "1.0.0", 0), |
230 | | - ("high-priority-baz", "1.0.0", -1000), |
231 | | - ]; |
232 | | - for krate in &test_crates { |
233 | | - queue.add_crate(krate.0, krate.1, krate.2)?; |
234 | | - } |
235 | | - |
236 | | - let assert_next = |name| -> Result<()> { |
237 | | - queue.process_next_crate(|krate| { |
238 | | - assert_eq!(name, krate.name); |
239 | | - Ok(()) |
240 | | - })?; |
241 | | - Ok(()) |
242 | | - }; |
243 | | - let assert_next_and_fail = |name| -> Result<()> { |
244 | | - queue.process_next_crate(|krate| { |
245 | | - assert_eq!(name, krate.name); |
246 | | - failure::bail!("simulate a failure"); |
247 | | - })?; |
248 | | - Ok(()) |
249 | | - }; |
250 | | - |
251 | | - // The first processed item is the one with the highest priority added first. |
252 | | - assert_next("high-priority-foo")?; |
253 | | - |
254 | | - // Simulate a failure in high-priority-bar. |
255 | | - assert_next_and_fail("high-priority-bar")?; |
256 | | - |
257 | | - // Continue with the next high priority crate. |
258 | | - assert_next("high-priority-baz")?; |
259 | | - |
260 | | - // After all the crates with the max priority are processed, before starting to process |
261 | | - // crates with a lower priority the failed crates with the max priority will be tried |
262 | | - // again. |
263 | | - assert_next("high-priority-bar")?; |
264 | | - |
265 | | - // Continue processing according to the priority. |
266 | | - assert_next("medium-priority")?; |
267 | | - assert_next("standard-priority")?; |
268 | | - |
269 | | - // Simulate the crate failing many times. |
270 | | - for _ in 0..MAX_ATTEMPTS { |
271 | | - assert_next_and_fail("low-priority")?; |
272 | | - } |
273 | | - |
274 | | - // Since low-priority failed many times it will be removed from the queue. Because of |
275 | | - // that the queue should now be empty. |
276 | | - let mut called = false; |
277 | | - queue.process_next_crate(|_| { |
278 | | - called = true; |
279 | | - Ok(()) |
280 | | - })?; |
281 | | - assert!(!called, "there were still items in the queue"); |
282 | | - |
283 | | - Ok(()) |
284 | | - }) |
285 | | - } |
286 | | - |
287 | | - #[test] |
288 | | - fn test_pending_count() { |
289 | | - crate::test::wrapper(|env| { |
290 | | - let queue = env.build_queue(); |
291 | | - |
292 | | - assert_eq!(queue.pending_count()?, 0); |
293 | | - queue.add_crate("foo", "1.0.0", 0)?; |
294 | | - assert_eq!(queue.pending_count()?, 1); |
295 | | - queue.add_crate("bar", "1.0.0", 0)?; |
296 | | - assert_eq!(queue.pending_count()?, 2); |
297 | | - |
298 | | - queue.process_next_crate(|krate| { |
299 | | - assert_eq!("foo", krate.name); |
300 | | - Ok(()) |
301 | | - })?; |
302 | | - assert_eq!(queue.pending_count()?, 1); |
303 | | - |
304 | | - Ok(()) |
305 | | - }); |
306 | | - } |
307 | | - |
308 | | - #[test] |
309 | | - fn test_prioritized_count() { |
310 | | - crate::test::wrapper(|env| { |
311 | | - let queue = env.build_queue(); |
312 | | - |
313 | | - assert_eq!(queue.prioritized_count()?, 0); |
314 | | - queue.add_crate("foo", "1.0.0", 0)?; |
315 | | - assert_eq!(queue.prioritized_count()?, 1); |
316 | | - queue.add_crate("bar", "1.0.0", -100)?; |
317 | | - assert_eq!(queue.prioritized_count()?, 2); |
318 | | - queue.add_crate("baz", "1.0.0", 100)?; |
319 | | - assert_eq!(queue.prioritized_count()?, 2); |
320 | | - |
321 | | - queue.process_next_crate(|krate| { |
322 | | - assert_eq!("bar", krate.name); |
323 | | - Ok(()) |
324 | | - })?; |
325 | | - assert_eq!(queue.prioritized_count()?, 1); |
326 | | - |
327 | | - Ok(()) |
328 | | - }); |
329 | | - } |
330 | | - |
331 | | - #[test] |
332 | | - fn test_failed_count() { |
333 | | - const MAX_ATTEMPTS: u16 = 3; |
334 | | - crate::test::wrapper(|env| { |
335 | | - env.override_config(|config| { |
336 | | - config.build_attempts = 3; |
337 | | - }); |
338 | | - let queue = env.build_queue(); |
339 | | - |
340 | | - assert_eq!(queue.failed_count()?, 0); |
341 | | - queue.add_crate("foo", "1.0.0", -100)?; |
342 | | - assert_eq!(queue.failed_count()?, 0); |
343 | | - queue.add_crate("bar", "1.0.0", 0)?; |
344 | | - |
345 | | - for _ in 0..MAX_ATTEMPTS { |
346 | | - assert_eq!(queue.failed_count()?, 0); |
347 | | - queue.process_next_crate(|krate| { |
348 | | - assert_eq!("foo", krate.name); |
349 | | - failure::bail!("this failed"); |
350 | | - })?; |
351 | | - } |
352 | | - assert_eq!(queue.failed_count()?, 1); |
353 | | - |
354 | | - queue.process_next_crate(|krate| { |
355 | | - assert_eq!("bar", krate.name); |
356 | | - Ok(()) |
357 | | - })?; |
358 | | - assert_eq!(queue.failed_count()?, 1); |
359 | | - |
360 | | - Ok(()) |
361 | | - }); |
362 | | - } |
363 | | - |
364 | | - #[test] |
365 | | - fn test_queued_crates() { |
366 | | - crate::test::wrapper(|env| { |
367 | | - let queue = env.build_queue(); |
368 | | - |
369 | | - let test_crates = [ |
370 | | - ("foo", "1.0.0", -10), |
371 | | - ("bar", "1.0.0", 0), |
372 | | - ("baz", "1.0.0", 10), |
373 | | - ]; |
374 | | - for krate in &test_crates { |
375 | | - queue.add_crate(krate.0, krate.1, krate.2)?; |
376 | | - } |
377 | | - |
378 | | - assert_eq!( |
379 | | - vec![ |
380 | | - QueuedCrate { |
381 | | - id: 1, |
382 | | - name: "foo".into(), |
383 | | - version: "1.0.0".into(), |
384 | | - priority: -10, |
385 | | - }, |
386 | | - QueuedCrate { |
387 | | - id: 2, |
388 | | - name: "bar".into(), |
389 | | - version: "1.0.0".into(), |
390 | | - priority: 0, |
391 | | - }, |
392 | | - QueuedCrate { |
393 | | - id: 3, |
394 | | - name: "baz".into(), |
395 | | - version: "1.0.0".into(), |
396 | | - priority: 10, |
397 | | - }, |
398 | | - ], |
399 | | - queue.queued_crates()? |
400 | | - ); |
401 | | - |
402 | | - Ok(()) |
403 | | - }); |
404 | | - } |
405 | | -} |
0 commit comments