feat: promote workers to Workspace-owned UUIDv7 identities

This commit is contained in:
2026-08-19 23:04:02 +09:00
parent 25baeedc03
commit e35b5797a3
15 changed files with 1188 additions and 268 deletions
+105 -15
View File
@@ -76,11 +76,12 @@ use crate::hosts::{
EmbeddedWorkerRuntime, HostSummary, RemoteRuntimeConfig, RemoteWorkerRuntime,
RuntimeDiagnostic, RuntimeRegistry, RuntimeRegistryError, RuntimeRegistryUnregisterResult,
RuntimeSummary, TicketWorkerRole, WorkerCapabilitySummary, WorkerCompletionsRequest,
WorkerCompletionsResult, WorkerControlOperation, WorkerImplementationSummary, WorkerInputKind,
WorkerInputRequest, WorkerInputResult, WorkerLifecycleRequest, WorkerLifecycleResult,
WorkerOperationState, WorkerRestoreResult, WorkerSpawnAcceptanceRequirement, WorkerSpawnIntent,
WorkerSpawnRequest, WorkerSpawnResult, WorkerSpawnWorkingDirectoryRequest, WorkerSummary,
WorkerTicketAssignmentRequest, WorkerWorkspaceSummary,
WorkerCompletionsResult, WorkerControlOperation, WorkerCreateBinding,
WorkerImplementationSummary, WorkerInputKind, WorkerInputRequest, WorkerInputResult,
WorkerLifecycleRequest, WorkerLifecycleResult, WorkerOperationState, WorkerRestoreResult,
WorkerSpawnAcceptanceRequirement, WorkerSpawnIntent, WorkerSpawnRequest, WorkerSpawnResult,
WorkerSpawnWorkingDirectoryRequest, WorkerSummary, WorkerTicketAssignmentRequest,
WorkerWorkspaceSummary, worker_spawn_create_fingerprint,
};
use crate::identity::WorkspaceIdentity;
use crate::memory_backend::execute_memory_backend_operation_with_authority;
@@ -120,7 +121,7 @@ use worker_runtime::http_server::{
RuntimeHttpConfigBundleAvailabilityResponse, RuntimeHttpConfigBundlesResponse,
RuntimeHttpSummaryResponse, RuntimeHttpWorkerResponse, RuntimeHttpWorkersResponse,
};
use worker_runtime::identity::RuntimeWorkerRef;
use worker_runtime::identity::{RuntimeWorkerRef, WorkerId};
const EMBEDDED_WORKER_RUNTIME_ID: &str = "embedded-worker-runtime";
@@ -887,7 +888,40 @@ impl WorkspaceApi {
&now_registry_timestamp(),
)?;
}
let result = match self.runtime.spawn_worker(runtime_id, request) {
let create_fingerprint = worker_spawn_create_fingerprint(&request)
.map_err(|message| Error::Config(message.to_string()))?;
let allocation_key = request
.resolved_control_operation
.as_ref()
.map(|operation| operation.operation_id.clone())
.or_else(|| {
request
.ticket_assignment
.as_ref()
.map(|assignment| assignment.operation_id.clone())
})
.unwrap_or_else(|| format!("manual:{}", WorkerId::now_v7()));
let worker_id = self
.config_store
.reserve_worker_create(
&self.config.workspace_id,
runtime_id,
&allocation_key,
&create_fingerprint,
)
.map_err(|error| Error::RuntimeOperationFailed {
runtime_id: runtime_id.to_string(),
code: "workspace_worker_allocation_conflict".to_string(),
message: error.to_string(),
})?;
let create_binding = WorkerCreateBinding {
worker_id,
create_fingerprint,
};
let result = match self
.runtime
.spawn_worker(runtime_id, create_binding, request)
{
Ok(result) => result,
Err(error) => {
if let Some((workdir_id, reservation_id)) = attachment_reservation.as_ref() {
@@ -911,6 +945,24 @@ impl WorkspaceApi {
return Ok(result);
};
let worker_ref = worker.worker.clone();
if worker_ref.worker_id != worker_id.to_string() {
if let Some((workdir_id, reservation_id)) = attachment_reservation.as_ref() {
let _ = self.store.release_worker_workdir_attachment_reservation(
&self.config.workspace_id,
workdir_id,
reservation_id,
);
}
return Err(Error::RuntimeOperationFailed {
runtime_id: runtime_id.to_string(),
code: "workspace_worker_identity_mismatch".to_string(),
message: format!(
"Runtime returned Worker {} for reserved Workspace Worker {}",
worker_ref.worker_id, worker_id
),
}
.into());
}
let replacement = match self
.runtime
.replace_worker_workspace_api(&worker_ref, workspace_api)
@@ -1017,6 +1069,9 @@ impl WorkspaceApi {
return Err(error);
}
}
self.config_store
.complete_worker_create_reservation(&self.config.workspace_id, worker_id)
.map_err(|error| Error::Config(error.to_string()))?;
Ok(result)
}
@@ -11889,11 +11944,11 @@ fn working_directory_request_for_browser(
})
}
fn parse_runtime_worker_id_for_registry(worker_id: &str) -> ApiResult<u64> {
worker_id.parse::<u64>().map_err(|_| {
fn parse_runtime_worker_id_for_registry(worker_id: &str) -> ApiResult<WorkerId> {
worker_id.parse::<WorkerId>().map_err(|_| {
settings_bad_request(
"workspace_worker_id_invalid",
"Runtime Worker id must be an unsigned integer",
"Workspace Worker id must be a UUIDv7",
)
})
}
@@ -12420,6 +12475,13 @@ mod tests {
ObjectiveTicketLinkRecord, SqliteWorkspaceStore, WorkspaceRecord,
};
fn test_create_binding() -> WorkerCreateBinding {
WorkerCreateBinding {
worker_id: WorkerId::now_v7(),
create_fingerprint: "sha256:test-create".to_string(),
}
}
#[test]
fn reopen_confirmation_rejects_api_token_actor_before_session_resolution() {
let mut headers = HeaderMap::new();
@@ -14084,6 +14146,7 @@ mod tests {
.runtime
.spawn_worker(
EMBEDDED_WORKER_RUNTIME_ID,
test_create_binding(),
WorkerSpawnRequest {
requested_worker_name: Some(MEMORY_CONSOLIDATION_PROFILE.to_string()),
intent: WorkerSpawnIntent::WorkspaceOrchestrator,
@@ -14313,6 +14376,7 @@ mod tests {
.runtime
.spawn_worker(
EMBEDDED_WORKER_RUNTIME_ID,
test_create_binding(),
WorkerSpawnRequest {
requested_worker_name: Some("notification-source".to_string()),
intent: WorkerSpawnIntent::TicketRole {
@@ -14533,13 +14597,21 @@ mod tests {
};
let source_worker = api
.runtime
.spawn_worker(EMBEDDED_WORKER_RUNTIME_ID, spawn("source-worker"))
.spawn_worker(
EMBEDDED_WORKER_RUNTIME_ID,
test_create_binding(),
spawn("source-worker"),
)
.unwrap()
.worker
.unwrap();
let recipient_worker = api
.runtime
.spawn_worker(EMBEDDED_WORKER_RUNTIME_ID, spawn("recipient-worker"))
.spawn_worker(
EMBEDDED_WORKER_RUNTIME_ID,
test_create_binding(),
spawn("recipient-worker"),
)
.unwrap()
.worker
.unwrap();
@@ -14727,6 +14799,7 @@ mod tests {
.runtime
.spawn_worker(
EMBEDDED_WORKER_RUNTIME_ID,
test_create_binding(),
WorkerSpawnRequest {
requested_worker_name: Some("orchestrator-source".to_string()),
intent: WorkerSpawnIntent::TicketRole {
@@ -15037,9 +15110,25 @@ mod tests {
TEST_CREATED_AT,
)
.unwrap();
let reserved_worker_id = api
.config_store
.reserve_worker_create(
TEST_WORKSPACE_ID,
EMBEDDED_WORKER_RUNTIME_ID,
"pending-spawn-operation",
&pending_fingerprint,
)
.unwrap();
let spawned_before_backend_failure = api
.runtime
.spawn_worker(EMBEDDED_WORKER_RUNTIME_ID, pending_request.clone())
.spawn_worker(
EMBEDDED_WORKER_RUNTIME_ID,
WorkerCreateBinding {
worker_id: reserved_worker_id,
create_fingerprint: pending_fingerprint.clone(),
},
pending_request.clone(),
)
.unwrap()
.worker
.unwrap();
@@ -16632,8 +16721,8 @@ mod tests {
fn runtime_create_request() -> worker_runtime::catalog::CreateWorkerRequest {
let bundle = runtime_test_bundle();
worker_runtime::catalog::CreateWorkerRequest {
idempotency_key: None,
idempotency_fingerprint: None,
worker_id: WorkerId::now_v7(),
create_fingerprint: "test-create".to_string(),
profile: worker_runtime::catalog::ProfileSelector::Builtin(
"builtin:companion".to_string(),
),
@@ -17847,6 +17936,7 @@ mod tests {
.runtime
.spawn_worker(
"embedded-worker-runtime",
test_create_binding(),
WorkerSpawnRequest {
intent: WorkerSpawnIntent::TicketRole {
ticket_id: "00001KVZSGT0Q".to_string(),