| author | wenzelm | 
| Tue, 23 Jan 2024 19:56:52 +0100 | |
| changeset 79521 | db2b5c04075d | 
| parent 79502 | c7a98469c0e7 | 
| child 79527 | f1f08ca40d96 | 
| permissions | -rw-r--r-- | 
| 79502 | 1 | /* Title: Pure/Build/build_process.scala | 
| 77238 | 2 | Author: Makarius | 
| 3 | ||
| 4 | Build process for sessions, with build database, optional heap, and | |
| 5 | optional presentation. | |
| 6 | */ | |
| 7 | ||
| 8 | package isabelle | |
| 9 | ||
| 10 | ||
| 77505 | 11 | import scala.collection.immutable.SortedMap | 
| 77246 | 12 | import scala.math.Ordering | 
| 77257 | 13 | import scala.annotation.tailrec | 
| 77246 | 14 | |
| 15 | ||
| 77238 | 16 | object Build_Process {
 | 
| 78421 | 17 | /** state vs. database **/ | 
| 77257 | 18 | |
| 78238 | 19 | sealed case class Build( | 
| 78173 | 20 | build_uuid: String, // Database_Progress.context_uuid | 
| 77660 | 21 | ml_platform: String, | 
| 22 | options: String, | |
| 23 | start: Date, | |
| 78226 | 24 | stop: Option[Date], | 
| 25 | sessions: List[String] | |
| 78222 | 26 |   ) {
 | 
| 27 | def active: Boolean = stop.isEmpty | |
| 78633 | 28 | |
| 29 | def print: String = | |
| 30 | build_uuid + " (platform: " + ml_platform + ", start: " + Build_Log.print_date(start) + | |
| 31 | if_proper(stop, ", stop: " + Build_Log.print_date(stop.get)) + ")" | |
| 78222 | 32 | } | 
| 77660 | 33 | |
| 78238 | 34 | sealed case class Worker( | 
| 78173 | 35 | worker_uuid: String, // Database_Progress.agent_uuid | 
| 77631 | 36 | build_uuid: String, | 
| 37 | start: Date, | |
| 38 | stamp: Date, | |
| 39 | stop: Option[Date], | |
| 40 | serial: Long | |
| 41 | ) | |
| 42 | ||
| 78238 | 43 | sealed case class Task( | 
| 77631 | 44 | name: String, | 
| 45 | deps: List[String], | |
| 77661 
45bd5c26cbcc
proper build_uuid for Build_Process.Task: thus old entries are removed via prepare_database/clean_build;
 wenzelm parents: 
77660diff
changeset | 46 | info: JSON.Object.T, | 
| 
45bd5c26cbcc
proper build_uuid for Build_Process.Task: thus old entries are removed via prepare_database/clean_build;
 wenzelm parents: 
77660diff
changeset | 47 | build_uuid: String | 
| 77631 | 48 |   ) {
 | 
| 77313 
f8aa1647d156
more elementary data structures, to fit better to SQL database;
 wenzelm parents: 
77312diff
changeset | 49 | def is_ready: Boolean = deps.isEmpty | 
| 77585 | 50 | def resolve(dep: String): Task = | 
| 77313 
f8aa1647d156
more elementary data structures, to fit better to SQL database;
 wenzelm parents: 
77312diff
changeset | 51 | if (deps.contains(dep)) copy(deps = deps.filterNot(_ == dep)) else this | 
| 
f8aa1647d156
more elementary data structures, to fit better to SQL database;
 wenzelm parents: 
77312diff
changeset | 52 | } | 
| 
f8aa1647d156
more elementary data structures, to fit better to SQL database;
 wenzelm parents: 
77312diff
changeset | 53 | |
| 78238 | 54 | sealed case class Job( | 
| 77630 
86ef80d13544
clarified signature: avoid confusion due to object-orientation;
 wenzelm parents: 
77629diff
changeset | 55 | name: String, | 
| 
86ef80d13544
clarified signature: avoid confusion due to object-orientation;
 wenzelm parents: 
77629diff
changeset | 56 | worker_uuid: String, | 
| 77634 | 57 | build_uuid: String, | 
| 77630 
86ef80d13544
clarified signature: avoid confusion due to object-orientation;
 wenzelm parents: 
77629diff
changeset | 58 | node_info: Host.Node_Info, | 
| 78841 
7f61688d4e8d
added start date to build jobs, e.g., for build time estimation;
 Fabian Huch <huch@in.tum.de> parents: 
78839diff
changeset | 59 | start_date: Date, | 
| 77630 
86ef80d13544
clarified signature: avoid confusion due to object-orientation;
 wenzelm parents: 
77629diff
changeset | 60 | build: Option[Build_Job] | 
| 77652 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 61 |   ) extends Library.Named {
 | 
| 78530 | 62 | def join_build: Option[Build_Job.Result] = build.flatMap(_.join) | 
| 77630 
86ef80d13544
clarified signature: avoid confusion due to object-orientation;
 wenzelm parents: 
77629diff
changeset | 63 | } | 
| 
86ef80d13544
clarified signature: avoid confusion due to object-orientation;
 wenzelm parents: 
77629diff
changeset | 64 | |
| 78238 | 65 | sealed case class Result( | 
| 77651 | 66 | name: String, | 
| 67 | worker_uuid: String, | |
| 68 | build_uuid: String, | |
| 69 | node_info: Host.Node_Info, | |
| 77461 | 70 | process_result: Process_Result, | 
| 77460 
ccca589d7027
tuned signature: support general Build_Job instances;
 wenzelm parents: 
77459diff
changeset | 71 | output_shasum: SHA1.Shasum, | 
| 77461 | 72 | current: Boolean | 
| 77652 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 73 |   ) extends Library.Named {
 | 
| 77259 
61fc2afe4c8b
clarified signature: prefer stateful object-oriented style, to make it fit better into physical world;
 wenzelm parents: 
77258diff
changeset | 74 | def ok: Boolean = process_result.ok | 
| 
61fc2afe4c8b
clarified signature: prefer stateful object-oriented style, to make it fit better into physical world;
 wenzelm parents: 
77258diff
changeset | 75 | } | 
| 77312 | 76 | |
| 78237 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 77 |   object Sessions {
 | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 78 | type Graph = isabelle.Graph[String, Build_Job.Session_Context] | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 79 | val empty: Sessions = new Sessions(Graph.string) | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 80 | } | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 81 | |
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 82 |   final class Sessions private(val graph: Sessions.Graph) {
 | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 83 | override def toString: String = graph.toString | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 84 | |
| 78500 
fc6d8a2895ca
more robust ancestor_results: avoid total existence failure after build_process has crashed elsewhere;
 wenzelm parents: 
78440diff
changeset | 85 | def defined(name: String): Boolean = graph.defined(name) | 
| 78237 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 86 | def apply(name: String): Build_Job.Session_Context = graph.get_node(name) | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 87 | |
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 88 | def iterator: Iterator[Build_Job.Session_Context] = | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 89 | for (name <- graph.topological_order.iterator) yield apply(name) | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 90 | |
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 91 | def make(new_graph: Sessions.Graph): Sessions = | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 92 | if (graph == new_graph) this | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 93 |       else {
 | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 94 | new Sessions( | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 95 |           new_graph.iterator.foldLeft(new_graph) {
 | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 96 | case (g, (name, (session, _))) => g.add_deps_acyclic(name, session.deps) | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 97 | }) | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 98 | } | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 99 | |
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 100 | def pull( | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 101 | data_domain: Set[String], | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 102 | data: Set[String] => List[Build_Job.Session_Context] | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 103 |     ): Sessions = {
 | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 104 | val dom = data_domain -- iterator.map(_.name) | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 105 |       make(data(dom).foldLeft(graph.restrict(dom)) { case (g, e) => g.new_node(e.name, e) })
 | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 106 | } | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 107 | |
| 78372 | 108 | def init( | 
| 78421 | 109 | build_context: isabelle.Build.Context, | 
| 78374 | 110 | database_server: Option[SQL.Database], | 
| 111 | progress: Progress = new Progress | |
| 78372 | 112 |     ): Sessions = {
 | 
| 78237 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 113 | val sessions_structure = build_context.sessions_structure | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 114 | make( | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 115 |         sessions_structure.build_graph.iterator.foldLeft(graph) {
 | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 116 | case (graph0, (name, (info, _))) => | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 117 | val deps = info.parent.toList | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 118 | val prefs = info.session_prefs | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 119 | val ancestors = sessions_structure.build_requirements(deps) | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 120 | val sources_shasum = build_context.build_deps.sources_shasum(name) | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 121 | |
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 122 |             if (graph0.defined(name)) {
 | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 123 | val session0 = graph0.get_node(name) | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 124 | val prefs0 = session0.session_prefs | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 125 | val ancestors0 = session0.ancestors | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 126 | val sources_shasum0 = session0.sources_shasum | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 127 | |
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 128 | def err(msg: String, a: String, b: String): Nothing = | 
| 78501 | 129 |                 error("Conflicting dependencies for session " + quote(name) + ": " + msg + "\n" +
 | 
| 130 | Library.indent_lines(2, a) + "\nvs.\n" + Library.indent_lines(2, b)) | |
| 78237 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 131 | |
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 132 |               if (prefs0 != prefs) {
 | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 133 |                 err("preferences disagree",
 | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 134 | Symbol.cartouche_decoded(prefs0), Symbol.cartouche_decoded(prefs)) | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 135 | } | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 136 |               if (ancestors0 != ancestors) {
 | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 137 |                 err("ancestors disagree", commas_quote(ancestors0), commas_quote(ancestors))
 | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 138 | } | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 139 |               if (sources_shasum0 != sources_shasum) {
 | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 140 | val a = sources_shasum0 - sources_shasum | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 141 | val b = sources_shasum - sources_shasum0 | 
| 78501 | 142 |                 err("sources disagree",
 | 
| 143 | Library.trim_line(a.toString), | |
| 144 | Library.trim_line(b.toString)) | |
| 78237 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 145 | } | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 146 | |
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 147 | graph0 | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 148 | } | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 149 |             else {
 | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 150 | val session = | 
| 78374 | 151 | Build_Job.Session_Context.load(database_server, | 
| 78237 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 152 | build_context.build_uuid, name, deps, ancestors, prefs, sources_shasum, | 
| 78374 | 153 | info.timeout, build_context.store, progress = progress) | 
| 78237 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 154 | graph0.new_node(name, session) | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 155 | } | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 156 | } | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 157 | ) | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 158 | } | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 159 | |
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 160 |     lazy val max_time: Map[String, Double] = {
 | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 161 | val maximals = graph.maximals.toSet | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 162 |       def descendants_time(name: String): Double = {
 | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 163 | if (maximals.contains(name)) apply(name).old_time.seconds | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 164 |         else {
 | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 165 | val descendants = graph.all_succs(List(name)).toSet | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 166 | val g = graph.restrict(descendants) | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 167 |           (0.0 :: g.maximals.flatMap { desc =>
 | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 168 | val ps = g.all_preds(List(desc)) | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 169 | if (ps.exists(p => !graph.defined(p))) None | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 170 | else Some(ps.map(p => apply(p).old_time.seconds).sum) | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 171 | }).max | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 172 | } | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 173 | } | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 174 | Map.from( | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 175 | for (name <- graph.keys_iterator) | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 176 | yield name -> descendants_time(name)).withDefaultValue(0.0) | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 177 | } | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 178 | |
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 179 | lazy val ordering: Ordering[String] = | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 180 | (a: String, b: String) => | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 181 |         max_time(b) compare max_time(a) match {
 | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 182 | case 0 => | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 183 |             apply(b).timeout compare apply(a).timeout match {
 | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 184 | case 0 => a compare b | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 185 | case ord => ord | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 186 | } | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 187 | case ord => ord | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 188 | } | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 189 | } | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 190 | |
| 77659 
d7eb6a4522b8
more explicit snapshot of "_state" and "_database";
 wenzelm parents: 
77658diff
changeset | 191 | sealed case class Snapshot( | 
| 77660 | 192 | builds: List[Build], // available build configurations | 
| 77659 
d7eb6a4522b8
more explicit snapshot of "_state" and "_database";
 wenzelm parents: 
77658diff
changeset | 193 | workers: List[Worker], // available worker processes | 
| 78237 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 194 | sessions: Sessions, // static build targets | 
| 77659 
d7eb6a4522b8
more explicit snapshot of "_state" and "_database";
 wenzelm parents: 
77658diff
changeset | 195 | pending: State.Pending, // dynamic build "queue" | 
| 
d7eb6a4522b8
more explicit snapshot of "_state" and "_database";
 wenzelm parents: 
77658diff
changeset | 196 | running: State.Running, // presently running jobs | 
| 
d7eb6a4522b8
more explicit snapshot of "_state" and "_database";
 wenzelm parents: 
77658diff
changeset | 197 | results: State.Results) // finished results | 
| 
d7eb6a4522b8
more explicit snapshot of "_state" and "_database";
 wenzelm parents: 
77658diff
changeset | 198 | |
| 77496 | 199 |   object State {
 | 
| 77585 | 200 | type Pending = List[Task] | 
| 77630 
86ef80d13544
clarified signature: avoid confusion due to object-orientation;
 wenzelm parents: 
77629diff
changeset | 201 | type Running = Map[String, Job] | 
| 77496 | 202 | type Results = Map[String, Result] | 
| 203 | } | |
| 204 | ||
| 77334 
0231e62956a6
clarified state: more explicit type as plain value, which is also easier to sync with external db;
 wenzelm parents: 
77333diff
changeset | 205 | sealed case class State( | 
| 77372 | 206 | serial: Long = 0, | 
| 78237 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 207 | numa_nodes: List[Int] = Nil, | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 208 | sessions: Sessions = Sessions.empty, | 
| 77659 
d7eb6a4522b8
more explicit snapshot of "_state" and "_database";
 wenzelm parents: 
77658diff
changeset | 209 | pending: State.Pending = Nil, | 
| 
d7eb6a4522b8
more explicit snapshot of "_state" and "_database";
 wenzelm parents: 
77658diff
changeset | 210 | running: State.Running = Map.empty, | 
| 
d7eb6a4522b8
more explicit snapshot of "_state" and "_database";
 wenzelm parents: 
77658diff
changeset | 211 | results: State.Results = Map.empty | 
| 77334 
0231e62956a6
clarified state: more explicit type as plain value, which is also easier to sync with external db;
 wenzelm parents: 
77333diff
changeset | 212 |   ) {
 | 
| 77513 | 213 | require(serial >= 0, "serial underflow") | 
| 78574 | 214 |     def inc_serial: State = {
 | 
| 215 | require(serial < Long.MaxValue, "serial overflow") | |
| 216 | copy(serial = serial + 1) | |
| 77513 | 217 | } | 
| 218 | ||
| 79020 
ef76705bf402
clarified ready vs. next ready;
 Fabian Huch <huch@in.tum.de> parents: 
78969diff
changeset | 219 | def ready: State.Pending = pending.filter(_.is_ready) | 
| 
ef76705bf402
clarified ready vs. next ready;
 Fabian Huch <huch@in.tum.de> parents: 
78969diff
changeset | 220 | def next_ready: State.Pending = ready.filter(entry => !is_running(entry.name)) | 
| 78968 
faa5af35fb65
clarified signature: more operations;
 Fabian Huch <huch@in.tum.de> parents: 
78893diff
changeset | 221 | |
| 77334 
0231e62956a6
clarified state: more explicit type as plain value, which is also easier to sync with external db;
 wenzelm parents: 
77333diff
changeset | 222 | def remove_pending(name: String): State = | 
| 
0231e62956a6
clarified state: more explicit type as plain value, which is also easier to sync with external db;
 wenzelm parents: 
77333diff
changeset | 223 | copy(pending = pending.flatMap( | 
| 
0231e62956a6
clarified state: more explicit type as plain value, which is also easier to sync with external db;
 wenzelm parents: 
77333diff
changeset | 224 | entry => if (entry.name == name) None else Some(entry.resolve(name)))) | 
| 
0231e62956a6
clarified state: more explicit type as plain value, which is also easier to sync with external db;
 wenzelm parents: 
77333diff
changeset | 225 | |
| 
0231e62956a6
clarified state: more explicit type as plain value, which is also easier to sync with external db;
 wenzelm parents: 
77333diff
changeset | 226 | def is_running(name: String): Boolean = running.isDefinedAt(name) | 
| 
0231e62956a6
clarified state: more explicit type as plain value, which is also easier to sync with external db;
 wenzelm parents: 
77333diff
changeset | 227 | |
| 77630 
86ef80d13544
clarified signature: avoid confusion due to object-orientation;
 wenzelm parents: 
77629diff
changeset | 228 | def stop_running(): Unit = | 
| 
86ef80d13544
clarified signature: avoid confusion due to object-orientation;
 wenzelm parents: 
77629diff
changeset | 229 | for (job <- running.valuesIterator; build <- job.build) build.cancel() | 
| 77334 
0231e62956a6
clarified state: more explicit type as plain value, which is also easier to sync with external db;
 wenzelm parents: 
77333diff
changeset | 230 | |
| 78529 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78510diff
changeset | 231 | def build_running: List[Build_Job] = | 
| 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78510diff
changeset | 232 | List.from(for (job <- running.valuesIterator; build <- job.build) yield build) | 
| 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78510diff
changeset | 233 | |
| 77649 | 234 | def finished_running(): List[Job] = | 
| 77630 
86ef80d13544
clarified signature: avoid confusion due to object-orientation;
 wenzelm parents: 
77629diff
changeset | 235 | List.from( | 
| 
86ef80d13544
clarified signature: avoid confusion due to object-orientation;
 wenzelm parents: 
77629diff
changeset | 236 | for (job <- running.valuesIterator; build <- job.build if build.is_finished) | 
| 78529 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78510diff
changeset | 237 | yield job) | 
| 77334 
0231e62956a6
clarified state: more explicit type as plain value, which is also easier to sync with external db;
 wenzelm parents: 
77333diff
changeset | 238 | |
| 77630 
86ef80d13544
clarified signature: avoid confusion due to object-orientation;
 wenzelm parents: 
77629diff
changeset | 239 | def add_running(job: Job): State = | 
| 
86ef80d13544
clarified signature: avoid confusion due to object-orientation;
 wenzelm parents: 
77629diff
changeset | 240 | copy(running = running + (job.name -> job)) | 
| 77334 
0231e62956a6
clarified state: more explicit type as plain value, which is also easier to sync with external db;
 wenzelm parents: 
77333diff
changeset | 241 | |
| 
0231e62956a6
clarified state: more explicit type as plain value, which is also easier to sync with external db;
 wenzelm parents: 
77333diff
changeset | 242 | def remove_running(name: String): State = | 
| 
0231e62956a6
clarified state: more explicit type as plain value, which is also easier to sync with external db;
 wenzelm parents: 
77333diff
changeset | 243 | copy(running = running - name) | 
| 
0231e62956a6
clarified state: more explicit type as plain value, which is also easier to sync with external db;
 wenzelm parents: 
77333diff
changeset | 244 | |
| 77336 | 245 | def make_result( | 
| 77651 | 246 | result_name: (String, String, String), | 
| 77461 | 247 | process_result: Process_Result, | 
| 77460 
ccca589d7027
tuned signature: support general Build_Job instances;
 wenzelm parents: 
77459diff
changeset | 248 | output_shasum: SHA1.Shasum, | 
| 77475 | 249 | node_info: Host.Node_Info = Host.Node_Info.none, | 
| 77461 | 250 | current: Boolean = false | 
| 77334 
0231e62956a6
clarified state: more explicit type as plain value, which is also easier to sync with external db;
 wenzelm parents: 
77333diff
changeset | 251 |     ): State = {
 | 
| 77651 | 252 | val (name, worker_uuid, build_uuid) = result_name | 
| 253 | val result = | |
| 254 | Result(name, worker_uuid, build_uuid, node_info, process_result, output_shasum, current) | |
| 255 | copy(results = results + (name -> result)) | |
| 77334 
0231e62956a6
clarified state: more explicit type as plain value, which is also easier to sync with external db;
 wenzelm parents: 
77333diff
changeset | 256 | } | 
| 78500 
fc6d8a2895ca
more robust ancestor_results: avoid total existence failure after build_process has crashed elsewhere;
 wenzelm parents: 
78440diff
changeset | 257 | |
| 
fc6d8a2895ca
more robust ancestor_results: avoid total existence failure after build_process has crashed elsewhere;
 wenzelm parents: 
78440diff
changeset | 258 |     def ancestor_results(name: String): Option[List[Result]] = {
 | 
| 
fc6d8a2895ca
more robust ancestor_results: avoid total existence failure after build_process has crashed elsewhere;
 wenzelm parents: 
78440diff
changeset | 259 | val defined = | 
| 
fc6d8a2895ca
more robust ancestor_results: avoid total existence failure after build_process has crashed elsewhere;
 wenzelm parents: 
78440diff
changeset | 260 | sessions.defined(name) && | 
| 
fc6d8a2895ca
more robust ancestor_results: avoid total existence failure after build_process has crashed elsewhere;
 wenzelm parents: 
78440diff
changeset | 261 | sessions(name).ancestors.forall(a => sessions.defined(a) && results.isDefinedAt(a)) | 
| 
fc6d8a2895ca
more robust ancestor_results: avoid total existence failure after build_process has crashed elsewhere;
 wenzelm parents: 
78440diff
changeset | 262 | if (defined) Some(sessions(name).ancestors.map(results)) else None | 
| 
fc6d8a2895ca
more robust ancestor_results: avoid total existence failure after build_process has crashed elsewhere;
 wenzelm parents: 
78440diff
changeset | 263 | } | 
| 77334 
0231e62956a6
clarified state: more explicit type as plain value, which is also easier to sync with external db;
 wenzelm parents: 
77333diff
changeset | 264 | } | 
| 
0231e62956a6
clarified state: more explicit type as plain value, which is also easier to sync with external db;
 wenzelm parents: 
77333diff
changeset | 265 | |
| 
0231e62956a6
clarified state: more explicit type as plain value, which is also easier to sync with external db;
 wenzelm parents: 
77333diff
changeset | 266 | |
| 77436 | 267 | |
| 268 | /** SQL data model **/ | |
| 77372 | 269 | |
| 78396 
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
 wenzelm parents: 
78394diff
changeset | 270 |   object private_data extends SQL.Data("isabelle_build") {
 | 
| 78168 | 271 |     val database: Path = Path.explode("$ISABELLE_HOME_USER/build.db")
 | 
| 272 | ||
| 78244 | 273 | def pull[A <: Library.Named]( | 
| 77652 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 274 | data_domain: Set[String], | 
| 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 275 | data_iterator: Set[String] => Iterator[A], | 
| 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 276 | old_data: Map[String, A] | 
| 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 277 |     ): Map[String, A] = {
 | 
| 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 278 | val dom = data_domain -- old_data.keysIterator | 
| 78268 | 279 | val data = old_data -- old_data.keysIterator.filterNot(data_domain) | 
| 77652 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 280 | if (dom.isEmpty) data | 
| 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 281 |       else data_iterator(dom).foldLeft(data) { case (map, a) => map + (a.name -> a) }
 | 
| 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 282 | } | 
| 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 283 | |
| 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 284 | def pull0[A <: Library.Named]( | 
| 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 285 | new_data: Map[String, A], | 
| 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 286 | old_data: Map[String, A] | 
| 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 287 |     ): Map[String, A] = {
 | 
| 78244 | 288 | pull(new_data.keySet, dom => new_data.valuesIterator.filter(a => dom(a.name)), old_data) | 
| 77652 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 289 | } | 
| 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 290 | |
| 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 291 | def pull1[A <: Library.Named]( | 
| 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 292 | data_domain: Set[String], | 
| 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 293 | data_base: Set[String] => Map[String, A], | 
| 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 294 | old_data: Map[String, A] | 
| 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 295 |     ): Map[String, A] = {
 | 
| 78244 | 296 | pull(data_domain, dom => data_base(dom).valuesIterator, old_data) | 
| 77652 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 297 | } | 
| 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 298 | |
| 77372 | 299 |     object Generic {
 | 
| 77529 
40ccee0fe19a
separate static build_uuid from dynamic worker_uuid, to allow multiple worker processes participate in one build process;
 wenzelm parents: 
77527diff
changeset | 300 |       val build_uuid = SQL.Column.string("build_uuid")
 | 
| 
40ccee0fe19a
separate static build_uuid from dynamic worker_uuid, to allow multiple worker processes participate in one build process;
 wenzelm parents: 
77527diff
changeset | 301 |       val worker_uuid = SQL.Column.string("worker_uuid")
 | 
| 77372 | 302 |       val name = SQL.Column.string("name")
 | 
| 303 | ||
| 77529 
40ccee0fe19a
separate static build_uuid from dynamic worker_uuid, to allow multiple worker processes participate in one build process;
 wenzelm parents: 
77527diff
changeset | 304 | def sql( | 
| 
40ccee0fe19a
separate static build_uuid from dynamic worker_uuid, to allow multiple worker processes participate in one build process;
 wenzelm parents: 
77527diff
changeset | 305 | build_uuid: String = "", | 
| 
40ccee0fe19a
separate static build_uuid from dynamic worker_uuid, to allow multiple worker processes participate in one build process;
 wenzelm parents: 
77527diff
changeset | 306 | worker_uuid: String = "", | 
| 
40ccee0fe19a
separate static build_uuid from dynamic worker_uuid, to allow multiple worker processes participate in one build process;
 wenzelm parents: 
77527diff
changeset | 307 | names: Iterable[String] = Nil | 
| 
40ccee0fe19a
separate static build_uuid from dynamic worker_uuid, to allow multiple worker processes participate in one build process;
 wenzelm parents: 
77527diff
changeset | 308 | ): SQL.Source = | 
| 77372 | 309 | SQL.and( | 
| 77529 
40ccee0fe19a
separate static build_uuid from dynamic worker_uuid, to allow multiple worker processes participate in one build process;
 wenzelm parents: 
77527diff
changeset | 310 | if_proper(build_uuid, Generic.build_uuid.equal(build_uuid)), | 
| 
40ccee0fe19a
separate static build_uuid from dynamic worker_uuid, to allow multiple worker processes participate in one build process;
 wenzelm parents: 
77527diff
changeset | 311 | if_proper(worker_uuid, Generic.worker_uuid.equal(worker_uuid)), | 
| 77375 | 312 | if_proper(names, Generic.name.member(names))) | 
| 77662 | 313 | |
| 314 | def sql_where( | |
| 315 | build_uuid: String = "", | |
| 316 | worker_uuid: String = "", | |
| 317 | names: Iterable[String] = Nil | |
| 318 |       ): SQL.Source = {
 | |
| 77663 | 319 | SQL.where(sql(build_uuid = build_uuid, worker_uuid = worker_uuid, names = names)) | 
| 77662 | 320 | } | 
| 77372 | 321 | } | 
| 322 | ||
| 77536 
7c7f1473e51a
clarified database content and prepare/init stages;
 wenzelm parents: 
77534diff
changeset | 323 | |
| 
7c7f1473e51a
clarified database content and prepare/init stages;
 wenzelm parents: 
77534diff
changeset | 324 | /* base table */ | 
| 
7c7f1473e51a
clarified database content and prepare/init stages;
 wenzelm parents: 
77534diff
changeset | 325 | |
| 77417 | 326 |     object Base {
 | 
| 77529 
40ccee0fe19a
separate static build_uuid from dynamic worker_uuid, to allow multiple worker processes participate in one build process;
 wenzelm parents: 
77527diff
changeset | 327 | val build_uuid = Generic.build_uuid.make_primary_key | 
| 77387 
cd10b8edfdf5
clarified db content: avoid redundancy of historic ML_IDENTIFIER;
 wenzelm parents: 
77385diff
changeset | 328 |       val ml_platform = SQL.Column.string("ml_platform")
 | 
| 77372 | 329 |       val options = SQL.Column.string("options")
 | 
| 77536 
7c7f1473e51a
clarified database content and prepare/init stages;
 wenzelm parents: 
77534diff
changeset | 330 |       val start = SQL.Column.date("start")
 | 
| 77545 | 331 |       val stop = SQL.Column.date("stop")
 | 
| 77372 | 332 | |
| 78266 | 333 | val table = make_table(List(build_uuid, ml_platform, options, start, stop)) | 
| 77536 
7c7f1473e51a
clarified database content and prepare/init stages;
 wenzelm parents: 
77534diff
changeset | 334 | } | 
| 
7c7f1473e51a
clarified database content and prepare/init stages;
 wenzelm parents: 
77534diff
changeset | 335 | |
| 78632 | 336 |     def read_builds(db: SQL.Database): List[Build] = {
 | 
| 78226 | 337 | val builds = | 
| 78632 | 338 | db.execute_query_statement(Base.table.select(), List.from[Build], | 
| 78226 | 339 |           { res =>
 | 
| 340 | val build_uuid = res.string(Base.build_uuid) | |
| 341 | val ml_platform = res.string(Base.ml_platform) | |
| 342 | val options = res.string(Base.options) | |
| 343 | val start = res.date(Base.start) | |
| 344 | val stop = res.get_date(Base.stop) | |
| 345 | Build(build_uuid, ml_platform, options, start, stop, Nil) | |
| 346 | }) | |
| 347 | ||
| 348 |       for (build <- builds.sortBy(_.start)(Date.Ordering)) yield {
 | |
| 78396 
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
 wenzelm parents: 
78394diff
changeset | 349 | val sessions = private_data.read_sessions_domain(db, build_uuid = build.build_uuid) | 
| 78226 | 350 | build.copy(sessions = sessions.toList.sorted) | 
| 351 | } | |
| 352 | } | |
| 77660 | 353 | |
| 78635 | 354 | def remove_builds(db: SQL.Database, remove: List[String]): Unit = | 
| 355 |       if (remove.nonEmpty) {
 | |
| 356 | val sql = Generic.build_uuid.where_member(remove) | |
| 357 | db.execute_statement(SQL.MULTI(build_uuid_tables.map(_.delete(sql = sql)))) | |
| 358 | } | |
| 359 | ||
| 77536 
7c7f1473e51a
clarified database content and prepare/init stages;
 wenzelm parents: 
77534diff
changeset | 360 | def start_build( | 
| 
7c7f1473e51a
clarified database content and prepare/init stages;
 wenzelm parents: 
77534diff
changeset | 361 | db: SQL.Database, | 
| 
7c7f1473e51a
clarified database content and prepare/init stages;
 wenzelm parents: 
77534diff
changeset | 362 | build_uuid: String, | 
| 
7c7f1473e51a
clarified database content and prepare/init stages;
 wenzelm parents: 
77534diff
changeset | 363 | ml_platform: String, | 
| 78156 
da5cc332ded3
prefer Database_Progress, which is more robust (amending afb1a19307c4);
 wenzelm parents: 
78154diff
changeset | 364 | options: String | 
| 77536 
7c7f1473e51a
clarified database content and prepare/init stages;
 wenzelm parents: 
77534diff
changeset | 365 |     ): Unit = {
 | 
| 77541 | 366 | db.execute_statement(Base.table.insert(), body = | 
| 367 |         { stmt =>
 | |
| 368 | stmt.string(1) = build_uuid | |
| 369 | stmt.string(2) = ml_platform | |
| 370 | stmt.string(3) = options | |
| 371 | stmt.date(4) = db.now() | |
| 372 | stmt.date(5) = None | |
| 373 | }) | |
| 77372 | 374 | } | 
| 375 | ||
| 77545 | 376 | def stop_build(db: SQL.Database, build_uuid: String): Unit = | 
| 77541 | 377 | db.execute_statement( | 
| 77636 | 378 | Base.table.update(List(Base.stop), sql = Base.build_uuid.where_equal(build_uuid)), | 
| 77541 | 379 |         body = { stmt => stmt.date(1) = db.now() })
 | 
| 77538 | 380 | |
| 77539 
2b996a0df1ce
proper clean_build of old data at start of new process --- allow to inspect remains of the last process;
 wenzelm parents: 
77538diff
changeset | 381 |     def clean_build(db: SQL.Database): Unit = {
 | 
| 78635 | 382 | val remove = | 
| 77552 | 383 | db.execute_query_statement( | 
| 384 | Base.table.select(List(Base.build_uuid), sql = SQL.where(Base.stop.defined)), | |
| 385 | List.from[String], res => res.string(Base.build_uuid)) | |
| 77539 
2b996a0df1ce
proper clean_build of old data at start of new process --- allow to inspect remains of the last process;
 wenzelm parents: 
77538diff
changeset | 386 | |
| 78635 | 387 | remove_builds(db, remove) | 
| 77539 
2b996a0df1ce
proper clean_build of old data at start of new process --- allow to inspect remains of the last process;
 wenzelm parents: 
77538diff
changeset | 388 | } | 
| 
2b996a0df1ce
proper clean_build of old data at start of new process --- allow to inspect remains of the last process;
 wenzelm parents: 
77538diff
changeset | 389 | |
| 77416 
d88c12f22ab0
clarified scope of "serial" and "numa_index" within database;
 wenzelm parents: 
77415diff
changeset | 390 | |
| 77531 | 391 | /* sessions */ | 
| 77505 | 392 | |
| 77496 | 393 |     object Sessions {
 | 
| 394 | val name = Generic.name.make_primary_key | |
| 395 |       val deps = SQL.Column.string("deps")
 | |
| 396 |       val ancestors = SQL.Column.string("ancestors")
 | |
| 77610 
3b09ae9e40cb
clarified session prefs (or "options" within the database);
 wenzelm parents: 
77609diff
changeset | 397 |       val options = SQL.Column.string("options")
 | 
| 77496 | 398 |       val sources = SQL.Column.string("sources")
 | 
| 399 |       val timeout = SQL.Column.long("timeout")
 | |
| 400 |       val old_time = SQL.Column.long("old_time")
 | |
| 401 |       val old_command_timings = SQL.Column.bytes("old_command_timings")
 | |
| 77529 
40ccee0fe19a
separate static build_uuid from dynamic worker_uuid, to allow multiple worker processes participate in one build process;
 wenzelm parents: 
77527diff
changeset | 402 | val build_uuid = Generic.build_uuid | 
| 77496 | 403 | |
| 78266 | 404 | val table = | 
| 405 | make_table( | |
| 406 | List(name, deps, ancestors, options, sources, timeout, | |
| 407 | old_time, old_command_timings, build_uuid), | |
| 408 | name = "sessions") | |
| 77496 | 409 | } | 
| 410 | ||
| 78218 
a625bfb0e549
clarified signature: more operations and options;
 wenzelm parents: 
78217diff
changeset | 411 | def read_sessions_domain(db: SQL.Database, build_uuid: String = ""): Set[String] = | 
| 77552 | 412 | db.execute_query_statement( | 
| 78218 
a625bfb0e549
clarified signature: more operations and options;
 wenzelm parents: 
78217diff
changeset | 413 | Sessions.table.select(List(Sessions.name), | 
| 78251 | 414 | sql = if_proper(build_uuid, Sessions.build_uuid.where_equal(build_uuid))), | 
| 77552 | 415 | Set.from[String], res => res.string(Sessions.name)) | 
| 77496 | 416 | |
| 78218 
a625bfb0e549
clarified signature: more operations and options;
 wenzelm parents: 
78217diff
changeset | 417 | def read_sessions(db: SQL.Database, | 
| 
a625bfb0e549
clarified signature: more operations and options;
 wenzelm parents: 
78217diff
changeset | 418 | names: Iterable[String] = Nil, | 
| 
a625bfb0e549
clarified signature: more operations and options;
 wenzelm parents: 
78217diff
changeset | 419 | build_uuid: String = "" | 
| 78237 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 420 |     ): List[Build_Job.Session_Context] = {
 | 
| 77552 | 421 | db.execute_query_statement( | 
| 78218 
a625bfb0e549
clarified signature: more operations and options;
 wenzelm parents: 
78217diff
changeset | 422 | Sessions.table.select( | 
| 
a625bfb0e549
clarified signature: more operations and options;
 wenzelm parents: 
78217diff
changeset | 423 | sql = | 
| 
a625bfb0e549
clarified signature: more operations and options;
 wenzelm parents: 
78217diff
changeset | 424 | SQL.where_and( | 
| 
a625bfb0e549
clarified signature: more operations and options;
 wenzelm parents: 
78217diff
changeset | 425 | if_proper(names, Sessions.name.member(names)), | 
| 
a625bfb0e549
clarified signature: more operations and options;
 wenzelm parents: 
78217diff
changeset | 426 | if_proper(build_uuid, Sessions.build_uuid.equal(build_uuid))) | 
| 
a625bfb0e549
clarified signature: more operations and options;
 wenzelm parents: 
78217diff
changeset | 427 | ), | 
| 78237 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 428 | List.from[Build_Job.Session_Context], | 
| 77552 | 429 |         { res =>
 | 
| 430 | val name = res.string(Sessions.name) | |
| 431 | val deps = split_lines(res.string(Sessions.deps)) | |
| 432 | val ancestors = split_lines(res.string(Sessions.ancestors)) | |
| 77610 
3b09ae9e40cb
clarified session prefs (or "options" within the database);
 wenzelm parents: 
77609diff
changeset | 433 | val options = res.string(Sessions.options) | 
| 77552 | 434 | val sources_shasum = SHA1.fake_shasum(res.string(Sessions.sources)) | 
| 435 | val timeout = Time.ms(res.long(Sessions.timeout)) | |
| 436 | val old_time = Time.ms(res.long(Sessions.old_time)) | |
| 437 | val old_command_timings_blob = res.bytes(Sessions.old_command_timings) | |
| 438 | val build_uuid = res.string(Sessions.build_uuid) | |
| 78237 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 439 | Build_Job.Session_Context(name, deps, ancestors, options, sources_shasum, | 
| 77552 | 440 | timeout, old_time, old_command_timings_blob, build_uuid) | 
| 77496 | 441 | } | 
| 77552 | 442 | ) | 
| 78237 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 443 | } | 
| 77496 | 444 | |
| 78553 | 445 | def update_sessions( | 
| 446 | db: SQL.Database, | |
| 447 | sessions: Build_Process.Sessions, | |
| 448 | old_sessions: Build_Process.Sessions | |
| 449 |     ): Boolean = {
 | |
| 450 | val insert = sessions.iterator.filterNot(s => old_sessions.defined(s.name)).toList | |
| 77496 | 451 | |
| 78552 
384adc74e27d
performance tuning: avoid multiple db roundtrips;
 wenzelm parents: 
78546diff
changeset | 452 |       if (insert.nonEmpty) {
 | 
| 
384adc74e27d
performance tuning: avoid multiple db roundtrips;
 wenzelm parents: 
78546diff
changeset | 453 | db.execute_batch_statement(Sessions.table.insert(), batch = | 
| 
384adc74e27d
performance tuning: avoid multiple db roundtrips;
 wenzelm parents: 
78546diff
changeset | 454 |           for (session <- insert) yield { (stmt: SQL.Statement) =>
 | 
| 78237 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 455 | stmt.string(1) = session.name | 
| 77541 | 456 | stmt.string(2) = cat_lines(session.deps) | 
| 457 | stmt.string(3) = cat_lines(session.ancestors) | |
| 77610 
3b09ae9e40cb
clarified session prefs (or "options" within the database);
 wenzelm parents: 
77609diff
changeset | 458 | stmt.string(4) = session.session_prefs | 
| 
3b09ae9e40cb
clarified session prefs (or "options" within the database);
 wenzelm parents: 
77609diff
changeset | 459 | stmt.string(5) = session.sources_shasum.toString | 
| 
3b09ae9e40cb
clarified session prefs (or "options" within the database);
 wenzelm parents: 
77609diff
changeset | 460 | stmt.long(6) = session.timeout.ms | 
| 
3b09ae9e40cb
clarified session prefs (or "options" within the database);
 wenzelm parents: 
77609diff
changeset | 461 | stmt.long(7) = session.old_time.ms | 
| 
3b09ae9e40cb
clarified session prefs (or "options" within the database);
 wenzelm parents: 
77609diff
changeset | 462 | stmt.bytes(8) = session.old_command_timings_blob | 
| 
3b09ae9e40cb
clarified session prefs (or "options" within the database);
 wenzelm parents: 
77609diff
changeset | 463 | stmt.string(9) = session.build_uuid | 
| 77541 | 464 | }) | 
| 77496 | 465 | } | 
| 466 | ||
| 467 | insert.nonEmpty | |
| 468 | } | |
| 469 | ||
| 77531 | 470 | |
| 471 | /* workers */ | |
| 472 | ||
| 473 |     object Workers {
 | |
| 78173 | 474 | val worker_uuid = Generic.worker_uuid.make_primary_key | 
| 475 | val build_uuid = Generic.build_uuid | |
| 77546 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 476 |       val start = SQL.Column.date("start")
 | 
| 77531 | 477 |       val stamp = SQL.Column.date("stamp")
 | 
| 77546 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 478 |       val stop = SQL.Column.date("stop")
 | 
| 77531 | 479 |       val serial = SQL.Column.long("serial")
 | 
| 480 | ||
| 78266 | 481 | val table = | 
| 482 | make_table(List(worker_uuid, build_uuid, start, stamp, stop, serial), name = "workers") | |
| 77531 | 483 | } | 
| 484 | ||
| 77655 | 485 | def read_serial(db: SQL.Database): Long = | 
| 486 | db.execute_query_statementO[Long]( | |
| 78151 | 487 | Workers.table.select(List(Workers.serial.max)), _.long(Workers.serial)).getOrElse(0L) | 
| 77655 | 488 | |
| 77586 | 489 | def read_workers( | 
| 490 | db: SQL.Database, | |
| 491 | build_uuid: String = "", | |
| 492 | worker_uuid: String = "" | |
| 77657 
a2a4adc268b8
removed redundant State.workers: directly maintained within the database, using with SQL update;
 wenzelm parents: 
77656diff
changeset | 493 |     ): List[Worker] = {
 | 
| 77586 | 494 | db.execute_query_statement( | 
| 77662 | 495 | Workers.table.select( | 
| 496 | sql = Generic.sql_where(build_uuid = build_uuid, worker_uuid = worker_uuid)), | |
| 77586 | 497 | List.from[Worker], | 
| 498 |           { res =>
 | |
| 499 | Worker( | |
| 500 | worker_uuid = res.string(Workers.worker_uuid), | |
| 501 | build_uuid = res.string(Workers.build_uuid), | |
| 502 | start = res.date(Workers.start), | |
| 503 | stamp = res.date(Workers.stamp), | |
| 504 | stop = res.get_date(Workers.stop), | |
| 505 | serial = res.long(Workers.serial)) | |
| 506 | }) | |
| 507 | } | |
| 508 | ||
| 77584 | 509 | def start_worker( | 
| 510 | db: SQL.Database, | |
| 511 | worker_uuid: String, | |
| 512 | build_uuid: String, | |
| 77652 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 513 | serial: Long | 
| 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 514 |     ): Unit = {
 | 
| 77546 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 515 | def err(msg: String): Nothing = | 
| 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 516 |         error("Cannot start worker " + worker_uuid + if_proper(msg, "\n" + msg))
 | 
| 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 517 | |
| 77552 | 518 | val build_stop = | 
| 519 | db.execute_query_statementO( | |
| 77636 | 520 | Base.table.select(List(Base.stop), sql = Base.build_uuid.where_equal(build_uuid)), | 
| 77552 | 521 | res => res.get_date(Base.stop)) | 
| 522 | ||
| 77546 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 523 |       build_stop match {
 | 
| 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 524 | case Some(None) => | 
| 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 525 |         case Some(Some(_)) => err("for already stopped build process " + build_uuid)
 | 
| 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 526 |         case None => err("for unknown build process " + build_uuid)
 | 
| 77531 | 527 | } | 
| 528 | ||
| 77546 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 529 | db.execute_statement(Workers.table.insert(), body = | 
| 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 530 |         { stmt =>
 | 
| 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 531 | val now = db.now() | 
| 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 532 | stmt.string(1) = worker_uuid | 
| 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 533 | stmt.string(2) = build_uuid | 
| 78172 
43ed2541b758
omit redundant data: already stored in progress database;
 wenzelm parents: 
78170diff
changeset | 534 | stmt.date(3) = now | 
| 
43ed2541b758
omit redundant data: already stored in progress database;
 wenzelm parents: 
78170diff
changeset | 535 | stmt.date(4) = now | 
| 
43ed2541b758
omit redundant data: already stored in progress database;
 wenzelm parents: 
78170diff
changeset | 536 | stmt.date(5) = None | 
| 
43ed2541b758
omit redundant data: already stored in progress database;
 wenzelm parents: 
78170diff
changeset | 537 | stmt.long(6) = serial | 
| 77546 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 538 | }) | 
| 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 539 | } | 
| 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 540 | |
| 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 541 | def stamp_worker( | 
| 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 542 | db: SQL.Database, | 
| 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 543 | worker_uuid: String, | 
| 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 544 | serial: Long, | 
| 78246 
76dd9b9cf624
more robust "stop": further "stamp" ticks may happen afterwards;
 wenzelm parents: 
78245diff
changeset | 545 | stop_now: Boolean = false | 
| 77546 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 546 |     ): Unit = {
 | 
| 78246 
76dd9b9cf624
more robust "stop": further "stamp" ticks may happen afterwards;
 wenzelm parents: 
78245diff
changeset | 547 | val sql = Workers.worker_uuid.where_equal(worker_uuid) | 
| 
76dd9b9cf624
more robust "stop": further "stamp" ticks may happen afterwards;
 wenzelm parents: 
78245diff
changeset | 548 | |
| 
76dd9b9cf624
more robust "stop": further "stamp" ticks may happen afterwards;
 wenzelm parents: 
78245diff
changeset | 549 | val stop = | 
| 
76dd9b9cf624
more robust "stop": further "stamp" ticks may happen afterwards;
 wenzelm parents: 
78245diff
changeset | 550 | db.execute_query_statementO( | 
| 
76dd9b9cf624
more robust "stop": further "stamp" ticks may happen afterwards;
 wenzelm parents: 
78245diff
changeset | 551 | Workers.table.select(List(Workers.stop), sql = sql), _.get_date(Workers.stop)).flatten | 
| 
76dd9b9cf624
more robust "stop": further "stamp" ticks may happen afterwards;
 wenzelm parents: 
78245diff
changeset | 552 | |
| 78241 | 553 | db.execute_statement( | 
| 78246 
76dd9b9cf624
more robust "stop": further "stamp" ticks may happen afterwards;
 wenzelm parents: 
78245diff
changeset | 554 | Workers.table.update(List(Workers.stamp, Workers.stop, Workers.serial), sql = sql), | 
| 78241 | 555 |         body = { stmt =>
 | 
| 77546 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 556 | val now = db.now() | 
| 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 557 | stmt.date(1) = now | 
| 78246 
76dd9b9cf624
more robust "stop": further "stamp" ticks may happen afterwards;
 wenzelm parents: 
78245diff
changeset | 558 | stmt.date(2) = if (stop_now) Some(now) else stop | 
| 77546 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 559 | stmt.long(3) = serial | 
| 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 560 | }) | 
| 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 561 | } | 
| 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 562 | |
| 77531 | 563 | |
| 564 | /* pending jobs */ | |
| 565 | ||
| 566 |     object Pending {
 | |
| 567 | val name = Generic.name.make_primary_key | |
| 568 |       val deps = SQL.Column.string("deps")
 | |
| 569 |       val info = SQL.Column.string("info")
 | |
| 77661 
45bd5c26cbcc
proper build_uuid for Build_Process.Task: thus old entries are removed via prepare_database/clean_build;
 wenzelm parents: 
77660diff
changeset | 570 | val build_uuid = Generic.build_uuid | 
| 77531 | 571 | |
| 78266 | 572 | val table = make_table(List(name, deps, info, build_uuid), name = "pending") | 
| 77531 | 573 | } | 
| 574 | ||
| 77585 | 575 | def read_pending(db: SQL.Database): List[Task] = | 
| 77552 | 576 | db.execute_query_statement( | 
| 577 | Pending.table.select(sql = SQL.order_by(List(Pending.name))), | |
| 77585 | 578 | List.from[Task], | 
| 77552 | 579 |         { res =>
 | 
| 580 | val name = res.string(Pending.name) | |
| 581 | val deps = res.string(Pending.deps) | |
| 582 | val info = res.string(Pending.info) | |
| 77661 
45bd5c26cbcc
proper build_uuid for Build_Process.Task: thus old entries are removed via prepare_database/clean_build;
 wenzelm parents: 
77660diff
changeset | 583 | val build_uuid = res.string(Pending.build_uuid) | 
| 
45bd5c26cbcc
proper build_uuid for Build_Process.Task: thus old entries are removed via prepare_database/clean_build;
 wenzelm parents: 
77660diff
changeset | 584 | Task(name, split_lines(deps), JSON.Object.parse(info), build_uuid) | 
| 77552 | 585 | }) | 
| 77372 | 586 | |
| 78553 | 587 | def update_pending( | 
| 588 | db: SQL.Database, | |
| 589 | pending: State.Pending, | |
| 590 | old_pending: State.Pending | |
| 591 |     ): Boolean = {
 | |
| 77372 | 592 | val (delete, insert) = Library.symmetric_difference(old_pending, pending) | 
| 593 | ||
| 594 |       if (delete.nonEmpty) {
 | |
| 77540 | 595 | db.execute_statement( | 
| 77662 | 596 | Pending.table.delete(sql = Generic.sql_where(names = delete.map(_.name)))) | 
| 77372 | 597 | } | 
| 598 | ||
| 78552 
384adc74e27d
performance tuning: avoid multiple db roundtrips;
 wenzelm parents: 
78546diff
changeset | 599 |       if (insert.nonEmpty) {
 | 
| 
384adc74e27d
performance tuning: avoid multiple db roundtrips;
 wenzelm parents: 
78546diff
changeset | 600 | db.execute_batch_statement(Pending.table.insert(), batch = | 
| 
384adc74e27d
performance tuning: avoid multiple db roundtrips;
 wenzelm parents: 
78546diff
changeset | 601 |           for (task <- insert) yield { (stmt: SQL.Statement) =>
 | 
| 77661 
45bd5c26cbcc
proper build_uuid for Build_Process.Task: thus old entries are removed via prepare_database/clean_build;
 wenzelm parents: 
77660diff
changeset | 602 | stmt.string(1) = task.name | 
| 
45bd5c26cbcc
proper build_uuid for Build_Process.Task: thus old entries are removed via prepare_database/clean_build;
 wenzelm parents: 
77660diff
changeset | 603 | stmt.string(2) = cat_lines(task.deps) | 
| 
45bd5c26cbcc
proper build_uuid for Build_Process.Task: thus old entries are removed via prepare_database/clean_build;
 wenzelm parents: 
77660diff
changeset | 604 | stmt.string(3) = JSON.Format(task.info) | 
| 
45bd5c26cbcc
proper build_uuid for Build_Process.Task: thus old entries are removed via prepare_database/clean_build;
 wenzelm parents: 
77660diff
changeset | 605 | stmt.string(4) = task.build_uuid | 
| 77541 | 606 | }) | 
| 77372 | 607 | } | 
| 608 | ||
| 609 | delete.nonEmpty || insert.nonEmpty | |
| 610 | } | |
| 611 | ||
| 77531 | 612 | |
| 613 | /* running jobs */ | |
| 614 | ||
| 615 |     object Running {
 | |
| 616 | val name = Generic.name.make_primary_key | |
| 77587 | 617 | val worker_uuid = Generic.worker_uuid | 
| 77634 | 618 | val build_uuid = Generic.build_uuid | 
| 77531 | 619 |       val hostname = SQL.Column.string("hostname")
 | 
| 620 |       val numa_node = SQL.Column.int("numa_node")
 | |
| 78839 
7799ec03b8bd
generalized node infos: allow addressing of numa node segments via relative cpus;
 Fabian Huch <huch@in.tum.de> parents: 
78635diff
changeset | 621 |       val rel_cpus = SQL.Column.string("rel_cpus")
 | 
| 78841 
7f61688d4e8d
added start date to build jobs, e.g., for build time estimation;
 Fabian Huch <huch@in.tum.de> parents: 
78839diff
changeset | 622 |       val start_date = SQL.Column.date("start_date")
 | 
| 77531 | 623 | |
| 78266 | 624 | val table = | 
| 78839 
7799ec03b8bd
generalized node infos: allow addressing of numa node segments via relative cpus;
 Fabian Huch <huch@in.tum.de> parents: 
78635diff
changeset | 625 | make_table( | 
| 78841 
7f61688d4e8d
added start date to build jobs, e.g., for build time estimation;
 Fabian Huch <huch@in.tum.de> parents: 
78839diff
changeset | 626 | List(name, worker_uuid, build_uuid, hostname, numa_node, rel_cpus, start_date), | 
| 
7f61688d4e8d
added start date to build jobs, e.g., for build time estimation;
 Fabian Huch <huch@in.tum.de> parents: 
78839diff
changeset | 627 | name = "running") | 
| 77531 | 628 | } | 
| 629 | ||
| 77652 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 630 | def read_running(db: SQL.Database): State.Running = | 
| 77552 | 631 | db.execute_query_statement( | 
| 632 | Running.table.select(sql = SQL.order_by(List(Running.name))), | |
| 77652 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 633 | Map.from[String, Job], | 
| 77552 | 634 |         { res =>
 | 
| 635 | val name = res.string(Running.name) | |
| 77587 | 636 | val worker_uuid = res.string(Running.worker_uuid) | 
| 77634 | 637 | val build_uuid = res.string(Running.build_uuid) | 
| 77552 | 638 | val hostname = res.string(Running.hostname) | 
| 639 | val numa_node = res.get_int(Running.numa_node) | |
| 78839 
7799ec03b8bd
generalized node infos: allow addressing of numa node segments via relative cpus;
 Fabian Huch <huch@in.tum.de> parents: 
78635diff
changeset | 640 | val rel_cpus = res.string(Running.rel_cpus) | 
| 78841 
7f61688d4e8d
added start date to build jobs, e.g., for build time estimation;
 Fabian Huch <huch@in.tum.de> parents: 
78839diff
changeset | 641 | val start_date = res.date(Running.start_date) | 
| 78839 
7799ec03b8bd
generalized node infos: allow addressing of numa node segments via relative cpus;
 Fabian Huch <huch@in.tum.de> parents: 
78635diff
changeset | 642 | |
| 
7799ec03b8bd
generalized node infos: allow addressing of numa node segments via relative cpus;
 Fabian Huch <huch@in.tum.de> parents: 
78635diff
changeset | 643 | val node_info = Host.Node_Info(hostname, numa_node, Host.Range.from(rel_cpus)) | 
| 78841 
7f61688d4e8d
added start date to build jobs, e.g., for build time estimation;
 Fabian Huch <huch@in.tum.de> parents: 
78839diff
changeset | 644 | name -> Job(name, worker_uuid, build_uuid, node_info, start_date, None) | 
| 77552 | 645 | } | 
| 646 | ) | |
| 77372 | 647 | |
| 78553 | 648 | def update_running( | 
| 649 | db: SQL.Database, | |
| 650 | running: State.Running, | |
| 651 | old_running: State.Running | |
| 652 |     ): Boolean = {
 | |
| 653 | val running0 = old_running.valuesIterator.toList | |
| 78573 
980f3cfcbc2c
more accurate treatment of state vs. serial vs. db;
 wenzelm parents: 
78571diff
changeset | 654 | val running1 = running.valuesIterator.toList | 
| 77630 
86ef80d13544
clarified signature: avoid confusion due to object-orientation;
 wenzelm parents: 
77629diff
changeset | 655 | val (delete, insert) = Library.symmetric_difference(running0, running1) | 
| 77372 | 656 | |
| 657 |       if (delete.nonEmpty) {
 | |
| 77540 | 658 | db.execute_statement( | 
| 77662 | 659 | Running.table.delete(sql = Generic.sql_where(names = delete.map(_.name)))) | 
| 77372 | 660 | } | 
| 661 | ||
| 78552 
384adc74e27d
performance tuning: avoid multiple db roundtrips;
 wenzelm parents: 
78546diff
changeset | 662 |       if (insert.nonEmpty) {
 | 
| 
384adc74e27d
performance tuning: avoid multiple db roundtrips;
 wenzelm parents: 
78546diff
changeset | 663 | db.execute_batch_statement(Running.table.insert(), batch = | 
| 
384adc74e27d
performance tuning: avoid multiple db roundtrips;
 wenzelm parents: 
78546diff
changeset | 664 |           for (job <- insert) yield { (stmt: SQL.Statement) =>
 | 
| 77630 
86ef80d13544
clarified signature: avoid confusion due to object-orientation;
 wenzelm parents: 
77629diff
changeset | 665 | stmt.string(1) = job.name | 
| 77587 | 666 | stmt.string(2) = job.worker_uuid | 
| 77634 | 667 | stmt.string(3) = job.build_uuid | 
| 668 | stmt.string(4) = job.node_info.hostname | |
| 669 | stmt.int(5) = job.node_info.numa_node | |
| 78839 
7799ec03b8bd
generalized node infos: allow addressing of numa node segments via relative cpus;
 Fabian Huch <huch@in.tum.de> parents: 
78635diff
changeset | 670 | stmt.string(6) = Host.Range(job.node_info.rel_cpus) | 
| 78841 
7f61688d4e8d
added start date to build jobs, e.g., for build time estimation;
 Fabian Huch <huch@in.tum.de> parents: 
78839diff
changeset | 671 | stmt.date(7) = job.start_date | 
| 77541 | 672 | }) | 
| 77372 | 673 | } | 
| 674 | ||
| 675 | delete.nonEmpty || insert.nonEmpty | |
| 676 | } | |
| 677 | ||
| 77531 | 678 | |
| 679 | /* job results */ | |
| 680 | ||
| 681 |     object Results {
 | |
| 682 | val name = Generic.name.make_primary_key | |
| 77651 | 683 | val worker_uuid = Generic.worker_uuid | 
| 684 | val build_uuid = Generic.build_uuid | |
| 77531 | 685 |       val hostname = SQL.Column.string("hostname")
 | 
| 78839 
7799ec03b8bd
generalized node infos: allow addressing of numa node segments via relative cpus;
 Fabian Huch <huch@in.tum.de> parents: 
78635diff
changeset | 686 |       val numa_node = SQL.Column.int("numa_node")
 | 
| 
7799ec03b8bd
generalized node infos: allow addressing of numa node segments via relative cpus;
 Fabian Huch <huch@in.tum.de> parents: 
78635diff
changeset | 687 |       val rel_cpus = SQL.Column.string("rel_cpus")
 | 
| 77531 | 688 |       val rc = SQL.Column.int("rc")
 | 
| 689 |       val out = SQL.Column.string("out")
 | |
| 690 |       val err = SQL.Column.string("err")
 | |
| 691 |       val timing_elapsed = SQL.Column.long("timing_elapsed")
 | |
| 692 |       val timing_cpu = SQL.Column.long("timing_cpu")
 | |
| 693 |       val timing_gc = SQL.Column.long("timing_gc")
 | |
| 77651 | 694 |       val output_shasum = SQL.Column.string("output_shasum")
 | 
| 695 |       val current = SQL.Column.bool("current")
 | |
| 77531 | 696 | |
| 697 | val table = | |
| 78266 | 698 | make_table( | 
| 78839 
7799ec03b8bd
generalized node infos: allow addressing of numa node segments via relative cpus;
 Fabian Huch <huch@in.tum.de> parents: 
78635diff
changeset | 699 | List(name, worker_uuid, build_uuid, hostname, numa_node, rel_cpus, | 
| 78266 | 700 | rc, out, err, timing_elapsed, timing_cpu, timing_gc, output_shasum, current), | 
| 701 | name = "results") | |
| 77531 | 702 | } | 
| 703 | ||
| 77496 | 704 | def read_results_domain(db: SQL.Database): Set[String] = | 
| 77552 | 705 | db.execute_query_statement( | 
| 706 | Results.table.select(List(Results.name)), | |
| 707 | Set.from[String], res => res.string(Results.name)) | |
| 77496 | 708 | |
| 77652 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 709 | def read_results(db: SQL.Database, names: Iterable[String] = Nil): State.Results = | 
| 77552 | 710 | db.execute_query_statement( | 
| 711 | Results.table.select(sql = if_proper(names, Results.name.where_member(names))), | |
| 77651 | 712 | Map.from[String, Result], | 
| 77552 | 713 |         { res =>
 | 
| 714 | val name = res.string(Results.name) | |
| 77651 | 715 | val worker_uuid = res.string(Results.worker_uuid) | 
| 716 | val build_uuid = res.string(Results.build_uuid) | |
| 77552 | 717 | val hostname = res.string(Results.hostname) | 
| 718 | val numa_node = res.get_int(Results.numa_node) | |
| 78839 
7799ec03b8bd
generalized node infos: allow addressing of numa node segments via relative cpus;
 Fabian Huch <huch@in.tum.de> parents: 
78635diff
changeset | 719 | val rel_cpus = res.string(Results.rel_cpus) | 
| 
7799ec03b8bd
generalized node infos: allow addressing of numa node segments via relative cpus;
 Fabian Huch <huch@in.tum.de> parents: 
78635diff
changeset | 720 | val node_info = Host.Node_Info(hostname, numa_node, Host.Range.from(rel_cpus)) | 
| 77651 | 721 | |
| 77552 | 722 | val rc = res.int(Results.rc) | 
| 723 | val out = res.string(Results.out) | |
| 724 | val err = res.string(Results.err) | |
| 725 | val timing = | |
| 726 | res.timing( | |
| 727 | Results.timing_elapsed, | |
| 728 | Results.timing_cpu, | |
| 729 | Results.timing_gc) | |
| 730 | val process_result = | |
| 731 | Process_Result(rc, | |
| 732 | out_lines = split_lines(out), | |
| 733 | err_lines = split_lines(err), | |
| 734 | timing = timing) | |
| 77651 | 735 | |
| 736 | val output_shasum = SHA1.fake_shasum(res.string(Results.output_shasum)) | |
| 737 | val current = res.bool(Results.current) | |
| 738 | ||
| 739 | name -> | |
| 740 | Result(name, worker_uuid, build_uuid, node_info, process_result, output_shasum, current) | |
| 77496 | 741 | } | 
| 77552 | 742 | ) | 
| 77372 | 743 | |
| 78553 | 744 | def update_results( | 
| 745 | db: SQL.Database, | |
| 746 | results: State.Results, | |
| 747 | old_results: State.Results | |
| 748 |     ): Boolean = {
 | |
| 78267 | 749 | val insert = | 
| 78553 | 750 | results.valuesIterator.filterNot(res => old_results.isDefinedAt(res.name)).toList | 
| 77372 | 751 | |
| 78552 
384adc74e27d
performance tuning: avoid multiple db roundtrips;
 wenzelm parents: 
78546diff
changeset | 752 |       if (insert.nonEmpty) {
 | 
| 
384adc74e27d
performance tuning: avoid multiple db roundtrips;
 wenzelm parents: 
78546diff
changeset | 753 | db.execute_batch_statement(Results.table.insert(), batch = | 
| 
384adc74e27d
performance tuning: avoid multiple db roundtrips;
 wenzelm parents: 
78546diff
changeset | 754 |           for (result <- insert) yield { (stmt: SQL.Statement) =>
 | 
| 
384adc74e27d
performance tuning: avoid multiple db roundtrips;
 wenzelm parents: 
78546diff
changeset | 755 | val process_result = result.process_result | 
| 77651 | 756 | stmt.string(1) = result.name | 
| 757 | stmt.string(2) = result.worker_uuid | |
| 758 | stmt.string(3) = result.build_uuid | |
| 759 | stmt.string(4) = result.node_info.hostname | |
| 760 | stmt.int(5) = result.node_info.numa_node | |
| 78839 
7799ec03b8bd
generalized node infos: allow addressing of numa node segments via relative cpus;
 Fabian Huch <huch@in.tum.de> parents: 
78635diff
changeset | 761 | stmt.string(6) = Host.Range(result.node_info.rel_cpus) | 
| 
7799ec03b8bd
generalized node infos: allow addressing of numa node segments via relative cpus;
 Fabian Huch <huch@in.tum.de> parents: 
78635diff
changeset | 762 | stmt.int(7) = process_result.rc | 
| 
7799ec03b8bd
generalized node infos: allow addressing of numa node segments via relative cpus;
 Fabian Huch <huch@in.tum.de> parents: 
78635diff
changeset | 763 | stmt.string(8) = cat_lines(process_result.out_lines) | 
| 
7799ec03b8bd
generalized node infos: allow addressing of numa node segments via relative cpus;
 Fabian Huch <huch@in.tum.de> parents: 
78635diff
changeset | 764 | stmt.string(9) = cat_lines(process_result.err_lines) | 
| 
7799ec03b8bd
generalized node infos: allow addressing of numa node segments via relative cpus;
 Fabian Huch <huch@in.tum.de> parents: 
78635diff
changeset | 765 | stmt.long(10) = process_result.timing.elapsed.ms | 
| 
7799ec03b8bd
generalized node infos: allow addressing of numa node segments via relative cpus;
 Fabian Huch <huch@in.tum.de> parents: 
78635diff
changeset | 766 | stmt.long(11) = process_result.timing.cpu.ms | 
| 
7799ec03b8bd
generalized node infos: allow addressing of numa node segments via relative cpus;
 Fabian Huch <huch@in.tum.de> parents: 
78635diff
changeset | 767 | stmt.long(12) = process_result.timing.gc.ms | 
| 
7799ec03b8bd
generalized node infos: allow addressing of numa node segments via relative cpus;
 Fabian Huch <huch@in.tum.de> parents: 
78635diff
changeset | 768 | stmt.string(13) = result.output_shasum.toString | 
| 
7799ec03b8bd
generalized node infos: allow addressing of numa node segments via relative cpus;
 Fabian Huch <huch@in.tum.de> parents: 
78635diff
changeset | 769 | stmt.bool(14) = result.current | 
| 77541 | 770 | }) | 
| 77372 | 771 | } | 
| 772 | ||
| 773 | insert.nonEmpty | |
| 774 | } | |
| 775 | ||
| 77531 | 776 | |
| 777 | /* collective operations */ | |
| 778 | ||
| 78187 
2df0f3604a67
clarified signature: more explicit class SQL.Data;
 wenzelm parents: 
78185diff
changeset | 779 | override val tables = | 
| 77596 | 780 | SQL.Tables( | 
| 77534 | 781 | Base.table, | 
| 782 | Workers.table, | |
| 783 | Sessions.table, | |
| 784 | Pending.table, | |
| 785 | Running.table, | |
| 78174 
cc0bd66eb498
separate host.db for independent db.transaction_lock;
 wenzelm parents: 
78173diff
changeset | 786 | Results.table) | 
| 77534 | 787 | |
| 77658 | 788 | val build_uuid_tables = | 
| 78187 
2df0f3604a67
clarified signature: more explicit class SQL.Data;
 wenzelm parents: 
78185diff
changeset | 789 | tables.filter(table => | 
| 77658 | 790 | table.columns.exists(column => column.name == Generic.build_uuid.name)) | 
| 791 | ||
| 78546 | 792 |     def pull_database(db: SQL.Database, worker_uuid: String, state: State): State = {
 | 
| 77655 | 793 | val serial_db = read_serial(db) | 
| 794 | if (serial_db == state.serial) state | |
| 77652 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 795 |       else {
 | 
| 77655 | 796 | val serial = serial_db max state.serial | 
| 77652 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 797 | stamp_worker(db, worker_uuid, serial) | 
| 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 798 | |
| 78237 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 799 | val sessions = state.sessions.pull(read_sessions_domain(db), read_sessions(db, _)) | 
| 77652 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 800 | val pending = read_pending(db) | 
| 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 801 | val running = pull0(read_running(db), state.running) | 
| 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 802 | val results = pull1(read_results_domain(db), read_results(db, _), state.results) | 
| 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 803 | |
| 78174 
cc0bd66eb498
separate host.db for independent db.transaction_lock;
 wenzelm parents: 
78173diff
changeset | 804 | state.copy(serial = serial, sessions = sessions, pending = pending, | 
| 
cc0bd66eb498
separate host.db for independent db.transaction_lock;
 wenzelm parents: 
78173diff
changeset | 805 | running = running, results = results) | 
| 77652 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 806 | } | 
| 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 807 | } | 
| 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 808 | |
| 78553 | 809 | def update_database( | 
| 810 | db: SQL.Database, | |
| 811 | worker_uuid: String, | |
| 812 | state: State, | |
| 813 | old_state: State | |
| 814 |     ): State = {
 | |
| 77416 
d88c12f22ab0
clarified scope of "serial" and "numa_index" within database;
 wenzelm parents: 
77415diff
changeset | 815 | val changed = | 
| 
d88c12f22ab0
clarified scope of "serial" and "numa_index" within database;
 wenzelm parents: 
77415diff
changeset | 816 | List( | 
| 78553 | 817 | update_sessions(db, state.sessions, old_state.sessions), | 
| 818 | update_pending(db, state.pending, old_state.pending), | |
| 819 | update_running(db, state.running, old_state.running), | |
| 820 | update_results(db, state.results, old_state.results)) | |
| 77372 | 821 | |
| 78574 | 822 | val state1 = if (changed.exists(identity)) state.inc_serial else state | 
| 823 | if (state1.serial != state.serial) stamp_worker(db, worker_uuid, state1.serial) | |
| 77372 | 824 | |
| 78574 | 825 | state1 | 
| 77372 | 826 | } | 
| 827 | } | |
| 78218 
a625bfb0e549
clarified signature: more operations and options;
 wenzelm parents: 
78217diff
changeset | 828 | |
| 
a625bfb0e549
clarified signature: more operations and options;
 wenzelm parents: 
78217diff
changeset | 829 | def read_builds(db: SQL.Database): List[Build] = | 
| 78396 
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
 wenzelm parents: 
78394diff
changeset | 830 |     private_data.transaction_lock(db, create = true, label = "Build_Process.read_builds") {
 | 
| 
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
 wenzelm parents: 
78394diff
changeset | 831 | private_data.read_builds(db) | 
| 78356 | 832 | } | 
| 77396 | 833 | } | 
| 77372 | 834 | |
| 835 | ||
| 77436 | 836 | |
| 837 | /** main process **/ | |
| 77259 
61fc2afe4c8b
clarified signature: prefer stateful object-oriented style, to make it fit better into physical world;
 wenzelm parents: 
