feat: add durable backend job runner

This commit is contained in:
2026-10-01 10:06:55 +09:00
parent 6b2f40a2a2
commit ca838e4acf
27 changed files with 2823 additions and 13 deletions
+26
View File
@@ -631,6 +631,24 @@ impl SubscriptionWorkerWorkdirAttachment {
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
#[serde(deny_unknown_fields)]
pub struct SubscriptionWorkerJob {
pub job_id: String,
pub attempt_id: String,
pub purpose: String,
}
impl SubscriptionWorkerJob {
pub fn validate(&self) -> Result<(), SubscriptionValidationError> {
validate_identifier("job_id", &self.job_id, MAX_RESOURCE_ID_BYTES)?;
validate_identifier("attempt_id", &self.attempt_id, MAX_RESOURCE_ID_BYTES)?;
validate_identifier("job_purpose", &self.purpose, MAX_RESOURCE_ID_BYTES)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
@@ -664,6 +682,10 @@ pub struct SubscriptionWorker {
pub display_name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub profile: Option<String>,
/// Backend-owned Job binding. Runtime producers leave this unset; the
/// Workspace projection enriches both snapshots and live updates.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub job: Option<SubscriptionWorkerJob>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub workdir_attachments: Vec<SubscriptionWorkerWorkdirAttachment>,
}
@@ -677,6 +699,9 @@ impl SubscriptionWorker {
if let Some(resource_key) = &self.resource_key {
validate_identifier("resource_key", resource_key, MAX_RESOURCE_ID_BYTES)?;
}
if let Some(job) = &self.job {
job.validate()?;
}
let mut aliases = HashSet::new();
let mut workdir_ids = HashSet::new();
for attachment in &self.workdir_attachments {
@@ -952,6 +977,7 @@ mod tests {
workspace_id: Some("workspace-1".to_string()),
display_name: Some(format!("Worker {value}")),
profile: Some("builtin:coder".to_string()),
job: None,
workdir_attachments: Vec::new(),
}
}
+3 -1
View File
@@ -28,7 +28,7 @@ use crate::{
SubscriptionRequest, SubscriptionRequestId, SubscriptionResponse, SubscriptionSnapshot,
SubscriptionTerminationCode, SubscriptionWorkdirId, SubscriptionWorker,
SubscriptionWorkerAvailability, SubscriptionWorkerId, SubscriptionWorkerIds,
SubscriptionWorkerProtocolMethod, SubscriptionWorkerState,
SubscriptionWorkerJob, SubscriptionWorkerProtocolMethod, SubscriptionWorkerState,
SubscriptionWorkerWorkdirAttachment, WorkspaceSubscriptionWorkdir,
},
};
@@ -132,6 +132,7 @@ pub fn generated_protocol_types() -> String {
push_decl::<SubscriptionWorkerAvailability>(&cfg, &mut output);
push_decl::<EventSubscriptionSelector>(&cfg, &mut output);
push_decl::<SubscriptionWorkerWorkdirAttachment>(&cfg, &mut output);
push_decl::<SubscriptionWorkerJob>(&cfg, &mut output);
push_decl::<SubscriptionWorker>(&cfg, &mut output);
push_decl::<WorkspaceSubscriptionWorkdir>(&cfg, &mut output);
push_decl::<SubscriptionSnapshot>(&cfg, &mut output);
@@ -232,6 +233,7 @@ fn rust_serialized_subscription_frame_fixtures() -> Vec<SubscriptionFrame> {
workspace_id: Some("fixture-workspace".to_string()),
display_name: None,
profile: None,
job: None,
workdir_attachments: vec![SubscriptionWorkerWorkdirAttachment {
alias: "external".to_string(),
repository_id: None,