Publish Clusterflux 45bbc21

This commit is contained in:
Michel Paulissen 2026-07-20 11:08:26 +02:00
parent eb1077f380
commit 3f88254c70
28 changed files with 896 additions and 167 deletions

View file

@ -240,6 +240,7 @@ pub(crate) fn run_adapter() -> Result<()> {
}
Err(message) => {
state.breakpoints_installed = false;
emit_unverified_breakpoints(&mut writer, &state, &message)?;
writer.output("stderr", format!("{message}\n"))?;
}
}
@ -1025,6 +1026,28 @@ fn emit_verified_breakpoints(writer: &mut DapWriter, state: &AdapterState) -> Re
Ok(())
}
fn emit_unverified_breakpoints(
writer: &mut DapWriter,
state: &AdapterState,
coordinator_error: &str,
) -> Result<()> {
for (index, line) in state.breakpoints.iter().enumerate() {
writer.event(
"breakpoint",
json!({
"reason": "changed",
"breakpoint": {
"id": index + 1,
"verified": false,
"line": line,
"message": format!("Coordinator breakpoint installation failed: {coordinator_error}"),
}
}),
)?;
}
Ok(())
}
fn emit_thread_lifecycle(
writer: &mut DapWriter,
previous: &BTreeMap<i64, VirtualThread>,

View file

@ -21,7 +21,7 @@ use debug_protocol::{
};
use local_tools::{child_stderr_suffix, local_tool_command};
pub(crate) use transport::client_user_request;
use transport::{coordinator_request, CoordinatorSession};
use transport::{coordinator_request, coordinator_request_allow_error, CoordinatorSession};
pub(crate) struct LocalRuntimeSession {
coordinator: Option<Child>,
@ -300,7 +300,8 @@ fn launch_services_debug_entrypoint(
) -> Result<RuntimeLaunchRecord> {
let bundle = build_debug_bundle(state, repo)?;
validate_inline_bundle_size(bundle.module_size_bytes)?;
let started = match coordinator_request(
let launch_attempt = new_launch_attempt_id();
let started = match coordinator_request_allow_error(
coordinator,
client_user_request(
state,
@ -310,25 +311,56 @@ fn launch_services_debug_entrypoint(
"project": state.project_id,
"actor_user": state.actor_user,
"process": state.process.as_str(),
"launch_attempt": launch_attempt,
"restart": state.restart_existing,
}),
),
) {
Ok(started) => started,
Err(error) => return Err(debug_launch_error_with_rollback(coordinator, state, error)),
Err(error) => {
return Err(debug_launch_error_with_rollback(
coordinator,
state,
&launch_attempt,
error,
));
}
};
if started.get("type").and_then(Value::as_str) == Some("error") {
return Err(anyhow!(
"{}",
started
.get("message")
.and_then(Value::as_str)
.unwrap_or("coordinator rejected the debug launch")
));
}
if started.get("launch_attempt").and_then(Value::as_str) != Some(launch_attempt.as_str()) {
return Err(debug_launch_error_with_rollback(
coordinator,
state,
&launch_attempt,
anyhow!("coordinator returned a debug launch owned by a different attempt"),
));
}
let epoch = match started.get("epoch").and_then(Value::as_u64) {
Some(epoch) => epoch,
None => {
return Err(debug_launch_error_with_rollback(
coordinator,
state,
&launch_attempt,
anyhow!("coordinator did not report a process epoch"),
));
}
};
if let Err(error) = set_services_debug_breakpoints_at(coordinator, state) {
return Err(debug_launch_error_with_rollback(coordinator, state, error));
return Err(debug_launch_error_with_rollback(
coordinator,
state,
&launch_attempt,
error,
));
}
let launch = match coordinator_request(
coordinator,
@ -368,12 +400,20 @@ fn launch_services_debug_entrypoint(
),
) {
Ok(launch) => launch,
Err(error) => return Err(debug_launch_error_with_rollback(coordinator, state, error)),
Err(error) => {
return Err(debug_launch_error_with_rollback(
coordinator,
state,
&launch_attempt,
error,
));
}
};
if launch.get("type").and_then(Value::as_str) != Some("main_launched") {
return Err(debug_launch_error_with_rollback(
coordinator,
state,
&launch_attempt,
anyhow!(
"coordinator did not start the capless main runtime: {}",
serde_json::to_string(&launch)?
@ -432,6 +472,7 @@ fn validate_inline_bundle_size(module_size_bytes: usize) -> Result<()> {
fn debug_launch_error_with_rollback(
coordinator: &str,
state: &AdapterState,
launch_attempt: &str,
launch_error: anyhow::Error,
) -> anyhow::Error {
let rollback = coordinator_request(
@ -444,6 +485,7 @@ fn debug_launch_error_with_rollback(
"project": state.project_id,
"actor_user": state.actor_user,
"process": state.process.as_str(),
"launch_attempt": launch_attempt,
}),
),
);
@ -460,7 +502,27 @@ fn debug_launch_error_with_rollback(
}
}
static NEXT_LAUNCH_ATTEMPT: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(1);
fn new_launch_attempt_id() -> String {
let sequence = NEXT_LAUNCH_ATTEMPT.fetch_add(1, Ordering::Relaxed);
let nanos = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos();
format!("dap-launch-{}-{nanos}-{sequence}", std::process::id())
}
fn set_services_debug_breakpoints_at(coordinator: &str, state: &AdapterState) -> Result<()> {
static INJECTED_INSTALL_FAILURE: AtomicBool = AtomicBool::new(false);
if std::env::var_os("CLUSTERFLUX_TEST_DAP_BREAKPOINT_INSTALLATION_FAILURE").is_some()
&& !state.breakpoints.is_empty()
&& !INJECTED_INSTALL_FAILURE.swap(true, Ordering::SeqCst)
{
return Err(anyhow!(
"coordinator breakpoint installation failed: injected live-install failure"
));
}
coordinator_request(
coordinator,
client_user_request(
@ -1267,7 +1329,7 @@ pub(crate) fn observe_services_runtime(
return Ok(());
}
} else {
poll_delay = (poll_delay * 2).min(Duration::from_secs(1));
poll_delay = (poll_delay * 2).min(Duration::from_secs(5));
}
std::thread::sleep(poll_delay);
}
@ -1449,6 +1511,7 @@ mod transactional_launch_tests {
let error = debug_launch_error_with_rollback(
&address,
&state,
"launch-test",
anyhow!("launch acknowledgement was lost"),
);
assert!(error

View file

@ -33,8 +33,7 @@ impl CoordinatorSession {
}
pub(super) fn request(&mut self, request: Value) -> Result<Value> {
let wire_request = coordinator_wire_request("dap-1", request);
let response = self.session.request(&wire_request)?;
let response = self.request_allow_error(request)?;
if response.get("type").and_then(Value::as_str) == Some("error") {
return Err(anyhow!(
"{}",
@ -46,8 +45,17 @@ impl CoordinatorSession {
}
Ok(response)
}
pub(super) fn request_allow_error(&mut self, request: Value) -> Result<Value> {
let wire_request = coordinator_wire_request("dap-1", request);
Ok(self.session.request(&wire_request)?)
}
}
pub(super) fn coordinator_request(addr: &str, request: Value) -> Result<Value> {
CoordinatorSession::connect(addr)?.request(request)
}
pub(super) fn coordinator_request_allow_error(addr: &str, request: Value) -> Result<Value> {
CoordinatorSession::connect(addr)?.request_allow_error(request)
}