77258diff
changeset | 838 | |
| 77505 | 839 | class Build_Process( | 
| 78421 | 840 | protected final val build_context: Build.Context, | 
| 78372 | 841 | protected final val build_progress: Progress, | 
| 842 | protected final val server: SSH.Server | |
| 77505 | 843 | ) | 
| 77436 | 844 | extends AutoCloseable {
 | 
| 845 | /* context */ | |
| 846 | ||
| 78178 | 847 | protected final val store: Store = build_context.store | 
| 77530 | 848 | protected final val build_options: Options = store.options | 
| 78237 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 849 | protected final val build_deps: isabelle.Sessions.Deps = build_context.build_deps | 
| 77653 | 850 | protected final val hostname: String = build_context.hostname | 
| 77530 | 851 | protected final val build_uuid: String = build_context.build_uuid | 
| 78156 
da5cc332ded3
prefer Database_Progress, which is more robust (amending afb1a19307c4);
 wenzelm parents: 
78154diff
changeset | 852 | |
| 
da5cc332ded3
prefer Database_Progress, which is more robust (amending afb1a19307c4);
 wenzelm parents: 
78154diff
changeset | 853 | |
| 78413 | 854 | /* global resources with common close() operation */ | 
| 78156 
da5cc332ded3
prefer Database_Progress, which is more robust (amending afb1a19307c4);
 wenzelm parents: 
78154diff
changeset | 855 | |
| 78969 
1b05c2b10c9f
finalize current sessions before generating schedule;
 Fabian Huch <huch@in.tum.de> parents: 
78968diff
changeset | 856 | protected val _database_server: Option[SQL.Database] = | 
| 78372 | 857 |     try { store.maybe_open_database_server(server = server) }
 | 
| 78214 | 858 |     catch { case exn: Throwable => close(); throw exn }
 | 
| 859 | ||
| 78203 | 860 | private val _build_database: Option[SQL.Database] = | 
| 78234 | 861 |     try {
 | 
| 78372 | 862 |       for (db <- store.maybe_open_build_database(server = server)) yield {
 | 
| 78577 
a945b541efff
clarified default options: SQLite build_database is unsupported for Isabelle2023, due to lack of proper transaction_lock;
 wenzelm parents: 
78575diff
changeset | 863 |         if (!db.is_postgresql) {
 | 
| 78893 | 864 |           error("Distributed build requires PostgreSQL (option build_database_server)")
 | 
| 78577 
a945b541efff
clarified default options: SQLite build_database is unsupported for Isabelle2023, due to lack of proper transaction_lock;
 wenzelm parents: 
78575diff
changeset | 865 | } | 
| 78389 
41e8ae87184d
clarified signature: eliminate SQL.Tables.empty to avoid confusion (see also 0bd366fad888);
 wenzelm parents: 
78385diff
changeset | 866 | val store_tables = db.is_postgresql | 
| 78396 
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
 wenzelm parents: 
78394diff
changeset | 867 | Build_Process.private_data.transaction_lock(db, | 
| 78356 | 868 | create = true, | 
| 869 | label = "Build_Process.build_database" | |
| 870 |         ) {
 | |
| 78396 
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
 wenzelm parents: 
78394diff
changeset | 871 | Build_Process.private_data.clean_build(db) | 
| 
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
 wenzelm parents: 
78394diff
changeset | 872 | if (store_tables) Store.private_data.tables.lock(db, create = true) | 
| 78234 | 873 | } | 
| 78389 
41e8ae87184d
clarified signature: eliminate SQL.Tables.empty to avoid confusion (see also 0bd366fad888);
 wenzelm parents: 
78385diff
changeset | 874 |         if (build_context.master) {
 | 
| 78396 
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
 wenzelm parents: 
78394diff
changeset | 875 | db.vacuum(Build_Process.private_data.tables.list) | 
| 
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
 wenzelm parents: 
78394diff
changeset | 876 | if (store_tables) db.vacuum(Store.private_data.tables.list) | 
| 78389 
41e8ae87184d
clarified signature: eliminate SQL.Tables.empty to avoid confusion (see also 0bd366fad888);
 wenzelm parents: 
78385diff
changeset | 877 | } | 
| 78234 | 878 | db | 
| 879 | } | |
| 880 | } | |
| 78214 | 881 |     catch { case exn: Throwable => close(); throw exn }
 | 
| 77259 
61fc2afe4c8b
clarified signature: prefer stateful object-oriented style, to make it fit better into physical world;
 wenzelm parents: 
77258diff
changeset | 882 | |
| 78409 | 883 |   protected val build_delay: Time = {
 | 
| 884 | val option = | |
| 885 |       _build_database match {
 | |
| 886 | case Some(db) if db.is_postgresql => "build_cluster_delay" | |
| 887 | case _ => "build_delay" | |
| 888 | } | |
| 889 | build_options.seconds(option) | |
| 890 | } | |
| 78406 
2ece6509ad6f
clarified options: accommodate potentially slow database connection;
 wenzelm parents: 
78401diff
changeset | 891 | |
| 78844 
c7f436a63108
always use host database and make protected;
 Fabian Huch <huch@in.tum.de> parents: 
78842diff
changeset | 892 | protected val _host_database: SQL.Database = | 
| 
c7f436a63108
always use host database and make protected;
 Fabian Huch <huch@in.tum.de> parents: 
78842diff
changeset | 893 |     try { store.open_build_database(path = Host.private_data.database, server = server) }
 | 
| 78214 | 894 |     catch { case exn: Throwable => close(); throw exn }
 | 
| 78213 
fd0430a7b7a4
avoid repeated open_database_server: synchronized transaction_lock;
 wenzelm parents: 
78205diff
changeset | 895 | |
| 78156 
da5cc332ded3
prefer Database_Progress, which is more robust (amending afb1a19307c4);
 wenzelm parents: 
78154diff
changeset | 896 |   protected val (progress, worker_uuid) = synchronized {
 | 
| 78569 | 897 | if (_build_database.isEmpty) (build_progress, UUID.random().toString) | 
| 898 |     else {
 | |
| 899 |       try {
 | |
| 900 | val db = store.open_build_database(Progress.private_data.database, server = server) | |
| 901 | val progress = | |
| 902 | new Database_Progress(db, build_progress, | |
| 903 | input_messages = build_context.master, | |
| 904 | output_stopped = build_context.master, | |
| 905 | hostname = hostname, | |
| 906 | context_uuid = build_uuid, | |
| 907 | kind = "build_process", | |
| 908 | timeout = Some(build_delay)) | |
| 909 | (progress, progress.agent_uuid) | |
| 910 | } | |
| 911 |       catch { case exn: Throwable => close(); throw exn }
 | |
| 78156 
da5cc332ded3
prefer Database_Progress, which is more robust (amending afb1a19307c4);
 wenzelm parents: 
78154diff
changeset | 912 | } | 
| 
da5cc332ded3
prefer Database_Progress, which is more robust (amending afb1a19307c4);
 wenzelm parents: 
78154diff
changeset | 913 | } | 
| 77638 | 914 | |
| 78156 
da5cc332ded3
prefer Database_Progress, which is more robust (amending afb1a19307c4);
 wenzelm parents: 
78154diff
changeset | 915 | protected val log: Logger = Logger.make_system_log(progress, build_options) | 
| 
da5cc332ded3
prefer Database_Progress, which is more robust (amending afb1a19307c4);
 wenzelm parents: 
78154diff
changeset | 916 | |
| 78434 
b4ec7ea073da
clarified signature: delegate policies to Build_Cluster implementation, potentially provided by Build.Engine via Build_Process.open_build_cluster;
 wenzelm parents: 
78430diff
changeset | 917 |   protected def open_build_cluster(): Build_Cluster = {
 | 
| 
b4ec7ea073da
clarified signature: delegate policies to Build_Cluster implementation, potentially provided by Build.Engine via Build_Process.open_build_cluster;
 wenzelm parents: 
78430diff
changeset | 918 | val build_cluster = Build_Cluster.make(build_context, progress = build_progress) | 
| 
b4ec7ea073da
clarified signature: delegate policies to Build_Cluster implementation, potentially provided by Build.Engine via Build_Process.open_build_cluster;
 wenzelm parents: 
78430diff
changeset | 919 | build_cluster.open() | 
| 
b4ec7ea073da
clarified signature: delegate policies to Build_Cluster implementation, potentially provided by Build.Engine via Build_Process.open_build_cluster;
 wenzelm parents: 
78430diff
changeset | 920 | build_cluster | 
| 78430 
0461fc9d43e8
more build_cluster management: open SSH connections in parallel, but synchronously;
 wenzelm parents: 
78424diff
changeset | 921 | } | 
| 
0461fc9d43e8
more build_cluster management: open SSH connections in parallel, but synchronously;
 wenzelm parents: 
78424diff
changeset | 922 | |
| 78413 | 923 | private val _build_cluster = | 
| 924 |     try {
 | |
| 78434 
b4ec7ea073da
clarified signature: delegate policies to Build_Cluster implementation, potentially provided by Build.Engine via Build_Process.open_build_cluster;
 wenzelm parents: 
78430diff
changeset | 925 | if (build_context.master && _build_database.isDefined) open_build_cluster() | 
| 
b4ec7ea073da
clarified signature: delegate policies to Build_Cluster implementation, potentially provided by Build.Engine via Build_Process.open_build_cluster;
 wenzelm parents: 
78430diff
changeset | 926 | else Build_Cluster.none | 
| 78413 | 927 | } | 
| 928 |     catch { case exn: Throwable => close(); throw exn }
 | |
| 929 | ||
| 78156 
da5cc332ded3
prefer Database_Progress, which is more robust (amending afb1a19307c4);
 wenzelm parents: 
78154diff
changeset | 930 |   def close(): Unit = synchronized {
 | 
| 78214 | 931 | Option(_database_server).flatten.foreach(_.close()) | 
| 932 | Option(_build_database).flatten.foreach(_.close()) | |
| 78844 
c7f436a63108
always use host database and make protected;
 Fabian Huch <huch@in.tum.de> parents: 
78842diff
changeset | 933 | Option(_host_database).foreach(_.close()) | 
| 78434 
b4ec7ea073da
clarified signature: delegate policies to Build_Cluster implementation, potentially provided by Build.Engine via Build_Process.open_build_cluster;
 wenzelm parents: 
78430diff
changeset | 934 | Option(_build_cluster).foreach(_.close()) | 
| 78156 
da5cc332ded3
prefer Database_Progress, which is more robust (amending afb1a19307c4);
 wenzelm parents: 
78154diff
changeset | 935 |     progress match {
 | 
| 78563 | 936 | case db_progress: Database_Progress => db_progress.exit(close = true) | 
| 78159 | 937 | case _ => | 
| 78156 
da5cc332ded3
prefer Database_Progress, which is more robust (amending afb1a19307c4);
 wenzelm parents: 
78154diff
changeset | 938 | } | 
| 
da5cc332ded3
prefer Database_Progress, which is more robust (amending afb1a19307c4);
 wenzelm parents: 
78154diff
changeset | 939 | } | 
| 77436 | 940 | |
| 78413 | 941 | |
| 77437 
dcbf96acae27
clarified signature: do not expose global state to object-oriented variants;
 wenzelm parents: 
77436diff
changeset | 942 | /* global state: internal var vs. external database */ | 
| 77436 | 943 | |
| 77652 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 944 | private var _state: Build_Process.State = Build_Process.State() | 
| 77437 
dcbf96acae27
clarified signature: do not expose global state to object-oriented variants;
 wenzelm parents: 
77436diff
changeset | 945 | |
| 78356 | 946 | protected def synchronized_database[A](label: String)(body: => A): A = | 
| 947 |     synchronized {
 | |
| 948 |       _build_database match {
 | |
| 949 | case None => body | |
| 950 | case Some(db) => | |
| 78396 
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
 wenzelm parents: 
78394diff
changeset | 951 |           Build_Process.private_data.transaction_lock(db, label = label) {
 | 
| 78553 | 952 | val old_state = Build_Process.private_data.pull_database(db, worker_uuid, _state) | 
| 953 | _state = old_state | |
| 78356 | 954 | val res = body | 
| 78553 | 955 | _state = Build_Process.private_data.update_database(db, worker_uuid, _state, old_state) | 
| 78356 | 956 | res | 
| 957 | } | |
| 958 | } | |
| 77522 
a1d30297cd61
more complete coverage of non-final Progress methods, notably for Server.Connection_Progress;
 wenzelm parents: 
77521diff
changeset | 959 | } | 
| 
a1d30297cd61
more complete coverage of non-final Progress methods, notably for Server.Connection_Progress;
 wenzelm parents: 
77521diff
changeset | 960 | |
| 77505 | 961 | |
| 77437 
dcbf96acae27
clarified signature: do not expose global state to object-oriented variants;
 wenzelm parents: 
77436diff
changeset | 962 | /* policy operations */ | 
| 77333 | 963 | |
| 77415 
6b928419f109
clarified signature: allow more general init, e.g. from existing database;
 wenzelm parents: 
77414diff
changeset | 964 |   protected def init_state(state: Build_Process.State): Build_Process.State = {
 | 
| 78374 | 965 | val sessions1 = state.sessions.init(build_context, _database_server, progress = build_progress) | 
| 77496 | 966 | |
| 77415 
6b928419f109
clarified signature: allow more general init, e.g. from existing database;
 wenzelm parents: 
77414diff
changeset | 967 | val old_pending = state.pending.iterator.map(_.name).toSet | 
| 
6b928419f109
clarified signature: allow more general init, e.g. from existing database;
 wenzelm parents: 
77414diff
changeset | 968 | val new_pending = | 
| 
6b928419f109
clarified signature: allow more general init, e.g. from existing database;
 wenzelm parents: 
77414diff
changeset | 969 | List.from( | 
| 78237 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 970 | for (session <- sessions1.iterator if !old_pending(session.name)) | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 971 | yield Build_Process.Task(session.name, session.deps, JSON.Object.empty, build_uuid)) | 
| 77496 | 972 | val pending1 = new_pending ::: state.pending | 
| 973 | ||
| 78571 | 974 | state.copy(sessions = sessions1, pending = pending1) | 
| 77415 
6b928419f109
clarified signature: allow more general init, e.g. from existing database;
 wenzelm parents: 
77414diff
changeset | 975 | } | 
| 77259 
61fc2afe4c8b
clarified signature: prefer stateful object-oriented style, to make it fit better into physical world;
 wenzelm parents: 
77258diff
changeset | 976 | |
| 78394 
761d12b043d0
proper running limit, based on this worker process;
 wenzelm parents: 
78393diff
changeset | 977 |   protected def next_jobs(state: Build_Process.State): List[String] = {
 | 
| 78529 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78510diff
changeset | 978 |     val limit = {
 | 
| 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78510diff
changeset | 979 |       if (progress.stopped) { if (build_context.master) Int.MaxValue else 0 }
 | 
| 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78510diff
changeset | 980 | else build_context.max_jobs - state.build_running.length | 
| 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78510diff
changeset | 981 | } | 
| 79020 
ef76705bf402
clarified ready vs. next ready;
 Fabian Huch <huch@in.tum.de> parents: 
78969diff
changeset | 982 | if (limit > 0) state.next_ready.sortBy(_.name)(state.sessions.ordering).take(limit).map(_.name) | 
| 78394 
761d12b043d0
proper running limit, based on this worker process;
 wenzelm parents: 
78393diff
changeset | 983 | else Nil | 
| 
761d12b043d0
proper running limit, based on this worker process;
 wenzelm parents: 
78393diff
changeset | 984 | } | 
| 77259 
61fc2afe4c8b
clarified signature: prefer stateful object-oriented style, to make it fit better into physical world;
 wenzelm parents: 
77258diff
changeset | 985 | |
| 78842 
eb572f7b6689
prefer extensible next_node_info in build process over process_options in build engine (which needs the final node info anyway);
 Fabian Huch <huch@in.tum.de> parents: 
78841diff
changeset | 986 |   protected def next_node_info(state: Build_Process.State, session_name: String): Host.Node_Info = {
 | 
| 
eb572f7b6689
prefer extensible next_node_info in build process over process_options in build engine (which needs the final node info anyway);
 Fabian Huch <huch@in.tum.de> parents: 
78841diff
changeset | 987 | def used_nodes: Set[Int] = | 
| 
eb572f7b6689
prefer extensible next_node_info in build process over process_options in build engine (which needs the final node info anyway);
 Fabian Huch <huch@in.tum.de> parents: 
78841diff
changeset | 988 | Set.from(for (job <- state.running.valuesIterator; i <- job.node_info.numa_node) yield i) | 
| 78844 
c7f436a63108
always use host database and make protected;
 Fabian Huch <huch@in.tum.de> parents: 
78842diff
changeset | 989 | val numa_node = Host.next_numa_node(_host_database, hostname, state.numa_nodes, used_nodes) | 
| 78842 
eb572f7b6689
prefer extensible next_node_info in build process over process_options in build engine (which needs the final node info anyway);
 Fabian Huch <huch@in.tum.de> parents: 
78841diff
changeset | 990 | Host.Node_Info(hostname, numa_node, Nil) | 
| 
eb572f7b6689
prefer extensible next_node_info in build process over process_options in build engine (which needs the final node info anyway);
 Fabian Huch <huch@in.tum.de> parents: 
78841diff
changeset | 991 | } | 
| 
eb572f7b6689
prefer extensible next_node_info in build process over process_options in build engine (which needs the final node info anyway);
 Fabian Huch <huch@in.tum.de> parents: 
78841diff
changeset | 992 | |
| 78500 
fc6d8a2895ca
more robust ancestor_results: avoid total existence failure after build_process has crashed elsewhere;
 wenzelm parents: 
78440diff
changeset | 993 | protected def start_session( | 
| 
fc6d8a2895ca
more robust ancestor_results: avoid total existence failure after build_process has crashed elsewhere;
 wenzelm parents: 
78440diff
changeset | 994 | state: Build_Process.State, | 
| 
fc6d8a2895ca
more robust ancestor_results: avoid total existence failure after build_process has crashed elsewhere;
 wenzelm parents: 
78440diff
changeset | 995 | session_name: String, | 
| 
fc6d8a2895ca
more robust ancestor_results: avoid total existence failure after build_process has crashed elsewhere;
 wenzelm parents: 
78440diff
changeset | 996 | ancestor_results: List[Build_Process.Result] | 
| 
fc6d8a2895ca
more robust ancestor_results: avoid total existence failure after build_process has crashed elsewhere;
 wenzelm parents: 
78440diff
changeset | 997 |   ): Build_Process.State = {
 | 
| 78237 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 998 | val sources_shasum = state.sessions(session_name).sources_shasum | 
| 77467 
e27bc7957297
more robust: proper synchronization of transition from next_job to start_session;
 wenzelm parents: 
77466diff
changeset | 999 | |
| 77460 
ccca589d7027
tuned signature: support general Build_Job instances;
 wenzelm parents: 
77459diff
changeset | 1000 | val input_shasum = | 
| 77650 | 1001 | if (ancestor_results.isEmpty) ML_Process.bootstrap_shasum() | 
| 77460 
ccca589d7027
tuned signature: support general Build_Job instances;
 wenzelm parents: 
77459diff
changeset | 1002 | else SHA1.flat_shasum(ancestor_results.map(_.output_shasum)) | 
| 77257 | 1003 | |
| 78237 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 1004 | val store_heap = | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 1005 | build_context.build_heap || Sessions.is_pure(session_name) || | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 1006 | state.sessions.iterator.exists(_.ancestors.contains(session_name)) | 
| 77469 | 1007 | |
| 1008 | val (current, output_shasum) = | |
| 78374 | 1009 | store.check_output(_database_server, session_name, | 
| 77675 
9e5f8f6e58a0
more thorough treatment of build prefs, guarded by system option "build_through": avoid accidental rebuild of HOL etc.;
 wenzelm parents: 
77664diff
changeset | 1010 | session_options = build_context.sessions_structure(session_name).options, | 
| 78237 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 1011 | sources_shasum = sources_shasum, | 
| 77469 | 1012 | input_shasum = input_shasum, | 
| 1013 | fresh_build = build_context.fresh_build, | |
| 1014 | store_heap = store_heap) | |
| 1015 | ||
| 78195 | 1016 | val finished = current && ancestor_results.forall(_.current) | 
| 1017 | val skipped = build_context.no_build | |
| 1018 | val cancelled = progress.stopped || !ancestor_results.forall(_.ok) | |
| 77257 | 1019 | |
| 78196 
140a6f2e3728
restore heaps from database, which takes precedence over file-system;
 wenzelm parents: 
78195diff
changeset | 1020 |     if (!skipped && !cancelled) {
 | 
| 78510 
8f45302a9ff0
more thorough ML_Heap.restore: include ancestors;                                                         prefer simultaneous ML_Heap.get_entries: just one database access for heap hierarchy;
 wenzelm parents: 
78507diff
changeset | 1021 | val heaps = (session_name :: ancestor_results.map(_.name)).map(store.output_heap) | 
| 
8f45302a9ff0
more thorough ML_Heap.restore: include ancestors;                                                         prefer simultaneous ML_Heap.get_entries: just one database access for heap hierarchy;
 wenzelm parents: 
78507diff
changeset | 1022 | ML_Heap.restore(_database_server, heaps, cache = store.cache.compress) | 
| 78196 
140a6f2e3728
restore heaps from database, which takes precedence over file-system;
 wenzelm parents: 
78195diff
changeset | 1023 | } | 
| 
140a6f2e3728
restore heaps from database, which takes precedence over file-system;
 wenzelm parents: 
78195diff
changeset | 1024 | |
| 77651 | 1025 | val result_name = (session_name, worker_uuid, build_uuid) | 
| 1026 | ||
| 78195 | 1027 |     if (finished) {
 | 
| 77467 
e27bc7957297
more robust: proper synchronization of transition from next_job to start_session;
 wenzelm parents: 
77466diff
changeset | 1028 | state | 
| 
e27bc7957297
more robust: proper synchronization of transition from next_job to start_session;
 wenzelm parents: 
77466diff
changeset | 1029 | .remove_pending(session_name) | 
| 77651 | 1030 | .make_result(result_name, Process_Result.ok, output_shasum, current = true) | 
| 77260 | 1031 | } | 
| 78195 | 1032 |     else if (skipped) {
 | 
| 77521 
5642de4d225d
clarified signature: manage "verbose" flag via "progress";
 wenzelm parents: 
77514diff
changeset | 1033 |       progress.echo("Skipping " + session_name + " ...", verbose = true)
 | 
| 77467 
e27bc7957297
more robust: proper synchronization of transition from next_job to start_session;
 wenzelm parents: 
77466diff
changeset | 1034 | state. | 
| 
e27bc7957297
more robust: proper synchronization of transition from next_job to start_session;
 wenzelm parents: 
77466diff
changeset | 1035 | remove_pending(session_name). | 
| 77651 | 1036 | make_result(result_name, Process_Result.error, output_shasum) | 
| 77260 | 1037 | } | 
| 78195 | 1038 |     else if (cancelled) {
 | 
| 78529 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78510diff
changeset | 1039 |       if (build_context.master) {
 | 
| 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78510diff
changeset | 1040 | progress.echo(session_name + " CANCELLED") | 
| 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78510diff
changeset | 1041 | state | 
| 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78510diff
changeset | 1042 | .remove_pending(session_name) | 
| 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78510diff
changeset | 1043 | .make_result(result_name, Process_Result.undefined, output_shasum) | 
| 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78510diff
changeset | 1044 | } | 
| 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78510diff
changeset | 1045 | else state | 
| 77452 | 1046 | } | 
| 1047 |     else {
 | |
| 78842 
eb572f7b6689
prefer extensible next_node_info in build process over process_options in build engine (which needs the final node info anyway);
 Fabian Huch <huch@in.tum.de> parents: 
78841diff
changeset | 1048 | val node_info = next_node_info(state, session_name) | 
| 77551 | 1049 | |
| 78575 | 1050 | val print_node_info = | 
| 78839 
7799ec03b8bd
generalized node infos: allow addressing of numa node segments via relative cpus;
 Fabian Huch <huch@in.tum.de> parents: 
78635diff
changeset | 1051 | node_info.numa_node.isDefined || node_info.rel_cpus.nonEmpty || | 
| 78575 | 1052 | _build_database.isDefined && _build_database.get.is_postgresql | 
| 1053 | ||
| 77551 | 1054 | progress.echo( | 
| 1055 | (if (store_heap) "Building " else "Running ") + session_name + | |
| 78575 | 1056 | (if (print_node_info) " (on " + node_info + ")" else "") + " ...") | 
| 77260 | 1057 | |
| 78237 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 1058 | val session = state.sessions(session_name) | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 1059 | |
| 77630 
86ef80d13544
clarified signature: avoid confusion due to object-orientation;
 wenzelm parents: 
77629diff
changeset | 1060 | val build = | 
| 78372 | 1061 | Build_Job.start_session(build_context, session, progress, log, server, | 
| 78237 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78234diff
changeset | 1062 | build_deps.background(session_name), sources_shasum, input_shasum, node_info, store_heap) | 
| 77630 
86ef80d13544
clarified signature: avoid confusion due to object-orientation;
 wenzelm parents: 
77629diff
changeset | 1063 | |
| 78841 
7f61688d4e8d
added start date to build jobs, e.g., for build time estimation;
 Fabian Huch <huch@in.tum.de> parents: 
78839diff
changeset | 1064 | val job = | 
| 
7f61688d4e8d
added start date to build jobs, e.g., for build time estimation;
 Fabian Huch <huch@in.tum.de> parents: 
78839diff
changeset | 1065 | Build_Process.Job(session_name, worker_uuid, build_uuid, node_info, Date.now(), Some(build)) | 
| 77634 | 1066 | |
| 78174 
cc0bd66eb498
separate host.db for independent db.transaction_lock;
 wenzelm parents: 
78173diff
changeset | 1067 | state.add_running(job) | 
| 77260 | 1068 | } | 
| 1069 | } | |
| 77257 | 1070 | |
| 77436 | 1071 | |
| 77546 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 1072 | /* build process roles */ | 
| 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 1073 | |
| 77654 | 1074 | final def is_session_name(job_name: String): Boolean = | 
| 1075 | !Long_Name.is_qualified(job_name) | |
| 77648 | 1076 | |
| 78356 | 1077 |   protected final def start_build(): Unit = synchronized_database("Build_Process.start_build") {
 | 
| 78203 | 1078 |     for (db <- _build_database) {
 | 
| 78396 
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
 wenzelm parents: 
78394diff
changeset | 1079 | Build_Process.private_data.start_build(db, build_uuid, build_context.ml_platform, | 
| 78156 
da5cc332ded3
prefer Database_Progress, which is more robust (amending afb1a19307c4);
 wenzelm parents: 
78154diff
changeset | 1080 | build_context.sessions_structure.session_prefs) | 
| 77546 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 1081 | } | 
| 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 1082 | } | 
| 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 1083 | |
| 78356 | 1084 |   protected final def stop_build(): Unit = synchronized_database("Build_Process.stop_build") {
 | 
| 78203 | 1085 |     for (db <- _build_database) {
 | 
| 78396 
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
 wenzelm parents: 
78394diff
changeset | 1086 | Build_Process.private_data.stop_build(db, build_uuid) | 
| 77546 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 1087 | } | 
| 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 1088 | } | 
| 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 1089 | |
| 78356 | 1090 |   protected final def start_worker(): Unit = synchronized_database("Build_Process.start_worker") {
 | 
| 78203 | 1091 |     for (db <- _build_database) {
 | 
| 77652 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 1092 | _state = _state.inc_serial | 
| 78396 
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
 wenzelm parents: 
78394diff
changeset | 1093 | Build_Process.private_data.start_worker(db, worker_uuid, build_uuid, _state.serial) | 
| 77546 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 1094 | } | 
| 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 1095 | } | 
| 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 1096 | |
| 78356 | 1097 |   protected final def stop_worker(): Unit = synchronized_database("Build_Process.stop_worker") {
 | 
| 78203 | 1098 |     for (db <- _build_database) {
 | 
| 78396 
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
 wenzelm parents: 
78394diff
changeset | 1099 | Build_Process.private_data.stamp_worker(db, worker_uuid, _state.serial, stop_now = true) | 
| 77546 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 1100 | } | 
| 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 1101 | } | 
| 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 1102 | |
| 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 1103 | |
| 77436 | 1104 | /* run */ | 
| 77372 | 1105 | |
| 78440 
126a12483c67
support for Build_Cluster.Session.init (rsync + Admin/init);
 wenzelm parents: 
78434diff
changeset | 1106 |   def run(): Build.Results = {
 | 
| 78571 | 1107 |     synchronized_database("Build_Process.init") {
 | 
| 1108 |       if (build_context.master) {
 | |
| 1109 | _build_cluster.init() | |
| 1110 | _state = init_state(_state) | |
| 1111 | } | |
| 1112 | _state = _state.copy(numa_nodes = Host.numa_nodes(enabled = build_context.numa_shuffling)) | |
| 78356 | 1113 | } | 
| 77652 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 1114 | |
| 78529 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78510diff
changeset | 1115 |     def finished(): Boolean = synchronized_database("Build_Process.test") {
 | 
| 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78510diff
changeset | 1116 | if (!build_context.master && progress.stopped) _state.build_running.isEmpty | 
| 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78510diff
changeset | 1117 | else _state.pending.isEmpty | 
| 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78510diff
changeset | 1118 | } | 
| 77400 
f3e5b3fe230e
clarified signature: more explicit "synchronized" regions;
 wenzelm parents: 
77398diff
changeset | 1119 | |
| 77437 
dcbf96acae27
clarified signature: do not expose global state to object-oriented variants;
 wenzelm parents: 
77436diff
changeset | 1120 | def sleep(): Unit = | 
| 78406 
2ece6509ad6f
clarified options: accommodate potentially slow database connection;
 wenzelm parents: 
78401diff
changeset | 1121 |       Isabelle_Thread.interrupt_handler(_ => progress.stop()) { build_delay.sleep() }
 | 
| 77437 
dcbf96acae27
clarified signature: do not expose global state to object-oriented variants;
 wenzelm parents: 
77436diff
changeset | 1122 | |
| 78585 | 1123 | val build_progress_warnings = Synchronized(Set.empty[String]) | 
| 1124 | def build_progress_warning(msg: String): Unit = | |
| 1125 | build_progress_warnings.change(seen => | |
| 1126 |         if (seen(msg)) seen else { build_progress.echo_warning(msg); seen + msg })
 | |
| 1127 | ||
| 78394 
761d12b043d0
proper running limit, based on this worker process;
 wenzelm parents: 
78393diff
changeset | 1128 |     def check_jobs(): Boolean = synchronized_database("Build_Process.check_jobs") {
 | 
| 
761d12b043d0
proper running limit, based on this worker process;
 wenzelm parents: 
78393diff
changeset | 1129 | val jobs = next_jobs(_state) | 
| 
761d12b043d0
proper running limit, based on this worker process;
 wenzelm parents: 
78393diff
changeset | 1130 |       for (name <- jobs) {
 | 
| 
761d12b043d0
proper running limit, based on this worker process;
 wenzelm parents: 
78393diff
changeset | 1131 |         if (is_session_name(name)) {
 | 
| 78507 | 1132 |           if (build_context.sessions_structure.defined(name)) {
 | 
| 1133 |             _state.ancestor_results(name) match {
 | |
| 1134 | case Some(results) => _state = start_session(_state, name, results) | |
| 1135 | case None => | |
| 78585 | 1136 |                 build_progress_warning("Bad build job " + quote(name) + ": no ancestor results")
 | 
| 78507 | 1137 | } | 
| 78500 
fc6d8a2895ca
more robust ancestor_results: avoid total existence failure after build_process has crashed elsewhere;
 wenzelm parents: 
78440diff
changeset | 1138 | } | 
| 78585 | 1139 |           else build_progress_warning("Bad build job " + quote(name) + ": no session info")
 | 
| 78394 
761d12b043d0
proper running limit, based on this worker process;
 wenzelm parents: 
78393diff
changeset | 1140 | } | 
| 78585 | 1141 |         else build_progress_warning("Bad build job " + quote(name))
 | 
| 77467 
e27bc7957297
more robust: proper synchronization of transition from next_job to start_session;
 wenzelm parents: 
77466diff
changeset | 1142 | } | 
| 78394 
761d12b043d0
proper running limit, based on this worker process;
 wenzelm parents: 
78393diff
changeset | 1143 | jobs.nonEmpty | 
| 77467 
e27bc7957297
more robust: proper synchronization of transition from next_job to start_session;
 wenzelm parents: 
77466diff
changeset | 1144 | } | 
| 
e27bc7957297
more robust: proper synchronization of transition from next_job to start_session;
 wenzelm parents: 
77466diff
changeset | 1145 | |
| 77335 | 1146 |     if (finished()) {
 | 
| 1147 |       progress.echo_warning("Nothing to build")
 | |
| 78440 
126a12483c67
support for Build_Cluster.Session.init (rsync + Admin/init);
 wenzelm parents: 
78434diff
changeset | 1148 | Build.Results(build_context) | 
| 77335 | 1149 | } | 
| 1150 |     else {
 | |
| 77579 
69d3547206db
clarified signature: prefer Build_Process.Context for parameters;
 wenzelm parents: 
77578diff
changeset | 1151 | if (build_context.master) start_build() | 
| 78241 | 1152 | start_worker() | 
| 78434 
b4ec7ea073da
clarified signature: delegate policies to Build_Cluster implementation, potentially provided by Build.Engine via Build_Process.open_build_cluster;
 wenzelm parents: 
78430diff
changeset | 1153 | _build_cluster.start() | 
| 77578 
149d48a4801b
support for "isabelle build -j0": require external workers to make progress;
 wenzelm parents: 
77561diff
changeset | 1154 | |
| 78434 
b4ec7ea073da
clarified signature: delegate policies to Build_Cluster implementation, potentially provided by Build.Engine via Build_Process.open_build_cluster;
 wenzelm parents: 
78430diff
changeset | 1155 |       if (build_context.master && !build_context.worker_active && _build_cluster.active()) {
 | 
| 78252 | 1156 |         build_progress.echo("Waiting for external workers ...")
 | 
| 77580 
32f9e75c92e9
clarified worker state: always maintain database content via worker_uuid;
 wenzelm parents: 
77579diff
changeset | 1157 | } | 
| 77578 
149d48a4801b
support for "isabelle build -j0": require external workers to make progress;
 wenzelm parents: 
77561diff
changeset | 1158 | |
| 77538 | 1159 |       try {
 | 
| 1160 |         while (!finished()) {
 | |
| 78356 | 1161 |           synchronized_database("Build_Process.main") {
 | 
| 77639 | 1162 | if (progress.stopped) _state.stop_running() | 
| 77310 | 1163 | |
| 77649 | 1164 |             for (job <- _state.finished_running()) {
 | 
| 78529 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78510diff
changeset | 1165 |               job.join_build match {
 | 
| 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78510diff
changeset | 1166 | case None => | 
| 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78510diff
changeset | 1167 | _state = _state.remove_running(job.name) | 
| 78530 | 1168 | case Some(result) => | 
| 78529 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78510diff
changeset | 1169 | val result_name = (job.name, worker_uuid, build_uuid) | 
| 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78510diff
changeset | 1170 | _state = _state. | 
| 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78510diff
changeset | 1171 | remove_pending(job.name). | 
| 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78510diff
changeset | 1172 | remove_running(job.name). | 
| 78530 | 1173 | make_result(result_name, | 
| 1174 | result.process_result, | |
| 1175 | result.output_shasum, | |
| 78529 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78510diff
changeset | 1176 | node_info = job.node_info) | 
| 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78510diff
changeset | 1177 | } | 
| 77538 | 1178 | } | 
| 1179 | } | |
| 1180 | ||
| 78394 
761d12b043d0
proper running limit, based on this worker process;
 wenzelm parents: 
78393diff
changeset | 1181 | if (!check_jobs()) sleep() | 
| 77396 | 1182 | } | 
| 77310 | 1183 | } | 
| 77546 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 1184 |       finally {
 | 
| 78434 
b4ec7ea073da
clarified signature: delegate policies to Build_Cluster implementation, potentially provided by Build.Engine via Build_Process.open_build_cluster;
 wenzelm parents: 
78430diff
changeset | 1185 | _build_cluster.stop() | 
| 77580 
32f9e75c92e9
clarified worker state: always maintain database content via worker_uuid;
 wenzelm parents: 
77579diff
changeset | 1186 | stop_worker() | 
| 77579 
69d3547206db
clarified signature: prefer Build_Process.Context for parameters;
 wenzelm parents: 
77578diff
changeset | 1187 | if (build_context.master) stop_build() | 
| 77546 
9b9179cda155
clarified build process roles: "worker" vs. "build";
 wenzelm parents: 
77545diff
changeset | 1188 | } | 
| 77467 
e27bc7957297
more robust: proper synchronization of transition from next_job to start_session;
 wenzelm parents: 
77466diff
changeset | 1189 | |
| 78356 | 1190 |       synchronized_database("Build_Process.result") {
 | 
| 78440 
126a12483c67
support for Build_Cluster.Session.init (rsync + Admin/init);
 wenzelm parents: 
78434diff
changeset | 1191 | val results = for ((name, result) <- _state.results) yield name -> result.process_result | 
| 
126a12483c67
support for Build_Cluster.Session.init (rsync + Admin/init);
 wenzelm parents: 
78434diff
changeset | 1192 | Build.Results(build_context, results = results, other_rc = _build_cluster.rc) | 
| 77257 | 1193 | } | 
| 1194 | } | |
| 1195 | } | |
| 77659 
d7eb6a4522b8
more explicit snapshot of "_state" and "_database";
 wenzelm parents: 
77658diff
changeset | 1196 | |
| 
d7eb6a4522b8
more explicit snapshot of "_state" and "_database";
 wenzelm parents: 
77658diff
changeset | 1197 | |
| 
d7eb6a4522b8
more explicit snapshot of "_state" and "_database";
 wenzelm parents: 
77658diff
changeset | 1198 | /* snapshot */ | 
| 
d7eb6a4522b8
more explicit snapshot of "_state" and "_database";
 wenzelm parents: 
77658diff
changeset | 1199 | |
| 78356 | 1200 |   def snapshot(): Build_Process.Snapshot = synchronized_database("Build_Process.snapshot") {
 | 
| 78156 
da5cc332ded3
prefer Database_Progress, which is more robust (amending afb1a19307c4);
 wenzelm parents: 
78154diff
changeset | 1201 | val (builds, workers) = | 
| 78203 | 1202 |       _build_database match {
 | 
| 78156 
da5cc332ded3
prefer Database_Progress, which is more robust (amending afb1a19307c4);
 wenzelm parents: 
78154diff
changeset | 1203 | case None => (Nil, Nil) | 
| 77659 
d7eb6a4522b8
more explicit snapshot of "_state" and "_database";
 wenzelm parents: 
77658diff
changeset | 1204 | case Some(db) => | 
| 78396 
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
 wenzelm parents: 
78394diff
changeset | 1205 | (Build_Process.private_data.read_builds(db), | 
| 
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
 wenzelm parents: 
78394diff
changeset | 1206 | Build_Process.private_data.read_workers(db)) | 
| 77659 
d7eb6a4522b8
more explicit snapshot of "_state" and "_database";
 wenzelm parents: 
77658diff
changeset | 1207 | } | 
| 
d7eb6a4522b8
more explicit snapshot of "_state" and "_database";
 wenzelm parents: 
77658diff
changeset | 1208 | Build_Process.Snapshot( | 
| 77660 | 1209 | builds = builds, | 
| 77659 
d7eb6a4522b8
more explicit snapshot of "_state" and "_database";
 wenzelm parents: 
77658diff
changeset | 1210 | workers = workers, | 
| 
d7eb6a4522b8
more explicit snapshot of "_state" and "_database";
 wenzelm parents: 
77658diff
changeset | 1211 | sessions = _state.sessions, | 
| 
d7eb6a4522b8
more explicit snapshot of "_state" and "_database";
 wenzelm parents: 
77658diff
changeset | 1212 | pending = _state.pending, | 
| 
d7eb6a4522b8
more explicit snapshot of "_state" and "_database";
 wenzelm parents: 
77658diff
changeset | 1213 | running = _state.running, | 
| 
d7eb6a4522b8
more explicit snapshot of "_state" and "_database";
 wenzelm parents: 
77658diff
changeset | 1214 | results = _state.results) | 
| 
d7eb6a4522b8
more explicit snapshot of "_state" and "_database";
 wenzelm parents: 
77658diff
changeset | 1215 | } | 
| 78156 
da5cc332ded3
prefer Database_Progress, which is more robust (amending afb1a19307c4);
 wenzelm parents: 
78154diff
changeset | 1216 | |
| 
da5cc332ded3
prefer Database_Progress, which is more robust (amending afb1a19307c4);
 wenzelm parents: 
78154diff
changeset | 1217 | |
| 
da5cc332ded3
prefer Database_Progress, which is more robust (amending afb1a19307c4);
 wenzelm parents: 
78154diff
changeset | 1218 | /* toString */ | 
| 
da5cc332ded3
prefer Database_Progress, which is more robust (amending afb1a19307c4);
 wenzelm parents: 
78154diff
changeset | 1219 | |
| 
da5cc332ded3
prefer Database_Progress, which is more robust (amending afb1a19307c4);
 wenzelm parents: 
78154diff
changeset | 1220 | override def toString: String = | 
| 
da5cc332ded3
prefer Database_Progress, which is more robust (amending afb1a19307c4);
 wenzelm parents: 
78154diff
changeset | 1221 | "Build_Process(worker_uuid = " + quote(worker_uuid) + ", build_uuid = " + quote(build_uuid) + | 
| 
da5cc332ded3
prefer Database_Progress, which is more robust (amending afb1a19307c4);
 wenzelm parents: 
78154diff
changeset | 1222 | if_proper(build_context.master, ", master = true") + ")" | 
| 77238 | 1223 | } |