|
4 | 4 |
|
5 | 5 |
|
6 | 6 | use std::{env, thread};
|
| 7 | +use std::panic::{catch_unwind, AssertUnwindSafe}; |
7 | 8 | use std::process::exit;
|
8 | 9 | use std::fs::File;
|
9 | 10 | use std::io::Write;
|
@@ -44,80 +45,160 @@ pub fn start_daemon() {
|
44 | 45 | exit(0);
|
45 | 46 | }
|
46 | 47 |
|
47 |
| - |
48 | 48 | // check new crates every minute
|
49 | 49 | thread::spawn(move || {
|
| 50 | + // space this out to prevent it from clashing against the queue-builder thread on launch |
| 51 | + thread::sleep(Duration::from_secs(30)); |
50 | 52 | loop {
|
| 53 | + let opts = opts(); |
| 54 | + let mut doc_builder = DocBuilder::new(opts); |
| 55 | + |
| 56 | + debug!("Checking new crates"); |
| 57 | + match doc_builder.get_new_crates() { |
| 58 | + Ok(n) => debug!("{} crates added to queue", n), |
| 59 | + Err(e) => error!("Failed to get new crates: {}", e), |
| 60 | + } |
| 61 | + |
51 | 62 | thread::sleep(Duration::from_secs(60));
|
| 63 | + } |
| 64 | + }); |
| 65 | + |
| 66 | + // build new crates every minute |
| 67 | + thread::spawn(move || { |
| 68 | + let mut opts = opts(); |
| 69 | + opts.skip_if_exists = true; |
| 70 | + let mut doc_builder = DocBuilder::new(opts); |
| 71 | + |
| 72 | + /// Represents the current state of the builder thread. |
| 73 | + enum BuilderState { |
| 74 | + /// The builder thread has just started, and hasn't built any crates yet. |
| 75 | + Fresh, |
| 76 | + /// The builder has just seen an empty build queue. |
| 77 | + EmptyQueue, |
| 78 | + /// The builder has just seen the lock file. |
| 79 | + Locked, |
| 80 | + /// The builder has just finished building a crate. The enclosed count is the number of |
| 81 | + /// crates built since the caches have been refreshed. |
| 82 | + QueueInProgress(usize), |
| 83 | + } |
52 | 84 |
|
53 |
| - let mut opts = opts(); |
54 |
| - opts.skip_if_exists = true; |
| 85 | + let mut status = BuilderState::Fresh; |
| 86 | + |
| 87 | + loop { |
| 88 | + if !status.is_in_progress() { |
| 89 | + thread::sleep(Duration::from_secs(60)); |
| 90 | + } |
55 | 91 |
|
56 | 92 | // check lock file
|
57 |
| - if opts.prefix.join("cratesfyi.lock").exists() { |
| 93 | + if doc_builder.is_locked() { |
58 | 94 | warn!("Lock file exits, skipping building new crates");
|
| 95 | + status = BuilderState::Locked; |
59 | 96 | continue;
|
60 | 97 | }
|
61 | 98 |
|
62 |
| - let mut doc_builder = DocBuilder::new(opts); |
| 99 | + if status.count() > 10 { |
| 100 | + // periodically, we need to flush our caches and ping the hubs |
| 101 | + debug!("10 builds in a row; flushing caches"); |
| 102 | + status = BuilderState::QueueInProgress(0); |
63 | 103 |
|
64 |
| - debug!("Checking new crates"); |
65 |
| - let queue_count = match doc_builder.get_new_crates() { |
66 |
| - Ok(size) => size, |
67 |
| - Err(e) => { |
68 |
| - error!("Failed to get new crates: {}", e); |
69 |
| - continue; |
| 104 | + match pubsubhubbub::ping_hubs() { |
| 105 | + Err(e) => error!("Failed to ping hub: {}", e), |
| 106 | + Ok(n) => debug!("Succesfully pinged {} hubs", n) |
70 | 107 | }
|
71 |
| - }; |
72 | 108 |
|
73 |
| - // Only build crates if there is any |
74 |
| - if queue_count == 0 { |
75 |
| - debug!("Queue is empty, going back to sleep"); |
76 |
| - continue; |
77 |
| - } |
| 109 | + if let Err(e) = doc_builder.load_cache() { |
| 110 | + error!("Failed to load cache: {}", e); |
| 111 | + } |
78 | 112 |
|
79 |
| - info!("Building {} crates from queue", queue_count); |
| 113 | + if let Err(e) = doc_builder.save_cache() { |
| 114 | + error!("Failed to save cache: {}", e); |
| 115 | + } |
80 | 116 |
|
81 |
| - // update index |
82 |
| - if let Err(e) = update_sources() { |
83 |
| - error!("Failed to update sources: {}", e); |
84 |
| - continue; |
| 117 | + if let Err(e) = update_sources() { |
| 118 | + error!("Failed to update sources: {}", e); |
| 119 | + continue; |
| 120 | + } |
85 | 121 | }
|
86 | 122 |
|
87 |
| - if let Err(e) = doc_builder.load_cache() { |
88 |
| - error!("Failed to load cache: {}", e); |
89 |
| - continue; |
| 123 | + // Only build crates if there are any to build |
| 124 | + debug!("Checking build queue"); |
| 125 | + match doc_builder.get_queue_count() { |
| 126 | + Err(e) => { |
| 127 | + error!("Failed to read the number of crates in the queue: {}", e); |
| 128 | + continue; |
| 129 | + } |
| 130 | + Ok(0) => { |
| 131 | + if status.is_in_progress() { |
| 132 | + // ping the hubs before continuing |
| 133 | + match pubsubhubbub::ping_hubs() { |
| 134 | + Err(e) => error!("Failed to ping hub: {}", e), |
| 135 | + Ok(n) => debug!("Succesfully pinged {} hubs", n) |
| 136 | + } |
| 137 | + |
| 138 | + if let Err(e) = doc_builder.save_cache() { |
| 139 | + error!("Failed to save cache: {}", e); |
| 140 | + } |
| 141 | + } |
| 142 | + debug!("Queue is empty, going back to sleep"); |
| 143 | + status = BuilderState::EmptyQueue; |
| 144 | + continue; |
| 145 | + } |
| 146 | + Ok(queue_count) => { |
| 147 | + info!("Starting build with {} crates in queue (currently on a {} crate streak)", |
| 148 | + queue_count, status.count()); |
| 149 | + } |
90 | 150 | }
|
91 | 151 |
|
| 152 | + // if we're starting a new batch, reload our caches and sources |
| 153 | + if !status.is_in_progress() { |
| 154 | + if let Err(e) = doc_builder.load_cache() { |
| 155 | + error!("Failed to load cache: {}", e); |
| 156 | + continue; |
| 157 | + } |
| 158 | + |
| 159 | + if let Err(e) = update_sources() { |
| 160 | + error!("Failed to update sources: {}", e); |
| 161 | + continue; |
| 162 | + } |
| 163 | + } |
92 | 164 |
|
93 |
| - // Run build_packages_queue in it's own thread to catch panics |
| 165 | + // Run build_packages_queue under `catch_unwind` to catch panics |
94 | 166 | // This only panicked twice in the last 6 months but its just a better
|
95 | 167 | // idea to do this.
|
96 |
| - let res = thread::spawn(move || { |
97 |
| - match doc_builder.build_packages_queue() { |
98 |
| - Err(e) => error!("Failed build new crates: {}", e), |
99 |
| - Ok(n) => { |
100 |
| - if n > 0 { |
101 |
| - match pubsubhubbub::ping_hubs() { |
102 |
| - Err(e) => error!("Failed to ping hub: {}", e), |
103 |
| - Ok(n) => debug!("Succesfully pinged {} hubs", n) |
104 |
| - } |
105 |
| - } |
106 |
| - } |
107 |
| - } |
108 |
| - |
109 |
| - if let Err(e) = doc_builder.save_cache() { |
110 |
| - error!("Failed to save cache: {}", e); |
| 168 | + let res = catch_unwind(AssertUnwindSafe(|| { |
| 169 | + match doc_builder.build_next_queue_package() { |
| 170 | + Err(e) => error!("Failed to build crate from queue: {}", e), |
| 171 | + Ok(crate_built) => if crate_built { |
| 172 | + status.increment(); |
111 | 173 | }
|
112 | 174 |
|
113 |
| - debug!("Finished building new crates, going back to sleep"); |
114 |
| - }) |
115 |
| - .join(); |
| 175 | + } |
| 176 | + })); |
116 | 177 |
|
117 | 178 | if let Err(e) = res {
|
118 | 179 | error!("GRAVE ERROR Building new crates panicked: {:?}", e);
|
119 | 180 | }
|
120 | 181 | }
|
| 182 | + |
| 183 | + impl BuilderState { |
| 184 | + fn count(&self) -> usize { |
| 185 | + match *self { |
| 186 | + BuilderState::QueueInProgress(n) => n, |
| 187 | + _ => 0, |
| 188 | + } |
| 189 | + } |
| 190 | + |
| 191 | + fn is_in_progress(&self) -> bool { |
| 192 | + match *self { |
| 193 | + BuilderState::QueueInProgress(_) => true, |
| 194 | + _ => false, |
| 195 | + } |
| 196 | + } |
| 197 | + |
| 198 | + fn increment(&mut self) { |
| 199 | + *self = BuilderState::QueueInProgress(self.count() + 1); |
| 200 | + } |
| 201 | + } |
121 | 202 | });
|
122 | 203 |
|
123 | 204 |
|
|
0 commit comments