huskies: merge 891
This commit is contained in:
@@ -103,11 +103,11 @@ mod tests {
|
||||
// Confirm the stale claim is in place.
|
||||
let before = read_item(story_id).expect("item should exist");
|
||||
assert_eq!(
|
||||
before.claimed_by.as_deref(),
|
||||
before.claimed_by(),
|
||||
Some(stale_holder),
|
||||
"pre-condition: item should be claimed by the stale holder"
|
||||
);
|
||||
let age = chrono::Utc::now().timestamp() as f64 - before.claimed_at.unwrap_or(0.0);
|
||||
let age = chrono::Utc::now().timestamp() as f64 - before.claimed_at().unwrap_or(0.0);
|
||||
assert!(
|
||||
age >= CLAIM_TIMEOUT_SECS,
|
||||
"pre-condition: claim age ({age}s) must exceed TTL ({CLAIM_TIMEOUT_SECS}s)"
|
||||
@@ -134,12 +134,12 @@ mod tests {
|
||||
let our_id = our_node_id().expect("node id should be available after init_for_test");
|
||||
let after = read_item(story_id).expect("item should still exist");
|
||||
assert_eq!(
|
||||
after.claimed_by.as_deref(),
|
||||
after.claimed_by(),
|
||||
Some(our_id.as_str()),
|
||||
"new claim should have displaced the stale holder"
|
||||
);
|
||||
assert_ne!(
|
||||
after.claimed_by.as_deref(),
|
||||
after.claimed_by(),
|
||||
Some(stale_holder),
|
||||
"stale holder must no longer own the claim"
|
||||
);
|
||||
|
||||
@@ -42,27 +42,28 @@ pub(super) async fn scan_and_claim(
|
||||
|
||||
for item in &items {
|
||||
// Only claim stories in active stages.
|
||||
if !crate::pipeline_state::Stage::from_dir(&item.stage).is_some_and(|s| s.is_active()) {
|
||||
if !crate::pipeline_state::Stage::from_dir(item.stage_str()).is_some_and(|s| s.is_active())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip blocked stories.
|
||||
if item.blocked == Some(true) {
|
||||
if item.blocked() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If already claimed by us, skip.
|
||||
if item.claimed_by.as_deref() == Some(&our_node) {
|
||||
if item.claimed_by() == Some(our_node.as_str()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If claimed by another node, respect the claim while it is fresh.
|
||||
// Once the TTL expires the claim is considered stale regardless of
|
||||
// whether the holder appears alive — displacement is purely TTL-driven.
|
||||
if let Some(ref claimer) = item.claimed_by
|
||||
if let Some(claimer) = item.claimed_by()
|
||||
&& !claimer.is_empty()
|
||||
&& claimer != &our_node
|
||||
&& let Some(claimed_at) = item.claimed_at
|
||||
&& claimer != our_node.as_str()
|
||||
&& let Some(claimed_at) = item.claimed_at()
|
||||
{
|
||||
let now = chrono::Utc::now().timestamp() as f64;
|
||||
let age = now - claimed_at;
|
||||
@@ -74,7 +75,7 @@ pub(super) async fn scan_and_claim(
|
||||
slog!(
|
||||
"[agent-mode] Displacing stale claim on '{}' held by {:.12}… \
|
||||
(age {}s > TTL {}s)",
|
||||
item.story_id,
|
||||
item.story_id(),
|
||||
claimer,
|
||||
age as u64,
|
||||
CLAIM_TIMEOUT_SECS as u64,
|
||||
@@ -97,10 +98,10 @@ pub(super) async fn scan_and_claim(
|
||||
})
|
||||
.map(|n| n.node_id)
|
||||
.collect();
|
||||
if !should_self_claim(&our_node, &item.story_id, &alive_peers) {
|
||||
if !should_self_claim(&our_node, item.story_id(), &alive_peers) {
|
||||
slog!(
|
||||
"[agent-mode] Hash tie-break: deferring claim on '{}' to lower-hash peer",
|
||||
item.story_id
|
||||
item.story_id()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
@@ -108,11 +109,11 @@ pub(super) async fn scan_and_claim(
|
||||
// Try to claim this story.
|
||||
slog!(
|
||||
"[agent-mode] Claiming story '{}' for this node",
|
||||
item.story_id
|
||||
item.story_id()
|
||||
);
|
||||
if crdt_state::write_claim(&item.story_id) {
|
||||
if crdt_state::write_claim(item.story_id()) {
|
||||
let now = chrono::Utc::now().timestamp() as f64;
|
||||
our_claims.insert(item.story_id.clone(), now);
|
||||
our_claims.insert(item.story_id().to_string(), now);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,27 +166,28 @@ pub(super) fn reclaim_timed_out_work(_project_root: &Path) {
|
||||
let now = chrono::Utc::now().timestamp() as f64;
|
||||
|
||||
for item in &items {
|
||||
if !crate::pipeline_state::Stage::from_dir(&item.stage).is_some_and(|s| s.is_active()) {
|
||||
if !crate::pipeline_state::Stage::from_dir(item.stage_str()).is_some_and(|s| s.is_active())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Release the claim if the TTL has expired — regardless of whether the
|
||||
// holder is still alive. A node actively working should refresh its
|
||||
// claim before the TTL window closes.
|
||||
if let Some(ref claimer) = item.claimed_by {
|
||||
if let Some(claimer) = item.claimed_by() {
|
||||
if claimer.is_empty() {
|
||||
continue;
|
||||
}
|
||||
if let Some(claimed_at) = item.claimed_at
|
||||
if let Some(claimed_at) = item.claimed_at()
|
||||
&& now - claimed_at >= CLAIM_TIMEOUT_SECS
|
||||
{
|
||||
slog!(
|
||||
"[agent-mode] Releasing stale claim on '{}' held by {:.12}… (age {}s)",
|
||||
item.story_id,
|
||||
item.story_id(),
|
||||
claimer,
|
||||
(now - claimed_at) as u64,
|
||||
);
|
||||
crdt_state::release_claim(&item.story_id);
|
||||
crdt_state::release_claim(item.story_id());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user