diff --git a/MonkeyWrench.DataClasses/Database/DBLane.generated.cs b/MonkeyWrench.DataClasses/Database/DBLane.generated.cs index 92e0920..401c18b 100644 --- a/MonkeyWrench.DataClasses/Database/DBLane.generated.cs +++ b/MonkeyWrench.DataClasses/Database/DBLane.generated.cs @@ -29,6 +29,7 @@ public partial class DBLane : DBRecord private bool _enabled; private DateTime? _changed_date; private string _additional_roles; + private int _priority; public string @lane { get { return _lane; } set { _lane = value; } } public string @source_control { get { return _source_control; } set { _source_control = value; } } @@ -41,6 +42,7 @@ public partial class DBLane : DBRecord public bool @enabled { get { return _enabled; } set { _enabled = value; } } public DateTime? @changed_date { get { return _changed_date; } set { _changed_date = value; } } public string @additional_roles { get { return _additional_roles; } set { _additional_roles = value; } } + public int @priority { get { return _priority; } set { _priority = value; } } public override string Table @@ -53,7 +55,7 @@ public override string Table { get { - return new string [] { "lane", "source_control", "repository", "min_revision", "max_revision", "parent_lane_id", "commit_filter", "traverse_merge", "enabled", "changed_date", "additional_roles" }; + return new string [] { "lane", "source_control", "repository", "min_revision", "max_revision", "parent_lane_id", "commit_filter", "traverse_merge", "enabled", "changed_date", "additional_roles", "priority" }; } } diff --git a/MonkeyWrench.DataClasses/Database/DBQueueManagement.cs b/MonkeyWrench.DataClasses/Database/DBQueueManagement.cs index ed1ff17..8b99764 100755 --- a/MonkeyWrench.DataClasses/Database/DBQueueManagement.cs +++ b/MonkeyWrench.DataClasses/Database/DBQueueManagement.cs @@ -21,5 +21,6 @@ public enum DBQueueManagement FinishBeforeNew = 0, ExecuteLatestAsap = 1, // currently same as FinishBeforeNew (i.e. ignored) OneRevisionWorkAtATime = 2, // + ChooseHighestPriorityLeastRecent = 3 } } diff --git a/MonkeyWrench.DataClasses/Database/DBRevisionWork.generated.cs b/MonkeyWrench.DataClasses/Database/DBRevisionWork.generated.cs index 084d5f5..5a19c37 100644 --- a/MonkeyWrench.DataClasses/Database/DBRevisionWork.generated.cs +++ b/MonkeyWrench.DataClasses/Database/DBRevisionWork.generated.cs @@ -29,6 +29,7 @@ public partial class DBRevisionWork : DBRecord private DateTime? _assignedtime; private DateTime? _startedtime; private DateTime? _endtime; + private int _priority; public int @lane_id { get { return _lane_id; } set { _lane_id = value; } } public int @host_id { get { return _host_id; } set { _host_id = value; } } @@ -41,6 +42,7 @@ public partial class DBRevisionWork : DBRecord public DateTime? @assignedtime { get { return _assignedtime; } set { _assignedtime = value; } } public DateTime? @startedtime { get { return _startedtime; } set { _startedtime = value; } } public DateTime? @endtime { get { return _endtime; } set { _endtime = value; } } + public int @priority { get { return _priority; } set { _priority = value; } } public override string Table @@ -53,7 +55,7 @@ public override string Table { get { - return new string [] { "lane_id", "host_id", "workhost_id", "revision_id", "state", "lock_expires", "completed", "createdtime", "assignedtime", "startedtime", "endtime" }; + return new string [] { "lane_id", "host_id", "workhost_id", "revision_id", "state", "lock_expires", "completed", "createdtime", "assignedtime", "startedtime", "endtime", "priority" }; } } diff --git a/MonkeyWrench.Database/DB.cs b/MonkeyWrench.Database/DB.cs index 86c4dc6..2f254da 100755 --- a/MonkeyWrench.Database/DB.cs +++ b/MonkeyWrench.Database/DB.cs @@ -1163,7 +1163,7 @@ INNER JOIN AND RevisionWork.lane_id = @lane_id AND RevisionWork.state <> @dependencynotfulfilled AND RevisionWork.state <> 10 AND RevisionWork.State <> @ignore AND RevisionWork.completed = false -ORDER BY RevisionWork.workhost_id IS NULL ASC, Revision.date DESC +ORDER BY RevisionWork.workhost_id IS NULL ASC, RevisionWork.priority DESC, Revision.date DESC LIMIT 1 ;"; DB.CreateParameter (cmd, "host_id", host.id); diff --git a/MonkeyWrench.Web.UI/EditHost.aspx b/MonkeyWrench.Web.UI/EditHost.aspx index 98b3b4d..bcc355a 100755 --- a/MonkeyWrench.Web.UI/EditHost.aspx +++ b/MonkeyWrench.Web.UI/EditHost.aspx @@ -47,6 +47,7 @@ + diff --git a/MonkeyWrench.Web.UI/EditHost.aspx.designer.cs b/MonkeyWrench.Web.UI/EditHost.aspx.designer.cs index 1b62e96..14bf229 100755 --- a/MonkeyWrench.Web.UI/EditHost.aspx.designer.cs +++ b/MonkeyWrench.Web.UI/EditHost.aspx.designer.cs @@ -1,194 +1,56 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:2.0.50727.3082 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - - - -public partial class EditHost { - - /// - /// tblData control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Table tblData; - - /// - /// txtID control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox txtID; - - /// - /// txtHost control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox txtHost; - - /// - /// txtPassword control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox txtPassword; - - /// - /// cmbQueueManagement control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.DropDownList cmbQueueManagement; - - /// - /// txtDescription control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox txtDescription; - - /// - /// txtArchitecture control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox txtArchitecture; - - /// - /// chkEnabled control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CheckBox chkEnabled; - - /// - /// cmdSave control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button cmdSave; - - /// - /// cmdDeleteAllWork control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button cmdDeleteAllWork; - - /// - /// cmdClearAllWork control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button cmdClearAllWork; - - /// - /// lblPasswordWarning control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Label lblPasswordWarning; - - /// - /// lblMessage control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Label lblMessage; - - /// - /// tblLanes control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Table tblLanes; - - /// - /// tblMasters control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Table tblMasters; - - /// - /// cmbMasterHosts control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.DropDownList cmbMasterHosts; - - /// - /// cmdAddMasterHost control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.LinkButton cmdAddMasterHost; - - /// - /// tblSlaves control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Table tblSlaves; - - /// - /// editorVariables control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::MonkeyWrench.Web.UI.EnvironmentVariablesEditor editorVariables; - - /// - /// lblConfiguration control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlGenericControl lblConfiguration; -} +// ------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Mono Runtime Version: 4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ + + + +public partial class EditHost { + + protected System.Web.UI.WebControls.Content Content2; + + protected System.Web.UI.WebControls.Table tblData; + + protected System.Web.UI.WebControls.TextBox txtID; + + protected System.Web.UI.WebControls.TextBox txtHost; + + protected System.Web.UI.WebControls.TextBox txtPassword; + + protected System.Web.UI.WebControls.DropDownList cmbQueueManagement; + + protected System.Web.UI.WebControls.TextBox txtDescription; + + protected System.Web.UI.WebControls.TextBox txtArchitecture; + + protected System.Web.UI.WebControls.CheckBox chkEnabled; + + protected System.Web.UI.WebControls.Button cmdSave; + + protected System.Web.UI.WebControls.Button cmdDeleteAllWork; + + protected System.Web.UI.WebControls.Button cmdClearAllWork; + + protected System.Web.UI.WebControls.Label lblPasswordWarning; + + protected System.Web.UI.WebControls.Label lblMessage; + + protected System.Web.UI.WebControls.Table tblLanes; + + protected System.Web.UI.WebControls.Table tblMasters; + + protected System.Web.UI.WebControls.DropDownList cmbMasterHosts; + + protected System.Web.UI.WebControls.LinkButton cmdAddMasterHost; + + protected System.Web.UI.WebControls.Table tblSlaves; + + protected MonkeyWrench.Web.UI.EnvironmentVariablesEditor editorVariables; + + protected System.Web.UI.HtmlControls.HtmlGenericControl lblConfiguration; +} diff --git a/MonkeyWrench.Web.UI/EditLane.aspx b/MonkeyWrench.Web.UI/EditLane.aspx index 9fa9efa..f210c33 100755 --- a/MonkeyWrench.Web.UI/EditLane.aspx +++ b/MonkeyWrench.Web.UI/EditLane.aspx @@ -57,6 +57,17 @@ Comma-separated list of additional roles (besides admin) that can edit this lane. + + Priority: + + + + + + + + Default priority of all builds in the lane. + Parent lane: diff --git a/MonkeyWrench.Web.UI/EditLane.aspx.cs b/MonkeyWrench.Web.UI/EditLane.aspx.cs index 41d08cb..d860c2a 100755 --- a/MonkeyWrench.Web.UI/EditLane.aspx.cs +++ b/MonkeyWrench.Web.UI/EditLane.aspx.cs @@ -90,6 +90,8 @@ protected override void OnInit (EventArgs e) } } + lstPriority.SelectedIndex = Int32.Parse (lstPriority.Items.FindByValue (response.Lane.priority.ToString ()).Value); + if (!IsPostBack) { for (int i = 0; i < cmbSourceControl.Items.Count; i++) { cmbSourceControl.Items [i].Selected = lane.source_control == cmbSourceControl.Items [i].Text; @@ -701,6 +703,7 @@ protected void cmdSave_Click (object sender, EventArgs e) lane.traverse_merge = chkTraverseMerges.Checked; lane.enabled = chkEnabled.Checked; lane.additional_roles = txtRoles.Text; + lane.priority = Int32.Parse (lstPriority.SelectedItem.Value); Utils.LocalWebService.EditLaneWithTags (Master.WebServiceLogin, lane, !string.IsNullOrEmpty (txtTags.Text) ? txtTags.Text.Split (',') : null); RedirectToSelf (); diff --git a/MonkeyWrench.Web.UI/EditLane.aspx.designer.cs b/MonkeyWrench.Web.UI/EditLane.aspx.designer.cs index 1f0872f..58c703e 100644 --- a/MonkeyWrench.Web.UI/EditLane.aspx.designer.cs +++ b/MonkeyWrench.Web.UI/EditLane.aspx.designer.cs @@ -33,6 +33,8 @@ public partial class EditLane { protected System.Web.UI.WebControls.TextBox txtTags; protected System.Web.UI.WebControls.TextBox txtRoles; + + protected System.Web.UI.WebControls.DropDownList lstPriority; protected System.Web.UI.WebControls.DropDownList lstParentLane; diff --git a/MonkeyWrench.Web.WebService/Scheduler/Scheduler.cs b/MonkeyWrench.Web.WebService/Scheduler/Scheduler.cs index e943234..ca8397e 100755 --- a/MonkeyWrench.Web.WebService/Scheduler/Scheduler.cs +++ b/MonkeyWrench.Web.WebService/Scheduler/Scheduler.cs @@ -193,8 +193,8 @@ public static bool AddRevisionWork (DB db, List lanes, List try { using (var cmd = db.CreateCommand (@" - INSERT INTO RevisionWork (lane_id, host_id, revision_id, state) - SELECT Lane.id, Host.id, Revision.id, 10 + INSERT INTO RevisionWork (lane_id, host_id, revision_id, priority, state) + SELECT Lane.id, Host.id, Revision.id, Lane.priority, 10 FROM HostLane INNER JOIN Host ON HostLane.host_id = Host.id INNER JOIN Lane ON HostLane.lane_id = Lane.id @@ -266,8 +266,8 @@ public static bool AddRevisionWorkSlow (DB db, List lanes, List 1) { + int lane_index = -1; + int highest_priority = 0; + int p = 0; + DateTime latest_date = DateTime.MaxValue; + DateTime ld = DateTime.MaxValue; + + // we need to find the latest revisionwork each hostlane has completed. + // we want to work on the hostlane which has waited the longest amount + // of time without getting work done (but which has pending work to do). + + for (int i = 0; i < hostlanes.Count; i++) { + DBHostLane hl = hostlanes [i]; + // check if this hostlane has pending work and get its priority. + // this would ideally be included in the query below, but I'm not sure + // how to do that while still distinguising the case where nothing has + // been done ever for a hostlane. + using (IDbCommand cmd = db.CreateCommand ()) { + cmd.CommandText = @" +SELECT RevisionWork.priority +FROM RevisionWork +WHERE + RevisionWork.host_id = @host_id +AND (RevisionWork.workhost_id = @workhost_id OR RevisionWork.workhost_id IS NULL) +AND RevisionWork.completed = false +AND RevisionWork.state <> 9 AND RevisionWork.state <> 10 AND RevisionWork.state <> 11 +AND lane_id = @lane_id +AND RevisionWork.priority >= @priority +ORDER BY RevisionWork.priority DESC +LIMIT 1; + "; + DB.CreateParameter (cmd, "lane_id", hl.lane_id); + DB.CreateParameter (cmd, "host_id", hl.host_id); + DB.CreateParameter (cmd, "workhost_id", response.Host.id); + DB.CreateParameter (cmd, "priority", highest_priority); + + object obj = cmd.ExecuteScalar (); + if (obj == DBNull.Value || obj == null) { + // there is nothing to do for this hostlane + continue; + } else { + p = (int)obj; + } + } + + // find the latest completed (this may not be correct, maybe find the latest unstarted?) + // revisionwork for this hostlane. + using (IDbCommand cmd = db.CreateCommand ()) { + cmd.CommandText = @" +SELECT RevisionWork.endtime +FROM RevisionWork +WHERE + RevisionWork.host_id = @host_id +AND (RevisionWork.workhost_id = @workhost_id OR RevisionWork.workhost_id IS NULL) +AND RevisionWork.completed = true +AND lane_id = @lane_id +ORDER BY RevisionWork.endtime DESC +LIMIT 1; + "; + + DB.CreateParameter (cmd, "lane_id", hl.lane_id); + DB.CreateParameter (cmd, "host_id", hl.host_id); + DB.CreateParameter (cmd, "workhost_id", response.Host.id); + + object obj = cmd.ExecuteScalar (); + + if (obj is DateTime) { + ld = (DateTime) obj; + } else { + // Nothing has been done for this lane + ld = DateTime.MinValue; + } + + if (p > highest_priority || (p == highest_priority && ld < latest_date)) { + highest_priority = p; + latest_date = ld; + lane_index = i; + } + } + } + + if (lane_index >= 0) { + DBHostLane tmp = hostlanes [lane_index]; + hostlanes.Clear (); + hostlanes.Add (tmp); + } else { + hostlanes.Clear (); // there is nothing to do at all + } + } + break; } foreach (DBHostLane hl in hostlanes) { diff --git a/scripts/database.sql b/scripts/database.sql index 9b5761a..ade4cdb 100644 --- a/scripts/database.sql +++ b/scripts/database.sql @@ -78,7 +78,12 @@ CREATE TABLE Lane ( traverse_merge boolean NOT NULL DEFAULT FALSE, -- if commits from a merge (besides the merge commit itself) should be included. enabled boolean NOT NULL DEFAULT TRUE, -- if a lane is enabled or not. changed_date timestamp NULL DEFAULT NULL, -- the latest date something happened in this lane, - additional_roles text NULL DEFAULT NULL, -- additional roles for access (for users who are not admin) + additional_roles text NULL DEFAULT NULL, -- additional roles for access (for users who are not admin) + priority int NOT NULL DEFAULT 1 CHECK (priority >= 0), -- default priority for new revisionworks + -- Possible values: + -- * 0: PR lanes, least priority + -- * 1: Unremarkable lanes + -- * 2: Release lanes, highest priority UNIQUE (lane) ); INSERT INTO Lane (lane, source_control, repository) VALUES ('monkeywrench', 'git', 'git://github.com/mono/monkeywrench'); @@ -86,6 +91,7 @@ INSERT INTO Lane (lane, source_control, repository) VALUES ('monkeywrench', 'git -- ALTER TABLE Lane ADD COLUMN enabled boolean NOT NULL DEFAULT TRUE; -- ALTER TABLE Lane ADD COLUMN changed_date timestamp NULL DEFAULT NULL; -- ALTER TABLE Lane ADD CONSTRAINT parentlane_fkey FOREIGN KEY (parent_lane_id) REFERENCES Lane (id); +-- ALTER TABLE Lane ADD COLUMN priority integer DEFAULT 1 CHECK (priority >= 0), ALTER COLUMN priority SET NOT NULL; -- Command to set the latest changed_date on every lane. -- UPDATE Lane SET changed_date = (SELECT MAX(endtime) FROM RevisionWork WHERE RevisionWork.lane_id = Lane.id); @@ -254,6 +260,7 @@ CREATE TABLE RevisionWork ( assignedtime timestamptz NULL, -- Time that workhost_id was assigned startedtime timestamptz NULL, -- Time that the first work was created, denormalized from `MIN(work.starttime) WHERE work.revisionwork_id = id` endtime timestamptz NULL, -- Time that the RevisionWork was completed + priority int NOT NULL DEFAULT 1 CHECK (priority >= 0), -- alter table revisionwork add column endtime timestamp NOT NULL DEFAULT '2000-01-01 00:00:00+0'; @@ -269,6 +276,7 @@ CREATE TABLE RevisionWork ( -- WHERE endtime != '2000-01-01 00:00:00+0'::timestamp; -- ALTER TABLE revisionwork DROP COLUMN endtime; -- ALTER TABLE revisionwork RENAME COLUMN end_time_temp TO endtime; + UNIQUE (lane_id, host_id, revision_id) ); @@ -299,6 +307,7 @@ CREATE INDEX RevisionWork_state_idx ON RevisionWork (state); -- alter table RevisionWork drop constraint revisionwork_workhost_id_fkey; -- alter table RevisionWork add constraint revisionwork_workhost_id_fkey foreign key (workhost_id) references host (id) on delete cascade; -- alter table RevisionWork drop constraint revisionwork_workhost_id_fkey2; +-- ALTER TABLE revisionwork ADD COLUMN priority integer DEFAULT 1 CHECK (priority >= 0), ALTER COLUMN priority SET NOT NULL; CREATE TABLE Work ( id serial PRIMARY KEY,