Repository: armadaproject/armada
Pull request #3533 (2024-04-24 15:42:07)

[Lookout] Set run states correctly when jobs cancelled/preempted
Currently job run states don't always end up in the correct state when jobs are cancelled/preempted

When jobs are cancelled - runs were left in whatever state they were in (typically Running)
 - Now they will correct transition to a new job run state Cancelled

When jobs are preempted - runs were either left in Preempted or Failed
 - Now runs should consistently go to Preempted



Patch:
diff --git a/internal/common/database/lookout/jobstates.go b/internal/common/database/lookout/jobstates.go
index b3df6438..3c97e00c 100644
--- a/internal/common/database/lookout/jobstates.go
+++ b/internal/common/database/lookout/jobstates.go
@@ -36,6 +36,7 @@ const (
 	JobRunLeaseExpired     JobRunState = "RUN_LEASE_EXPIRED"
 	JobRunMaxRunsExceeded  JobRunState = "RUN_MAX_RUNS_EXCEEDED"
 	JobRunLeased           JobRunState = "RUN_LEASED"
+	JobRunCancelled        JobRunState = "RUN_CANCELLED"
 
 	JobRunPendingOrdinal          = 1
 	JobRunRunningOrdinal          = 2
@@ -48,6 +49,7 @@ const (
 	JobRunLeaseExpiredOrdinal     = 9
 	JobRunMaxRunsExceededOrdinal  = 10
 	JobRunLeasedOrdinal           = 11
+	JobRunCancelledOrdinal        = 12
 )
 
 var (
@@ -82,6 +84,7 @@ var (
 		JobRunRunningOrdinal:          JobRunRunning,
 		JobRunSucceededOrdinal:        JobRunSucceeded,
 		JobRunFailedOrdinal:           JobRunFailed,
+		JobRunCancelledOrdinal:        JobRunCancelled,
 		JobRunTerminatedOrdinal:       JobRunTerminated,
 		JobRunPreemptedOrdinal:        JobRunPreempted,
 		JobRunUnableToScheduleOrdinal: JobRunUnableToSchedule,
diff --git a/internal/lookout/ui/src/models/lookoutV2Models.ts b/internal/lookout/ui/src/models/lookoutV2Models.ts
index d402f44e..3be082be 100644
--- a/internal/lookout/ui/src/models/lookoutV2Models.ts
+++ b/internal/lookout/ui/src/models/lookoutV2Models.ts
@@ -36,6 +36,7 @@ export enum JobRunState {
   RunLeaseExpired = "RUN_LEASE_EXPIRED",
   RunMaxRunsExceeded = "RUN_MAX_RUNS_EXCEEDED",
   RunLeased = "RUN_LEASED",
+  RunCancelled = "RUN_CANCELLED",
 }
 
 export const jobRunStateDisplayInfo: Record<JobRunState, { displayName: string }> = {
@@ -50,6 +51,7 @@ export const jobRunStateDisplayInfo: Record<JobRunState, { displayName: string }
   [JobRunState.RunLeaseExpired]: { displayName: "Lease Expired" },
   [JobRunState.RunMaxRunsExceeded]: { displayName: "Max Runs Exceeded" },
   [JobRunState.RunLeased]: { displayName: "Leased" },
+  [JobRunState.RunCancelled]: { displayName: "Cancelled" },
 }
 
 type ColoredState = {
diff --git a/internal/lookoutingesterv2/instructions/instructions.go b/internal/lookoutingesterv2/instructions/instructions.go
index 5c031a58..a08fa839 100644
--- a/internal/lookoutingesterv2/instructions/instructions.go
+++ b/internal/lookoutingesterv2/instructions/instructions.go
@@ -1,7 +1,6 @@
 package instructions
 
 import (
-	"fmt"
 	"sort"
 	"strings"
 	"time"
@@ -102,6 +101,8 @@ func (c *InstructionConverter) convertSequence(
 			err = c.handleJobRunAssigned(ts, event.GetJobRunAssigned(), update)
 		case *armadaevents.EventSequence_Event_JobRunRunning:
 			err = c.handleJobRunRunning(ts, event.GetJobRunRunning(), update)
+		case *armadaevents.EventSequence_Event_JobRunCancelled:
+			err = c.handleJobRunCancelled(ts, event.GetJobRunCancelled(), update)
 		case *armadaevents.EventSequence_Event_JobRunSucceeded:
 			err = c.handleJobRunSucceeded(ts, event.GetJobRunSucceeded(), update)
 		case *armadaevents.EventSequence_Event_JobRunErrors:
@@ -324,24 +325,29 @@ func (c *InstructionConverter) handleJobErrors(ts time.Time, event *armadaevents
 		return err
 	}
 
-	isTerminal := false
-
 	for _, e := range event.GetErrors() {
-		if e.Terminal {
-			isTerminal = true
-			break
+		if !e.Terminal {
+			continue
+		}
+
+		state := lookout.JobFailedOrdinal
+		switch e.Reason.(type) {
+		// We should have a JobPreempted event rather than relying on type of JobErrors
+		// For now this is how we can identify if the job was preempted or failed
+		case *armadaevents.Error_JobRunPreemptedError:
+			state = lookout.JobPreemptedOrdinal
 		}
-	}
 
-	if isTerminal {
 		jobUpdate := model.UpdateJobInstruction{
 			JobId:                     jobId,
-			State:                     pointer.Int32(int32(lookout.JobFailedOrdinal)),
+			State:                     pointer.Int32(int32(state)),
 			LastTransitionTime:        &ts,
 			LastTransitionTimeSeconds: pointer.Int64(ts.Unix()),
 		}
 		update.JobsToUpdate = append(update.JobsToUpdate, &jobUpdate)
+		break
 	}
+
 	return nil
 }
 
@@ -466,6 +472,22 @@ func (c *InstructionConverter) handleJobRunAssigned(ts time.Time, event *armadae
 	return nil
 }
 
+func (c *InstructionConverter) handleJobRunCancelled(ts time.Time, event *armadaevents.JobRunCancelled, update *model.InstructionSet) error {
+	runId, err := armadaevents.UuidStringFromProtoUuid(event.RunId)
+	if err != nil {
+		c.metrics.RecordPulsarMessageError(metrics.PulsarMessageErrorProcessing)
+		return errors.WithStack(err)
+	}
+
+	jobRun := model.UpdateJobRunInstruction{
+		RunId:       runId,
+		Finished:    &ts,
+		JobRunState: pointer.Int32(lookout.JobRunCancelledOrdinal),
+	}
+	update.JobRunsToUpdate = append(update.JobRunsToUpdate, &jobRun)
+	return nil
+}
+
 func (c *InstructionConverter) handleJobRunSucceeded(ts time.Time, event *armadaevents.JobRunSucceeded, update *model.InstructionSet) error {
 	runId, err := armadaevents.UuidStringFromProtoUuid(event.RunId)
 	if err != nil {
@@ -519,6 +541,10 @@ func (c *InstructionConverter) handleJobRunErrors(ts time.Time, event *armadaeve
 			jobRunUpdate.ExitCode = pointer.Int32(exitCode)
 		case *armadaevents.Error_PodTerminated:
 			continue
+		case *armadaevents.Error_JobRunPreemptedError:
+			// This case is already handled by the JobRunPreempted event
+			// When we formalise that as a terminal event, we'll remove this JobRunError getting produced
+			continue
 		case *armadaevents.Error_PodUnschedulable:
 			jobRunUpdate.Node = extractNodeName(reason.PodUnschedulable)
 		case *armadaevents.Error_PodLeaseReturned:
@@ -551,51 +577,16 @@ func (c *InstructionConverter) handleJobRunPreempted(ts time.Time, event *armada
 		return err
 	}
 
-	// Update Job
-	job := model.UpdateJobInstruction{
-		JobId:                     jobId,
-		State:                     pointer.Int32(int32(lookout.JobPreemptedOrdinal)),
-		LastTransitionTime:        &ts,
-		LastTransitionTimeSeconds: pointer.Int64(ts.Unix()),
-		LatestRunId:               &runId,
-	}
-
-	update.JobsToUpdate = append(update.JobsToUpdate, &job)
-
-	// Update job run
-	errorString := "preempted by non armada pod"
-	preemptiveJobId, err := parseUlidString(event.PreemptiveJobId)
-	if err != nil {
-		log.WithError(err).Debug("failed to convert preemptive job id")
-	} else {
-		errorString = fmt.Sprintf("preempted by job %s", preemptiveJobId)
-	}
-
 	jobRun := model.UpdateJobRunInstruction{
 		RunId:       runId,
 		JobRunState: pointer.Int32(lookout.JobRunPreemptedOrdinal),
 		Finished:    &ts,
-		Error:       tryCompressError(jobId, errorString, c.compressor),
+		Error:       tryCompressError(jobId, "preempted", c.compressor),
 	}
 	update.JobRunsToUpdate = append(update.JobRunsToUpdate, &jobRun)
 	return nil
 }
 
-func parseUlidString(id *armadaevents.Uuid) (string, error) {
-	if id == nil {
-		return "", errors.New("uuid is nil")
-	}
-	// Likely wrong if it is zeroed
-	if id.High64 == 0 && id.Low64 == 0 {
-		return "", errors.New("")
-	}
-	stringId, err := armadaevents.UlidStringFromProtoUuid(id)
-	if err != nil {
-		return "", errors.Wrap(err, "could not convert non-nil preemptive job id")
-	}
-	return stringId, nil
-}
-
 func tryCompressError(jobId string, errorString string, compressor compress.Compressor) []byte {
 	compressedError, err := compressor.Compress([]byte(errorString))
 	if err != nil {
diff --git a/internal/lookoutv2/gen/models/job.go b/internal/lookoutv2/gen/models/job.go
index fbab4765..deeb01e1 100644
--- a/internal/lookoutv2/gen/models/job.go
+++ b/internal/lookoutv2/gen/models/job.go
@@ -32,6 +32,10 @@ type Job struct {
 	// Format: date-time
 	Cancelled *strfmt.DateTime `json:"cancelled,omitempty"`
 
+	// cluster
+	// Required: true
+	Cluster string `json:"cluster"`
+
 	// cpu
 	// Required: true
 	CPU int64 `json:"cpu"`
@@ -44,6 +48,9 @@ type Job struct {
 	// Required: true
 	EphemeralStorage int64 `json:"ephemeralStorage"`
 
+	// exit code
+	ExitCode *int32 `json:"exitCode,omitempty"`
+
 	// gpu
 	// Required: true
 	Gpu int64 `json:"gpu"`
@@ -74,6 +81,9 @@ type Job struct {
 	// namespace
 	Namespace *string `json:"namespace,omitempty"`
 
+	// node
+	Node *string `json:"node,omitempty"`
+
 	// owner
 	// Required: true
 	// Min Length: 1
@@ -105,18 +115,6 @@ type Job struct {
 	// Min Length: 1
 	// Format: date-time
 	Submitted strfmt.DateTime `json:"submitted"`
-
-	// node
-	// Required: false
-	Node *string `json:"node,omitempty"`
-
-	// cluster
-	// Required: true
-	Cluster string `json:"cluster"`
-
-	//exitCode
-	// Required: false
-	ExitCode *int32 `json:"exitCode,omitempty"`
 }
 
 // Validate validates this job
@@ -131,6 +129,10 @@ func (m *Job) Validate(formats strfmt.Registry) error {
 		res = append(res, err)
 	}
 
+	if err := m.validateCluster(formats); err != nil {
+		res = append(res, err)
+	}
+
 	if err := m.validateCPU(formats); err != nil {
 		res = append(res, err)
 	}
@@ -214,6 +216,15 @@ func (m *Job) validateCancelled(formats strfmt.Registry) error {
 	return nil
 }
 
+func (m *Job) validateCluster(formats strfmt.Registry) error {
+
+	if err := validate.RequiredString("cluster", "body", m.Cluster); err != nil {
+		return err
+	}
+
+	return nil
+}
+
 func (m *Job) validateCPU(formats strfmt.Registry) error {
 
 	if err := validate.Required("cpu", "body", int64(m.CPU)); err != nil {
diff --git a/internal/lookoutv2/gen/models/run.go b/internal/lookoutv2/gen/models/run.go
index c821fb28..c2e46a61 100644
--- a/internal/lookoutv2/gen/models/run.go
+++ b/internal/lookoutv2/gen/models/run.go
@@ -34,7 +34,7 @@ type Run struct {
 
 	// job run state
 	// Required: true
-	// Enum: [RUN_PENDING RUN_RUNNING RUN_SUCCEEDED RUN_FAILED RUN_TERMINATED RUN_PREEMPTED RUN_UNABLE_TO_SCHEDULE RUN_LEASE_RETURNED RUN_LEASE_EXPIRED RUN_MAX_RUNS_EXCEEDED RUN_LEASED]
+	// Enum: [RUN_PENDING RUN_RUNNING RUN_SUCCEEDED RUN_FAILED RUN_TERMINATED RUN_PREEMPTED RUN_UNABLE_TO_SCHEDULE RUN_LEASE_RETURNED RUN_LEASE_EXPIRED RUN_MAX_RUNS_EXCEEDED RUN_LEASED RUN_CANCELLED]
 	JobRunState string `json:"jobRunState"`
 
 	// leased
@@ -127,7 +127,7 @@ var runTypeJobRunStatePropEnum []interface{}
 
 func init() {
 	var res []string
-	if err := json.Unmarshal([]byte(`["RUN_PENDING","RUN_RUNNING","RUN_SUCCEEDED","RUN_FAILED","RUN_TERMINATED","RUN_PREEMPTED","RUN_UNABLE_TO_SCHEDULE","RUN_LEASE_RETURNED","RUN_LEASE_EXPIRED","RUN_MAX_RUNS_EXCEEDED","RUN_LEASED"]`), &res); err != nil {
+	if err := json.Unmarshal([]byte(`["RUN_PENDING","RUN_RUNNING","RUN_SUCCEEDED","RUN_FAILED","RUN_TERMINATED","RUN_PREEMPTED","RUN_UNABLE_TO_SCHEDULE","RUN_LEASE_RETURNED","RUN_LEASE_EXPIRED","RUN_MAX_RUNS_EXCEEDED","RUN_LEASED","RUN_CANCELLED"]`), &res); err != nil {
 		panic(err)
 	}
 	for _, v := range res {
@@ -169,6 +169,9 @@ const (
 
 	// RunJobRunStateRUNLEASED captures enum value "RUN_LEASED"
 	RunJobRunStateRUNLEASED string = "RUN_LEASED"
+
+	// RunJobRunStateRUNCANCELLED captures enum value "RUN_CANCELLED"
+	RunJobRunStateRUNCANCELLED string = "RUN_CANCELLED"
 )
 
 // prop value enum
diff --git a/internal/lookoutv2/gen/restapi/embedded_spec.go b/internal/lookoutv2/gen/restapi/embedded_spec.go
index eb940a1a..254f7c4d 100644
--- a/internal/lookoutv2/gen/restapi/embedded_spec.go
+++ b/internal/lookoutv2/gen/restapi/embedded_spec.go
@@ -456,7 +456,8 @@ func init() {
         "lastTransitionTime",
         "duplicate",
         "annotations",
-        "runs"
+        "runs",
+        "cluster"
       ],
       "properties": {
         "annotations": {
@@ -475,6 +476,10 @@ func init() {
           "format": "date-time",
           "x-nullable": true
         },
+        "cluster": {
+          "type": "string",
+          "x-nullable": false
+        },
         "cpu": {
           "type": "integer",
           "format": "int64",
@@ -489,6 +494,11 @@ func init() {
           "format": "int64",
           "x-nullable": false
         },
+        "exitCode": {
+          "type": "integer",
+          "format": "int32",
+          "x-nullable": true
+        },
         "gpu": {
           "type": "integer",
           "format": "int64",
@@ -523,6 +533,10 @@ func init() {
           "type": "string",
           "x-nullable": true
         },
+        "node": {
+          "type": "string",
+          "x-nullable": true
+        },
         "owner": {
           "type": "string",
           "minLength": 1,
@@ -629,7 +643,8 @@ func init() {
             "RUN_LEASE_RETURNED",
             "RUN_LEASE_EXPIRED",
             "RUN_MAX_RUNS_EXCEEDED",
-            "RUN_LEASED"
+            "RUN_LEASED",
+            "RUN_CANCELLED"
           ],
           "x-nullable": false
         },
@@ -1142,7 +1157,8 @@ func init() {
         "lastTransitionTime",
         "duplicate",
         "annotations",
-        "runs"
+        "runs",
+        "cluster"
       ],
       "properties": {
         "annotations": {
@@ -1161,6 +1177,10 @@ func init() {
           "format": "date-time",
           "x-nullable": true
         },
+        "cluster": {
+          "type": "string",
+          "x-nullable": false
+        },
         "cpu": {
           "type": "integer",
           "format": "int64",
@@ -1175,6 +1195,11 @@ func init() {
           "format": "int64",
           "x-nullable": false
         },
+        "exitCode": {
+          "type": "integer",
+          "format": "int32",
+          "x-nullable": true
+        },
         "gpu": {
           "type": "integer",
           "format": "int64",
@@ -1209,6 +1234,10 @@ func init() {
           "type": "string",
           "x-nullable": true
         },
+        "node": {
+          "type": "string",
+          "x-nullable": true
+        },
         "owner": {
           "type": "string",
           "minLength": 1,
@@ -1315,7 +1344,8 @@ func init() {
             "RUN_LEASE_RETURNED",
             "RUN_LEASE_EXPIRED",
             "RUN_MAX_RUNS_EXCEEDED",
-            "RUN_LEASED"
+            "RUN_LEASED",
+            "RUN_CANCELLED"
           ],
           "x-nullable": false
         },
diff --git a/internal/lookoutv2/repository/util.go b/internal/lookoutv2/repository/util.go
index 37f7d3c5..1bc83078 100644
--- a/internal/lookoutv2/repository/util.go
+++ b/internal/lookoutv2/repository/util.go
@@ -457,23 +457,34 @@ func (js *JobSimulator) Failed(node string, exitCode int32, message string, time
 
 func (js *JobSimulator) Preempted(timestamp time.Time) *JobSimulator {
 	ts := timestampOrNow(timestamp)
-	jobIdProto, err := armadaevents.ProtoUuidFromUlidString(util.NewULID())
-	if err != nil {
-		log.WithError(err).Errorf("Could not convert job ID to UUID: %s", util.NewULID())
+
+	preemptedJob := &armadaevents.EventSequence_Event{
+		Created: &ts,
+		Event: &armadaevents.EventSequence_Event_JobErrors{
+			JobErrors: &armadaevents.JobErrors{
+				JobId: js.jobId,
+				Errors: []*armadaevents.Error{
+					{
+						Terminal: true,
+						Reason: &armadaevents.Error_JobRunPreemptedError{
+							JobRunPreemptedError: &armadaevents.JobRunPreemptedError{},
+						},
+					},
+				},
+			},
+		},
 	}
 
-	preempted := &armadaevents.EventSequence_Event{
+	preemptedRun := &armadaevents.EventSequence_Event{
 		Created: &ts,
 		Event: &armadaevents.EventSequence_Event_JobRunPreempted{
 			JobRunPreempted: &armadaevents.JobRunPreempted{
-				PreemptedJobId:  js.jobId,
-				PreemptiveJobId: jobIdProto,
-				PreemptedRunId:  armadaevents.ProtoUuidFromUuid(uuid.MustParse(uuid.NewString())),
-				PreemptiveRunId: armadaevents.ProtoUuidFromUuid(uuid.MustParse(uuid.NewString())),
+				PreemptedJobId: js.jobId,
+				PreemptedRunId: armadaevents.ProtoUuidFromUuid(uuid.MustParse(uuid.NewString())),
 			},
 		},
 	}
-	js.events = append(js.events, preempted)
+	js.events = append(js.events, preemptedJob, preemptedRun)
 
 	js.job.LastTransitionTime = ts
 	js.job.State = string(lookout.JobPreempted)
diff --git a/internal/lookoutv2/swagger.yaml b/internal/lookoutv2/swagger.yaml
index 39024f57..17c3da56 100644
--- a/internal/lookoutv2/swagger.yaml
+++ b/internal/lookoutv2/swagger.yaml
@@ -25,6 +25,7 @@ definitions:
       - duplicate
       - annotations
       - runs
+      - cluster
     properties:
       jobId:
         type: string
@@ -113,6 +114,16 @@ definitions:
       cancelReason:
         type: string
         x-nullable: true
+      node:
+        type: string
+        x-nullable: true
+      cluster:
+        type: string
+        x-nullable: false
+      exitCode:
+        type: integer
+        format: int32
+        x-nullable: true
   run:
     type: object
     required:
@@ -163,6 +174,7 @@ definitions:
           - RUN_LEASE_EXPIRED
           - RUN_MAX_RUNS_EXCEEDED
           - RUN_LEASED
+          - RUN_CANCELLED
         x-nullable: false
       exitCode:
         type: integer
diff --git a/internal/scheduler/scheduler.go b/internal/scheduler/scheduler.go
index 36f0e693..c673d24a 100644
--- a/internal/scheduler/scheduler.go
+++ b/internal/scheduler/scheduler.go
@@ -703,8 +703,19 @@ func (s *Scheduler) generateUpdateMessagesFromJob(job *jobdb.Job, jobRunErrors m
 
 	// Has the job been requested cancelled. If so, cancel the job
 	if job.CancelRequested() {
-		for _, run := range job.AllRuns() {
-			job = job.WithUpdatedRun(run.WithRunning(false).WithoutTerminal().WithCancelled(true))
+		if job.HasRuns() {
+			lastRun := job.LatestRun()
+			job = job.WithUpdatedRun(lastRun.WithoutTerminal().WithCancelled(true))
+
+			events = append(events, &armadaevents.EventSequence_Event{
+				Created: s.now(),
+				Event: &armadaevents.EventSequence_Event_JobRunCancelled{
+					JobRunCancelled: &armadaevents.JobRunCancelled{
+						RunId: armadaevents.ProtoUuidFromUuid(lastRun.Id()),
+						JobId: jobId,
+					},
+				},
+			})
 		}
 		job = job.WithQueued(false).WithoutTerminal().WithCancelled(true)
 		cancel := &armadaevents.EventSequence_Event{
@@ -715,9 +726,6 @@ func (s *Scheduler) generateUpdateMessagesFromJob(job *jobdb.Job, jobRunErrors m
 		}
 		events = append(events, cancel)
 	} else if job.CancelByJobsetRequested() {
-		for _, run := range job.AllRuns() {
-			job = job.WithUpdatedRun(run.WithRunning(false).WithoutTerminal().WithCancelled(true))
-		}
 		job = job.WithQueued(false).WithoutTerminal().WithCancelled(true)
 		cancelRequest := &armadaevents.EventSequence_Event{
 			Created: s.now(),
@@ -725,13 +733,30 @@ func (s *Scheduler) generateUpdateMessagesFromJob(job *jobdb.Job, jobRunErrors m
 				CancelJob: &armadaevents.CancelJob{JobId: jobId},
 			},
 		}
+		events = append(events, cancelRequest)
+
+		if job.HasRuns() {
+			lastRun := job.LatestRun()
+			job = job.WithUpdatedRun(lastRun.WithoutTerminal().WithCancelled(true))
+
+			events = append(events, &armadaevents.EventSequence_Event{
+				Created: s.now(),
+				Event: &armadaevents.EventSequence_Event_JobRunCancelled{
+					JobRunCancelled: &armadaevents.JobRunCancelled{
+						RunId: armadaevents.ProtoUuidFromUuid(lastRun.Id()),
+						JobId: jobId,
+					},
+				},
+			})
+		}
+
 		cancel := &armadaevents.EventSequence_Event{
 			Created: s.now(),
 			Event: &armadaevents.EventSequence_Event_CancelledJob{
 				CancelledJob: &armadaevents.CancelledJob{JobId: jobId},
 			},
 		}
-		events = append(events, cancelRequest, cancel)
+		events = append(events, cancel)
 	} else if job.HasRuns() {
 		lastRun := job.LatestRun()
 		// InTerminalState states. Can only have one of these
diff --git a/pkg/armadaevents/events.pb.go b/pkg/armadaevents/events.pb.go
index b30c811f..948774a1 100644
--- a/pkg/armadaevents/events.pb.go
+++ b/pkg/armadaevents/events.pb.go
@@ -205,6 +205,7 @@ type EventSequence_Event struct {
 	//	*EventSequence_Event_JobRunPreemptionRequested
 	//	*EventSequence_Event_JobRequeued
 	//	*EventSequence_Event_JobPreemptionRequested
+	//	*EventSequence_Event_JobRunCancelled
 	Event isEventSequence_Event_Event `protobuf_oneof:"event"`
 }
 
@@ -313,6 +314,9 @@ type EventSequence_Event_JobRequeued struct {
 type EventSequence_Event_JobPreemptionRequested struct {
 	JobPreemptionRequested *JobPreemptionRequested `protobuf:"bytes,23,opt,name=jobPreemptionRequested,proto3,oneof" json:"jobPreemptionRequested,omitempty"`
 }
+type EventSequence_Event_JobRunCancelled struct {
+	JobRunCancelled *JobRunCancelled `protobuf:"bytes,24,opt,name=jobRunCancelled,proto3,oneof" json:"jobRunCancelled,omitempty"`
+}
 
 func (*EventSequence_Event_SubmitJob) isEventSequence_Event_Event()                 {}
 func (*EventSequence_Event_ReprioritiseJob) isEventSequence_Event_Event()           {}
@@ -336,6 +340,7 @@ func (*EventSequence_Event_PartitionMarker) isEventSequence_Event_Event()
 func (*EventSequence_Event_JobRunPreemptionRequested) isEventSequence_Event_Event() {}
 func (*EventSequence_Event_JobRequeued) isEventSequence_Event_Event()               {}
 func (*EventSequence_Event_JobPreemptionRequested) isEventSequence_Event_Event()    {}
+func (*EventSequence_Event_JobRunCancelled) isEventSequence_Event_Event()           {}
 
 func (m *EventSequence_Event) GetEvent() isEventSequence_Event_Event {
 	if m != nil {
@@ -505,6 +510,13 @@ func (m *EventSequence_Event) GetJobPreemptionRequested() *JobPreemptionRequeste
 	return nil
 }
 
+func (m *EventSequence_Event) GetJobRunCancelled() *JobRunCancelled {
+	if x, ok := m.GetEvent().(*EventSequence_Event_JobRunCancelled); ok {
+		return x.JobRunCancelled
+	}
+	return nil
+}
+
 // XXX_OneofWrappers is for the internal use of the proto package.
 func (*EventSequence_Event) XXX_OneofWrappers() []interface{} {
 	return []interface{}{
@@ -530,6 +542,7 @@ func (*EventSequence_Event) XXX_OneofWrappers() []interface{} {
 		(*EventSequence_Event_JobRunPreemptionRequested)(nil),
 		(*EventSequence_Event_JobRequeued)(nil),
 		(*EventSequence_Event_JobPreemptionRequested)(nil),
+		(*EventSequence_Event_JobRunCancelled)(nil),
 	}
 }
 
@@ -3542,6 +3555,60 @@ func (m *JobPreemptionRequested) GetJobId() *Uuid {
 	return nil
 }
 
+// Generated by the scheduler when a job is cancelled, all active job runs are also cancelled
+// One such message is generated per job run that was cancelled.
+type JobRunCancelled struct {
+	RunId *Uuid `protobuf:"bytes,1,opt,name=run_id,json=runId,proto3" json:"runId,omitempty"`
+	JobId *Uuid `protobuf:"bytes,2,opt,name=job_id,json=jobId,proto3" json:"jobId,omitempty"`
+}
+
+func (m *JobRunCancelled) Reset()         { *m = JobRunCancelled{} }
+func (m *JobRunCancelled) String() string { return proto.CompactTextString(m) }
+func (*JobRunCancelled) ProtoMessage()    {}
+func (*JobRunCancelled) Descriptor() ([]byte, []int) {
+	return fileDescriptor_6aab92ca59e015f8, []int{44}
+}
+func (m *JobRunCancelled) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *JobRunCancelled) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_JobRunCancelled.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *JobRunCancelled) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_JobRunCancelled.Merge(m, src)
+}
+func (m *JobRunCancelled) XXX_Size() int {
+	return m.Size()
+}
+func (m *JobRunCancelled) XXX_DiscardUnknown() {
+	xxx_messageInfo_JobRunCancelled.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_JobRunCancelled proto.InternalMessageInfo
+
+func (m *JobRunCancelled) GetRunId() *Uuid {
+	if m != nil {
+		return m.RunId
+	}
+	return nil
+}
+
+func (m *JobRunCancelled) GetJobId() *Uuid {
+	if m != nil {
+		return m.JobId
+	}
+	return nil
+}
+
 func init() {
 	proto.RegisterEnum("armadaevents.JobState", JobState_name, JobState_value)
 	proto.RegisterEnum("armadaevents.KubernetesReason", KubernetesReason_name, KubernetesReason_value)
@@ -3596,241 +3663,244 @@ func init() {
 	proto.RegisterType((*PartitionMarker)(nil), "armadaevents.PartitionMarker")
 	proto.RegisterType((*JobRunPreemptionRequested)(nil), "armadaevents.JobRunPreemptionRequested")
 	proto.RegisterType((*JobPreemptionRequested)(nil), "armadaevents.JobPreemptionRequested")
+	proto.RegisterType((*JobRunCancelled)(nil), "armadaevents.JobRunCancelled")
 }
 
 func init() { proto.RegisterFile("pkg/armadaevents/events.proto", fileDescriptor_6aab92ca59e015f8) }
 
 var fileDescriptor_6aab92ca59e015f8 = []byte{
-	// 3660 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x5b, 0x4b, 0x6c, 0x1b, 0xd7,
-	0xd5, 0xf6, 0x90, 0x12, 0x1f, 0x87, 0x7a, 0xd0, 0xd7, 0x92, 0x4c, 0x2b, 0xb6, 0x28, 0x8f, 0xf3,
-	0xff, 0x71, 0x82, 0x84, 0x4a, 0x9c, 0x07, 0xf2, 0x28, 0x12, 0x88, 0xb6, 0x62, 0x5b, 0xb1, 0x6c,
-	0x85, 0xb2, 0x52, 0x37, 0x48, 0xc1, 0x0c, 0x39, 0x57, 0xd4, 0x58, 0xe4, 0x0c, 0x33, 0x0f, 0x59,
-	0x02, 0xb2, 0x68, 0x8a, 0x34, 0xdd, 0xa5, 0x06, 0x1a, 0xa0, 0x05, 0xba, 0x48, 0xb7, 0x0d, 0xd0,
-	0x75, 0xd7, 0x5d, 0x35, 0x8b, 0xa2, 0x48, 0x77, 0x5d, 0xb1, 0x45, 0x82, 0x6e, 0xb8, 0x28, 0xba,
-	0x6c, 0xbb, 0x69, 0x71, 0x5f, 0x33, 0xf7, 0xce, 0x0c, 0x6d, 0xf9, 0x55, 0xa7, 0xf0, 0x4a, 0x9a,
-	0xef, 0xbc, 0xee, 0xe3, 0xdc, 0x33, 0xe7, 0x9c, 0xb9, 0x84, 0x13, 0xfd, 0x9d, 0xce, 0x92, 0xe1,
-	0xf6, 0x0c, 0xd3, 0xc0, 0xbb, 0xd8, 0xf6, 0xbd, 0x25, 0xf6, 0xa7, 0xd6, 0x77, 0x1d, 0xdf, 0x41,
-	0x13, 0x32, 0x69, 0x5e, 0xdf, 0x79, 0xd9, 0xab, 0x59, 0xce, 0x92, 0xd1, 0xb7, 0x96, 0xda, 0x8e,
-	0x8b, 0x97, 0x76, 0x9f, 0x5b, 0xea, 0x60, 0x1b, 0xbb, 0x86, 0x8f, 0x4d, 0x26, 0x31, 0x7f, 0x5a,
-	0xe2, 0xb1, 0xb1, 0x7f, 0xc3, 0x71, 0x77, 0x2c, 0xbb, 0x93, 0xc6, 0x59, 0xed, 0x38, 0x4e, 0xa7,
-	0x8b, 0x97, 0xe8, 0x53, 0x2b, 0xd8, 0x5a, 0xf2, 0xad, 0x1e, 0xf6, 0x7c, 0xa3, 0xd7, 0xe7, 0x0c,
-	0x2f, 0x44, 0xaa, 0x7a, 0x46, 0x7b, 0xdb, 0xb2, 0xb1, 0xbb, 0xbf, 0x44, 0xc7, 0xdb, 0xb7, 0x96,
-	0x5c, 0xec, 0x39, 0x81, 0xdb, 0xc6, 0x09, 0xb5, 0xcf, 0x74, 0x2c, 0x7f, 0x3b, 0x68, 0xd5, 0xda,
-	0x4e, 0x6f, 0xa9, 0xe3, 0x74, 0x9c, 0x48, 0x3f, 0x79, 0xa2, 0x0f, 0xf4, 0x3f, 0xce, 0xfe, 0xaa,
-	0x65, 0xfb, 0xd8, 0xb5, 0x8d, 0xee, 0x92, 0xd7, 0xde, 0xc6, 0x66, 0xd0, 0xc5, 0x6e, 0xf4, 0x9f,
-	0xd3, 0xba, 0x8e, 0xdb, 0xbe, 0x97, 0x00, 0x98, 0xac, 0xfe, 0xb3, 0x59, 0x98, 0x5c, 0x21, 0x4b,
-	0xb3, 0x81, 0x3f, 0x08, 0xb0, 0xdd, 0xc6, 0xe8, 0x49, 0x18, 0xff, 0x20, 0xc0, 0x01, 0xae, 0x68,
-	0x8b, 0xda, 0xe9, 0x62, 0xfd, 0xc8, 0x70, 0x50, 0x9d, 0xa6, 0xc0, 0xd3, 0x4e, 0xcf, 0xf2, 0x71,
-	0xaf, 0xef, 0xef, 0x37, 0x18, 0x07, 0x7a, 0x15, 0x26, 0xae, 0x3b, 0xad, 0xa6, 0x87, 0xfd, 0xa6,
-	0x6d, 0xf4, 0x70, 0x25, 0x43, 0x25, 0x2a, 0xc3, 0x41, 0x75, 0xe6, 0xba, 0xd3, 0xda, 0xc0, 0xfe,
-	0x65, 0xa3, 0x27, 0x8b, 0x41, 0x84, 0xa2, 0x67, 0x20, 0x1f, 0x78, 0xd8, 0x6d, 0x5a, 0x66, 0x25,
-	0x4b, 0xc5, 0x66, 0x86, 0x83, 0x6a, 0x99, 0x40, 0x17, 0x4d, 0x49, 0x24, 0xc7, 0x10, 0xf4, 0x34,
-	0xe4, 0x3a, 0xae, 0x13, 0xf4, 0xbd, 0xca, 0xd8, 0x62, 0x56, 0x70, 0x33, 0x44, 0xe6, 0x66, 0x08,
-	0xba, 0x02, 0x39, 0xb6, 0xdf, 0x95, 0xf1, 0xc5, 0xec, 0xe9, 0xd2, 0x99, 0x93, 0x35, 0xd9, 0x09,
-	0x6a, 0xca, 0x84, 0xd9, 0x13, 0x53, 0xc8, 0xe8, 0xb2, 0x42, 0xee, 0x36, 0x7f, 0x47, 0x30, 0x4e,
-	0xf9, 0xd0, 0x15, 0xc8, 0xb7, 0x5d, 0x4c, 0x36, 0xab, 0x82, 0x16, 0xb5, 0xd3, 0xa5, 0x33, 0xf3,
-	0x35, 0xe6, 0x04, 0x35, 0xb1, 0x49, 0xb5, 0xab, 0xc2, 0x09, 0xea, 0xc7, 0x86, 0x83, 0xea, 0x61,
-	0xce, 0x1e, 0x69, 0xbd, 0xf9, 0xe7, 0xaa, 0xd6, 0x10, 0x5a, 0xd0, 0x3a, 0x14, 0xbd, 0xa0, 0xd5,
-	0xb3, 0xfc, 0x55, 0xa7, 0x45, 0xd7, 0xbc, 0x74, 0xe6, 0xa8, 0x3a, 0xdc, 0x0d, 0x41, 0xae, 0x1f,
-	0x1d, 0x0e, 0xaa, 0x47, 0x42, 0xee, 0x48, 0xe3, 0x85, 0x43, 0x8d, 0x48, 0x09, 0xda, 0x86, 0x69,
-	0x17, 0xf7, 0x5d, 0xcb, 0x71, 0x2d, 0xdf, 0xf2, 0x30, 0xd1, 0x9b, 0xa1, 0x7a, 0x4f, 0xa8, 0x7a,
-	0x1b, 0x2a, 0x53, 0xfd, 0xc4, 0x70, 0x50, 0x3d, 0x16, 0x93, 0x54, 0x6c, 0xc4, 0xd5, 0x22, 0x1f,
-	0x50, 0x0c, 0xda, 0xc0, 0x3e, 0xdd, 0xcf, 0xd2, 0x99, 0xc5, 0x5b, 0x1a, 0xdb, 0xc0, 0x7e, 0x7d,
-	0x71, 0x38, 0xa8, 0x1e, 0x4f, 0xca, 0x2b, 0x26, 0x53, 0xf4, 0xa3, 0x2e, 0x94, 0x65, 0xd4, 0x24,
-	0x13, 0x1c, 0xa3, 0x36, 0x17, 0x46, 0xdb, 0x24, 0x5c, 0xf5, 0x85, 0xe1, 0xa0, 0x3a, 0x1f, 0x97,
-	0x55, 0xec, 0x25, 0x34, 0x93, 0xfd, 0x69, 0x1b, 0x76, 0x1b, 0x77, 0x89, 0x99, 0xf1, 0xb4, 0xfd,
-	0x39, 0x2b, 0xc8, 0x6c, 0x7f, 0x42, 0x6e, 0x75, 0x7f, 0x42, 0x18, 0xbd, 0x07, 0x13, 0xe1, 0x03,
-	0x59, 0xaf, 0x1c, 0xf7, 0xa3, 0x74, 0xa5, 0x64, 0xa5, 0xe6, 0x87, 0x83, 0xea, 0x9c, 0x2c, 0xa3,
-	0xa8, 0x56, 0xb4, 0x45, 0xda, 0xbb, 0x6c, 0x65, 0xf2, 0xa3, 0xb5, 0x33, 0x0e, 0x59, 0x7b, 0x37,
-	0xb9, 0x22, 0x8a, 0x36, 0xa2, 0x9d, 0x1c, 0xe2, 0xa0, 0xdd, 0xc6, 0xd8, 0xc4, 0x66, 0xa5, 0x90,
-	0xa6, 0x7d, 0x55, 0xe2, 0x60, 0xda, 0x65, 0x19, 0x55, 0xbb, 0x4c, 0x21, 0x6b, 0x7d, 0xdd, 0x69,
-	0xad, 0xb8, 0xae, 0xe3, 0x7a, 0x95, 0x62, 0xda, 0x5a, 0xaf, 0x0a, 0x32, 0x5b, 0xeb, 0x90, 0x5b,
-	0x5d, 0xeb, 0x10, 0xe6, 0xe3, 0x6d, 0x04, 0xf6, 0x25, 0x6c, 0x78, 0xd8, 0xac, 0xc0, 0x88, 0xf1,
-	0x86, 0x1c, 0xe1, 0x78, 0x43, 0x24, 0x31, 0xde, 0x90, 0x82, 0x4c, 0x98, 0x62, 0xcf, 0xcb, 0x9e,
-	0x67, 0x75, 0x6c, 0x6c, 0x56, 0x4a, 0x54, 0xff, 0xf1, 0x34, 0xfd, 0x82, 0xa7, 0x7e, 0x7c, 0x38,
-	0xa8, 0x56, 0x54, 0x39, 0xc5, 0x46, 0x4c, 0x27, 0x7a, 0x1f, 0x26, 0x19, 0xd2, 0x08, 0x6c, 0xdb,
-	0xb2, 0x3b, 0x95, 0x09, 0x6a, 0xe4, 0xb1, 0x34, 0x23, 0x9c, 0xa5, 0xfe, 0xd8, 0x70, 0x50, 0x3d,
-	0xaa, 0x48, 0x29, 0x26, 0x54, 0x85, 0x24, 0x62, 0x30, 0x20, 0xda, 0xd8, 0xc9, 0xb4, 0x88, 0xb1,
-	0xaa, 0x32, 0xb1, 0x88, 0x11, 0x93, 0x54, 0x23, 0x46, 0x8c, 0x18, 0xed, 0x07, 0xdf, 0xe4, 0xa9,
-	0xd1, 0xfb, 0xc1, 0xf7, 0x59, 0xda, 0x8f, 0x94, 0xad, 0x56, 0xb4, 0xa1, 0x0f, 0x81, 0xbc, 0x78,
-	0xce, 0x05, 0xfd, 0xae, 0xd5, 0x36, 0x7c, 0x7c, 0x0e, 0xfb, 0xb8, 0x4d, 0x22, 0xf5, 0x34, 0xb5,
-	0xa2, 0x27, 0xac, 0x24, 0x38, 0xeb, 0xfa, 0x70, 0x50, 0x5d, 0x48, 0xd3, 0xa1, 0x58, 0x4d, 0xb5,
-	0x82, 0x7e, 0xa0, 0xc1, 0xac, 0xe7, 0x1b, 0xb6, 0x69, 0x74, 0x1d, 0x1b, 0x5f, 0xb4, 0x3b, 0x2e,
-	0xf6, 0xbc, 0x8b, 0xf6, 0x96, 0x53, 0x29, 0x53, 0xfb, 0xa7, 0x62, 0x61, 0x3d, 0x8d, 0xb5, 0x7e,
-	0x6a, 0x38, 0xa8, 0x56, 0x53, 0xb5, 0x28, 0x23, 0x48, 0x37, 0x84, 0xf6, 0xe0, 0x88, 0xc8, 0x2a,
-	0x36, 0x7d, 0xab, 0x6b, 0x79, 0x86, 0x6f, 0x39, 0x76, 0xe5, 0x30, 0xb5, 0x7f, 0x32, 0x1e, 0x1d,
-	0x13, 0x8c, 0xf5, 0x93, 0xc3, 0x41, 0xf5, 0x44, 0x8a, 0x06, 0xc5, 0x76, 0x9a, 0x89, 0xc8, 0x85,
-	0xd6, 0x5d, 0x4c, 0x18, 0xb1, 0x59, 0x39, 0x32, 0xda, 0x85, 0x42, 0x26, 0xd9, 0x85, 0x42, 0x30,
-	0xcd, 0x85, 0x42, 0x22, 0xb1, 0xd4, 0x37, 0x5c, 0xdf, 0x22, 0x66, 0xd7, 0x0c, 0x77, 0x07, 0xbb,
-	0x95, 0x99, 0x34, 0x4b, 0xeb, 0x2a, 0x13, 0xb3, 0x14, 0x93, 0x54, 0x2d, 0xc5, 0x88, 0xe8, 0xa6,
-	0x06, 0xea, 0xd0, 0x2c, 0xc7, 0x6e, 0x90, 0xb4, 0xc1, 0x23, 0xd3, 0x9b, 0xa5, 0x46, 0x9f, 0xb8,
-	0xc5, 0xf4, 0x64, 0xf6, 0xfa, 0x13, 0xc3, 0x41, 0xf5, 0xd4, 0x48, 0x6d, 0xca, 0x40, 0x46, 0x1b,
-	0x45, 0xd7, 0xa0, 0x44, 0x88, 0x98, 0x26, 0x60, 0x66, 0x65, 0x8e, 0x8e, 0xe1, 0x58, 0x72, 0x0c,
-	0x9c, 0x81, 0x66, 0x20, 0xb3, 0x92, 0x84, 0x62, 0x47, 0x56, 0x85, 0x3e, 0xd6, 0x80, 0x1c, 0xb3,
-	0xb4, 0x99, 0x1e, 0xa5, 0x56, 0x1e, 0x4f, 0x58, 0x49, 0x9b, 0xe6, 0xe3, 0xc3, 0x41, 0x75, 0x31,
-	0x5d, 0x8f, 0x62, 0x7b, 0x84, 0xad, 0x7a, 0x1e, 0xc6, 0xa9, 0x01, 0x7d, 0x98, 0x83, 0x23, 0x29,
-	0x2e, 0x8a, 0x5e, 0x87, 0x9c, 0x1b, 0xd8, 0x24, 0x6f, 0x64, 0xc9, 0x12, 0x52, 0x87, 0xb5, 0x19,
-	0x58, 0x26, 0x4b, 0x5a, 0xdd, 0xc0, 0x56, 0x52, 0xc9, 0x71, 0x0a, 0x10, 0x79, 0x92, 0xb4, 0x5a,
-	0x26, 0x4f, 0x8a, 0x46, 0xca, 0x5f, 0x77, 0x5a, 0xaa, 0x3c, 0x05, 0x10, 0x86, 0x49, 0xe1, 0xff,
-	0x4d, 0x8b, 0x1c, 0xee, 0x6c, 0xda, 0xea, 0xbc, 0x15, 0xb4, 0xb0, 0x6b, 0x63, 0x1f, 0x7b, 0x62,
-	0x0e, 0xf4, 0x74, 0xd3, 0x60, 0xe6, 0x4a, 0x88, 0xa4, 0x7f, 0x42, 0xc6, 0xd1, 0x67, 0x1a, 0x54,
-	0x7a, 0xc6, 0x5e, 0x53, 0x80, 0x5e, 0x73, 0xcb, 0x71, 0x9b, 0x7d, 0xec, 0x5a, 0x8e, 0x49, 0x73,
-	0xe0, 0xd2, 0x99, 0xef, 0xdc, 0xf6, 0x3c, 0xd7, 0xd6, 0x8c, 0x3d, 0x01, 0x7b, 0x6f, 0x3a, 0xee,
-	0x3a, 0x15, 0x5f, 0xb1, 0x7d, 0x77, 0xbf, 0x7e, 0xe2, 0xcb, 0x41, 0xf5, 0x10, 0xf1, 0x8e, 0x5e,
-	0x1a, 0x4f, 0x23, 0x1d, 0x46, 0x3f, 0xd1, 0x60, 0xce, 0x77, 0x7c, 0xa3, 0xdb, 0x6c, 0x07, 0xbd,
-	0xa0, 0x6b, 0xf8, 0xd6, 0x2e, 0x6e, 0x06, 0x9e, 0xd1, 0xc1, 0x3c, 0xd5, 0x7e, 0xed, 0xf6, 0x83,
-	0xba, 0x4a, 0xe4, 0xcf, 0x86, 0xe2, 0x9b, 0x44, 0x9a, 0x8d, 0xe9, 0x38, 0x1f, 0xd3, 0x8c, 0x9f,
-	0xc2, 0xd2, 0x48, 0x45, 0xe7, 0x7f, 0xa9, 0xc1, 0xfc, 0xe8, 0x69, 0xa2, 0x53, 0x90, 0xdd, 0xc1,
-	0xfb, 0xbc, 0x98, 0x39, 0x3c, 0x1c, 0x54, 0x27, 0x77, 0xf0, 0xbe, 0xb4, 0xea, 0x84, 0x8a, 0xbe,
-	0x07, 0xe3, 0xbb, 0x46, 0x37, 0xc0, 0xdc, 0x25, 0x6a, 0x35, 0x56, 0xb6, 0xd5, 0xe4, 0xb2, 0xad,
-	0xd6, 0xdf, 0xe9, 0x10, 0xa0, 0x26, 0x76, 0xa4, 0xf6, 0x76, 0x60, 0xd8, 0xbe, 0xe5, 0xef, 0x33,
-	0x77, 0xa1, 0x0a, 0x64, 0x77, 0xa1, 0xc0, 0xab, 0x99, 0x97, 0xb5, 0xf9, 0xcf, 0x35, 0x38, 0x36,
-	0x72, 0xd2, 0xdf, 0x86, 0x11, 0xea, 0x4d, 0x18, 0x23, 0x8e, 0x4f, 0xca, 0xac, 0x6d, 0xab, 0xb3,
-	0xfd, 0xd2, 0x0b, 0x74, 0x38, 0x39, 0x56, 0x15, 0x31, 0x44, 0xae, 0x8a, 0x18, 0x42, 0x4a, 0xc5,
-	0xae, 0x73, 0xe3, 0xa5, 0x17, 0xe8, 0xa0, 0x72, 0xcc, 0x08, 0x05, 0x64, 0x23, 0x14, 0xd0, 0xff,
-	0x9d, 0x83, 0x62, 0x58, 0xc7, 0x48, 0x67, 0x50, 0xbb, 0xab, 0x33, 0x78, 0x01, 0xca, 0x26, 0x36,
-	0xf9, 0x0b, 0xd8, 0x72, 0x6c, 0x71, 0x9a, 0x8b, 0x2c, 0xc8, 0x2b, 0x34, 0x45, 0x7e, 0x3a, 0x46,
-	0x42, 0x67, 0xa0, 0xc0, 0xf3, 0xfd, 0x7d, 0x7a, 0x90, 0x27, 0xeb, 0x73, 0xc3, 0x41, 0x15, 0x09,
-	0x4c, 0x12, 0x0d, 0xf9, 0x50, 0x03, 0x80, 0x15, 0xd1, 0x6b, 0xd8, 0x37, 0x78, 0xe5, 0x51, 0x51,
-	0x67, 0x70, 0x25, 0xa4, 0xb3, 0x72, 0x38, 0xe2, 0x97, 0xcb, 0xe1, 0x08, 0x45, 0xef, 0x01, 0xf4,
-	0x0c, 0xcb, 0x66, 0x72, 0xbc, 0xcc, 0xd0, 0x47, 0x85, 0x94, 0xb5, 0x90, 0x93, 0x69, 0x8f, 0x24,
-	0x65, 0xed, 0x11, 0x4a, 0x8a, 0x56, 0x5e, 0xf6, 0x57, 0x72, 0xf4, 0x94, 0x2e, 0x8c, 0x52, 0xcd,
-	0xd5, 0xce, 0x92, 0xc2, 0x95, 0x8b, 0x48, 0x3a, 0x85, 0x16, 0xb2, 0x6c, 0x5d, 0x6b, 0x0b, 0xfb,
-	0x56, 0x0f, 0xd3, 0x02, 0x83, 0x2f, 0x9b, 0xc0, 0xe4, 0x65, 0x13, 0x18, 0x7a, 0x19, 0xc0, 0xf0,
-	0xd7, 0x1c, 0xcf, 0xbf, 0x62, 0xb7, 0x31, 0x2d, 0x1c, 0x0a, 0x6c, 0xf8, 0x11, 0x2a, 0x0f, 0x3f,
-	0x42, 0xd1, 0x6b, 0x50, 0xea, 0xf3, 0x57, 0x45, 0xab, 0x8b, 0x69, 0x61, 0x50, 0x60, 0x6f, 0x36,
-	0x09, 0x96, 0x64, 0x65, 0x6e, 0x74, 0x1e, 0xa6, 0xdb, 0x8e, 0xdd, 0x0e, 0x5c, 0x17, 0xdb, 0xed,
-	0xfd, 0x0d, 0x63, 0x0b, 0xd3, 0x22, 0xa0, 0xc0, 0x5c, 0x25, 0x46, 0x92, 0x5d, 0x25, 0x46, 0x42,
-	0x2f, 0x42, 0x31, 0x6c, 0xa2, 0xd0, 0x3c, 0xbf, 0xc8, 0xeb, 0x71, 0x01, 0x4a, 0xc2, 0x11, 0x27,
-	0x19, 0xbc, 0xe5, 0x85, 0xc9, 0x22, 0xcd, 0xdd, 0xf9, 0xe0, 0x25, 0x58, 0x1e, 0xbc, 0x04, 0xa3,
-	0x8b, 0x70, 0x98, 0xbe, 0x9e, 0x9b, 0xbe, 0xdf, 0x6d, 0x7a, 0xb8, 0xed, 0xd8, 0xa6, 0x47, 0x53,
-	0xf3, 0x2c, 0x1b, 0x3e, 0x25, 0x5e, 0xf5, 0xbb, 0x1b, 0x8c, 0x24, 0x0f, 0x3f, 0x46, 0xd2, 0x7f,
-	0xaf, 0xc1, 0x4c, 0x9a, 0x0b, 0xc5, 0xdc, 0x59, 0xbb, 0x2f, 0xee, 0xfc, 0x0e, 0x14, 0xfa, 0x8e,
-	0xd9, 0xf4, 0xfa, 0xb8, 0xcd, 0x23, 0x56, 0xcc, 0x99, 0xd7, 0x1d, 0x73, 0xa3, 0x8f, 0xdb, 0xdf,
-	0xb5, 0xfc, 0xed, 0xe5, 0x5d, 0xc7, 0x32, 0x2f, 0x59, 0x1e, 0xf7, 0xba, 0x3e, 0xa3, 0x28, 0xc9,
-	0x42, 0x9e, 0x83, 0xf5, 0x02, 0xe4, 0x98, 0x15, 0xfd, 0x0f, 0x59, 0x28, 0xc7, 0xdd, 0xf6, 0x7f,
-	0x69, 0x2a, 0xe8, 0x1a, 0xe4, 0x2d, 0x96, 0xb9, 0xf3, 0x0c, 0xe2, 0xff, 0xa4, 0x98, 0x5e, 0x8b,
-	0xfa, 0x8e, 0xb5, 0xdd, 0xe7, 0x6a, 0x3c, 0xc5, 0xa7, 0x4b, 0x40, 0x35, 0x73, 0x49, 0x55, 0x33,
-	0x07, 0x51, 0x03, 0xf2, 0x1e, 0x76, 0x77, 0xad, 0x36, 0xe6, 0xc1, 0xa9, 0x2a, 0x6b, 0x6e, 0x3b,
-	0x2e, 0x26, 0x3a, 0x37, 0x18, 0x4b, 0xa4, 0x93, 0xcb, 0xa8, 0x3a, 0x39, 0x88, 0xde, 0x81, 0x62,
-	0xdb, 0xb1, 0xb7, 0xac, 0xce, 0x9a, 0xd1, 0xe7, 0xe1, 0xe9, 0x44, 0x9a, 0xd6, 0xb3, 0x82, 0x89,
-	0xf7, 0x42, 0xc4, 0x63, 0xac, 0x17, 0x12, 0x72, 0x45, 0x1b, 0xfa, 0xb7, 0x31, 0x80, 0x68, 0x73,
-	0xd0, 0x2b, 0x50, 0xc2, 0x7b, 0xb8, 0x1d, 0xf8, 0x8e, 0x2b, 0xde, 0x13, 0xbc, 0xb5, 0x28, 0x60,
-	0x25, 0xb0, 0x43, 0x84, 0x92, 0x83, 0x6a, 0x1b, 0x3d, 0xec, 0xf5, 0x8d, 0xb6, 0xe8, 0x49, 0xd2,
-	0xc1, 0x84, 0xa0, 0x7c, 0x50, 0x43, 0x10, 0xfd, 0x3f, 0x8c, 0xd1, 0x2e, 0x26, 0x6b, 0x47, 0xa2,
-	0xe1, 0xa0, 0x3a, 0x65, 0xab, 0xfd, 0x4b, 0x4a, 0x47, 0x6f, 0xc0, 0xe4, 0x4e, 0xe8, 0x78, 0x64,
-	0x6c, 0x63, 0x54, 0x80, 0xa6, 0x76, 0x11, 0x41, 0x19, 0xdd, 0x84, 0x8c, 0xa3, 0x2d, 0x28, 0x19,
-	0xb6, 0xed, 0xf8, 0xf4, 0x1d, 0x24, 0x5a, 0x94, 0x4f, 0x8e, 0x72, 0xd3, 0xda, 0x72, 0xc4, 0xcb,
-	0xb2, 0x24, 0x1a, 0x3c, 0x24, 0x0d, 0x72, 0xf0, 0x90, 0x60, 0xd4, 0x80, 0x5c, 0xd7, 0x68, 0xe1,
-	0xae, 0x08, 0xfa, 0x8f, 0x8f, 0x34, 0x71, 0x89, 0xb2, 0x31, 0xed, 0xf4, 0x95, 0xcf, 0xe4, 0xe4,
-	0x57, 0x3e, 0x43, 0xe6, 0xb7, 0xa0, 0x1c, 0x1f, 0xcf, 0xc1, 0x12, 0x98, 0x27, 0xe5, 0x04, 0xa6,
-	0x78, 0xdb, 0x94, 0xc9, 0x80, 0x92, 0x34, 0xa8, 0x07, 0x61, 0x42, 0xff, 0x95, 0x06, 0x33, 0x69,
-	0x67, 0x17, 0xad, 0x49, 0x27, 0x5e, 0xe3, 0xad, 0x96, 0x14, 0x57, 0xe7, 0xb2, 0x23, 0x8e, 0x7a,
-	0x74, 0xd0, 0xeb, 0x30, 0x65, 0x3b, 0x26, 0x6e, 0x1a, 0xc4, 0x40, 0xd7, 0xf2, 0xfc, 0x4a, 0x86,
-	0xb6, 0xb0, 0x69, 0x8b, 0x86, 0x50, 0x96, 0x05, 0x41, 0x92, 0x9e, 0x54, 0x08, 0xfa, 0x8f, 0x34,
-	0x98, 0x8e, 0x75, 0x50, 0xef, 0x39, 0x89, 0x92, 0x53, 0x9f, 0xcc, 0xc1, 0x52, 0x1f, 0xfd, 0xa7,
-	0x19, 0x28, 0x49, 0xe5, 0xe5, 0x3d, 0x8f, 0xe1, 0x3a, 0x4c, 0xf3, 0x37, 0xa5, 0x65, 0x77, 0x58,
-	0x39, 0x95, 0xe1, 0xbd, 0x92, 0xc4, 0x07, 0x8b, 0x55, 0xa7, 0xb5, 0x11, 0xf2, 0xd2, 0x6a, 0x8a,
-	0x36, 0xd2, 0x3c, 0x05, 0x93, 0x4c, 0x4c, 0xa9, 0x14, 0x74, 0x0d, 0xe6, 0x82, 0xbe, 0x69, 0xf8,
-	0xb8, 0xe9, 0xf1, 0xd6, 0x7f, 0xd3, 0x0e, 0x7a, 0x2d, 0xec, 0xd2, 0x13, 0x3f, 0xce, 0x5a, 0x3f,
-	0x8c, 0x43, 0x7c, 0x1b, 0xb8, 0x4c, 0xe9, 0x92, 0xce, 0x99, 0x34, 0xba, 0x7e, 0x01, 0x50, 0xb2,
-	0xbd, 0xad, 0xac, 0xaf, 0x76, 0xc0, 0xf5, 0xfd, 0x44, 0x83, 0x72, 0xbc, 0x6b, 0xfd, 0x50, 0x36,
-	0x7a, 0x1f, 0x8a, 0x61, 0x07, 0xfa, 0x9e, 0x07, 0xf0, 0x34, 0xe4, 0x5c, 0x6c, 0x78, 0x8e, 0xcd,
-	0x4f, 0x26, 0x0d, 0x31, 0x0c, 0x91, 0x43, 0x0c, 0x43, 0xf4, 0xab, 0x30, 0xc1, 0x56, 0xf0, 0x4d,
-	0xab, 0xeb, 0x63, 0x17, 0x9d, 0x83, 0x9c, 0xe7, 0x1b, 0x3e, 0xf6, 0x2a, 0xda, 0x62, 0xf6, 0xf4,
-	0xd4, 0x99, 0xb9, 0x64, 0xb3, 0x99, 0x90, 0x99, 0x56, 0xc6, 0x29, 0x6b, 0x65, 0x88, 0xfe, 0x43,
-	0x0d, 0x26, 0xe4, 0x9e, 0xfa, 0xfd, 0x51, 0x7b, 0x87, 0x53, 0xfb, 0x50, 0x8c, 0xa1, 0x7b, 0x7f,
-	0x76, 0xf6, 0xce, 0xac, 0xff, 0x46, 0x63, 0x2b, 0x1b, 0x36, 0x63, 0xef, 0xd5, 0x7c, 0x27, 0x6a,
-	0x85, 0x90, 0x13, 0xe6, 0xd1, 0xc0, 0x76, 0xd0, 0x56, 0x08, 0x0d, 0x7f, 0x8a, 0xb8, 0x1c, 0xfe,
-	0x14, 0x82, 0xfe, 0xd9, 0x38, 0x1d, 0x79, 0xd4, 0x78, 0x7f, 0xd8, 0x4d, 0xa0, 0x58, 0x76, 0x92,
-	0xbd, 0x83, 0xec, 0xe4, 0x19, 0xc8, 0xd3, 0xd7, 0x41, 0x98, 0x38, 0xd0, 0x4d, 0x23, 0x90, 0xfa,
-	0xe1, 0x93, 0x21, 0xb7, 0x88, 0x5a, 0xe3, 0xf7, 0x16, 0xb5, 0x50, 0x13, 0x8e, 0x6d, 0x1b, 0x5e,
-	0x53, 0xc4, 0x59, 0xb3, 0x69, 0xf8, 0xcd, 0x30, 0x4e, 0xe4, 0x68, 0x99, 0x42, 0x9b, 0x79, 0xdb,
-	0x86, 0xb7, 0x21, 0x78, 0x96, 0xfd, 0xf5, 0x64, 0xd4, 0x98, 0x4b, 0xe7, 0x40, 0x9b, 0x30, 0x9b,
-	0xae, 0x3c, 0x4f, 0x47, 0x4e, 0x7b, 0xcd, 0xde, 0x2d, 0x35, 0x1f, 0x49, 0x21, 0xa3, 0x8f, 0x34,
-	0xa8, 0x90, 0xf7, 0xb3, 0x8b, 0x3f, 0x08, 0x2c, 0x17, 0xf7, 0xc8, 0x8e, 0x35, 0x9d, 0x5d, 0xec,
-	0x76, 0x8d, 0x7d, 0xfe, 0xd1, 0xe8, 0x64, 0xf2, 0xed, 0xb1, 0xee, 0x98, 0x0d, 0x49, 0x80, 0x4d,
-	0xad, 0xaf, 0x82, 0x57, 0x98, 0x12, 0x79, 0x6a, 0xe9, 0x1c, 0xab, 0x63, 0x85, 0x42, 0xb9, 0xa8,
-	0xff, 0x53, 0x83, 0x29, 0xf5, 0xdb, 0xce, 0x43, 0x77, 0xcc, 0xc4, 0x91, 0xcc, 0x3e, 0xa0, 0x23,
-	0xf9, 0x0f, 0x0d, 0x26, 0x95, 0x4f, 0x4e, 0x8f, 0xce, 0xd4, 0x7f, 0x9e, 0x81, 0xb9, 0x74, 0x35,
-	0x0f, 0xa4, 0x00, 0xbd, 0x00, 0x24, 0x95, 0xbc, 0x18, 0xe5, 0x46, 0xb3, 0x89, 0xfa, 0x93, 0x4e,
-	0x41, 0xe4, 0xa1, 0x89, 0x6f, 0x45, 0x42, 0x1c, 0x5d, 0x83, 0x92, 0x25, 0x7d, 0x95, 0xca, 0xa6,
-	0x7d, 0x3c, 0x90, 0xbf, 0x45, 0xb1, 0x2e, 0xc5, 0x88, 0x2f, 0x50, 0xb2, 0xaa, 0x7a, 0x0e, 0xc6,
-	0x48, 0xf2, 0xa6, 0xef, 0x42, 0x9e, 0x0f, 0x07, 0x3d, 0x0f, 0x45, 0x1a, 0xe7, 0x68, 0x4d, 0xc5,
-	0x12, 0x77, 0x9a, 0x76, 0x10, 0x30, 0x76, 0x2f, 0xa4, 0x20, 0x30, 0xf4, 0x12, 0x00, 0x39, 0xda,
-	0x3c, 0xc2, 0x65, 0x68, 0x9c, 0xa0, 0xb5, 0x5b, 0xdf, 0x31, 0x13, 0x61, 0xad, 0x18, 0x82, 0xfa,
-	0xaf, 0x33, 0x50, 0x92, 0xbf, 0x83, 0xdd, 0x95, 0xf1, 0x0f, 0x41, 0xd4, 0xd5, 0x4d, 0xc3, 0x34,
-	0xc9, 0x5f, 0x2c, 0x5e, 0x69, 0x4b, 0x23, 0x17, 0x49, 0xfc, 0xbf, 0x2c, 0x24, 0x58, 0x15, 0x45,
-	0x6f, 0x1a, 0x58, 0x31, 0x92, 0x64, 0xb5, 0x1c, 0xa7, 0xcd, 0xef, 0xc0, 0x6c, 0xaa, 0x2a, 0xb9,
-	0xf6, 0x19, 0xbf, 0x5f, 0xb5, 0xcf, 0x6f, 0xc7, 0x61, 0x36, 0xf5, 0xfb, 0xe3, 0x43, 0x3f, 0xc5,
-	0xea, 0x09, 0xca, 0xde, 0x97, 0x13, 0xf4, 0x89, 0x96, 0xb6, 0xb3, 0xec, 0x23, 0xca, 0x2b, 0x07,
-	0xf8, 0x28, 0x7b, 0xbf, 0xf6, 0x58, 0x75, 0xcb, 0xf1, 0xbb, 0x3a, 0x13, 0xb9, 0x83, 0x9e, 0x09,
-	0xf4, 0x2c, 0x2b, 0x63, 0xa9, 0xad, 0x3c, 0xb5, 0x25, 0x22, 0x44, 0xcc, 0x54, 0x9e, 0x43, 0xe8,
-	0x0d, 0x98, 0x14, 0x12, 0xac, 0x79, 0x52, 0x88, 0x3a, 0x1b, 0x9c, 0x27, 0xde, 0x3f, 0x99, 0x90,
-	0xf1, 0xff, 0xae, 0x0f, 0xff, 0x4b, 0x83, 0xe9, 0xd8, 0x85, 0x84, 0x47, 0xe7, 0x1d, 0xf4, 0xa9,
-	0x06, 0xc5, 0xf0, 0x2e, 0xcc, 0x3d, 0x27, 0xf2, 0xcb, 0x90, 0xc3, 0xec, 0x3e, 0x06, 0x0b, 0x77,
-	0x47, 0x62, 0xf7, 0xe5, 0x08, 0x8d, 0xdf, 0x90, 0x8b, 0x5d, 0xc1, 0x68, 0x70, 0x41, 0xfd, 0x8f,
-	0x9a, 0x48, 0xd1, 0xa3, 0x31, 0x3d, 0xd4, 0xad, 0x88, 0xe6, 0x94, 0xbd, 0xdb, 0x39, 0xfd, 0xae,
-	0x08, 0xe3, 0x94, 0x8f, 0x94, 0xd0, 0x3e, 0x76, 0x7b, 0x96, 0x6d, 0x74, 0xe9, 0x74, 0x0a, 0xec,
-	0xdc, 0x0a, 0x4c, 0x3e, 0xb7, 0x02, 0x43, 0xdb, 0x30, 0x1d, 0xb5, 0xfd, 0xa8, 0x9a, 0xf4, 0x6b,
-	0x78, 0x6f, 0xa9, 0x4c, 0xac, 0xb1, 0x1f, 0x93, 0x54, 0xef, 0x29, 0xc4, 0x88, 0xc8, 0x84, 0xa9,
-	0xb6, 0x63, 0xfb, 0x86, 0x65, 0x63, 0x97, 0x19, 0xca, 0xa6, 0x5d, 0x43, 0x3a, 0xab, 0xf0, 0xb0,
-	0xee, 0x89, 0x2a, 0xa7, 0x5e, 0x43, 0x52, 0x69, 0xe8, 0x7d, 0x98, 0x14, 0x65, 0x0c, 0x33, 0x32,
-	0x96, 0x76, 0x0d, 0x69, 0x45, 0x66, 0x61, 0x2e, 0xad, 0x48, 0xa9, 0xd7, 0x90, 0x14, 0x12, 0xea,
-	0x42, 0xb9, 0xef, 0x98, 0x9b, 0x36, 0x4f, 0xde, 0x8d, 0x56, 0x17, 0xf3, 0x5e, 0xf3, 0x42, 0x22,
-	0xe5, 0x51, 0xb8, 0x58, 0x28, 0x8e, 0xcb, 0xaa, 0x17, 0xfb, 0xe2, 0x54, 0xf4, 0x1e, 0x4c, 0x74,
-	0x49, 0x35, 0xb9, 0xb2, 0xd7, 0xb7, 0x5c, 0x6c, 0xa6, 0x5f, 0xc3, 0xbb, 0x24, 0x71, 0xb0, 0x40,
-	0x28, 0xcb, 0xa8, 0x57, 0x91, 0x64, 0x0a, 0xd9, 0xfd, 0x9e, 0xb1, 0xd7, 0x08, 0x6c, 0x6f, 0x65,
-	0x8f, 0x5f, 0xa9, 0xca, 0xa7, 0xed, 0xfe, 0x9a, 0xca, 0xc4, 0x76, 0x3f, 0x26, 0xa9, 0xee, 0x7e,
-	0x8c, 0x88, 0x2e, 0xd1, 0x38, 0xcf, 0xb6, 0x84, 0x5d, 0xc7, 0x9b, 0x4b, 0xac, 0x16, 0xdb, 0x0d,
-	0xd6, 0xf6, 0xe1, 0x4f, 0x8a, 0xd2, 0x50, 0x03, 0xdf, 0x03, 0x3a, 0xed, 0x06, 0xf6, 0x03, 0xd7,
-	0xc6, 0x26, 0x2f, 0xaa, 0x92, 0x7b, 0xa0, 0x70, 0x85, 0x7b, 0xa0, 0xa0, 0x89, 0x3d, 0x50, 0xa8,
-	0xc4, 0xa7, 0xfa, 0x8e, 0x79, 0x95, 0x1d, 0x19, 0x3f, 0xbc, 0x9f, 0xf7, 0x58, 0xc2, 0x54, 0xc4,
-	0xc2, 0x7c, 0x4a, 0x91, 0x52, 0x7d, 0x4a, 0x21, 0xf1, 0x2b, 0x61, 0xf2, 0x05, 0x22, 0xb6, 0x52,
-	0xa5, 0x11, 0x57, 0xc2, 0x12, 0x9c, 0xe1, 0x95, 0xb0, 0x04, 0x25, 0x71, 0x25, 0x2c, 0xc1, 0x41,
-	0xac, 0x77, 0x0c, 0xbb, 0xb3, 0xea, 0xb4, 0x54, 0xaf, 0x9e, 0x48, 0xb3, 0x7e, 0x3e, 0x85, 0x93,
-	0x59, 0x4f, 0xd3, 0xa1, 0x5a, 0x4f, 0xe3, 0xa8, 0x17, 0x44, 0x7b, 0x48, 0xff, 0x5c, 0x83, 0xe9,
-	0x58, 0x9c, 0x41, 0xaf, 0x43, 0x78, 0xe3, 0xe4, 0xea, 0x7e, 0x5f, 0xa4, 0xc9, 0xca, 0x0d, 0x15,
-	0x82, 0xa7, 0xdd, 0x50, 0x21, 0x38, 0xba, 0x04, 0x10, 0xbe, 0x93, 0x6e, 0x15, 0xa4, 0x69, 0x8e,
-	0x16, 0x71, 0xca, 0x39, 0x5a, 0x84, 0xea, 0x37, 0xc7, 0xa0, 0x20, 0x1c, 0xf5, 0x81, 0x94, 0x51,
-	0x4b, 0x90, 0xef, 0x61, 0x8f, 0xde, 0x54, 0xc9, 0x44, 0xd9, 0x10, 0x87, 0xe4, 0x6c, 0x88, 0x43,
-	0x6a, 0xb2, 0x96, 0xbd, 0xab, 0x64, 0x6d, 0xec, 0xc0, 0xc9, 0x1a, 0xa6, 0x5f, 0xa9, 0xa5, 0x70,
-	0x2b, 0xbe, 0x0b, 0xdd, 0x3a, 0x86, 0x8b, 0x6f, 0xd8, 0xb2, 0x60, 0xec, 0x1b, 0xb6, 0x4c, 0x42,
-	0x3b, 0x70, 0x58, 0xfa, 0x76, 0xc5, 0x7b, 0x87, 0x24, 0xf0, 0x4d, 0x8d, 0xbe, 0x12, 0xd0, 0xa0,
-	0x5c, 0xec, 0x78, 0xef, 0xc4, 0x50, 0x39, 0xdb, 0x8d, 0xd3, 0x88, 0x83, 0x99, 0xb8, 0x15, 0x74,
-	0xd6, 0xf8, 0xb2, 0xe7, 0x23, 0x07, 0x93, 0x71, 0xd9, 0xc1, 0x64, 0x5c, 0xff, 0x6b, 0x06, 0xa6,
-	0xd4, 0xf9, 0x3e, 0x10, 0xc7, 0x78, 0x1e, 0x8a, 0x78, 0xcf, 0xf2, 0x9b, 0x6d, 0xc7, 0xc4, 0xbc,
-	0xe4, 0xa4, 0xfb, 0x4c, 0xc0, 0xb3, 0x8e, 0xa9, 0xec, 0xb3, 0xc0, 0x64, 0x6f, 0xca, 0x1e, 0xc8,
-	0x9b, 0xa2, 0x56, 0xed, 0xd8, 0xed, 0x5b, 0xb5, 0xe9, 0xfb, 0x54, 0x7c, 0x30, 0xfb, 0xa4, 0x7f,
-	0x95, 0x81, 0x72, 0x3c, 0x9a, 0x7f, 0x3b, 0x8e, 0xa0, 0x7a, 0x9a, 0xb2, 0x07, 0x3e, 0x4d, 0x6f,
-	0xc0, 0x24, 0xc9, 0x3d, 0x0d, 0xdf, 0xe7, 0x57, 0x51, 0xc7, 0x68, 0xce, 0xc6, 0x62, 0x5b, 0x60,
-	0x2f, 0x0b, 0x5c, 0x89, 0x6d, 0x12, 0x9e, 0x70, 0xdd, 0xf1, 0x3b, 0x74, 0xdd, 0x8f, 0x32, 0x30,
-	0xa9, 0xbc, 0xb5, 0x1e, 0xbd, 0x90, 0xa6, 0x4f, 0xc3, 0xa4, 0x92, 0x0c, 0xea, 0x1f, 0x33, 0x3f,
-	0x53, 0xb3, 0xb0, 0x47, 0x6f, 0x5d, 0xa6, 0x60, 0x42, 0xce, 0x2a, 0xf5, 0x3a, 0x4c, 0xc7, 0x92,
-	0x40, 0x79, 0x02, 0xda, 0x41, 0x26, 0xa0, 0xcf, 0xc1, 0x4c, 0x5a, 0xee, 0xa2, 0x9f, 0x87, 0x99,
-	0xb4, 0xac, 0xe2, 0xce, 0x0d, 0x7c, 0xa1, 0x51, 0x0b, 0xc9, 0x4b, 0xef, 0x17, 0x00, 0x6c, 0x7c,
-	0xa3, 0x79, 0xdb, 0xf2, 0x93, 0xad, 0x27, 0xbe, 0xb1, 0x1a, 0xab, 0xd6, 0x0a, 0x02, 0x23, 0x9a,
-	0x9c, 0xae, 0xd9, 0xbc, 0x6d, 0xd1, 0x47, 0x35, 0x39, 0x5d, 0x33, 0xa1, 0x49, 0x60, 0xfa, 0x8f,
-	0xb3, 0xa2, 0x33, 0x10, 0xdd, 0x1a, 0x7f, 0x17, 0xca, 0x7d, 0xf1, 0x70, 0xfb, 0xd1, 0xd2, 0xda,
-	0x28, 0xe4, 0x8f, 0x5b, 0x9a, 0x52, 0x29, 0xaa, 0x6e, 0x5e, 0xf4, 0x66, 0x0e, 0xa8, 0xbb, 0x11,
-	0xab, 0x7e, 0xa7, 0x54, 0x0a, 0xfa, 0x3e, 0x1c, 0x16, 0xb7, 0xd9, 0x76, 0xb1, 0x18, 0x78, 0x76,
-	0xa4, 0x72, 0x76, 0xc9, 0x3d, 0x14, 0x88, 0x8f, 0x7c, 0x3a, 0x46, 0x8a, 0xa9, 0xe7, 0x63, 0x1f,
-	0x3b, 0xa8, 0xfa, 0xf8, 0xe0, 0xa7, 0x63, 0x24, 0xfd, 0x53, 0x0d, 0xa6, 0x63, 0xf7, 0xf0, 0xd1,
-	0x39, 0x28, 0xd0, 0x9f, 0xe9, 0xdd, 0x7a, 0x07, 0xa8, 0x43, 0x52, 0x3e, 0xc5, 0x42, 0x9e, 0x43,
-	0xe8, 0x45, 0x28, 0x86, 0xd7, 0xf5, 0xf9, 0x57, 0x6d, 0x76, 0xf8, 0x04, 0xa8, 0x1c, 0x3e, 0x01,
-	0xea, 0xbf, 0xd0, 0xe0, 0xd8, 0xc8, 0x3b, 0xfa, 0x0f, 0xbb, 0x67, 0xa1, 0x5f, 0x83, 0xb9, 0xf4,
-	0x6b, 0xf5, 0xf7, 0xda, 0xe1, 0x79, 0xea, 0x59, 0x28, 0x88, 0x2f, 0xda, 0x08, 0x20, 0xf7, 0xf6,
-	0xe6, 0xca, 0xe6, 0xca, 0xb9, 0xf2, 0x21, 0x54, 0x82, 0xfc, 0xfa, 0xca, 0xe5, 0x73, 0x17, 0x2f,
-	0x9f, 0x2f, 0x6b, 0xe4, 0xa1, 0xb1, 0x79, 0xf9, 0x32, 0x79, 0xc8, 0x3c, 0x75, 0x49, 0xbe, 0x5f,
-	0xc7, 0x33, 0xba, 0x09, 0x28, 0x2c, 0xf7, 0xfb, 0x34, 0xb4, 0x30, 0xd9, 0x95, 0x5d, 0x8b, 0x44,
-	0x81, 0xb2, 0x86, 0xf2, 0x90, 0xbd, 0x72, 0x65, 0xad, 0x9c, 0x41, 0x33, 0x50, 0x3e, 0x87, 0x0d,
-	0xb3, 0x6b, 0xd9, 0x58, 0xc4, 0xb3, 0x72, 0xb6, 0x7e, 0xfd, 0xcb, 0xaf, 0x17, 0xb4, 0xaf, 0xbe,
-	0x5e, 0xd0, 0xfe, 0xf2, 0xf5, 0x82, 0x76, 0xf3, 0x9b, 0x85, 0x43, 0x5f, 0x7d, 0xb3, 0x70, 0xe8,
-	0x4f, 0xdf, 0x2c, 0x1c, 0x7a, 0xf7, 0x59, 0xe9, 0xc7, 0xae, 0x6c, 0x4e, 0x7d, 0xd7, 0x21, 0xa1,
-	0x9c, 0x3f, 0x2d, 0xc5, 0x7f, 0xde, 0xfb, 0x45, 0xe6, 0xc4, 0x32, 0x7d, 0x5c, 0x67, 0x7c, 0xb5,
-	0x8b, 0x4e, 0x8d, 0x01, 0xf4, 0x17, 0x9a, 0x5e, 0x2b, 0x47, 0x7f, 0x89, 0xf9, 0xfc, 0x7f, 0x02,
-	0x00, 0x00, 0xff, 0xff, 0x2a, 0x6f, 0xe1, 0xc8, 0x19, 0x3c, 0x00, 0x00,
+	// 3691 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x3b, 0x4b, 0x70, 0x1b, 0xc7,
+	0x95, 0x1a, 0x80, 0xc4, 0xe7, 0x81, 0x1f, 0xa8, 0xf9, 0x11, 0x44, 0x4b, 0x04, 0x35, 0xf2, 0xae,
+	0x65, 0x97, 0x0d, 0xda, 0xf2, 0xa7, 0xfc, 0xd9, 0xb2, 0x8b, 0x90, 0x68, 0x49, 0xb4, 0x28, 0xd1,
+	0xa0, 0xe8, 0xd5, 0xba, 0xbc, 0x05, 0x0f, 0x30, 0x4d, 0x70, 0x44, 0x60, 0x06, 0x9e, 0x0f, 0x45,
+	0x56, 0xf9, 0xb0, 0xde, 0xf2, 0x7a, 0x6f, 0x8e, 0x52, 0xf1, 0x21, 0x55, 0x39, 0x38, 0xd7, 0xb8,
+	0x2a, 0xe7, 0x9c, 0x73, 0x8a, 0x0f, 0xa9, 0x94, 0x73, 0x4b, 0xe5, 0x80, 0xa4, 0xec, 0xca, 0x05,
+	0x87, 0x9c, 0x93, 0x5c, 0x92, 0xea, 0xdf, 0x4c, 0xf7, 0xcc, 0x40, 0xa2, 0x7e, 0x91, 0x53, 0x3a,
+	0x91, 0xf3, 0xfe, 0xdd, 0xfd, 0xfa, 0xf5, 0x7b, 0xaf, 0x1b, 0x70, 0xb2, 0xbf, 0xdb, 0x59, 0x36,
+	0xdc, 0x9e, 0x61, 0x1a, 0x78, 0x0f, 0xdb, 0xbe, 0xb7, 0xcc, 0xfe, 0xd4, 0xfa, 0xae, 0xe3, 0x3b,
+	0x68, 0x42, 0x46, 0x2d, 0xe8, 0xbb, 0xaf, 0x7a, 0x35, 0xcb, 0x59, 0x36, 0xfa, 0xd6, 0x72, 0xdb,
+	0x71, 0xf1, 0xf2, 0xde, 0x0b, 0xcb, 0x1d, 0x6c, 0x63, 0xd7, 0xf0, 0xb1, 0xc9, 0x38, 0x16, 0xce,
+	0x48, 0x34, 0x36, 0xf6, 0x6f, 0x3a, 0xee, 0xae, 0x65, 0x77, 0xd2, 0x28, 0xab, 0x1d, 0xc7, 0xe9,
+	0x74, 0xf1, 0x32, 0xfd, 0x6a, 0x05, 0xdb, 0xcb, 0xbe, 0xd5, 0xc3, 0x9e, 0x6f, 0xf4, 0xfa, 0x9c,
+	0xe0, 0xa5, 0x48, 0x54, 0xcf, 0x68, 0xef, 0x58, 0x36, 0x76, 0x0f, 0x96, 0xa9, 0xbd, 0x7d, 0x6b,
+	0xd9, 0xc5, 0x9e, 0x13, 0xb8, 0x6d, 0x9c, 0x10, 0xfb, 0x5c, 0xc7, 0xf2, 0x77, 0x82, 0x56, 0xad,
+	0xed, 0xf4, 0x96, 0x3b, 0x4e, 0xc7, 0x89, 0xe4, 0x93, 0x2f, 0xfa, 0x41, 0xff, 0xe3, 0xe4, 0xaf,
+	0x5b, 0xb6, 0x8f, 0x5d, 0xdb, 0xe8, 0x2e, 0x7b, 0xed, 0x1d, 0x6c, 0x06, 0x5d, 0xec, 0x46, 0xff,
+	0x39, 0xad, 0x1b, 0xb8, 0xed, 0x7b, 0x09, 0x00, 0xe3, 0xd5, 0x3f, 0x99, 0x87, 0xc9, 0x55, 0x32,
+	0x35, 0x9b, 0xf8, 0xa3, 0x00, 0xdb, 0x6d, 0x8c, 0x9e, 0x86, 0xf1, 0x8f, 0x02, 0x1c, 0xe0, 0x8a,
+	0xb6, 0xa4, 0x9d, 0x29, 0xd6, 0x67, 0x86, 0x83, 0xea, 0x34, 0x05, 0x3c, 0xeb, 0xf4, 0x2c, 0x1f,
+	0xf7, 0xfa, 0xfe, 0x41, 0x83, 0x51, 0xa0, 0xd7, 0x61, 0xe2, 0x86, 0xd3, 0x6a, 0x7a, 0xd8, 0x6f,
+	0xda, 0x46, 0x0f, 0x57, 0x32, 0x94, 0xa3, 0x32, 0x1c, 0x54, 0x67, 0x6f, 0x38, 0xad, 0x4d, 0xec,
+	0x5f, 0x31, 0x7a, 0x32, 0x1b, 0x44, 0x50, 0xf4, 0x1c, 0xe4, 0x03, 0x0f, 0xbb, 0x4d, 0xcb, 0xac,
+	0x64, 0x29, 0xdb, 0xec, 0x70, 0x50, 0x2d, 0x13, 0xd0, 0x25, 0x53, 0x62, 0xc9, 0x31, 0x08, 0x7a,
+	0x16, 0x72, 0x1d, 0xd7, 0x09, 0xfa, 0x5e, 0x65, 0x6c, 0x29, 0x2b, 0xa8, 0x19, 0x44, 0xa6, 0x66,
+	0x10, 0x74, 0x15, 0x72, 0x6c, 0xbd, 0x2b, 0xe3, 0x4b, 0xd9, 0x33, 0xa5, 0xb3, 0xa7, 0x6a, 0xb2,
+	0x13, 0xd4, 0x94, 0x01, 0xb3, 0x2f, 0x26, 0x90, 0xe1, 0x65, 0x81, 0xdc, 0x6d, 0x7e, 0x3f, 0x03,
+	0xe3, 0x94, 0x0e, 0x5d, 0x85, 0x7c, 0xdb, 0xc5, 0x64, 0xb1, 0x2a, 0x68, 0x49, 0x3b, 0x53, 0x3a,
+	0xbb, 0x50, 0x63, 0x4e, 0x50, 0x13, 0x8b, 0x54, 0xbb, 0x26, 0x9c, 0xa0, 0x7e, 0x7c, 0x38, 0xa8,
+	0x1e, 0xe5, 0xe4, 0x91, 0xd4, 0x5b, 0x7f, 0xa8, 0x6a, 0x0d, 0x21, 0x05, 0x6d, 0x40, 0xd1, 0x0b,
+	0x5a, 0x3d, 0xcb, 0x5f, 0x73, 0x5a, 0x74, 0xce, 0x4b, 0x67, 0x8f, 0xa9, 0xe6, 0x6e, 0x0a, 0x74,
+	0xfd, 0xd8, 0x70, 0x50, 0x9d, 0x09, 0xa9, 0x23, 0x89, 0x17, 0x8f, 0x34, 0x22, 0x21, 0x68, 0x07,
+	0xa6, 0x5d, 0xdc, 0x77, 0x2d, 0xc7, 0xb5, 0x7c, 0xcb, 0xc3, 0x44, 0x6e, 0x86, 0xca, 0x3d, 0xa9,
+	0xca, 0x6d, 0xa8, 0x44, 0xf5, 0x93, 0xc3, 0x41, 0xf5, 0x78, 0x8c, 0x53, 0xd1, 0x11, 0x17, 0x8b,
+	0x7c, 0x40, 0x31, 0xd0, 0x26, 0xf6, 0xe9, 0x7a, 0x96, 0xce, 0x2e, 0xdd, 0x56, 0xd9, 0x26, 0xf6,
+	0xeb, 0x4b, 0xc3, 0x41, 0xf5, 0x44, 0x92, 0x5f, 0x51, 0x99, 0x22, 0x1f, 0x75, 0xa1, 0x2c, 0x43,
+	0x4d, 0x32, 0xc0, 0x31, 0xaa, 0x73, 0x71, 0xb4, 0x4e, 0x42, 0x55, 0x5f, 0x1c, 0x0e, 0xaa, 0x0b,
+	0x71, 0x5e, 0x45, 0x5f, 0x42, 0x32, 0x59, 0x9f, 0xb6, 0x61, 0xb7, 0x71, 0x97, 0xa8, 0x19, 0x4f,
+	0x5b, 0x9f, 0x73, 0x02, 0xcd, 0xd6, 0x27, 0xa4, 0x56, 0xd7, 0x27, 0x04, 0xa3, 0x0f, 0x60, 0x22,
+	0xfc, 0x20, 0xf3, 0x95, 0xe3, 0x7e, 0x94, 0x2e, 0x94, 0xcc, 0xd4, 0xc2, 0x70, 0x50, 0x9d, 0x97,
+	0x79, 0x14, 0xd1, 0x8a, 0xb4, 0x48, 0x7a, 0x97, 0xcd, 0x4c, 0x7e, 0xb4, 0x74, 0x46, 0x21, 0x4b,
+	0xef, 0x26, 0x67, 0x44, 0x91, 0x46, 0xa4, 0x93, 0x4d, 0x1c, 0xb4, 0xdb, 0x18, 0x9b, 0xd8, 0xac,
+	0x14, 0xd2, 0xa4, 0xaf, 0x49, 0x14, 0x4c, 0xba, 0xcc, 0xa3, 0x4a, 0x97, 0x31, 0x64, 0xae, 0x6f,
+	0x38, 0xad, 0x55, 0xd7, 0x75, 0x5c, 0xaf, 0x52, 0x4c, 0x9b, 0xeb, 0x35, 0x81, 0x66, 0x73, 0x1d,
+	0x52, 0xab, 0x73, 0x1d, 0x82, 0xb9, 0xbd, 0x8d, 0xc0, 0xbe, 0x8c, 0x0d, 0x0f, 0x9b, 0x15, 0x18,
+	0x61, 0x6f, 0x48, 0x11, 0xda, 0x1b, 0x42, 0x12, 0xf6, 0x86, 0x18, 0x64, 0xc2, 0x14, 0xfb, 0x5e,
+	0xf1, 0x3c, 0xab, 0x63, 0x63, 0xb3, 0x52, 0xa2, 0xf2, 0x4f, 0xa4, 0xc9, 0x17, 0x34, 0xf5, 0x13,
+	0xc3, 0x41, 0xb5, 0xa2, 0xf2, 0x29, 0x3a, 0x62, 0x32, 0xd1, 0x87, 0x30, 0xc9, 0x20, 0x8d, 0xc0,
+	0xb6, 0x2d, 0xbb, 0x53, 0x99, 0xa0, 0x4a, 0x9e, 0x48, 0x53, 0xc2, 0x49, 0xea, 0x4f, 0x0c, 0x07,
+	0xd5, 0x63, 0x0a, 0x97, 0xa2, 0x42, 0x15, 0x48, 0x22, 0x06, 0x03, 0x44, 0x0b, 0x3b, 0x99, 0x16,
+	0x31, 0xd6, 0x54, 0x22, 0x16, 0x31, 0x62, 0x9c, 0x6a, 0xc4, 0x88, 0x21, 0xa3, 0xf5, 0xe0, 0x8b,
+	0x3c, 0x35, 0x7a, 0x3d, 0xf8, 0x3a, 0x4b, 0xeb, 0x91, 0xb2, 0xd4, 0x8a, 0x34, 0xf4, 0x31, 0x90,
+	0x83, 0xe7, 0x7c, 0xd0, 0xef, 0x5a, 0x6d, 0xc3, 0xc7, 0xe7, 0xb1, 0x8f, 0xdb, 0x24, 0x52, 0x4f,
+	0x53, 0x2d, 0x7a, 0x42, 0x4b, 0x82, 0xb2, 0xae, 0x0f, 0x07, 0xd5, 0xc5, 0x34, 0x19, 0x8a, 0xd6,
+	0x54, 0x2d, 0xe8, 0x7f, 0x34, 0x98, 0xf3, 0x7c, 0xc3, 0x36, 0x8d, 0xae, 0x63, 0xe3, 0x4b, 0x76,
+	0xc7, 0xc5, 0x9e, 0x77, 0xc9, 0xde, 0x76, 0x2a, 0x65, 0xaa, 0xff, 0x74, 0x2c, 0xac, 0xa7, 0x91,
+	0xd6, 0x4f, 0x0f, 0x07, 0xd5, 0x6a, 0xaa, 0x14, 0xc5, 0x82, 0x74, 0x45, 0x68, 0x1f, 0x66, 0x44,
+	0x56, 0xb1, 0xe5, 0x5b, 0x5d, 0xcb, 0x33, 0x7c, 0xcb, 0xb1, 0x2b, 0x47, 0xa9, 0xfe, 0x53, 0xf1,
+	0xe8, 0x98, 0x20, 0xac, 0x9f, 0x1a, 0x0e, 0xaa, 0x27, 0x53, 0x24, 0x28, 0xba, 0xd3, 0x54, 0x44,
+	0x2e, 0xb4, 0xe1, 0x62, 0x42, 0x88, 0xcd, 0xca, 0xcc, 0x68, 0x17, 0x0a, 0x89, 0x64, 0x17, 0x0a,
+	0x81, 0x69, 0x2e, 0x14, 0x22, 0x89, 0xa6, 0xbe, 0xe1, 0xfa, 0x16, 0x51, 0xbb, 0x6e, 0xb8, 0xbb,
+	0xd8, 0xad, 0xcc, 0xa6, 0x69, 0xda, 0x50, 0x89, 0x98, 0xa6, 0x18, 0xa7, 0xaa, 0x29, 0x86, 0x44,
+	0xb7, 0x34, 0x50, 0x4d, 0xb3, 0x1c, 0xbb, 0x41, 0xd2, 0x06, 0x8f, 0x0c, 0x6f, 0x8e, 0x2a, 0x7d,
+	0xea, 0x36, 0xc3, 0x93, 0xc9, 0xeb, 0x4f, 0x0d, 0x07, 0xd5, 0xd3, 0x23, 0xa5, 0x29, 0x86, 0x8c,
+	0x56, 0x8a, 0xae, 0x43, 0x89, 0x20, 0x31, 0x4d, 0xc0, 0xcc, 0xca, 0x3c, 0xb5, 0xe1, 0x78, 0xd2,
+	0x06, 0x4e, 0x40, 0x33, 0x90, 0x39, 0x89, 0x43, 0xd1, 0x23, 0x8b, 0x42, 0x9f, 0x6a, 0x40, 0xb6,
+	0x59, 0xda, 0x48, 0x8f, 0x51, 0x2d, 0x4f, 0x26, 0xb4, 0xa4, 0x0d, 0xf3, 0xc9, 0xe1, 0xa0, 0xba,
+	0x94, 0x2e, 0x47, 0xd1, 0x3d, 0x42, 0x57, 0xe4, 0x47, 0xe1, 0x11, 0x55, 0xa9, 0x8c, 0xf6, 0xa3,
+	0x90, 0x48, 0xf6, 0xa3, 0x10, 0x98, 0xe6, 0x47, 0x11, 0x47, 0x1e, 0xc6, 0xa9, 0x2c, 0x7d, 0x98,
+	0x83, 0x99, 0x94, 0xcd, 0x80, 0xde, 0x84, 0x9c, 0x1b, 0xd8, 0x24, 0x43, 0x65, 0x69, 0x19, 0x52,
+	0x2d, 0xd8, 0x0a, 0x2c, 0x93, 0xa5, 0xc7, 0x6e, 0x60, 0x2b, 0x49, 0xeb, 0x38, 0x05, 0x10, 0x7e,
+	0x92, 0x1e, 0x5b, 0x26, 0x4f, 0xbf, 0x46, 0xf2, 0xdf, 0x70, 0x5a, 0x2a, 0x3f, 0x05, 0x20, 0x0c,
+	0x93, 0x62, 0xa7, 0x35, 0x2d, 0x12, 0x46, 0xb2, 0x69, 0xeb, 0xf0, 0x4e, 0xd0, 0xc2, 0xae, 0x8d,
+	0x7d, 0xec, 0x89, 0x31, 0xd0, 0x38, 0x42, 0xc3, 0xa6, 0x2b, 0x41, 0x24, 0xf9, 0x13, 0x32, 0x1c,
+	0x7d, 0xa1, 0x41, 0xa5, 0x67, 0xec, 0x37, 0x05, 0xd0, 0x6b, 0x6e, 0x3b, 0x6e, 0xb3, 0x8f, 0x5d,
+	0xcb, 0x31, 0x69, 0xb6, 0x5d, 0x3a, 0xfb, 0x1f, 0x77, 0x8c, 0x1c, 0xb5, 0x75, 0x63, 0x5f, 0x80,
+	0xbd, 0xb7, 0x1d, 0x77, 0x83, 0xb2, 0xaf, 0xda, 0xbe, 0x7b, 0x50, 0x3f, 0xf9, 0xf5, 0xa0, 0x7a,
+	0x84, 0xf8, 0x61, 0x2f, 0x8d, 0xa6, 0x91, 0x0e, 0x46, 0x3f, 0xd0, 0x60, 0xde, 0x77, 0x7c, 0xa3,
+	0xdb, 0x6c, 0x07, 0xbd, 0xa0, 0x6b, 0xf8, 0xd6, 0x1e, 0x6e, 0x06, 0x9e, 0xd1, 0xc1, 0x3c, 0xa9,
+	0x7f, 0xe3, 0xce, 0x46, 0x5d, 0x23, 0xfc, 0xe7, 0x42, 0xf6, 0x2d, 0xc2, 0xcd, 0x6c, 0x3a, 0xc1,
+	0x6d, 0x9a, 0xf5, 0x53, 0x48, 0x1a, 0xa9, 0xd0, 0x85, 0x9f, 0x6a, 0xb0, 0x30, 0x7a, 0x98, 0xe8,
+	0x34, 0x64, 0x77, 0xf1, 0x01, 0x2f, 0x9b, 0x8e, 0x0e, 0x07, 0xd5, 0xc9, 0x5d, 0x7c, 0x20, 0xcd,
+	0x3a, 0xc1, 0xa2, 0xff, 0x82, 0xf1, 0x3d, 0xa3, 0x1b, 0x60, 0xee, 0x12, 0xb5, 0x1a, 0x2b, 0x10,
+	0x6b, 0x72, 0x81, 0x58, 0xeb, 0xef, 0x76, 0x08, 0xa0, 0x26, 0x56, 0xa4, 0xf6, 0x6e, 0x60, 0xd8,
+	0xbe, 0xe5, 0x1f, 0x30, 0x77, 0xa1, 0x02, 0x64, 0x77, 0xa1, 0x80, 0xd7, 0x33, 0xaf, 0x6a, 0x0b,
+	0x5f, 0x6a, 0x70, 0x7c, 0xe4, 0xa0, 0xbf, 0x0f, 0x16, 0xea, 0x4d, 0x18, 0x23, 0x8e, 0x4f, 0x0a,
+	0xba, 0x1d, 0xab, 0xb3, 0xf3, 0xca, 0x4b, 0xd4, 0x9c, 0x1c, 0xab, 0xbf, 0x18, 0x44, 0xae, 0xbf,
+	0x18, 0x84, 0x14, 0xa5, 0x5d, 0xe7, 0xe6, 0x2b, 0x2f, 0x51, 0xa3, 0x72, 0x4c, 0x09, 0x05, 0xc8,
+	0x4a, 0x28, 0x40, 0xff, 0x7b, 0x0e, 0x8a, 0x61, 0xc5, 0x24, 0xed, 0x41, 0xed, 0x9e, 0xf6, 0xe0,
+	0x45, 0x28, 0x9b, 0xd8, 0xe4, 0x47, 0xbd, 0xe5, 0xd8, 0x62, 0x37, 0x17, 0x59, 0xc0, 0x51, 0x70,
+	0x0a, 0xff, 0x74, 0x0c, 0x85, 0xce, 0x42, 0x81, 0x57, 0x16, 0x07, 0x74, 0x23, 0x4f, 0xd6, 0xe7,
+	0x87, 0x83, 0x2a, 0x12, 0x30, 0x89, 0x35, 0xa4, 0x43, 0x0d, 0x00, 0x56, 0xae, 0xaf, 0x63, 0xdf,
+	0xe0, 0x35, 0x4e, 0x45, 0x1d, 0xc1, 0xd5, 0x10, 0xcf, 0x0a, 0xef, 0x88, 0x5e, 0x2e, 0xbc, 0x23,
+	0x28, 0xfa, 0x00, 0xa0, 0x67, 0x58, 0x36, 0xe3, 0xe3, 0x05, 0x8d, 0x3e, 0x2a, 0xa4, 0xac, 0x87,
+	0x94, 0x4c, 0x7a, 0xc4, 0x29, 0x4b, 0x8f, 0xa0, 0xa4, 0x3c, 0xe6, 0x0d, 0x86, 0x4a, 0x8e, 0xee,
+	0xd2, 0xc5, 0x51, 0xa2, 0xb9, 0xd8, 0x39, 0x52, 0x22, 0x73, 0x16, 0x49, 0xa6, 0x90, 0x42, 0xa6,
+	0xad, 0x6b, 0x6d, 0x63, 0xdf, 0xea, 0x61, 0x5a, 0xca, 0xf0, 0x69, 0x13, 0x30, 0x79, 0xda, 0x04,
+	0x0c, 0xbd, 0x0a, 0x60, 0xf8, 0xeb, 0x8e, 0xe7, 0x5f, 0xb5, 0xdb, 0x98, 0x96, 0x28, 0x05, 0x66,
+	0x7e, 0x04, 0x95, 0xcd, 0x8f, 0xa0, 0xe8, 0x0d, 0x28, 0xf5, 0xf9, 0xa1, 0xd4, 0xea, 0x62, 0x5a,
+	0x82, 0x14, 0xd8, 0x19, 0x2a, 0x81, 0x25, 0x5e, 0x99, 0x1a, 0x5d, 0x80, 0xe9, 0xb6, 0x63, 0xb7,
+	0x03, 0xd7, 0xc5, 0x76, 0xfb, 0x60, 0xd3, 0xd8, 0xc6, 0xb4, 0xdc, 0x28, 0x30, 0x57, 0x89, 0xa1,
+	0x64, 0x57, 0x89, 0xa1, 0xd0, 0xcb, 0x50, 0x0c, 0xdb, 0x35, 0xb4, 0xa2, 0x28, 0xf2, 0xca, 0x5f,
+	0x00, 0x25, 0xe6, 0x88, 0x92, 0x18, 0x6f, 0x79, 0x61, 0x5a, 0x4a, 0xab, 0x04, 0x6e, 0xbc, 0x04,
+	0x96, 0x8d, 0x97, 0xc0, 0xe8, 0x12, 0x1c, 0xa5, 0x89, 0x40, 0xd3, 0xf7, 0xbb, 0x4d, 0x0f, 0xb7,
+	0x1d, 0xdb, 0xf4, 0x68, 0x11, 0x90, 0x65, 0xe6, 0x53, 0xe4, 0x35, 0xbf, 0xbb, 0xc9, 0x50, 0xb2,
+	0xf9, 0x31, 0x94, 0xfe, 0x6b, 0x0d, 0x66, 0xd3, 0x5c, 0x28, 0xe6, 0xce, 0xda, 0x03, 0x71, 0xe7,
+	0xf7, 0xa0, 0xd0, 0x77, 0xcc, 0xa6, 0xd7, 0xc7, 0x6d, 0x1e, 0xb1, 0x62, 0xce, 0xbc, 0xe1, 0x98,
+	0x9b, 0x7d, 0xdc, 0xfe, 0x4f, 0xcb, 0xdf, 0x59, 0xd9, 0x73, 0x2c, 0xf3, 0xb2, 0xe5, 0x71, 0xaf,
+	0xeb, 0x33, 0x8c, 0x92, 0x25, 0xe4, 0x39, 0xb0, 0x5e, 0x80, 0x1c, 0xd3, 0xa2, 0xff, 0x26, 0x0b,
+	0xe5, 0xb8, 0xdb, 0xfe, 0x2b, 0x0d, 0x05, 0x5d, 0x87, 0xbc, 0xc5, 0x6a, 0x04, 0x9e, 0x41, 0xfc,
+	0x9b, 0x14, 0xd3, 0x6b, 0x51, 0x87, 0xb3, 0xb6, 0xf7, 0x42, 0x8d, 0x17, 0x13, 0x74, 0x0a, 0xa8,
+	0x64, 0xce, 0xa9, 0x4a, 0xe6, 0x40, 0xd4, 0x80, 0xbc, 0x87, 0xdd, 0x3d, 0xab, 0x8d, 0x79, 0x70,
+	0xaa, 0xca, 0x92, 0xdb, 0x8e, 0x8b, 0x89, 0xcc, 0x4d, 0x46, 0x12, 0xc9, 0xe4, 0x3c, 0xaa, 0x4c,
+	0x0e, 0x44, 0xef, 0x41, 0xb1, 0xed, 0xd8, 0xdb, 0x56, 0x67, 0xdd, 0xe8, 0xf3, 0xf0, 0x74, 0x32,
+	0x4d, 0xea, 0x39, 0x41, 0xc4, 0xbb, 0x2e, 0xe2, 0x33, 0xd6, 0x75, 0x09, 0xa9, 0xa2, 0x05, 0xfd,
+	0xf3, 0x18, 0x40, 0xb4, 0x38, 0xe8, 0x35, 0x28, 0xe1, 0x7d, 0xdc, 0x0e, 0x7c, 0xc7, 0x15, 0xe7,
+	0x04, 0x6f, 0x62, 0x0a, 0xb0, 0x12, 0xd8, 0x21, 0x82, 0x92, 0x8d, 0x6a, 0x1b, 0x3d, 0xec, 0xf5,
+	0x8d, 0xb6, 0xe8, 0x7e, 0x52, 0x63, 0x42, 0xa0, 0xbc, 0x51, 0x43, 0x20, 0xfa, 0x77, 0x18, 0xa3,
+	0xfd, 0x52, 0xd6, 0xf8, 0x44, 0xc3, 0x41, 0x75, 0xca, 0x56, 0x3b, 0xa5, 0x14, 0x8f, 0xde, 0x82,
+	0xc9, 0xdd, 0xd0, 0xf1, 0x88, 0x6d, 0x63, 0x94, 0x81, 0xa6, 0x76, 0x11, 0x42, 0xb1, 0x6e, 0x42,
+	0x86, 0xa3, 0x6d, 0x28, 0x19, 0xb6, 0xed, 0xf8, 0xf4, 0x0c, 0x12, 0xcd, 0xd0, 0xa7, 0x47, 0xb9,
+	0x69, 0x6d, 0x25, 0xa2, 0x65, 0x59, 0x12, 0x0d, 0x1e, 0x92, 0x04, 0x39, 0x78, 0x48, 0x60, 0xd4,
+	0x80, 0x5c, 0xd7, 0x68, 0xe1, 0xae, 0x08, 0xfa, 0x4f, 0x8e, 0x54, 0x71, 0x99, 0x92, 0x31, 0xe9,
+	0xf4, 0xc8, 0x67, 0x7c, 0xf2, 0x91, 0xcf, 0x20, 0x0b, 0xdb, 0x50, 0x8e, 0xdb, 0x73, 0xb8, 0x04,
+	0xe6, 0x69, 0x39, 0x81, 0x29, 0xde, 0x31, 0x65, 0x32, 0xa0, 0x24, 0x19, 0xf5, 0x30, 0x54, 0xe8,
+	0x3f, 0xd3, 0x60, 0x36, 0x6d, 0xef, 0xa2, 0x75, 0x69, 0xc7, 0x6b, 0xbc, 0xa9, 0x93, 0xe2, 0xea,
+	0x9c, 0x77, 0xc4, 0x56, 0x8f, 0x36, 0x7a, 0x1d, 0xa6, 0x6c, 0xc7, 0xc4, 0x4d, 0x83, 0x28, 0xe8,
+	0x5a, 0x9e, 0x5f, 0xc9, 0xd0, 0x66, 0x39, 0x6d, 0x06, 0x11, 0xcc, 0x8a, 0x40, 0x48, 0xdc, 0x93,
+	0x0a, 0x42, 0xff, 0x3f, 0x0d, 0xa6, 0x63, 0xbd, 0xda, 0xfb, 0x4e, 0xa2, 0xe4, 0xd4, 0x27, 0x73,
+	0xb8, 0xd4, 0x47, 0xff, 0x51, 0x06, 0x4a, 0x52, 0x21, 0x7b, 0xdf, 0x36, 0xdc, 0x80, 0x69, 0x7e,
+	0x52, 0x5a, 0x76, 0x87, 0x95, 0x53, 0x19, 0xde, 0x95, 0x49, 0x5c, 0x8d, 0xac, 0x39, 0xad, 0xcd,
+	0x90, 0x96, 0x56, 0x53, 0xb4, 0x65, 0xe7, 0x29, 0x30, 0x49, 0xc5, 0x94, 0x8a, 0x41, 0xd7, 0x61,
+	0x3e, 0xe8, 0x9b, 0x86, 0x8f, 0x9b, 0x1e, 0xbf, 0x64, 0x68, 0xda, 0x41, 0xaf, 0x85, 0x5d, 0xba,
+	0xe3, 0xc7, 0x59, 0x93, 0x89, 0x51, 0x88, 0x5b, 0x88, 0x2b, 0x14, 0x2f, 0xc9, 0x9c, 0x4d, 0xc3,
+	0xeb, 0x17, 0x01, 0x25, 0x1b, 0xe9, 0xca, 0xfc, 0x6a, 0x87, 0x9c, 0xdf, 0xcf, 0x34, 0x28, 0xc7,
+	0xfb, 0xe3, 0x8f, 0x64, 0xa1, 0x0f, 0xa0, 0x18, 0xf6, 0xba, 0xef, 0xdb, 0x80, 0x67, 0x21, 0xe7,
+	0x62, 0xc3, 0x73, 0x6c, 0xbe, 0x33, 0x69, 0x88, 0x61, 0x10, 0x39, 0xc4, 0x30, 0x88, 0x7e, 0x0d,
+	0x26, 0xd8, 0x0c, 0xbe, 0x6d, 0x75, 0x7d, 0xec, 0xa2, 0xf3, 0x90, 0xf3, 0x7c, 0xc3, 0xc7, 0x5e,
+	0x45, 0x5b, 0xca, 0x9e, 0x99, 0x3a, 0x3b, 0x9f, 0x6c, 0x6b, 0x13, 0x34, 0x93, 0xca, 0x28, 0x65,
+	0xa9, 0x0c, 0xa2, 0xff, 0xaf, 0x06, 0x13, 0x72, 0xf7, 0xfe, 0xc1, 0x88, 0xbd, 0xcb, 0xa1, 0x7d,
+	0x2c, 0x6c, 0xe8, 0x3e, 0x98, 0x95, 0xbd, 0x3b, 0xed, 0xbf, 0xd0, 0xd8, 0xcc, 0x86, 0x6d, 0xdf,
+	0xfb, 0x55, 0xdf, 0x89, 0x5a, 0x21, 0x64, 0x87, 0x79, 0x34, 0xb0, 0x1d, 0xb6, 0x15, 0x42, 0xc3,
+	0x9f, 0xc2, 0x2e, 0x87, 0x3f, 0x05, 0xa1, 0x7f, 0x31, 0x4e, 0x2d, 0x8f, 0x5a, 0xfc, 0x8f, 0xba,
+	0x09, 0x14, 0xcb, 0x4e, 0xb2, 0x77, 0x91, 0x9d, 0x3c, 0x07, 0x79, 0x7a, 0x1c, 0x84, 0x89, 0x03,
+	0x5d, 0x34, 0x02, 0x52, 0xaf, 0x58, 0x19, 0xe4, 0x36, 0x51, 0x6b, 0xfc, 0xfe, 0xa2, 0x16, 0x6a,
+	0xc2, 0xf1, 0x1d, 0xc3, 0x6b, 0x8a, 0x38, 0x6b, 0x36, 0x0d, 0xbf, 0x19, 0xc6, 0x89, 0x1c, 0x2d,
+	0x53, 0x68, 0xdb, 0x70, 0xc7, 0xf0, 0x36, 0x05, 0xcd, 0x8a, 0xbf, 0x91, 0x8c, 0x1a, 0xf3, 0xe9,
+	0x14, 0x68, 0x0b, 0xe6, 0xd2, 0x85, 0xe7, 0xa9, 0xe5, 0xb4, 0xab, 0xed, 0xdd, 0x56, 0xf2, 0x4c,
+	0x0a, 0x1a, 0x7d, 0xa2, 0x41, 0x85, 0x9c, 0xcf, 0x2e, 0xfe, 0x28, 0xb0, 0x5c, 0xdc, 0x23, 0x2b,
+	0xd6, 0x74, 0xf6, 0xb0, 0xdb, 0x35, 0x0e, 0xf8, 0xf5, 0xd4, 0xa9, 0xe4, 0xe9, 0xb1, 0xe1, 0x98,
+	0x0d, 0x89, 0x81, 0x0d, 0xad, 0xaf, 0x02, 0xaf, 0x32, 0x21, 0xf2, 0xd0, 0xd2, 0x29, 0xd6, 0xc6,
+	0x0a, 0x85, 0x72, 0x51, 0xff, 0xab, 0x06, 0x53, 0xea, 0x2d, 0xd2, 0x23, 0x77, 0xcc, 0xc4, 0x96,
+	0xcc, 0x3e, 0xa4, 0x2d, 0xf9, 0x17, 0x0d, 0x26, 0x95, 0xcb, 0xad, 0xc7, 0x67, 0xe8, 0x3f, 0xce,
+	0xc0, 0x7c, 0xba, 0x98, 0x87, 0x52, 0x80, 0x5e, 0x04, 0x92, 0x4a, 0x5e, 0x8a, 0x72, 0xa3, 0xb9,
+	0x44, 0xfd, 0x49, 0x87, 0x20, 0xf2, 0xd0, 0xc4, 0xad, 0x94, 0x60, 0x47, 0xd7, 0xa1, 0x64, 0x49,
+	0xf7, 0x5f, 0xd9, 0xb4, 0x6b, 0x0a, 0xf9, 0xd6, 0x8b, 0x75, 0x29, 0x46, 0xdc, 0x75, 0xc9, 0xa2,
+	0xea, 0x39, 0x18, 0x23, 0xc9, 0x9b, 0xbe, 0x07, 0x79, 0x6e, 0x0e, 0x7a, 0x11, 0x8a, 0x34, 0xce,
+	0xd1, 0x9a, 0x8a, 0x25, 0xee, 0x34, 0xed, 0x20, 0xc0, 0xd8, 0x0b, 0x94, 0x82, 0x80, 0xa1, 0x57,
+	0x00, 0xc8, 0xd6, 0xe6, 0x11, 0x2e, 0x43, 0xe3, 0x04, 0xad, 0xdd, 0xfa, 0x8e, 0x99, 0x08, 0x6b,
+	0xc5, 0x10, 0xa8, 0xff, 0x3c, 0x03, 0x25, 0xf9, 0xc6, 0xed, 0x9e, 0x94, 0x7f, 0x0c, 0xa2, 0xae,
+	0x6e, 0x1a, 0xa6, 0x49, 0xfe, 0x62, 0x71, 0xa4, 0x2d, 0x8f, 0x9c, 0x24, 0xf1, 0xff, 0x8a, 0xe0,
+	0x60, 0x55, 0x14, 0x7d, 0xd3, 0x60, 0xc5, 0x50, 0x92, 0xd6, 0x72, 0x1c, 0xb7, 0xb0, 0x0b, 0x73,
+	0xa9, 0xa2, 0xe4, 0xda, 0x67, 0xfc, 0x41, 0xd5, 0x3e, 0xbf, 0x1c, 0x87, 0xb9, 0xd4, 0x9b, 0xce,
+	0x47, 0xbe, 0x8b, 0xd5, 0x1d, 0x94, 0x7d, 0x20, 0x3b, 0xe8, 0x33, 0x2d, 0x6d, 0x65, 0xd9, 0x25,
+	0xca, 0x6b, 0x87, 0xb8, 0xfe, 0x7d, 0x50, 0x6b, 0xac, 0xba, 0xe5, 0xf8, 0x3d, 0xed, 0x89, 0xdc,
+	0x61, 0xf7, 0x04, 0x7a, 0x9e, 0x95, 0xb1, 0x54, 0x57, 0x9e, 0xea, 0x12, 0x11, 0x22, 0xa6, 0x2a,
+	0xcf, 0x41, 0xe8, 0x2d, 0x98, 0x14, 0x1c, 0xac, 0x79, 0x52, 0x88, 0x3a, 0x1b, 0x9c, 0x26, 0xde,
+	0x3f, 0x99, 0x90, 0xe1, 0xff, 0x5c, 0x1f, 0xfe, 0x9b, 0x06, 0xd3, 0xb1, 0xa7, 0x0f, 0x8f, 0xcf,
+	0x19, 0xf4, 0xb9, 0x06, 0xc5, 0xf0, 0xd5, 0xcd, 0x7d, 0x27, 0xf2, 0x2b, 0x90, 0xc3, 0xec, 0xe5,
+	0x07, 0x0b, 0x77, 0x33, 0xb1, 0x97, 0x79, 0x04, 0xc7, 0xdf, 0xe2, 0xc5, 0x1e, 0x7b, 0x34, 0x38,
+	0xa3, 0xfe, 0x5b, 0x4d, 0xa4, 0xe8, 0x91, 0x4d, 0x8f, 0x74, 0x29, 0xa2, 0x31, 0x65, 0xef, 0x75,
+	0x4c, 0xbf, 0x2a, 0xc2, 0x38, 0xa5, 0x23, 0x25, 0xb4, 0x8f, 0xdd, 0x9e, 0x65, 0x1b, 0x5d, 0x3a,
+	0x9c, 0x02, 0xdb, 0xb7, 0x02, 0x26, 0xef, 0x5b, 0x01, 0x43, 0x3b, 0x30, 0x1d, 0xb5, 0xfd, 0xa8,
+	0x98, 0xf4, 0x07, 0x7f, 0xef, 0xa8, 0x44, 0xac, 0xb1, 0x1f, 0xe3, 0x54, 0xef, 0xcc, 0x63, 0x48,
+	0x64, 0xc2, 0x54, 0xdb, 0xb1, 0x7d, 0xc3, 0xb2, 0xb1, 0xcb, 0x14, 0x65, 0xd3, 0x1e, 0x3c, 0x9d,
+	0x53, 0x68, 0x58, 0xf7, 0x44, 0xe5, 0x53, 0x1f, 0x3c, 0xa9, 0x38, 0xf4, 0x21, 0x4c, 0x8a, 0x32,
+	0x86, 0x29, 0x19, 0x4b, 0x7b, 0xf0, 0xb4, 0x2a, 0x93, 0x30, 0x97, 0x56, 0xb8, 0xd4, 0x07, 0x4f,
+	0x0a, 0x0a, 0x75, 0xa1, 0xdc, 0x77, 0xcc, 0x2d, 0x9b, 0x27, 0xef, 0x46, 0xab, 0x8b, 0x79, 0xaf,
+	0x79, 0x31, 0x91, 0xf2, 0x28, 0x54, 0x2c, 0x14, 0xc7, 0x79, 0xd5, 0x27, 0x84, 0x71, 0x2c, 0xfa,
+	0x00, 0x26, 0xba, 0xa4, 0x9a, 0x5c, 0xdd, 0xef, 0x5b, 0x2e, 0x36, 0xd3, 0x1f, 0xfc, 0x5d, 0x96,
+	0x28, 0x58, 0x20, 0x94, 0x79, 0xd4, 0x47, 0x4f, 0x32, 0x86, 0xac, 0x7e, 0xcf, 0xd8, 0x6f, 0x04,
+	0xb6, 0xb7, 0xba, 0xcf, 0x1f, 0x6f, 0xe5, 0xd3, 0x56, 0x7f, 0x5d, 0x25, 0x62, 0xab, 0x1f, 0xe3,
+	0x54, 0x57, 0x3f, 0x86, 0x44, 0x97, 0x69, 0x9c, 0x67, 0x4b, 0xc2, 0x1e, 0xfe, 0xcd, 0x27, 0x66,
+	0x8b, 0xad, 0x06, 0x6b, 0xfb, 0xf0, 0x2f, 0x45, 0x68, 0x28, 0x81, 0xaf, 0x01, 0x1d, 0x76, 0x03,
+	0xfb, 0x81, 0x6b, 0x63, 0x93, 0x17, 0x55, 0xc9, 0x35, 0x50, 0xa8, 0xc2, 0x35, 0x50, 0xa0, 0x89,
+	0x35, 0x50, 0xb0, 0xc4, 0xa7, 0xfa, 0x8e, 0x79, 0x8d, 0x6d, 0x19, 0x3f, 0x7c, 0x09, 0xf8, 0x44,
+	0x42, 0x55, 0x44, 0xc2, 0x7c, 0x4a, 0xe1, 0x52, 0x7d, 0x4a, 0x41, 0xf1, 0xc7, 0x67, 0xf2, 0x53,
+	0x25, 0x36, 0x53, 0xa5, 0x11, 0x8f, 0xcf, 0x12, 0x94, 0xe1, 0xe3, 0xb3, 0x04, 0x26, 0xf1, 0xf8,
+	0x2c, 0x41, 0x41, 0xb4, 0x77, 0x0c, 0xbb, 0xb3, 0xe6, 0xb4, 0x54, 0xaf, 0x9e, 0x48, 0xd3, 0x7e,
+	0x21, 0x85, 0x92, 0x69, 0x4f, 0x93, 0xa1, 0x6a, 0x4f, 0xa3, 0xa8, 0x17, 0x44, 0x7b, 0x48, 0xff,
+	0x52, 0x83, 0xe9, 0x58, 0x9c, 0x41, 0x6f, 0x42, 0xf8, 0xe2, 0xe4, 0xda, 0x41, 0x5f, 0xa4, 0xc9,
+	0xca, 0x0b, 0x15, 0x02, 0x4f, 0x7b, 0xa1, 0x42, 0xe0, 0xe8, 0x32, 0x40, 0x78, 0x26, 0xdd, 0x2e,
+	0x48, 0xd3, 0x1c, 0x2d, 0xa2, 0x94, 0x73, 0xb4, 0x08, 0xaa, 0xdf, 0x1a, 0x83, 0x82, 0x70, 0xd4,
+	0x87, 0x52, 0x46, 0x2d, 0x43, 0xbe, 0x87, 0x3d, 0xfa, 0x52, 0x25, 0x13, 0x65, 0x43, 0x1c, 0x24,
+	0x67, 0x43, 0x1c, 0xa4, 0x26, 0x6b, 0xd9, 0x7b, 0x4a, 0xd6, 0xc6, 0x0e, 0x9d, 0xac, 0x61, 0x7a,
+	0x4b, 0x2d, 0x85, 0x5b, 0x71, 0x2f, 0x74, 0xfb, 0x18, 0x2e, 0xee, 0xb0, 0x65, 0xc6, 0xd8, 0x1d,
+	0xb6, 0x8c, 0x42, 0xbb, 0x70, 0x54, 0xba, 0xbb, 0xe2, 0xbd, 0x43, 0x12, 0xf8, 0xa6, 0x46, 0x3f,
+	0x09, 0x68, 0x50, 0x2a, 0xb6, 0xbd, 0x77, 0x63, 0x50, 0x39, 0xdb, 0x8d, 0xe3, 0x88, 0x83, 0x99,
+	0xb8, 0x15, 0x74, 0xd6, 0xf9, 0xb4, 0xe7, 0x23, 0x07, 0x93, 0xe1, 0xb2, 0x83, 0xc9, 0x70, 0xfd,
+	0x4f, 0x19, 0x98, 0x52, 0xc7, 0xfb, 0x50, 0x1c, 0xe3, 0x45, 0x28, 0xe2, 0x7d, 0xcb, 0x6f, 0xb6,
+	0x1d, 0x13, 0xf3, 0x92, 0x93, 0xae, 0x33, 0x01, 0x9e, 0x73, 0x4c, 0x65, 0x9d, 0x05, 0x4c, 0xf6,
+	0xa6, 0xec, 0xa1, 0xbc, 0x29, 0x6a, 0xd5, 0x8e, 0xdd, 0xb9, 0x55, 0x9b, 0xbe, 0x4e, 0xc5, 0x87,
+	0xb3, 0x4e, 0xfa, 0x37, 0x19, 0x28, 0xc7, 0xa3, 0xf9, 0xf7, 0x63, 0x0b, 0xaa, 0xbb, 0x29, 0x7b,
+	0xe8, 0xdd, 0xf4, 0x16, 0x4c, 0x92, 0xdc, 0xd3, 0xf0, 0x7d, 0xfe, 0xe8, 0x75, 0x8c, 0xe6, 0x6c,
+	0x2c, 0xb6, 0x05, 0xf6, 0x8a, 0x80, 0x2b, 0xb1, 0x4d, 0x82, 0x27, 0x5c, 0x77, 0xfc, 0x2e, 0x5d,
+	0xf7, 0x93, 0x0c, 0x4c, 0x2a, 0xa7, 0xd6, 0xe3, 0x17, 0xd2, 0xf4, 0x69, 0x98, 0x54, 0x92, 0x41,
+	0xfd, 0x53, 0xe6, 0x67, 0x6a, 0x16, 0xf6, 0xf8, 0xcd, 0xcb, 0x14, 0x4c, 0xc8, 0x59, 0xa5, 0x5e,
+	0x87, 0xe9, 0x58, 0x12, 0x28, 0x0f, 0x40, 0x3b, 0xcc, 0x00, 0xf4, 0x79, 0x98, 0x4d, 0xcb, 0x5d,
+	0xf4, 0x0b, 0x30, 0x9b, 0x96, 0x55, 0xdc, 0xbd, 0x82, 0xaf, 0x34, 0xaa, 0x21, 0xf9, 0xbc, 0xfe,
+	0x22, 0x80, 0x8d, 0x6f, 0x36, 0xef, 0x58, 0x7e, 0xb2, 0xf9, 0xc4, 0x37, 0xd7, 0x62, 0xd5, 0x5a,
+	0x41, 0xc0, 0x88, 0x24, 0xa7, 0x6b, 0x36, 0xef, 0x58, 0xf4, 0x51, 0x49, 0x4e, 0xd7, 0x4c, 0x48,
+	0x12, 0x30, 0xfd, 0xff, 0xb3, 0xa2, 0x33, 0x10, 0xbd, 0x4f, 0x7f, 0x1f, 0xca, 0x7d, 0xf1, 0x71,
+	0x67, 0x6b, 0x69, 0x6d, 0x14, 0xd2, 0xc7, 0x35, 0x4d, 0xa9, 0x18, 0x55, 0x36, 0x2f, 0x7a, 0x33,
+	0x87, 0x94, 0xdd, 0x88, 0x55, 0xbf, 0x53, 0x2a, 0x06, 0xfd, 0x37, 0x1c, 0x15, 0xaf, 0xd9, 0xf6,
+	0xb0, 0x30, 0x3c, 0x3b, 0x52, 0x38, 0x7b, 0x4e, 0x1f, 0x32, 0xc4, 0x2d, 0x9f, 0x8e, 0xa1, 0x62,
+	0xe2, 0xb9, 0xed, 0x63, 0x87, 0x15, 0x1f, 0x37, 0x7e, 0x3a, 0x86, 0xd2, 0x3f, 0xd7, 0x60, 0x3a,
+	0xf6, 0xe2, 0x1f, 0x9d, 0x87, 0x02, 0xfd, 0x41, 0xe0, 0xed, 0x57, 0x80, 0x3a, 0x24, 0xa5, 0x53,
+	0x34, 0xe4, 0x39, 0x08, 0xbd, 0x0c, 0xc5, 0xf0, 0x87, 0x01, 0xfc, 0x56, 0x9b, 0x6d, 0x3e, 0x01,
+	0x54, 0x36, 0x9f, 0x00, 0xea, 0x3f, 0xd1, 0xe0, 0xf8, 0xc8, 0x5f, 0x03, 0x3c, 0xea, 0x9e, 0x85,
+	0x7e, 0x1d, 0xe6, 0xd3, 0x1f, 0xf0, 0xdf, 0x6f, 0x87, 0x47, 0xff, 0x61, 0xd8, 0x2c, 0x0b, 0x2f,
+	0xa0, 0x1f, 0xf5, 0x68, 0x9f, 0x79, 0x1e, 0x0a, 0xe2, 0x96, 0x1d, 0x01, 0xe4, 0xde, 0xdd, 0x5a,
+	0xdd, 0x5a, 0x3d, 0x5f, 0x3e, 0x82, 0x4a, 0x90, 0xdf, 0x58, 0xbd, 0x72, 0xfe, 0xd2, 0x95, 0x0b,
+	0x65, 0x8d, 0x7c, 0x34, 0xb6, 0xae, 0x5c, 0x21, 0x1f, 0x99, 0x67, 0x2e, 0xcb, 0x6f, 0xfe, 0x78,
+	0x96, 0x39, 0x01, 0x85, 0x95, 0x7e, 0x9f, 0x86, 0x3b, 0xc6, 0xbb, 0xba, 0x67, 0x91, 0xc8, 0x54,
+	0xd6, 0x50, 0x1e, 0xb2, 0x57, 0xaf, 0xae, 0x97, 0x33, 0x68, 0x16, 0xca, 0xe7, 0xb1, 0x61, 0x76,
+	0x2d, 0x1b, 0x8b, 0x18, 0x5b, 0xce, 0xd6, 0x6f, 0x7c, 0xfd, 0xed, 0xa2, 0xf6, 0xcd, 0xb7, 0x8b,
+	0xda, 0x1f, 0xbf, 0x5d, 0xd4, 0x6e, 0x7d, 0xb7, 0x78, 0xe4, 0x9b, 0xef, 0x16, 0x8f, 0xfc, 0xee,
+	0xbb, 0xc5, 0x23, 0xef, 0x3f, 0x2f, 0xfd, 0xd4, 0x97, 0x8d, 0xa9, 0xef, 0x3a, 0xe4, 0x78, 0xe1,
+	0x5f, 0xcb, 0xf1, 0x1f, 0x37, 0x7f, 0x95, 0x39, 0xb9, 0x42, 0x3f, 0x37, 0x18, 0x5d, 0xed, 0x92,
+	0x53, 0x63, 0x00, 0xfa, 0xfb, 0x54, 0xaf, 0x95, 0xa3, 0xbf, 0x43, 0x7d, 0xf1, 0x1f, 0x01, 0x00,
+	0x00, 0xff, 0xff, 0x21, 0x0e, 0xd1, 0x62, 0x17, 0x3d, 0x00, 0x00,
 }
 
 func (m *EventSequence) Marshal() (dAtA []byte, err error) {
@@ -4420,6 +4490,29 @@ func (m *EventSequence_Event_JobPreemptionRequested) MarshalToSizedBuffer(dAtA [
 	}
 	return len(dAtA) - i, nil
 }
+func (m *EventSequence_Event_JobRunCancelled) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *EventSequence_Event_JobRunCancelled) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	if m.JobRunCancelled != nil {
+		{
+			size, err := m.JobRunCancelled.MarshalToSizedBuffer(dAtA[:i])
+			if err != nil {
+				return 0, err
+			}
+			i -= size
+			i = encodeVarintEvents(dAtA, i, uint64(size))
+		}
+		i--
+		dAtA[i] = 0x1
+		i--
+		dAtA[i] = 0xc2
+	}
+	return len(dAtA) - i, nil
+}
 func (m *ResourceUtilisation) Marshal() (dAtA []byte, err error) {
 	size := m.Size()
 	dAtA = make([]byte, size)
@@ -5253,20 +5346,20 @@ func (m *JobSetFilter) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	var l int
 	_ = l
 	if len(m.States) > 0 {
-		dAtA46 := make([]byte, len(m.States)*10)
-		var j45 int
+		dAtA47 := make([]byte, len(m.States)*10)
+		var j46 int
 		for _, num := range m.States {
 			for num >= 1<<7 {
-				dAtA46[j45] = uint8(uint64(num)&0x7f | 0x80)
+				dAtA47[j46] = uint8(uint64(num)&0x7f | 0x80)
 				num >>= 7
-				j45++
+				j46++
 			}
-			dAtA46[j45] = uint8(num)
-			j45++
+			dAtA47[j46] = uint8(num)
+			j46++
 		}
-		i -= j45
-		copy(dAtA[i:], dAtA46[:j45])
-		i = encodeVarintEvents(dAtA, i, uint64(j45))
+		i -= j46
+		copy(dAtA[i:], dAtA47[:j46])
+		i = encodeVarintEvents(dAtA, i, uint64(j46))
 		i--
 		dAtA[i] = 0xa
 	}
@@ -5301,20 +5394,20 @@ func (m *CancelJobSet) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 		dAtA[i] = 0x12
 	}
 	if len(m.States) > 0 {
-		dAtA48 := make([]byte, len(m.States)*10)
-		var j47 int
+		dAtA49 := make([]byte, len(m.States)*10)
+		var j48 int
 		for _, num := range m.States {
 			for num >= 1<<7 {
-				dAtA48[j47] = uint8(uint64(num)&0x7f | 0x80)
+				dAtA49[j48] = uint8(uint64(num)&0x7f | 0x80)
 				num >>= 7
-				j47++
+				j48++
 			}
-			dAtA48[j47] = uint8(num)
-			j47++
+			dAtA49[j48] = uint8(num)
+			j48++
 		}
-		i -= j47
-		copy(dAtA[i:], dAtA48[:j47])
-		i = encodeVarintEvents(dAtA, i, uint64(j47))
+		i -= j48
+		copy(dAtA[i:], dAtA49[:j48])
+		i = encodeVarintEvents(dAtA, i, uint64(j48))
 		i--
 		dAtA[i] = 0xa
 	}
@@ -7063,6 +7156,53 @@ func (m *JobPreemptionRequested) MarshalToSizedBuffer(dAtA []byte) (int, error)
 	return len(dAtA) - i, nil
 }
 
+func (m *JobRunCancelled) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *JobRunCancelled) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *JobRunCancelled) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.JobId != nil {
+		{
+			size, err := m.JobId.MarshalToSizedBuffer(dAtA[:i])
+			if err != nil {
+				return 0, err
+			}
+			i -= size
+			i = encodeVarintEvents(dAtA, i, uint64(size))
+		}
+		i--
+		dAtA[i] = 0x12
+	}
+	if m.RunId != nil {
+		{
+			size, err := m.RunId.MarshalToSizedBuffer(dAtA[:i])
+			if err != nil {
+				return 0, err
+			}
+			i -= size
+			i = encodeVarintEvents(dAtA, i, uint64(size))
+		}
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
 func encodeVarintEvents(dAtA []byte, offset int, v uint64) int {
 	offset -= sovEvents(v)
 	base := offset
@@ -7387,6 +7527,18 @@ func (m *EventSequence_Event_JobPreemptionRequested) Size() (n int) {
 	}
 	return n
 }
+func (m *EventSequence_Event_JobRunCancelled) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.JobRunCancelled != nil {
+		l = m.JobRunCancelled.Size()
+		n += 2 + l + sovEvents(uint64(l))
+	}
+	return n
+}
 func (m *ResourceUtilisation) Size() (n int) {
 	if m == nil {
 		return 0
@@ -8516,6 +8668,23 @@ func (m *JobPreemptionRequested) Size() (n int) {
 	return n
 }
 
+func (m *JobRunCancelled) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.RunId != nil {
+		l = m.RunId.Size()
+		n += 1 + l + sovEvents(uint64(l))
+	}
+	if m.JobId != nil {
+		l = m.JobId.Size()
+		n += 1 + l + sovEvents(uint64(l))
+	}
+	return n
+}
+
 func sovEvents(x uint64) (n int) {
 	return (math_bits.Len64(x|1) + 6) / 7
 }
@@ -9569,6 +9738,41 @@ func (m *EventSequence_Event) Unmarshal(dAtA []byte) error {
 			}
 			m.Event = &EventSequence_Event_JobPreemptionRequested{v}
 			iNdEx = postIndex
+		case 24:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field JobRunCancelled", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthEvents
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			v := &JobRunCancelled{}
+			if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			m.Event = &EventSequence_Event_JobRunCancelled{v}
+			iNdEx = postIndex
 		default:
 			iNdEx = preIndex
 			skippy, err := skipEvents(dAtA[iNdEx:])
@@ -16759,6 +16963,128 @@ func (m *JobPreemptionRequested) Unmarshal(dAtA []byte) error {
 	}
 	return nil
 }
+func (m *JobRunCancelled) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowEvents
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: JobRunCancelled: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: JobRunCancelled: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field RunId", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthEvents
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if m.RunId == nil {
+				m.RunId = &Uuid{}
+			}
+			if err := m.RunId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field JobId", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthEvents
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if m.JobId == nil {
+				m.JobId = &Uuid{}
+			}
+			if err := m.JobId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipEvents(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
 func skipEvents(dAtA []byte) (n int, err error) {
 	l := len(dAtA)
 	iNdEx := 0
diff --git a/pkg/armadaevents/events.proto b/pkg/armadaevents/events.proto
index 1321ae86..fa9e1bc5 100644
--- a/pkg/armadaevents/events.proto
+++ b/pkg/armadaevents/events.proto
@@ -91,6 +91,7 @@ message EventSequence {
             JobRunPreemptionRequested jobRunPreemptionRequested = 21;
             JobRequeued jobRequeued = 22;
             JobPreemptionRequested jobPreemptionRequested = 23;
+            JobRunCancelled jobRunCancelled = 24;
         }
     }
     // The system is namespaced by queue, and all events are associated with a job set.
@@ -549,5 +550,12 @@ message JobRunPreemptionRequested {
 
 // Indicates that a user has requested for the job to be pre-empted.
 message JobPreemptionRequested {
-  Uuid job_id = 1;
+    Uuid job_id = 1;
+}
+
+// Generated by the scheduler when a job is cancelled, all active job runs are also cancelled
+// One such message is generated per job run that was cancelled.
+message JobRunCancelled {
+    Uuid run_id = 1;
+    Uuid job_id = 2;
 }
diff --git a/pkg/armadaevents/jobid.go b/pkg/armadaevents/jobid.go
index 4bd2d33b..80eaf3dc 100644
--- a/pkg/armadaevents/jobid.go
+++ b/pkg/armadaevents/jobid.go
@@ -38,6 +38,8 @@ func JobIdFromEvent(event *EventSequence_Event) (*Uuid, error) {
 		return e.StandaloneIngressInfo.JobId, nil
 	case *EventSequence_Event_JobRunPreempted:
 		return e.JobRunPreempted.PreemptedJobId, nil
+	case *EventSequence_Event_JobRunCancelled:
+		return e.JobRunCancelled.JobId, nil
 	case *EventSequence_Event_JobRequeued:
 		return e.JobRequeued.JobId, nil
 	default:


Test patch:
diff --git a/internal/common/ingest/testfixtures/event.go b/internal/common/ingest/testfixtures/event.go
index 8e3b5d0e..bba02c70 100644
--- a/internal/common/ingest/testfixtures/event.go
+++ b/internal/common/ingest/testfixtures/event.go
@@ -258,6 +258,16 @@ var JobRunSucceeded = &armadaevents.EventSequence_Event{
 	},
 }
 
+var JobRunCancelled = &armadaevents.EventSequence_Event{
+	Created: &testfixtures.BaseTime,
+	Event: &armadaevents.EventSequence_Event_JobRunCancelled{
+		JobRunCancelled: &armadaevents.JobRunCancelled{
+			RunId: RunIdProto,
+			JobId: JobIdProto,
+		},
+	},
+}
+
 var LeaseReturned = &armadaevents.EventSequence_Event{
 	Created: &testfixtures.BaseTime,
 	Event: &armadaevents.EventSequence_Event_JobRunErrors{
@@ -392,7 +402,7 @@ var JobPreemptionRequested = &armadaevents.EventSequence_Event{
 	},
 }
 
-var JobPreempted = &armadaevents.EventSequence_Event{
+var JobRunPreempted = &armadaevents.EventSequence_Event{
 	Created: &testfixtures.BaseTime,
 	Event: &armadaevents.EventSequence_Event_JobRunPreempted{
 		JobRunPreempted: &armadaevents.JobRunPreempted{
@@ -474,6 +484,23 @@ var JobRunUnschedulable = &armadaevents.EventSequence_Event{
 	},
 }
 
+var JobPreempted = &armadaevents.EventSequence_Event{
+	Created: &testfixtures.BaseTime,
+	Event: &armadaevents.EventSequence_Event_JobErrors{
+		JobErrors: &armadaevents.JobErrors{
+			JobId: JobIdProto,
+			Errors: []*armadaevents.Error{
+				{
+					Terminal: true,
+					Reason: &armadaevents.Error_JobRunPreemptedError{
+						JobRunPreemptedError: &armadaevents.JobRunPreemptedError{},
+					},
+				},
+			},
+		},
+	},
+}
+
 var JobFailed = &armadaevents.EventSequence_Event{
 	Created: &testfixtures.BaseTime,
 	Event: &armadaevents.EventSequence_Event_JobErrors{
diff --git a/internal/lookoutingesterv2/instructions/instructions_test.go b/internal/lookoutingesterv2/instructions/instructions_test.go
index 1a14c261..b9429fc1 100644
--- a/internal/lookoutingesterv2/instructions/instructions_test.go
+++ b/internal/lookoutingesterv2/instructions/instructions_test.go
@@ -1,13 +1,11 @@
 package instructions
 
 import (
-	"fmt"
 	"strings"
 	"testing"
 
 	"github.com/apache/pulsar-client-go/pulsar"
 	"github.com/golang/protobuf/proto"
-	"github.com/google/uuid"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 	v1 "k8s.io/api/core/v1"
@@ -22,7 +20,6 @@ import (
 	"github.com/armadaproject/armada/internal/common/ingest/testfixtures"
 	protoutil "github.com/armadaproject/armada/internal/common/proto"
 	"github.com/armadaproject/armada/internal/common/pulsarutils"
-	"github.com/armadaproject/armada/internal/common/util"
 	"github.com/armadaproject/armada/internal/lookoutingesterv2/metrics"
 	"github.com/armadaproject/armada/internal/lookoutingesterv2/model"
 	"github.com/armadaproject/armada/pkg/api"
@@ -144,14 +141,19 @@ var expectedPreempted = model.UpdateJobInstruction{
 	State:                     pointer.Int32(lookout.JobPreemptedOrdinal),
 	LastTransitionTime:        &testfixtures.BaseTime,
 	LastTransitionTimeSeconds: pointer.Int64(testfixtures.BaseTime.Unix()),
-	LatestRunId:               pointer.String(testfixtures.RunIdString),
 }
 
 var expectedPreemptedRun = model.UpdateJobRunInstruction{
 	RunId:       testfixtures.RunIdString,
 	Finished:    &testfixtures.BaseTime,
 	JobRunState: pointer.Int32(lookout.JobRunPreemptedOrdinal),
-	Error:       []byte("preempted by non armada pod"),
+	Error:       []byte("preempted"),
+}
+
+var expectedCancelledRun = model.UpdateJobRunInstruction{
+	RunId:       testfixtures.RunIdString,
+	Finished:    &testfixtures.BaseTime,
+	JobRunState: pointer.Int32(lookout.JobRunCancelledOrdinal),
 }
 
 func TestConvert(t *testing.T) {
@@ -216,24 +218,6 @@ func TestConvert(t *testing.T) {
 		},
 	}
 
-	otherJobIdUlid := util.ULID()
-	otherJobId := util.StringFromUlid(otherJobIdUlid)
-	otherJobIdProto := armadaevents.ProtoUuidFromUlid(otherJobIdUlid)
-
-	otherRunIdUuid, err := uuid.NewUUID()
-	assert.NoError(t, err)
-	otherRunIdProto := armadaevents.ProtoUuidFromUuid(otherRunIdUuid)
-
-	preempted, err := testfixtures.DeepCopy(testfixtures.JobPreempted)
-	assert.NoError(t, err)
-	preempted.GetJobRunPreempted().PreemptiveJobId = otherJobIdProto
-	preempted.GetJobRunPreempted().PreemptiveRunId = otherRunIdProto
-
-	preemptedWithPrempteeWithZeroId, err := testfixtures.DeepCopy(testfixtures.JobPreempted)
-	assert.NoError(t, err)
-	preemptedWithPrempteeWithZeroId.GetJobRunPreempted().PreemptiveJobId = &armadaevents.Uuid{}
-	preemptedWithPrempteeWithZeroId.GetJobRunPreempted().PreemptiveRunId = &armadaevents.Uuid{}
-
 	cancelledWithReason, err := testfixtures.DeepCopy(testfixtures.JobCancelled)
 	assert.NoError(t, err)
 	cancelledWithReason.GetCancelledJob().Reason = "some reason"
@@ -319,7 +303,7 @@ func TestConvert(t *testing.T) {
 				MessageIds:   []pulsar.MessageID{pulsarutils.NewMessageId(1)},
 			},
 		},
-		"cancelled": {
+		"job cancelled": {
 			events: &ingest.EventSequencesWithIds{
 				EventSequences: []*armadaevents.EventSequence{testfixtures.NewEventSequence(testfixtures.JobCancelled)},
 				MessageIds:     []pulsar.MessageID{pulsarutils.NewMessageId(1)},
@@ -329,7 +313,7 @@ func TestConvert(t *testing.T) {
 				MessageIds:   []pulsar.MessageID{pulsarutils.NewMessageId(1)},
 			},
 		},
-		"cancelled with reason": {
+		"job cancelled with reason": {
 			events: &ingest.EventSequencesWithIds{
 				EventSequences: []*armadaevents.EventSequence{testfixtures.NewEventSequence(cancelledWithReason)},
 				MessageIds:     []pulsar.MessageID{pulsarutils.NewMessageId(1)},
@@ -346,6 +330,16 @@ func TestConvert(t *testing.T) {
 				MessageIds: []pulsar.MessageID{pulsarutils.NewMessageId(1)},
 			},
 		},
+		"job run cancelled": {
+			events: &ingest.EventSequencesWithIds{
+				EventSequences: []*armadaevents.EventSequence{testfixtures.NewEventSequence(testfixtures.JobRunCancelled)},
+				MessageIds:     []pulsar.MessageID{pulsarutils.NewMessageId(1)},
+			},
+			expected: &model.InstructionSet{
+				JobRunsToUpdate: []*model.UpdateJobRunInstruction{&expectedCancelledRun},
+				MessageIds:      []pulsar.MessageID{pulsarutils.NewMessageId(1)},
+			},
+		},
 		"reprioritized": {
 			events: &ingest.EventSequencesWithIds{
 				EventSequences: []*armadaevents.EventSequence{testfixtures.NewEventSequence(testfixtures.JobReprioritised)},
@@ -404,47 +398,24 @@ func TestConvert(t *testing.T) {
 				MessageIds: []pulsar.MessageID{pulsarutils.NewMessageId(1)},
 			},
 		},
-		"preempted": {
+		"job preempted": {
 			events: &ingest.EventSequencesWithIds{
 				EventSequences: []*armadaevents.EventSequence{testfixtures.NewEventSequence(testfixtures.JobPreempted)},
 				MessageIds:     []pulsar.MessageID{pulsarutils.NewMessageId(1)},
 			},
-			expected: &model.InstructionSet{
-				JobsToUpdate:    []*model.UpdateJobInstruction{&expectedPreempted},
-				JobRunsToUpdate: []*model.UpdateJobRunInstruction{&expectedPreemptedRun},
-				MessageIds:      []pulsar.MessageID{pulsarutils.NewMessageId(1)},
-			},
-		},
-		"preempted with preemptee": {
-			events: &ingest.EventSequencesWithIds{
-				EventSequences: []*armadaevents.EventSequence{testfixtures.NewEventSequence(preempted)},
-				MessageIds:     []pulsar.MessageID{pulsarutils.NewMessageId(1)},
-			},
 			expected: &model.InstructionSet{
 				JobsToUpdate: []*model.UpdateJobInstruction{&expectedPreempted},
-				JobRunsToUpdate: []*model.UpdateJobRunInstruction{{
-					RunId:       testfixtures.RunIdString,
-					Finished:    &testfixtures.BaseTime,
-					JobRunState: pointer.Int32(lookout.JobRunPreemptedOrdinal),
-					Error:       []byte(fmt.Sprintf("preempted by job %s", otherJobId)),
-				}},
-				MessageIds: []pulsar.MessageID{pulsarutils.NewMessageId(1)},
+				MessageIds:   []pulsar.MessageID{pulsarutils.NewMessageId(1)},
 			},
 		},
-		"preempted with zeroed preemptee id": {
+		"job run preempted": {
 			events: &ingest.EventSequencesWithIds{
-				EventSequences: []*armadaevents.EventSequence{testfixtures.NewEventSequence(preemptedWithPrempteeWithZeroId)},
+				EventSequences: []*armadaevents.EventSequence{testfixtures.NewEventSequence(testfixtures.JobRunPreempted)},
 				MessageIds:     []pulsar.MessageID{pulsarutils.NewMessageId(1)},
 			},
 			expected: &model.InstructionSet{
-				JobsToUpdate: []*model.UpdateJobInstruction{&expectedPreempted},
-				JobRunsToUpdate: []*model.UpdateJobRunInstruction{{
-					RunId:       testfixtures.RunIdString,
-					Finished:    &testfixtures.BaseTime,
-					JobRunState: pointer.Int32(lookout.JobRunPreemptedOrdinal),
-					Error:       []byte("preempted by non armada pod"),
-				}},
-				MessageIds: []pulsar.MessageID{pulsarutils.NewMessageId(1)},
+				JobRunsToUpdate: []*model.UpdateJobRunInstruction{&expectedPreemptedRun},
+				MessageIds:      []pulsar.MessageID{pulsarutils.NewMessageId(1)},
 			},
 		},
 		"invalid event without job id or run id": {
diff --git a/internal/scheduler/scheduler_test.go b/internal/scheduler/scheduler_test.go
index 5bc8f554..908a6ab0 100644
--- a/internal/scheduler/scheduler_test.go
+++ b/internal/scheduler/scheduler_test.go
@@ -353,6 +353,7 @@ func TestScheduler_TestCycle(t *testing.T) {
 		expectedJobsToFail               []string                          // ids of jobs we expect to fail without having failed the overall scheduling cycle
 		expectedJobsRunsToPreempt        []string                          // ids of jobs we expect to be preempted by the scheduler
 		expectedJobRunPreempted          []string                          // ids of jobs we expect to have produced jobRunPreempted messages
+		expectedJobRunCancelled          []string                          // ids of jobs we expect to have produced jobRunPreempted messages
 		expectedJobCancelled             []string                          // ids of jobs we expect to have  produced cancelled messages
 		expectedJobRequestCancel         []string                          // ids of jobs we expect to have produced request cancel
 		expectedJobReprioritised         []string                          // ids of jobs we expect to have  produced reprioritised messages
@@ -617,9 +618,10 @@ func TestScheduler_TestCycle(t *testing.T) {
 					Serial:          1,
 				},
 			},
-			expectedJobCancelled:  []string{leasedJob.Id()},
-			expectedTerminal:      []string{leasedJob.Id()},
-			expectedQueuedVersion: leasedJob.QueuedVersion(),
+			expectedJobRunCancelled: []string{leasedJob.Id()},
+			expectedJobCancelled:    []string{leasedJob.Id()},
+			expectedTerminal:        []string{leasedJob.Id()},
+			expectedQueuedVersion:   leasedJob.QueuedVersion(),
 		},
 		"Job Run preemption requested": {
 			initialJobs: []*jobdb.Job{preemptibleLeasedJob},
@@ -921,6 +923,7 @@ func TestScheduler_TestCycle(t *testing.T) {
 				fmt.Sprintf("%T", &armadaevents.EventSequence_Event_JobErrors{}):        stringSet(tc.expectedJobErrors),
 				fmt.Sprintf("%T", &armadaevents.EventSequence_Event_JobRunErrors{}):     stringSet(tc.expectedJobRunErrors),
 				fmt.Sprintf("%T", &armadaevents.EventSequence_Event_JobRunPreempted{}):  stringSet(tc.expectedJobRunPreempted),
+				fmt.Sprintf("%T", &armadaevents.EventSequence_Event_JobRunCancelled{}):  stringSet(tc.expectedJobRunCancelled),
 				fmt.Sprintf("%T", &armadaevents.EventSequence_Event_CancelledJob{}):     stringSet(tc.expectedJobCancelled),
 				fmt.Sprintf("%T", &armadaevents.EventSequence_Event_ReprioritisedJob{}): stringSet(tc.expectedJobReprioritised),
 				fmt.Sprintf("%T", &armadaevents.EventSequence_Event_JobSucceeded{}):     stringSet(tc.expectedJobSucceeded),
@@ -1899,6 +1902,15 @@ func TestCycleConsistency(t *testing.T) {
 					Queue:      queuedJobA.Queue,
 					JobSetName: queuedJobA.JobSet,
 					Events: []*armadaevents.EventSequence_Event{
+						{
+							Created: pointerFromValue(time.Unix(0, 0)),
+							Event: &armadaevents.EventSequence_Event_JobRunCancelled{
+								JobRunCancelled: &armadaevents.JobRunCancelled{
+									RunId: armadaevents.ProtoUuidFromUuid(testfixtures.UUIDFromInt(1)),
+									JobId: armadaevents.MustProtoUuidFromUlidString(queuedJobA.JobID),
+								},
+							},
+						},
 						{
 							Created: pointerFromValue(time.Unix(0, 0)),
 							Event: &armadaevents.EventSequence_Event_CancelledJob{
@@ -1941,6 +1953,15 @@ func TestCycleConsistency(t *testing.T) {
 								},
 							},
 						},
+						{
+							Created: pointerFromValue(time.Unix(0, 0)),
+							Event: &armadaevents.EventSequence_Event_JobRunCancelled{
+								JobRunCancelled: &armadaevents.JobRunCancelled{
+									RunId: armadaevents.ProtoUuidFromUuid(testfixtures.UUIDFromInt(1)),
+									JobId: armadaevents.MustProtoUuidFromUlidString(queuedJobA.JobID),
+								},
+							},
+						},
 						{
 							Created: pointerFromValue(time.Unix(0, 0)),
 							Event: &armadaevents.EventSequence_Event_CancelledJob{
diff --git a/internal/scheduleringester/instructions_test.go b/internal/scheduleringester/instructions_test.go
index 848c6d91..5e5cd945 100644
--- a/internal/scheduleringester/instructions_test.go
+++ b/internal/scheduleringester/instructions_test.go
@@ -89,7 +89,7 @@ func TestConvertSequence(t *testing.T) {
 			expected: []DbOperation{MarkRunsForJobPreemptRequested{JobSetKey{queue: f.Queue, jobSet: f.JobSetName}: []string{f.JobIdString}}},
 		},
 		"job run preempted": {
-			events:   []*armadaevents.EventSequence_Event{f.JobPreempted},
+			events:   []*armadaevents.EventSequence_Event{f.JobRunPreempted},
 			expected: []DbOperation{MarkRunsPreempted{f.RunIdUuid: f.BaseTime}},
 		},
 		"lease returned": {