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("catalog definition for job type {job_type} is invalid: {field} must be positive")]
49 InvalidJobDefinitionValue {
50 job_type: String,
52 field: &'static str,
54 },
55 #[error("failure code must be non-empty")]
57 InvalidFailureCode,
58 #[error("retry delay override must be positive")]
60 InvalidRetryDelay,
61 #[error("exact sync scope must include at least one job type")]
63 InvalidExactSyncScope,
64 #[error("exact sync scope job type {job_type:?} is invalid: {source}")]
66 InvalidExactSyncScopeJobType {
67 job_type: String,
69 #[source]
71 source: IdentifierValidationError,
72 },
73 #[error("exact sync requires at least one catalog job")]
75 EmptyExactSyncCatalog,
76 #[error("catalog job type {job_type} is outside the exact sync scope")]
78 JobTypeOutsideExactSyncScope {
79 job_type: String,
81 },
82 #[error("active schedule {schedule_name} still references absent catalog job type {job_type}")]
84 ActiveScheduleForAbsentJobType {
85 schedule_name: String,
87 job_type: String,
89 },
90 #[error(
92 "active schedule {schedule_name} still references disabled catalog job type {job_type}"
93 )]
94 ActiveScheduleForDisabledJobType {
95 schedule_name: String,
97 job_type: String,
99 },
100 #[error("job type {job_type} is not registered in the catalog")]
102 UnknownJobType {
103 job_type: String,
105 },
106 #[error("job type {job_type} is disabled in the catalog")]
108 DisabledJobType {
109 job_type: String,
111 },
112 #[error(transparent)]
114 WorkflowBuild(#[from] WorkflowBuildError),
115 #[error("failed to start job definition sync transaction: {0}")]
117 SyncFailure(#[source] Box<runledger_postgres::Error>),
118 #[error("failed to sync job definitions with an unmapped persistence error: {0}")]
120 DefinitionCatalogSyncFailure(#[source] Box<dyn std::error::Error + Send + Sync>),
121 #[error("failed to sync job definition {job_type}: {source}")]
123 DefinitionSyncFailure {
124 job_type: String,
126 #[source]
128 source: Box<runledger_postgres::Error>,
129 },
130 #[error("failed to commit job definition sync transaction: {0}")]
132 CommitFailure(#[source] sqlx::Error),
133 #[error("failed to bound job definition sync critical section: {0}")]
135 CriticalSectionTimeoutFailure(#[source] Box<runledger_postgres::Error>),
136 #[error("job definition sync input is invalid: {0}")]
138 DefinitionSyncValidationFailure(#[source] Box<runledger_postgres::Error>),
139 #[error("failed to lock job schedules before disabling job definitions: {0}")]
141 ScheduleLockFailure(#[source] Box<runledger_postgres::Error>),
142 #[error("failed to lock job definitions before disabling job definitions: {0}")]
144 DefinitionLockFailure(#[source] Box<runledger_postgres::Error>),
145 #[error("failed to check active schedules before disabling job definitions: {0}")]
147 ScheduleCheckFailure(#[source] Box<runledger_postgres::Error>),
148 #[error("failed to inspect job definitions before syncing catalog: {0}")]
150 DefinitionInspectFailure(#[source] Box<runledger_postgres::Error>),
151 #[error("failed to disable absent job definitions: {0}")]
153 DisableAbsentFailure(#[source] Box<runledger_postgres::Error>),
154 #[error("exact schedule sync scope must include at least one schedule name")]
156 InvalidExactScheduleSyncScope,
157 #[error("exact schedule sync scope schedule name {name:?} is invalid")]
159 InvalidExactScheduleSyncScopeName {
160 name: String,
162 },
163 #[error("exact schedule sync scope schedule name {name} is duplicated")]
165 DuplicateExactScheduleSyncScopeName {
166 name: String,
168 },
169 #[error("catalog schedule {name} is outside the exact schedule sync scope")]
171 ScheduleNameOutsideExactSyncScope {
172 name: String,
174 },
175 #[error("schedule name {name} is already registered in the catalog")]
177 DuplicateScheduleName {
178 name: String,
180 },
181 #[error("catalog schedule {name:?} is invalid: {field}")]
183 InvalidScheduleSpec {
184 name: String,
186 field: &'static str,
188 },
189 #[error("schedule sync batch contains duplicate schedule name {name}")]
191 DuplicateScheduleNameInSyncBatch {
192 name: String,
194 },
195 #[error("failed to start job catalog schedule sync transaction: {0}")]
197 ScheduleSyncFailure(#[source] Box<runledger_postgres::Error>),
198 #[error("failed to bound exact schedule sync critical section: {0}")]
200 ScheduleSyncCriticalSectionFailure(#[source] Box<runledger_postgres::Error>),
201 #[error("failed to sync schedule {name}: {source}")]
203 ScheduleSyncEntryFailure {
204 name: String,
206 #[source]
208 source: Box<runledger_postgres::Error>,
209 },
210 #[error("failed to deactivate absent catalog schedules: {0}")]
212 DeactivateAbsentSchedulesFailure(#[source] Box<runledger_postgres::Error>),
213 #[error("failed to commit job catalog schedule sync transaction: {0}")]
215 ScheduleSyncCommitFailure(#[source] Box<runledger_postgres::Error>),
216}
217
218impl CatalogError {
219 pub(crate) fn from_definition_catalog_sync_error(error: JobDefinitionCatalogSyncError) -> Self {
220 match error {
221 JobDefinitionCatalogSyncError::ActiveScheduleForAbsentJobType(reference) => {
222 Self::ActiveScheduleForAbsentJobType {
223 schedule_name: reference.schedule_name,
224 job_type: reference.job_type.to_string(),
225 }
226 }
227 JobDefinitionCatalogSyncError::ActiveScheduleForDisabledJobType(reference) => {
228 Self::ActiveScheduleForDisabledJobType {
229 schedule_name: reference.schedule_name,
230 job_type: reference.job_type.to_string(),
231 }
232 }
233 JobDefinitionCatalogSyncError::CriticalSectionTimeoutFailure(source) => {
234 Self::CriticalSectionTimeoutFailure(source)
235 }
236 JobDefinitionCatalogSyncError::ScheduleLockFailure(source) => {
237 Self::ScheduleLockFailure(source)
238 }
239 JobDefinitionCatalogSyncError::DefinitionLockFailure(source) => {
240 Self::DefinitionLockFailure(source)
241 }
242 JobDefinitionCatalogSyncError::ScheduleCheckFailure(source) => {
243 Self::ScheduleCheckFailure(source)
244 }
245 JobDefinitionCatalogSyncError::ValidationFailure(source) => {
246 Self::DefinitionSyncValidationFailure(source)
247 }
248 JobDefinitionCatalogSyncError::DefinitionInspectFailure(source) => {
249 Self::DefinitionInspectFailure(source)
250 }
251 JobDefinitionCatalogSyncError::DefinitionSyncFailure { job_type, source } => {
252 Self::DefinitionSyncFailure { job_type, source }
253 }
254 JobDefinitionCatalogSyncError::DisableAbsentFailure(source) => {
255 Self::DisableAbsentFailure(source)
256 }
257 _ => Self::DefinitionCatalogSyncFailure(Box::new(error)),
260 }
261 }
262}