|
2 | 2 | //! |
3 | 3 | //! Provides persistent state management for MapReduce jobs, enabling recovery |
4 | 4 | //! from failures and job resumption with minimal data loss. |
| 5 | +//! |
| 6 | +//! Note: This module is being migrated to use pure functions from state_pure. |
| 7 | +//! The imperative methods are now wrappers around pure state transitions. |
5 | 8 |
|
6 | | -use crate::cook::execution::mapreduce::{AgentResult, AgentStatus, MapReduceConfig}; |
| 9 | +#[cfg(test)] |
| 10 | +use crate::cook::execution::mapreduce::AgentStatus; |
| 11 | +use crate::cook::execution::mapreduce::{AgentResult, MapReduceConfig}; |
| 12 | +use crate::cook::execution::state_pure; |
7 | 13 | use crate::cook::workflow::WorkflowStep; |
8 | 14 | use anyhow::{anyhow, Context, Result}; |
9 | 15 | use chrono::{DateTime, Utc}; |
@@ -130,38 +136,21 @@ fn default_format_version() -> u32 { |
130 | 136 | 1 |
131 | 137 | } |
132 | 138 |
|
133 | | -/// Create an initial failure record for a work item |
134 | | -fn create_initial_failure_record(item_id: &str) -> FailureRecord { |
135 | | - FailureRecord { |
136 | | - item_id: item_id.to_string(), |
137 | | - attempts: 0, |
138 | | - last_error: String::new(), |
139 | | - last_attempt: Utc::now(), |
140 | | - worktree_info: None, |
141 | | - } |
| 139 | +/// Convert old MapReduceJobState to state_pure version |
| 140 | +fn to_pure_state(state: &MapReduceJobState) -> state_pure::MapReduceJobState { |
| 141 | + // Leverage serde for conversion since structures are identical |
| 142 | + let json = serde_json::to_string(state).expect("Failed to serialize state"); |
| 143 | + serde_json::from_str(&json).expect("Failed to deserialize to pure state") |
142 | 144 | } |
143 | 145 |
|
144 | | -/// Extract error message from agent status |
145 | | -fn extract_error_message(status: &AgentStatus) -> String { |
146 | | - match status { |
147 | | - AgentStatus::Failed(err) => err.clone(), |
148 | | - AgentStatus::Timeout => "Agent execution timed out".to_string(), |
149 | | - _ => String::new(), |
150 | | - } |
| 146 | +/// Convert state_pure MapReduceJobState back to old version |
| 147 | +fn from_pure_state(state: state_pure::MapReduceJobState) -> MapReduceJobState { |
| 148 | + // Leverage serde for conversion since structures are identical |
| 149 | + let json = serde_json::to_string(&state).expect("Failed to serialize pure state"); |
| 150 | + serde_json::from_str(&json).expect("Failed to deserialize from pure state") |
151 | 151 | } |
152 | 152 |
|
153 | | -/// Extract worktree info from agent result if available |
154 | | -fn extract_worktree_info(result: &AgentResult) -> Option<WorktreeInfo> { |
155 | | - match (&result.worktree_path, &result.branch_name) { |
156 | | - (Some(path), Some(name)) => Some(WorktreeInfo { |
157 | | - path: path.clone(), |
158 | | - name: name.clone(), |
159 | | - branch: result.branch_name.clone(), |
160 | | - session_id: result.worktree_session_id.clone(), |
161 | | - }), |
162 | | - _ => None, |
163 | | - } |
164 | | -} |
| 153 | +// Note: Helper functions removed - now using pure functions from state_pure module |
165 | 154 |
|
166 | 155 | /// Serialize job state to JSON string |
167 | 156 | fn serialize_state(state: &MapReduceJobState) -> Result<String> { |
@@ -266,110 +255,47 @@ impl MapReduceJobState { |
266 | 255 | } |
267 | 256 | } |
268 | 257 |
|
269 | | - /// Update state with a completed agent result |
| 258 | + /// Update state with a completed agent result (wrapper around pure function) |
270 | 259 | pub fn update_agent_result(&mut self, result: AgentResult) { |
271 | | - let item_id = result.item_id.clone(); |
272 | | - |
273 | | - self.update_counts_for_status(&result.status, &item_id); |
274 | | - self.store_agent_result(item_id.clone(), result); |
275 | | - self.finalize_result_update(item_id); |
276 | | - } |
277 | | - |
278 | | - /// Update success/failure counts based on agent status |
279 | | - fn update_counts_for_status(&mut self, status: &AgentStatus, item_id: &str) { |
280 | | - match status { |
281 | | - AgentStatus::Success => self.handle_success(item_id), |
282 | | - AgentStatus::Failed(_) | AgentStatus::Timeout => self.handle_failure(status, item_id), |
283 | | - _ => {} |
284 | | - } |
285 | | - } |
286 | | - |
287 | | - /// Handle successful agent completion |
288 | | - fn handle_success(&mut self, item_id: &str) { |
289 | | - self.successful_count += 1; |
290 | | - self.failed_agents.remove(item_id); |
291 | | - } |
292 | | - |
293 | | - /// Handle failed or timed-out agent execution |
294 | | - fn handle_failure(&mut self, status: &AgentStatus, item_id: &str) { |
295 | | - // Get or create failure record and update it in one operation to avoid borrow issues |
296 | | - let failure = self |
297 | | - .failed_agents |
298 | | - .entry(item_id.to_string()) |
299 | | - .or_insert_with(|| create_initial_failure_record(item_id)); |
300 | | - |
301 | | - // Update failure record inline |
302 | | - failure.attempts += 1; |
303 | | - failure.last_attempt = Utc::now(); |
304 | | - failure.last_error = extract_error_message(status); |
305 | | - |
306 | | - self.failed_count += 1; |
| 260 | + // Convert to pure state, apply transformation, convert back |
| 261 | + let pure_state = to_pure_state(self); |
| 262 | + let new_pure_state = state_pure::apply_agent_result(pure_state, result); |
| 263 | + *self = from_pure_state(new_pure_state); |
307 | 264 | } |
308 | 265 |
|
309 | | - /// Store agent result and mark as completed |
310 | | - fn store_agent_result(&mut self, item_id: String, result: AgentResult) { |
311 | | - if let Some(worktree_info) = extract_worktree_info(&result) { |
312 | | - if let Some(failure) = self.failed_agents.get_mut(&item_id) { |
313 | | - failure.worktree_info = Some(worktree_info); |
314 | | - } |
315 | | - } |
316 | | - self.agent_results.insert(item_id.clone(), result); |
317 | | - self.completed_agents.insert(item_id); |
318 | | - } |
319 | | - |
320 | | - /// Finalize result update (remove from pending, update metadata) |
321 | | - fn finalize_result_update(&mut self, item_id: String) { |
322 | | - self.pending_items.retain(|id| id != &item_id); |
323 | | - self.updated_at = Utc::now(); |
324 | | - self.checkpoint_version += 1; |
325 | | - } |
| 266 | + // Note: Deprecated helper methods removed - now using pure functions from state_pure module |
326 | 267 |
|
327 | | - /// Check if all agents have completed |
| 268 | + /// Check if all agents have completed (wrapper around pure function) |
328 | 269 | pub fn is_map_phase_complete(&self) -> bool { |
329 | | - self.pending_items.is_empty() && self.completed_agents.len() == self.total_items |
| 270 | + let pure_state = to_pure_state(self); |
| 271 | + state_pure::is_map_phase_complete(&pure_state) |
330 | 272 | } |
331 | 273 |
|
332 | | - /// Get items that can be retried |
| 274 | + /// Get items that can be retried (wrapper around pure function) |
333 | 275 | pub fn get_retriable_items(&self, max_retries: u32) -> Vec<String> { |
334 | | - self.failed_agents |
335 | | - .iter() |
336 | | - .filter(|(_, failure)| failure.attempts < max_retries) |
337 | | - .map(|(id, _)| id.clone()) |
338 | | - .collect() |
| 276 | + let pure_state = to_pure_state(self); |
| 277 | + state_pure::get_retriable_items(&pure_state, max_retries) |
339 | 278 | } |
340 | 279 |
|
341 | | - /// Mark reduce phase as started |
| 280 | + /// Mark reduce phase as started (wrapper around pure function) |
342 | 281 | pub fn start_reduce_phase(&mut self) { |
343 | | - self.reduce_phase_state = Some(ReducePhaseState { |
344 | | - started: true, |
345 | | - completed: false, |
346 | | - executed_commands: Vec::new(), |
347 | | - output: None, |
348 | | - error: None, |
349 | | - started_at: Some(Utc::now()), |
350 | | - completed_at: None, |
351 | | - }); |
352 | | - self.updated_at = Utc::now(); |
353 | | - self.checkpoint_version += 1; |
| 282 | + let pure_state = to_pure_state(self); |
| 283 | + let new_pure_state = state_pure::start_reduce_phase(pure_state); |
| 284 | + *self = from_pure_state(new_pure_state); |
354 | 285 | } |
355 | 286 |
|
356 | | - /// Mark reduce phase as completed |
| 287 | + /// Mark reduce phase as completed (wrapper around pure function) |
357 | 288 | pub fn complete_reduce_phase(&mut self, output: Option<String>) { |
358 | | - if let Some(ref mut state) = self.reduce_phase_state { |
359 | | - state.completed = true; |
360 | | - state.output = output; |
361 | | - state.completed_at = Some(Utc::now()); |
362 | | - } |
363 | | - self.is_complete = true; |
364 | | - self.updated_at = Utc::now(); |
365 | | - self.checkpoint_version += 1; |
| 289 | + let pure_state = to_pure_state(self); |
| 290 | + let new_pure_state = state_pure::complete_reduce_phase(pure_state, output); |
| 291 | + *self = from_pure_state(new_pure_state); |
366 | 292 | } |
367 | 293 |
|
368 | | - /// Mark job as complete |
| 294 | + /// Mark job as complete (wrapper around pure function) |
369 | 295 | pub fn mark_complete(&mut self) { |
370 | | - self.is_complete = true; |
371 | | - self.updated_at = Utc::now(); |
372 | | - self.checkpoint_version += 1; |
| 296 | + let pure_state = to_pure_state(self); |
| 297 | + let new_pure_state = state_pure::mark_complete(pure_state); |
| 298 | + *self = from_pure_state(new_pure_state); |
373 | 299 | } |
374 | 300 |
|
375 | 301 | /// Find a work item by ID |
|
0 commit comments