add serial for build schedule to avoid unnecessary db read/writes;
authorFabian Huch <huch@in.tum.de>
Fri, 08 Dec 2023 15:21:51 +0100
changeset 79190 2039f3609884
parent 79189 f52201fc15b4
child 79191 ee405c40db72
add serial for build schedule to avoid unnecessary db read/writes;
src/Pure/Tools/build_schedule.scala
--- a/src/Pure/Tools/build_schedule.scala	Thu Dec 07 13:57:48 2023 +0100
+++ b/src/Pure/Tools/build_schedule.scala	Fri Dec 08 15:21:51 2023 +0100
@@ -435,7 +435,19 @@
     def init(build_uuid: String): Schedule = Schedule(build_uuid, "none", Date.now(), Graph.empty)
   }
 
-  case class Schedule(build_uuid: String, generator: String, start: Date, graph: Schedule.Graph) {
+  case class Schedule(
+    build_uuid: String,
+    generator: String,
+    start: Date,
+    graph: Schedule.Graph,
+    serial: Long = 0,
+  ) {
+    require(serial >= 0, "serial underflow")
+    def inc_serial: Schedule = {
+      require(serial < Long.MaxValue, "serial overflow")
+      copy(serial = serial + 1)
+    }
+    
     def end: Date =
       if (graph.is_empty) start
       else graph.maximals.map(graph.get_node).map(_.end).maxBy(_.unix_epoch)
@@ -814,7 +826,7 @@
               val old_schedule = Build_Schedule.private_data.pull_schedule(db, _schedule)
               _schedule = old_schedule
               val res = body
-              Build_Schedule.private_data.update_schedule(db, _schedule)
+              _schedule = Build_Schedule.private_data.update_schedule(db, _schedule, old_schedule)
               res
             }
         }
@@ -968,10 +980,17 @@
       val build_uuid = Generic.build_uuid.make_primary_key
       val generator = SQL.Column.string("generator")
       val start = SQL.Column.date("start")
+      val serial = SQL.Column.long("serial")
 
-      val table = make_table(List(build_uuid, generator, start), name = "schedules")
+      val table = make_table(List(build_uuid, generator, start, serial), name = "schedules")
     }
 
+    def read_serial(db: SQL.Database, build_uuid: String = ""): Long =
+      db.execute_query_statementO[Long](
+        Schedules.table.select(List(Schedules.serial.max), sql = 
+          SQL.where(if_proper(build_uuid, Schedules.build_uuid.equal(build_uuid)))),
+          _.long(Schedules.serial)).getOrElse(0L)
+
     def read_scheduled_builds_domain(db: SQL.Database): List[String] =
       db.execute_query_statement(
         Schedules.table.select(List(Schedules.build_uuid)),
@@ -995,13 +1014,14 @@
       }
     }
 
-    def update_schedule(db: SQL.Database, schedule: Schedule): Unit = {
+    def write_schedule(db: SQL.Database, schedule: Schedule): Unit = {
       db.execute_statement(
         Schedules.table.delete(Schedules.build_uuid.where_equal(schedule.build_uuid)))
       db.execute_statement(Schedules.table.insert(), { stmt =>
         stmt.string(1) = schedule.build_uuid
         stmt.string(2) = schedule.generator
         stmt.date(3) = schedule.start
+        stmt.long(4) = schedule.serial
       })
       update_nodes(db, schedule.build_uuid, schedule.graph.dest)
     }
@@ -1062,14 +1082,29 @@
         })
     }
 
-    def pull_schedule(
-      db: SQL.Database,
-      schedule: Schedule,
-    ): Build_Schedule.Schedule =
-      read_schedules(db, schedule.build_uuid) match {
-        case Nil => schedule
-        case schedules => Library.the_single(schedules)
+    def pull_schedule(db: SQL.Database, old_schedule: Schedule): Build_Schedule.Schedule = {
+      val serial_db = read_serial(db)
+      if (serial_db == old_schedule.serial) old_schedule
+      else {
+        read_schedules(db, old_schedule.build_uuid) match {
+          case Nil => old_schedule
+          case schedules => Library.the_single(schedules)
+        }
       }
+    }
+
+    def update_schedule(db: SQL.Database, schedule: Schedule, old_schedule: Schedule): Schedule = {
+      val changed =
+        schedule.generator != old_schedule.generator ||
+        schedule.start != old_schedule.start ||
+        schedule.graph != old_schedule.graph
+      
+      val schedule1 =
+        if (changed) schedule.copy(serial = old_schedule.serial).inc_serial else schedule
+      if (schedule1.serial != schedule.serial) write_schedule(db, schedule1)
+      
+      schedule1
+    }
 
     def remove_schedules(db: SQL.Database, remove: List[String]): Unit =
       if (remove.nonEmpty) {