author | wenzelm |
Thu, 22 Feb 2024 21:03:55 +0100 | |
changeset 79709 | 90fbcdafb34e |
parent 79708 | f25a6b4c3e41 |
child 79711 | 5044f1d9196d |
permissions | -rw-r--r-- |
79502 | 1 |
/* Title: Pure/Build/store.scala |
78178 | 2 |
Author: Makarius |
3 |
||
4 |
Persistent store for session content: within file-system and/or SQL database. |
|
5 |
*/ |
|
6 |
||
7 |
package isabelle |
|
8 |
||
9 |
||
10 |
import java.sql.SQLException |
|
11 |
||
12 |
||
13 |
object Store { |
|
79676
0cac7e3634d0
more explicit build_cluster flag to guard open_build_database server;
wenzelm
parents:
79674
diff
changeset
|
14 |
def apply( |
0cac7e3634d0
more explicit build_cluster flag to guard open_build_database server;
wenzelm
parents:
79674
diff
changeset
|
15 |
options: Options, |
0cac7e3634d0
more explicit build_cluster flag to guard open_build_database server;
wenzelm
parents:
79674
diff
changeset
|
16 |
build_cluster: Boolean = false, |
0cac7e3634d0
more explicit build_cluster flag to guard open_build_database server;
wenzelm
parents:
79674
diff
changeset
|
17 |
cache: Term.Cache = Term.Cache.make() |
0cac7e3634d0
more explicit build_cluster flag to guard open_build_database server;
wenzelm
parents:
79674
diff
changeset
|
18 |
): Store = new Store(options, build_cluster, cache) |
78178 | 19 |
|
20 |
||
79684 | 21 |
/* file names */ |
22 |
||
23 |
def heap(name: String): Path = Path.basic(name) |
|
24 |
def log(name: String): Path = Path.basic("log") + Path.basic(name) |
|
25 |
def log_db(name: String): Path = log(name).db |
|
26 |
def log_gz(name: String): Path = log(name).gz |
|
27 |
||
28 |
||
79662 | 29 |
/* session */ |
30 |
||
79674 | 31 |
final class Session private[Store]( |
32 |
val name: String, |
|
33 |
val heap: Option[Path], |
|
34 |
val log_db: Option[Path], |
|
35 |
dirs: List[Path] |
|
36 |
) { |
|
79685 | 37 |
def log_db_name: String = Store.log_db(name).implode |
38 |
||
79662 | 39 |
def defined: Boolean = heap.isDefined || log_db.isDefined |
40 |
||
79674 | 41 |
def the_heap: Path = |
42 |
heap getOrElse |
|
43 |
error("Missing heap image for session " + quote(name) + " -- expected in:\n" + |
|
44 |
cat_lines(dirs.map(dir => " " + File.standard_path(dir)))) |
|
45 |
||
79663
4a299bdb5d61
clarified signature: more comprehensive operations;
wenzelm
parents:
79662
diff
changeset
|
46 |
def heap_digest(): Option[SHA1.Digest] = |
4a299bdb5d61
clarified signature: more comprehensive operations;
wenzelm
parents:
79662
diff
changeset
|
47 |
heap.flatMap(ML_Heap.read_file_digest) |
4a299bdb5d61
clarified signature: more comprehensive operations;
wenzelm
parents:
79662
diff
changeset
|
48 |
|
79662 | 49 |
override def toString: String = name |
50 |
} |
|
51 |
||
52 |
||
53 |
||
78179 | 54 |
/* session build info */ |
55 |
||
56 |
sealed case class Build_Info( |
|
57 |
sources: SHA1.Shasum, |
|
58 |
input_heaps: SHA1.Shasum, |
|
59 |
output_heap: SHA1.Shasum, |
|
60 |
return_code: Int, |
|
61 |
uuid: String |
|
62 |
) { |
|
63 |
def ok: Boolean = return_code == 0 |
|
64 |
} |
|
65 |
||
66 |
||
67 |
/* session sources */ |
|
78178 | 68 |
|
69 |
sealed case class Source_File( |
|
70 |
name: String, |
|
71 |
digest: SHA1.Digest, |
|
72 |
compressed: Boolean, |
|
73 |
body: Bytes, |
|
74 |
cache: Compress.Cache |
|
75 |
) { |
|
76 |
override def toString: String = name |
|
77 |
||
78 |
def bytes: Bytes = if (compressed) body.uncompress(cache = cache) else body |
|
79 |
} |
|
80 |
||
81 |
object Sources { |
|
82 |
def load(session_base: Sessions.Base, cache: Compress.Cache = Compress.Cache.none): Sources = |
|
83 |
new Sources( |
|
84 |
session_base.session_sources.foldLeft(Map.empty) { |
|
85 |
case (sources, (path, digest)) => |
|
86 |
def err(): Nothing = error("Incoherent digest for source file: " + path) |
|
87 |
val name = File.symbolic_path(path) |
|
88 |
sources.get(name) match { |
|
89 |
case Some(source_file) => |
|
90 |
if (source_file.digest == digest) sources else err() |
|
91 |
case None => |
|
92 |
val bytes = Bytes.read(path) |
|
93 |
if (bytes.sha1_digest == digest) { |
|
94 |
val (compressed, body) = |
|
95 |
bytes.maybe_compress(Compress.Options_Zstd(), cache = cache) |
|
96 |
val file = Source_File(name, digest, compressed, body, cache) |
|
97 |
sources + (name -> file) |
|
98 |
} |
|
99 |
else err() |
|
100 |
} |
|
101 |
}) |
|
102 |
} |
|
103 |
||
104 |
class Sources private(rep: Map[String, Source_File]) extends Iterable[Source_File] { |
|
105 |
override def toString: String = rep.values.toList.sortBy(_.name).mkString("Sources(", ", ", ")") |
|
106 |
override def iterator: Iterator[Source_File] = rep.valuesIterator |
|
107 |
||
108 |
def get(name: String): Option[Source_File] = rep.get(name) |
|
109 |
def apply(name: String): Source_File = |
|
110 |
get(name).getOrElse(error("Missing session sources entry " + quote(name))) |
|
111 |
} |
|
112 |
||
113 |
||
78179 | 114 |
/* SQL data model */ |
78178 | 115 |
|
78396
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
wenzelm
parents:
78377
diff
changeset
|
116 |
object private_data extends SQL.Data() { |
78260
0a7f7abbe4f0
more robust transaction_lock: avoid overlapping data spaces;
wenzelm
parents:
78227
diff
changeset
|
117 |
override lazy val tables = SQL.Tables(Session_Info.table, Sources.table) |
78187
2df0f3604a67
clarified signature: more explicit class SQL.Data;
wenzelm
parents:
78186
diff
changeset
|
118 |
|
78179 | 119 |
object Session_Info { |
120 |
val session_name = SQL.Column.string("session_name").make_primary_key |
|
121 |
||
122 |
// Build_Log.Session_Info |
|
123 |
val session_timing = SQL.Column.bytes("session_timing") |
|
124 |
val command_timings = SQL.Column.bytes("command_timings") |
|
125 |
val theory_timings = SQL.Column.bytes("theory_timings") |
|
126 |
val ml_statistics = SQL.Column.bytes("ml_statistics") |
|
127 |
val task_statistics = SQL.Column.bytes("task_statistics") |
|
128 |
val errors = SQL.Column.bytes("errors") |
|
129 |
val build_log_columns = |
|
130 |
List(session_name, session_timing, command_timings, theory_timings, |
|
131 |
ml_statistics, task_statistics, errors) |
|
78178 | 132 |
|
78179 | 133 |
// Build_Info |
134 |
val sources = SQL.Column.string("sources") |
|
135 |
val input_heaps = SQL.Column.string("input_heaps") |
|
136 |
val output_heap = SQL.Column.string("output_heap") |
|
137 |
val return_code = SQL.Column.int("return_code") |
|
138 |
val uuid = SQL.Column.string("uuid") |
|
139 |
val build_columns = List(sources, input_heaps, output_heap, return_code, uuid) |
|
140 |
||
141 |
val table = SQL.Table("isabelle_session_info", build_log_columns ::: build_columns) |
|
142 |
} |
|
143 |
||
144 |
object Sources { |
|
145 |
val session_name = SQL.Column.string("session_name").make_primary_key |
|
146 |
val name = SQL.Column.string("name").make_primary_key |
|
147 |
val digest = SQL.Column.string("digest") |
|
148 |
val compressed = SQL.Column.bool("compressed") |
|
149 |
val body = SQL.Column.bytes("body") |
|
78178 | 150 |
|
78179 | 151 |
val table = |
152 |
SQL.Table("isabelle_sources", List(session_name, name, digest, compressed, body)) |
|
153 |
||
154 |
def where_equal(session_name: String, name: String = ""): SQL.Source = |
|
155 |
SQL.where_and( |
|
156 |
Sources.session_name.equal(session_name), |
|
157 |
if_proper(name, Sources.name.equal(name))) |
|
158 |
} |
|
159 |
||
78265 | 160 |
def read_bytes(db: SQL.Database, name: String, column: SQL.Column): Bytes = |
161 |
db.execute_query_statementO[Bytes]( |
|
162 |
Session_Info.table.select(List(column), sql = Session_Info.session_name.where_equal(name)), |
|
163 |
res => res.bytes(column) |
|
164 |
).getOrElse(Bytes.empty) |
|
165 |
||
166 |
def read_properties( |
|
167 |
db: SQL.Database, name: String, column: SQL.Column, cache: Term.Cache |
|
168 |
): List[Properties.T] = Properties.uncompress(read_bytes(db, name, column), cache = cache) |
|
169 |
||
170 |
def read_session_timing(db: SQL.Database, name: String, cache: Term.Cache): Properties.T = |
|
171 |
Properties.decode(read_bytes(db, name, Session_Info.session_timing), cache = cache) |
|
172 |
||
173 |
def read_command_timings(db: SQL.Database, name: String): Bytes = |
|
174 |
read_bytes(db, name, Session_Info.command_timings) |
|
175 |
||
176 |
def read_theory_timings(db: SQL.Database, name: String, cache: Term.Cache): List[Properties.T] = |
|
177 |
read_properties(db, name, Session_Info.theory_timings, cache) |
|
178 |
||
179 |
def read_ml_statistics(db: SQL.Database, name: String, cache: Term.Cache): List[Properties.T] = |
|
180 |
read_properties(db, name, Session_Info.ml_statistics, cache) |
|
181 |
||
182 |
def read_task_statistics(db: SQL.Database, name: String, cache: Term.Cache): List[Properties.T] = |
|
183 |
read_properties(db, name, Session_Info.task_statistics, cache) |
|
184 |
||
185 |
def read_errors(db: SQL.Database, name: String, cache: Term.Cache): List[String] = |
|
186 |
Build_Log.uncompress_errors(read_bytes(db, name, Session_Info.errors), cache = cache) |
|
187 |
||
78377 | 188 |
def read_build(db: SQL.Database, name: String): Option[Store.Build_Info] = |
189 |
db.execute_query_statementO[Store.Build_Info]( |
|
190 |
Session_Info.table.select(sql = Session_Info.session_name.where_equal(name)), |
|
191 |
{ res => |
|
192 |
val uuid = |
|
193 |
try { Option(res.string(Session_Info.uuid)).getOrElse("") } |
|
194 |
catch { case _: SQLException => "" } |
|
195 |
Store.Build_Info( |
|
196 |
SHA1.fake_shasum(res.string(Session_Info.sources)), |
|
197 |
SHA1.fake_shasum(res.string(Session_Info.input_heaps)), |
|
198 |
SHA1.fake_shasum(res.string(Session_Info.output_heap)), |
|
199 |
res.int(Session_Info.return_code), |
|
200 |
uuid) |
|
201 |
}) |
|
78265 | 202 |
|
79682
1fa1b32b0379
build local log_db, with store/restore via optional database server;
wenzelm
parents:
79677
diff
changeset
|
203 |
def read_build_uuid(db: SQL.Database, name: String): String = |
1fa1b32b0379
build local log_db, with store/restore via optional database server;
wenzelm
parents:
79677
diff
changeset
|
204 |
db.execute_query_statementO[String]( |
1fa1b32b0379
build local log_db, with store/restore via optional database server;
wenzelm
parents:
79677
diff
changeset
|
205 |
Session_Info.table.select(List(Session_Info.uuid), |
1fa1b32b0379
build local log_db, with store/restore via optional database server;
wenzelm
parents:
79677
diff
changeset
|
206 |
sql = Session_Info.session_name.where_equal(name)), |
1fa1b32b0379
build local log_db, with store/restore via optional database server;
wenzelm
parents:
79677
diff
changeset
|
207 |
{ res => |
1fa1b32b0379
build local log_db, with store/restore via optional database server;
wenzelm
parents:
79677
diff
changeset
|
208 |
try { Option(res.string(Session_Info.uuid)).getOrElse("") } |
1fa1b32b0379
build local log_db, with store/restore via optional database server;
wenzelm
parents:
79677
diff
changeset
|
209 |
catch { case _: SQLException => "" } |
1fa1b32b0379
build local log_db, with store/restore via optional database server;
wenzelm
parents:
79677
diff
changeset
|
210 |
}).getOrElse("") |
1fa1b32b0379
build local log_db, with store/restore via optional database server;
wenzelm
parents:
79677
diff
changeset
|
211 |
|
78181 | 212 |
def write_session_info( |
213 |
db: SQL.Database, |
|
214 |
cache: Compress.Cache, |
|
215 |
session_name: String, |
|
216 |
build_log: Build_Log.Session_Info, |
|
217 |
build: Build_Info |
|
218 |
): Unit = { |
|
78262 | 219 |
db.execute_statement(Session_Info.table.insert(), body = |
78181 | 220 |
{ stmt => |
221 |
stmt.string(1) = session_name |
|
222 |
stmt.bytes(2) = Properties.encode(build_log.session_timing) |
|
223 |
stmt.bytes(3) = Properties.compress(build_log.command_timings, cache = cache) |
|
224 |
stmt.bytes(4) = Properties.compress(build_log.theory_timings, cache = cache) |
|
225 |
stmt.bytes(5) = Properties.compress(build_log.ml_statistics, cache = cache) |
|
226 |
stmt.bytes(6) = Properties.compress(build_log.task_statistics, cache = cache) |
|
227 |
stmt.bytes(7) = Build_Log.compress_errors(build_log.errors, cache = cache) |
|
228 |
stmt.string(8) = build.sources.toString |
|
229 |
stmt.string(9) = build.input_heaps.toString |
|
230 |
stmt.string(10) = build.output_heap.toString |
|
231 |
stmt.int(11) = build.return_code |
|
232 |
stmt.string(12) = build.uuid |
|
233 |
}) |
|
234 |
} |
|
235 |
||
78555 | 236 |
def write_sources( |
237 |
db: SQL.Database, |
|
238 |
session_name: String, |
|
239 |
source_files: Iterable[Source_File] |
|
240 |
): Unit = { |
|
241 |
db.execute_batch_statement(Sources.table.insert(), batch = |
|
242 |
for (source_file <- source_files) yield { (stmt: SQL.Statement) => |
|
243 |
stmt.string(1) = session_name |
|
244 |
stmt.string(2) = source_file.name |
|
245 |
stmt.string(3) = source_file.digest.toString |
|
246 |
stmt.bool(4) = source_file.compressed |
|
247 |
stmt.bytes(5) = source_file.body |
|
248 |
}) |
|
249 |
} |
|
78178 | 250 |
|
78179 | 251 |
def read_sources( |
252 |
db: SQL.Database, |
|
253 |
session_name: String, |
|
78265 | 254 |
name: String, |
255 |
cache: Compress.Cache |
|
78179 | 256 |
): List[Source_File] = { |
257 |
db.execute_query_statement( |
|
258 |
Sources.table.select( |
|
259 |
sql = Sources.where_equal(session_name, name = name) + SQL.order_by(List(Sources.name))), |
|
260 |
List.from[Source_File], |
|
261 |
{ res => |
|
262 |
val res_name = res.string(Sources.name) |
|
263 |
val digest = SHA1.fake_digest(res.string(Sources.digest)) |
|
264 |
val compressed = res.bool(Sources.compressed) |
|
265 |
val body = res.bytes(Sources.body) |
|
78180 | 266 |
Source_File(res_name, digest, compressed, body, cache) |
78179 | 267 |
} |
268 |
) |
|
269 |
} |
|
78178 | 270 |
} |
79682
1fa1b32b0379
build local log_db, with store/restore via optional database server;
wenzelm
parents:
79677
diff
changeset
|
271 |
|
1fa1b32b0379
build local log_db, with store/restore via optional database server;
wenzelm
parents:
79677
diff
changeset
|
272 |
def read_build_uuid(path: Path, session: String): String = |
1fa1b32b0379
build local log_db, with store/restore via optional database server;
wenzelm
parents:
79677
diff
changeset
|
273 |
try { using(SQLite.open_database(path))(private_data.read_build_uuid(_, session)) } |
1fa1b32b0379
build local log_db, with store/restore via optional database server;
wenzelm
parents:
79677
diff
changeset
|
274 |
catch { case _: SQLException => "" } |
78178 | 275 |
} |
276 |
||
79676
0cac7e3634d0
more explicit build_cluster flag to guard open_build_database server;
wenzelm
parents:
79674
diff
changeset
|
277 |
class Store private( |
0cac7e3634d0
more explicit build_cluster flag to guard open_build_database server;
wenzelm
parents:
79674
diff
changeset
|
278 |
val options: Options, |
0cac7e3634d0
more explicit build_cluster flag to guard open_build_database server;
wenzelm
parents:
79674
diff
changeset
|
279 |
val build_cluster: Boolean, |
0cac7e3634d0
more explicit build_cluster flag to guard open_build_database server;
wenzelm
parents:
79674
diff
changeset
|
280 |
val cache: Term.Cache |
0cac7e3634d0
more explicit build_cluster flag to guard open_build_database server;
wenzelm
parents:
79674
diff
changeset
|
281 |
) { |
78178 | 282 |
store => |
283 |
||
284 |
override def toString: String = "Store(output_dir = " + output_dir.absolute + ")" |
|
285 |
||
286 |
||
287 |
/* directories */ |
|
288 |
||
289 |
val system_output_dir: Path = Path.explode("$ISABELLE_HEAPS_SYSTEM/$ML_IDENTIFIER") |
|
290 |
val user_output_dir: Path = Path.explode("$ISABELLE_HEAPS/$ML_IDENTIFIER") |
|
291 |
||
292 |
def system_heaps: Boolean = options.bool("system_heaps") |
|
293 |
||
294 |
val output_dir: Path = |
|
295 |
if (system_heaps) system_output_dir else user_output_dir |
|
296 |
||
297 |
val input_dirs: List[Path] = |
|
298 |
if (system_heaps) List(system_output_dir) |
|
299 |
else List(user_output_dir, system_output_dir) |
|
300 |
||
79708 | 301 |
val clean_dirs: List[Path] = |
302 |
if (system_heaps) List(user_output_dir, system_output_dir) |
|
303 |
else List(user_output_dir) |
|
304 |
||
78178 | 305 |
def presentation_dir: Path = |
306 |
if (system_heaps) Path.explode("$ISABELLE_BROWSER_INFO_SYSTEM") |
|
307 |
else Path.explode("$ISABELLE_BROWSER_INFO") |
|
308 |
||
309 |
||
310 |
/* file names */ |
|
311 |
||
79684 | 312 |
def output_heap(name: String): Path = output_dir + Store.heap(name) |
313 |
def output_log(name: String): Path = output_dir + Store.log(name) |
|
314 |
def output_log_db(name: String): Path = output_dir + Store.log_db(name) |
|
315 |
def output_log_gz(name: String): Path = output_dir + Store.log_gz(name) |
|
78178 | 316 |
|
317 |
||
79662 | 318 |
/* session */ |
319 |
||
79663
4a299bdb5d61
clarified signature: more comprehensive operations;
wenzelm
parents:
79662
diff
changeset
|
320 |
def get_session(name: String): Store.Session = { |
79684 | 321 |
val heap = input_dirs.view.map(_ + Store.heap(name)).find(_.is_file) |
322 |
val log_db = input_dirs.view.map(_ + Store.log_db(name)).find(_.is_file) |
|
79674 | 323 |
new Store.Session(name, heap, log_db, input_dirs) |
79663
4a299bdb5d61
clarified signature: more comprehensive operations;
wenzelm
parents:
79662
diff
changeset
|
324 |
} |
79662 | 325 |
|
79682
1fa1b32b0379
build local log_db, with store/restore via optional database server;
wenzelm
parents:
79677
diff
changeset
|
326 |
def output_session(name: String, store_heap: Boolean = false): Store.Session = { |
1fa1b32b0379
build local log_db, with store/restore via optional database server;
wenzelm
parents:
79677
diff
changeset
|
327 |
val heap = if (store_heap) Some(output_heap(name)) else None |
1fa1b32b0379
build local log_db, with store/restore via optional database server;
wenzelm
parents:
79677
diff
changeset
|
328 |
val log_db = if (!build_database_server) Some(output_log_db(name)) else None |
1fa1b32b0379
build local log_db, with store/restore via optional database server;
wenzelm
parents:
79677
diff
changeset
|
329 |
new Store.Session(name, heap, log_db, List(output_dir)) |
1fa1b32b0379
build local log_db, with store/restore via optional database server;
wenzelm
parents:
79677
diff
changeset
|
330 |
} |
1fa1b32b0379
build local log_db, with store/restore via optional database server;
wenzelm
parents:
79677
diff
changeset
|
331 |
|
79662 | 332 |
|
78178 | 333 |
/* heap */ |
334 |
||
78212 | 335 |
def heap_shasum(database_server: Option[SQL.Database], name: String): SHA1.Shasum = { |
78510
8f45302a9ff0
more thorough ML_Heap.restore: include ancestors; prefer simultaneous ML_Heap.get_entries: just one database access for heap hierarchy;
wenzelm
parents:
78400
diff
changeset
|
336 |
def get_database: Option[SHA1.Digest] = { |
8f45302a9ff0
more thorough ML_Heap.restore: include ancestors; prefer simultaneous ML_Heap.get_entries: just one database access for heap hierarchy;
wenzelm
parents:
78400
diff
changeset
|
337 |
for { |
8f45302a9ff0
more thorough ML_Heap.restore: include ancestors; prefer simultaneous ML_Heap.get_entries: just one database access for heap hierarchy;
wenzelm
parents:
78400
diff
changeset
|
338 |
db <- database_server |
79677 | 339 |
digest <- ML_Heap.read_digests(db, List(name)).valuesIterator.nextOption() |
78510
8f45302a9ff0
more thorough ML_Heap.restore: include ancestors; prefer simultaneous ML_Heap.get_entries: just one database access for heap hierarchy;
wenzelm
parents:
78400
diff
changeset
|
340 |
} yield digest |
8f45302a9ff0
more thorough ML_Heap.restore: include ancestors; prefer simultaneous ML_Heap.get_entries: just one database access for heap hierarchy;
wenzelm
parents:
78400
diff
changeset
|
341 |
} |
8f45302a9ff0
more thorough ML_Heap.restore: include ancestors; prefer simultaneous ML_Heap.get_entries: just one database access for heap hierarchy;
wenzelm
parents:
78400
diff
changeset
|
342 |
|
79663
4a299bdb5d61
clarified signature: more comprehensive operations;
wenzelm
parents:
79662
diff
changeset
|
343 |
get_database orElse get_session(name).heap_digest() match { |
78196
140a6f2e3728
restore heaps from database, which takes precedence over file-system;
wenzelm
parents:
78190
diff
changeset
|
344 |
case Some(digest) => SHA1.shasum(digest, name) |
140a6f2e3728
restore heaps from database, which takes precedence over file-system;
wenzelm
parents:
78190
diff
changeset
|
345 |
case None => SHA1.no_shasum |
140a6f2e3728
restore heaps from database, which takes precedence over file-system;
wenzelm
parents:
78190
diff
changeset
|
346 |
} |
140a6f2e3728
restore heaps from database, which takes precedence over file-system;
wenzelm
parents:
78190
diff
changeset
|
347 |
} |
140a6f2e3728
restore heaps from database, which takes precedence over file-system;
wenzelm
parents:
78190
diff
changeset
|
348 |
|
78178 | 349 |
|
350 |
/* databases for build process and session content */ |
|
351 |
||
352 |
def build_database_server: Boolean = options.bool("build_database_server") |
|
78511 | 353 |
def build_database: Boolean = options.bool("build_database") |
78178 | 354 |
|
78366 | 355 |
def open_server(): SSH.Server = |
356 |
PostgreSQL.open_server(options, |
|
357 |
host = options.string("build_database_host"), |
|
358 |
port = options.int("build_database_port"), |
|
359 |
ssh_host = options.string("build_database_ssh_host"), |
|
360 |
ssh_port = options.int("build_database_ssh_port"), |
|
361 |
ssh_user = options.string("build_database_ssh_user")) |
|
362 |
||
78347 | 363 |
def open_database_server(server: SSH.Server = SSH.no_server): PostgreSQL.Database = |
364 |
PostgreSQL.open_database_server(options, server = server, |
|
78178 | 365 |
user = options.string("build_database_user"), |
366 |
password = options.string("build_database_password"), |
|
367 |
database = options.string("build_database_name"), |
|
368 |
host = options.string("build_database_host"), |
|
369 |
port = options.int("build_database_port"), |
|
78347 | 370 |
ssh_host = options.string("build_database_ssh_host"), |
371 |
ssh_port = options.int("build_database_ssh_port"), |
|
78863
f627ab8c276c
discontinued pointless option (reverting 63d55ba90a9f): performance tuning works better via SQL.Database.execute_batch_statement;
wenzelm
parents:
78555
diff
changeset
|
372 |
ssh_user = options.string("build_database_ssh_user")) |
78178 | 373 |
|
79682
1fa1b32b0379
build local log_db, with store/restore via optional database server;
wenzelm
parents:
79677
diff
changeset
|
374 |
def maybe_open_database_server( |
1fa1b32b0379
build local log_db, with store/restore via optional database server;
wenzelm
parents:
79677
diff
changeset
|
375 |
server: SSH.Server = SSH.no_server, |
1fa1b32b0379
build local log_db, with store/restore via optional database server;
wenzelm
parents:
79677
diff
changeset
|
376 |
guard: Boolean = build_database_server |
1fa1b32b0379
build local log_db, with store/restore via optional database server;
wenzelm
parents:
79677
diff
changeset
|
377 |
): Option[SQL.Database] = { |
1fa1b32b0379
build local log_db, with store/restore via optional database server;
wenzelm
parents:
79677
diff
changeset
|
378 |
if (guard) Some(open_database_server(server = server)) else None |
1fa1b32b0379
build local log_db, with store/restore via optional database server;
wenzelm
parents:
79677
diff
changeset
|
379 |
} |
1fa1b32b0379
build local log_db, with store/restore via optional database server;
wenzelm
parents:
79677
diff
changeset
|
380 |
|
1fa1b32b0379
build local log_db, with store/restore via optional database server;
wenzelm
parents:
79677
diff
changeset
|
381 |
def maybe_open_heaps_database( |
1fa1b32b0379
build local log_db, with store/restore via optional database server;
wenzelm
parents:
79677
diff
changeset
|
382 |
database_server: Option[SQL.Database], |
1fa1b32b0379
build local log_db, with store/restore via optional database server;
wenzelm
parents:
79677
diff
changeset
|
383 |
server: SSH.Server = SSH.no_server |
1fa1b32b0379
build local log_db, with store/restore via optional database server;
wenzelm
parents:
79677
diff
changeset
|
384 |
): Option[SQL.Database] = { |
1fa1b32b0379
build local log_db, with store/restore via optional database server;
wenzelm
parents:
79677
diff
changeset
|
385 |
if (database_server.isDefined) None |
1fa1b32b0379
build local log_db, with store/restore via optional database server;
wenzelm
parents:
79677
diff
changeset
|
386 |
else store.maybe_open_database_server(server = server, guard = build_cluster) |
1fa1b32b0379
build local log_db, with store/restore via optional database server;
wenzelm
parents:
79677
diff
changeset
|
387 |
} |
78205
a40ae2df39ad
clarified database for heaps: do not depend on build_database_test;
wenzelm
parents:
78198
diff
changeset
|
388 |
|
78372 | 389 |
def open_build_database(path: Path, server: SSH.Server = SSH.no_server): SQL.Database = |
79676
0cac7e3634d0
more explicit build_cluster flag to guard open_build_database server;
wenzelm
parents:
79674
diff
changeset
|
390 |
if (build_database_server || build_cluster) open_database_server(server = server) |
78178 | 391 |
else SQLite.open_database(path, restrict = true) |
392 |
||
78223 | 393 |
def maybe_open_build_database( |
78372 | 394 |
path: Path = Path.explode("$ISABELLE_HOME_USER/build.db"), |
395 |
server: SSH.Server = SSH.no_server |
|
396 |
): Option[SQL.Database] = { |
|
78511 | 397 |
if (build_database) Some(open_build_database(path, server = server)) else None |
78372 | 398 |
} |
78184 | 399 |
|
78178 | 400 |
def try_open_database( |
401 |
name: String, |
|
402 |
output: Boolean = false, |
|
78372 | 403 |
server: SSH.Server = SSH.no_server, |
78367 | 404 |
server_mode: Boolean = build_database_server |
78178 | 405 |
): Option[SQL.Database] = { |
406 |
def check(db: SQL.Database): Option[SQL.Database] = |
|
407 |
if (output || session_info_exists(db)) Some(db) else { db.close(); None } |
|
408 |
||
78372 | 409 |
if (server_mode) check(open_database_server(server = server)) |
79661
2a9d8c74eb3c
clarified signature: emphasize physical db files;
wenzelm
parents:
79502
diff
changeset
|
410 |
else if (output) Some(SQLite.open_database(output_log_db(name))) |
78178 | 411 |
else { |
412 |
(for { |
|
413 |
dir <- input_dirs.view |
|
79684 | 414 |
path = dir + Store.log_db(name) if path.is_file |
78178 | 415 |
db <- check(SQLite.open_database(path)) |
416 |
} yield db).headOption |
|
417 |
} |
|
418 |
} |
|
419 |
||
420 |
def error_database(name: String): Nothing = |
|
421 |
error("Missing build database for session " + quote(name)) |
|
422 |
||
78372 | 423 |
def open_database( |
424 |
name: String, |
|
425 |
output: Boolean = false, |
|
426 |
server: SSH.Server = SSH.no_server |
|
427 |
): SQL.Database = { |
|
428 |
try_open_database(name, output = output, server = server) getOrElse error_database(name) |
|
429 |
} |
|
78178 | 430 |
|
78213
fd0430a7b7a4
avoid repeated open_database_server: synchronized transaction_lock;
wenzelm
parents:
78212
diff
changeset
|
431 |
def clean_output( |
fd0430a7b7a4
avoid repeated open_database_server: synchronized transaction_lock;
wenzelm
parents:
78212
diff
changeset
|
432 |
database_server: Option[SQL.Database], |
fd0430a7b7a4
avoid repeated open_database_server: synchronized transaction_lock;
wenzelm
parents:
78212
diff
changeset
|
433 |
name: String, |
79709 | 434 |
session_init: Boolean = false, |
435 |
progress: Progress = new Progress |
|
436 |
): Unit = { |
|
78178 | 437 |
val relevant_db = |
78213
fd0430a7b7a4
avoid repeated open_database_server: synchronized transaction_lock;
wenzelm
parents:
78212
diff
changeset
|
438 |
database_server match { |
79682
1fa1b32b0379
build local log_db, with store/restore via optional database server;
wenzelm
parents:
79677
diff
changeset
|
439 |
case Some(db) => clean_session_info(db, name) |
78227
1ba48d402005
proper session_init *after* deleting db files (amending af6c493b0441);
wenzelm
parents:
78223
diff
changeset
|
440 |
case None => false |
78213
fd0430a7b7a4
avoid repeated open_database_server: synchronized transaction_lock;
wenzelm
parents:
78212
diff
changeset
|
441 |
} |
78178 | 442 |
|
443 |
val del = |
|
444 |
for { |
|
79708 | 445 |
dir <- clean_dirs |
79684 | 446 |
file <- List(Store.heap(name), Store.log_db(name), Store.log(name), Store.log_gz(name)) |
78178 | 447 |
path = dir + file if path.is_file |
448 |
} yield path.file.delete |
|
449 |
||
78227
1ba48d402005
proper session_init *after* deleting db files (amending af6c493b0441);
wenzelm
parents:
78223
diff
changeset
|
450 |
if (database_server.isEmpty && session_init) { |
1ba48d402005
proper session_init *after* deleting db files (amending af6c493b0441);
wenzelm
parents:
78223
diff
changeset
|
451 |
using(open_database(name, output = true))(clean_session_info(_, name)) |
1ba48d402005
proper session_init *after* deleting db files (amending af6c493b0441);
wenzelm
parents:
78223
diff
changeset
|
452 |
} |
1ba48d402005
proper session_init *after* deleting db files (amending af6c493b0441);
wenzelm
parents:
78223
diff
changeset
|
453 |
|
79709 | 454 |
if (relevant_db || del.nonEmpty) { |
455 |
if (del.forall(identity)) progress.echo("Cleaned " + name) |
|
456 |
else progress.echo(name + " FAILED to clean") |
|
457 |
} |
|
78178 | 458 |
} |
459 |
||
460 |
def check_output( |
|
78374 | 461 |
database_server: Option[SQL.Database], |
78178 | 462 |
name: String, |
463 |
session_options: Options, |
|
464 |
sources_shasum: SHA1.Shasum, |
|
465 |
input_shasum: SHA1.Shasum, |
|
466 |
fresh_build: Boolean, |
|
467 |
store_heap: Boolean |
|
468 |
): (Boolean, SHA1.Shasum) = { |
|
78374 | 469 |
def no_check: (Boolean, SHA1.Shasum) = (false, SHA1.no_shasum) |
470 |
||
471 |
def check(db: SQL.Database): (Boolean, SHA1.Shasum) = |
|
472 |
read_build(db, name) match { |
|
473 |
case Some(build) => |
|
474 |
val output_shasum = heap_shasum(if (db.is_postgresql) Some(db) else None, name) |
|
475 |
val current = |
|
476 |
!fresh_build && |
|
477 |
build.ok && |
|
478 |
Sessions.eq_sources(session_options, build.sources, sources_shasum) && |
|
479 |
build.input_heaps == input_shasum && |
|
480 |
build.output_heap == output_shasum && |
|
481 |
!(store_heap && output_shasum.is_empty) |
|
482 |
(current, output_shasum) |
|
483 |
case None => no_check |
|
484 |
} |
|
485 |
||
486 |
database_server match { |
|
487 |
case Some(db) => if (session_info_exists(db)) check(db) else no_check |
|
488 |
case None => using_option(try_open_database(name))(check) getOrElse no_check |
|
78178 | 489 |
} |
490 |
} |
|
491 |
||
492 |
||
493 |
/* session info */ |
|
494 |
||
78375
234f2ff9afe6
clarified signature: more specific exists_table --- avoid retrieving full list beforehand;
wenzelm
parents:
78374
diff
changeset
|
495 |
def session_info_exists(db: SQL.Database): Boolean = |
78396
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
wenzelm
parents:
78377
diff
changeset
|
496 |
Store.private_data.tables.forall(db.exists_table) |
78181 | 497 |
|
498 |
def session_info_defined(db: SQL.Database, name: String): Boolean = |
|
499 |
db.execute_query_statementB( |
|
78396
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
wenzelm
parents:
78377
diff
changeset
|
500 |
Store.private_data.Session_Info.table.select(List(Store.private_data.Session_Info.session_name), |
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
wenzelm
parents:
78377
diff
changeset
|
501 |
sql = Store.private_data.Session_Info.session_name.where_equal(name))) |
78181 | 502 |
|
78260
0a7f7abbe4f0
more robust transaction_lock: avoid overlapping data spaces;
wenzelm
parents:
78227
diff
changeset
|
503 |
def clean_session_info(db: SQL.Database, name: String): Boolean = { |
0a7f7abbe4f0
more robust transaction_lock: avoid overlapping data spaces;
wenzelm
parents:
78227
diff
changeset
|
504 |
Export.clean_session(db, name) |
0a7f7abbe4f0
more robust transaction_lock: avoid overlapping data spaces;
wenzelm
parents:
78227
diff
changeset
|
505 |
Document_Build.clean_session(db, name) |
0a7f7abbe4f0
more robust transaction_lock: avoid overlapping data spaces;
wenzelm
parents:
78227
diff
changeset
|
506 |
|
78396
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
wenzelm
parents:
78377
diff
changeset
|
507 |
Store.private_data.transaction_lock(db, create = true, label = "Store.clean_session_info") { |
78178 | 508 |
val already_defined = session_info_defined(db, name) |
509 |
||
510 |
db.execute_statement( |
|
78555 | 511 |
SQL.multi( |
512 |
Store.private_data.Session_Info.table.delete( |
|
513 |
sql = Store.private_data.Session_Info.session_name.where_equal(name)), |
|
514 |
Store.private_data.Sources.table.delete( |
|
515 |
sql = Store.private_data.Sources.where_equal(name)))) |
|
78178 | 516 |
|
517 |
already_defined |
|
518 |
} |
|
78260
0a7f7abbe4f0
more robust transaction_lock: avoid overlapping data spaces;
wenzelm
parents:
78227
diff
changeset
|
519 |
} |
78178 | 520 |
|
521 |
def write_session_info( |
|
522 |
db: SQL.Database, |
|
523 |
session_name: String, |
|
524 |
sources: Store.Sources, |
|
525 |
build_log: Build_Log.Session_Info, |
|
526 |
build: Store.Build_Info |
|
527 |
): Unit = { |
|
78396
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
wenzelm
parents:
78377
diff
changeset
|
528 |
Store.private_data.transaction_lock(db, label = "Store.write_session_info") { |
78555 | 529 |
for (source_files <- sources.iterator.toList.grouped(200)) { |
530 |
Store.private_data.write_sources(db, session_name, source_files) |
|
531 |
} |
|
78396
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
wenzelm
parents:
78377
diff
changeset
|
532 |
Store.private_data.write_session_info(db, cache.compress, session_name, build_log, build) |
78178 | 533 |
} |
534 |
} |
|
535 |
||
78265 | 536 |
def read_session_timing(db: SQL.Database, session: String): Properties.T = |
78396
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
wenzelm
parents:
78377
diff
changeset
|
537 |
Store.private_data.transaction_lock(db, label = "Store.read_session_timing") { |
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
wenzelm
parents:
78377
diff
changeset
|
538 |
Store.private_data.read_session_timing(db, session, cache) |
78356 | 539 |
} |
78178 | 540 |
|
78265 | 541 |
def read_command_timings(db: SQL.Database, session: String): Bytes = |
78396
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
wenzelm
parents:
78377
diff
changeset
|
542 |
Store.private_data.transaction_lock(db, label = "Store.read_command_timings") { |
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
wenzelm
parents:
78377
diff
changeset
|
543 |
Store.private_data.read_command_timings(db, session) |
78356 | 544 |
} |
78178 | 545 |
|
78265 | 546 |
def read_theory_timings(db: SQL.Database, session: String): List[Properties.T] = |
78396
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
wenzelm
parents:
78377
diff
changeset
|
547 |
Store.private_data.transaction_lock(db, label = "Store.read_theory_timings") { |
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
wenzelm
parents:
78377
diff
changeset
|
548 |
Store.private_data.read_theory_timings(db, session, cache) |
78356 | 549 |
} |
78178 | 550 |
|
78265 | 551 |
def read_ml_statistics(db: SQL.Database, session: String): List[Properties.T] = |
78396
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
wenzelm
parents:
78377
diff
changeset
|
552 |
Store.private_data.transaction_lock(db, label = "Store.read_ml_statistics") { |
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
wenzelm
parents:
78377
diff
changeset
|
553 |
Store.private_data.read_ml_statistics(db, session, cache) |
78356 | 554 |
} |
78178 | 555 |
|
78265 | 556 |
def read_task_statistics(db: SQL.Database, session: String): List[Properties.T] = |
78396
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
wenzelm
parents:
78377
diff
changeset
|
557 |
Store.private_data.transaction_lock(db, label = "Store.read_task_statistics") { |
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
wenzelm
parents:
78377
diff
changeset
|
558 |
Store.private_data.read_task_statistics(db, session, cache) |
78356 | 559 |
} |
78265 | 560 |
|
561 |
def read_theories(db: SQL.Database, session: String): List[String] = |
|
562 |
read_theory_timings(db, session).flatMap(Markup.Name.unapply) |
|
563 |
||
564 |
def read_errors(db: SQL.Database, session: String): List[String] = |
|
78396
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
wenzelm
parents:
78377
diff
changeset
|
565 |
Store.private_data.transaction_lock(db, label = "Store.read_errors") { |
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
wenzelm
parents:
78377
diff
changeset
|
566 |
Store.private_data.read_errors(db, session, cache) |
78356 | 567 |
} |
78265 | 568 |
|
569 |
def read_build(db: SQL.Database, session: String): Option[Store.Build_Info] = |
|
78396
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
wenzelm
parents:
78377
diff
changeset
|
570 |
Store.private_data.transaction_lock(db, label = "Store.read_build") { |
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
wenzelm
parents:
78377
diff
changeset
|
571 |
if (session_info_exists(db)) Store.private_data.read_build(db, session) else None |
78356 | 572 |
} |
78178 | 573 |
|
78179 | 574 |
def read_sources(db: SQL.Database, session: String, name: String = ""): List[Store.Source_File] = |
78396
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
wenzelm
parents:
78377
diff
changeset
|
575 |
Store.private_data.transaction_lock(db, label = "Store.read_sources") { |
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
wenzelm
parents:
78377
diff
changeset
|
576 |
Store.private_data.read_sources(db, session, name, cache.compress) |
78356 | 577 |
} |
78178 | 578 |
} |