runledger_runtime/catalog/
error.rs1use runledger_core::jobs::{IdentifierValidationError, WorkflowBuildError};
2use runledger_postgres::jobs::JobDefinitionCatalogSyncError;
3use thiserror::Error;
4
5#[non_exhaustive]
7#[derive(Debug, Error)]
8pub enum CatalogError {
9 #[error("job type {job_type:?} is invalid: {source}")]
11 InvalidJobType {
12 job_type: String,
14 #[source]
16 source: IdentifierValidationError,
17 },
18 #[error("handler job type {handler_job_type:?} is invalid: {source}")]
20 InvalidHandlerJobType {
21 handler_job_type: String,
23 #[source]
25 source: IdentifierValidationError,
26 },
27 #[error("job type {declared} does not match handler job type {handler}")]
29 HandlerJobTypeMismatch {
30 declared: String,
32 handler: String,
34 },
35 #[error("job type {job_type} is already registered in the catalog")]
37 DuplicateJobType {
38 job_type: String,
40 },
41 #[error("catalog defaults are invalid: {field} must be positive")]
43 InvalidDefinitionValue {
44 field: &'static str,
46 },
47 #[error("failure code must be non-empty")]
49 InvalidFailureCode,
50 #[error("retry delay override must be positive")]
52 InvalidRetryDelay,
53 #[error("exact sync scope must include at least one job type")]
55 InvalidExactSyncScope,
56 #[error("exact sync scope job type {job_type:?} is invalid: {source}")]
58 InvalidExactSyncScopeJobType {
59 job_type: String,
61 #[source]
63 source: IdentifierValidationError,
64 },
65 #[error("exact sync requires at least one catalog job")]
67 EmptyExactSyncCatalog,
68 #[error("catalog job type {job_type} is outside the exact sync scope")]
70 JobTypeOutsideExactSyncScope {
71 job_type: String,
73 },
74 #[error("active schedule {schedule_name} still references absent catalog job type {job_type}")]
76 ActiveScheduleForAbsentJobType {
77 schedule_name: String,
79 job_type: String,
81 },
82 #[error(
84 "active schedule {schedule_name} still references disabled catalog job type {job_type}"
85 )]
86 ActiveScheduleForDisabledJobType {
87 schedule_name: String,
89 job_type: String,
91 },
92 #[error("job type {job_type} is not registered in the catalog")]
94 UnknownJobType {
95 job_type: String,
97 },
98 #[error("job type {job_type} is disabled in the catalog")]
100 DisabledJobType {
101 job_type: String,
103 },
104 #[error(transparent)]
106 WorkflowBuild(#[from] WorkflowBuildError),
107 #[error("failed to start job definition sync transaction: {0}")]
109 SyncFailure(#[source] runledger_postgres::Error),
110 #[error("failed to sync job definitions with an unmapped persistence error: {0}")]
112 DefinitionCatalogSyncFailure(#[source] Box<dyn std::error::Error + Send + Sync>),
113 #[error("failed to sync job definition {job_type}: {source}")]
115 DefinitionSyncFailure {
116 job_type: String,
118 #[source]
120 source: runledger_postgres::Error,
121 },
122 #[error("failed to commit job definition sync transaction: {0}")]
124 CommitFailure(#[source] sqlx::Error),
125 #[error("failed to bound job definition sync critical section: {0}")]
127 CriticalSectionTimeoutFailure(#[source] runledger_postgres::Error),
128 #[error("job definition sync input is invalid: {0}")]
130 DefinitionSyncValidationFailure(#[source] runledger_postgres::Error),
131 #[error("failed to lock job schedules before disabling job definitions: {0}")]
133 ScheduleLockFailure(#[source] runledger_postgres::Error),
134 #[error("failed to lock job definitions before disabling job definitions: {0}")]
136 DefinitionLockFailure(#[source] runledger_postgres::Error),
137 #[error("failed to check active schedules before disabling job definitions: {0}")]
139 ScheduleCheckFailure(#[source] runledger_postgres::Error),
140 #[error("failed to inspect job definitions before syncing catalog: {0}")]
142 DefinitionInspectFailure(#[source] runledger_postgres::Error),
143 #[error("failed to disable absent job definitions: {0}")]
145 DisableAbsentFailure(#[source] runledger_postgres::Error),
146}
147
148impl CatalogError {
149 pub(crate) fn from_definition_catalog_sync_error(error: JobDefinitionCatalogSyncError) -> Self {
150 match error {
151 JobDefinitionCatalogSyncError::ActiveScheduleForAbsentJobType(reference) => {
152 Self::ActiveScheduleForAbsentJobType {
153 schedule_name: reference.schedule_name,
154 job_type: reference.job_type.to_string(),
155 }
156 }
157 JobDefinitionCatalogSyncError::ActiveScheduleForDisabledJobType(reference) => {
158 Self::ActiveScheduleForDisabledJobType {
159 schedule_name: reference.schedule_name,
160 job_type: reference.job_type.to_string(),
161 }
162 }
163 JobDefinitionCatalogSyncError::CriticalSectionTimeoutFailure(source) => {
164 Self::CriticalSectionTimeoutFailure(source)
165 }
166 JobDefinitionCatalogSyncError::ScheduleLockFailure(source) => {
167 Self::ScheduleLockFailure(source)
168 }
169 JobDefinitionCatalogSyncError::DefinitionLockFailure(source) => {
170 Self::DefinitionLockFailure(source)
171 }
172 JobDefinitionCatalogSyncError::ScheduleCheckFailure(source) => {
173 Self::ScheduleCheckFailure(source)
174 }
175 JobDefinitionCatalogSyncError::ValidationFailure(source) => {
176 Self::DefinitionSyncValidationFailure(source)
177 }
178 JobDefinitionCatalogSyncError::DefinitionInspectFailure(source) => {
179 Self::DefinitionInspectFailure(source)
180 }
181 JobDefinitionCatalogSyncError::DefinitionSyncFailure { job_type, source } => {
182 Self::DefinitionSyncFailure { job_type, source }
183 }
184 JobDefinitionCatalogSyncError::DisableAbsentFailure(source) => {
185 Self::DisableAbsentFailure(source)
186 }
187 _ => Self::DefinitionCatalogSyncFailure(Box::new(error)),
190 }
191 }
192}