Files
qiming/qiming-rcoder/.kiro/specs/unified-container-stop/design.md
2026-06-01 13:54:52 +08:00

9.1 KiB
Raw Permalink Blame History

Design Document

Overview

This design document outlines the unified container stopping module that will consolidate all container stopping logic into a single, reusable module within the docker_manager crate. The module will provide two distinct stopping strategies optimized for different scenarios: startup cleanup and runtime cleanup.

Architecture

Module Location

crates/docker_manager/src/
├── lib.rs                    (existing)
├── manager.rs                (existing)
├── types.rs                  (existing)
├── utils.rs                  (existing)
└── container_stop.rs         (new - unified stopping logic)

High-Level Design

graph TD
    A[main.rs startup] -->|startup cleanup| B[container_stop::startup_cleanup]
    C[cleanup_task.rs] -->|runtime cleanup| D[container_stop::runtime_cleanup]
    E[container_manager.rs] -->|runtime cleanup| D
    
    B --> F[stop_with_409_filter]
    D --> G[stop_with_quick_timeout]
    
    F --> H[DockerManager::stop_container_by_id_with_timeout]
    G --> H
    
    H --> I[Docker API]

Components and Interfaces

1. Container Stop Module (container_stop.rs)

Public API

/// 启动时容器清理策略
/// 
/// 特点:
/// - 使用5秒超时
/// - 过滤409冲突错误容器已在删除中
/// - 不阻塞服务启动
pub async fn startup_cleanup_containers(
    docker_manager: &DockerManager,
    pattern: &str,
) -> Result<CleanupResult, DockerError>

/// 运行时容器清理策略
///
/// 特点:
/// - 使用3秒优雅停止超时
/// - 立即强制停止
/// - 快速释放资源
pub async fn runtime_cleanup_container(
    docker_manager: &DockerManager,
    container_id: &str,
) -> Result<(), DockerError>

/// 运行时批量清理容器
pub async fn runtime_cleanup_containers(
    docker_manager: &DockerManager,
    container_ids: Vec<String>,
) -> Result<CleanupResult, DockerError>

Internal Helper Functions

/// 停止单个容器(启动场景)
async fn stop_container_startup_mode(
    docker_manager: &DockerManager,
    container_id: &str,
) -> Result<(), DockerError>

/// 停止单个容器(运行时场景)
async fn stop_container_runtime_mode(
    docker_manager: &DockerManager,
    container_id: &str,
) -> Result<(), DockerError>

/// 检查是否为409冲突错误
fn is_409_conflict_error(error: &DockerError) -> bool

/// 创建清理结果统计
fn create_cleanup_result(
    total: usize,
    successful: usize,
    failed: usize,
    removed_ids: Vec<String>,
    failures: Vec<ContainerRemovalFailure>,
) -> CleanupResult

2. Configuration Constants

/// 启动清理超时时间(秒)
const STARTUP_CLEANUP_TIMEOUT_SECONDS: u64 = 5;

/// 运行时清理超时时间(秒)
const RUNTIME_CLEANUP_TIMEOUT_SECONDS: u64 = 3;

/// 容器停止后的等待时间(毫秒)
const POST_STOP_WAIT_MS: u64 = 100;

Data Models

CleanupResult (existing, reused)

pub struct CleanupResult {
    pub total_found: usize,
    pub successfully_removed: usize,
    pub failed_removals: usize,
    pub removed_container_ids: Vec<String>,
    pub failed_removals_details: Vec<ContainerRemovalFailure>,
    pub duration_ms: u64,
}

ContainerRemovalFailure (existing, reused)

pub struct ContainerRemovalFailure {
    pub container_id: String,
    pub container_name: String,
    pub error_message: String,
}

Error Handling

Startup Cleanup Error Handling

  1. 409 Conflict Errors

    • Log at INFO level: "容器已在删除中,跳过"
    • Do not count as failure
    • Continue processing other containers
  2. Other Errors

    • Log at WARN level with error details
    • Count as failure in statistics
    • Continue processing other containers
    • Do not block service startup

Runtime Cleanup Error Handling

  1. All Errors

    • Log at WARN level with error details
    • Return error to caller
    • Caller decides whether to continue
  2. Timeout Handling

    • If graceful stop times out, force stop immediately
    • Log timeout at WARN level
    • Continue with force stop

Testing Strategy

Unit Tests

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_is_409_conflict_error() {
        // Test 409 error detection
    }

    #[test]
    fn test_cleanup_result_creation() {
        // Test result statistics calculation
    }

    #[tokio::test]
    async fn test_startup_cleanup_filters_409() {
        // Test that 409 errors are filtered during startup
    }

    #[tokio::test]
    async fn test_runtime_cleanup_quick_timeout() {
        // Test that runtime cleanup uses 3-second timeout
    }
}

Integration Tests

  1. Startup Cleanup Integration

    • Create test containers
    • Simulate 409 conflict scenario
    • Verify service continues without error
  2. Runtime Cleanup Integration

    • Create running container
    • Verify 3-second timeout behavior
    • Verify force stop after timeout

Implementation Details

Startup Cleanup Flow

1. List all containers matching pattern
2. For each container:
   a. Call stop_container_startup_mode
   b. Use 5-second timeout
   c. If 409 error: log INFO, skip to next
   d. If other error: log WARN, count as failure
   e. If success: count as success
3. Return CleanupResult with statistics

Runtime Cleanup Flow

1. For given container_id:
   a. Call stop_container_runtime_mode
   b. Use 3-second graceful timeout
   c. If timeout: force stop immediately
   d. Remove container
   e. Log result
2. Return success or error

Integration Points

main.rs Changes

// Before
match startup_cleanup_orphaned_containers(&docker_manager).await {
    Ok(cleaned_count) => { /* ... */ }
    Err(e) => { /* ... */ }
}

// After
use docker_manager::container_stop;

match container_stop::startup_cleanup_containers(&docker_manager, "rcoder-agent-*").await {
    Ok(result) => {
        if result.successfully_removed > 0 {
            info!("✅ 启动时清理完成,共清理了 {} 个遗留容器", result.successfully_removed);
        }
    }
    Err(e) => {
        warn!("⚠️ 启动时容器清理失败: {},但这不影响服务启动", e);
    }
}

cleanup_task.rs Changes

// In destroy_docker_container method
use docker_manager::container_stop;

// Replace stop_container_by_id_with_timeout call
container_stop::runtime_cleanup_container(&docker_manager, &container_id).await?;

container_manager.rs Changes

// If needed for container cleanup
use docker_manager::container_stop;

container_stop::runtime_cleanup_container(&docker_manager, &container_id).await?;

Performance Considerations

  1. Startup Cleanup

    • Parallel processing of containers (existing behavior maintained)
    • 5-second timeout prevents long blocking
    • 409 filtering reduces unnecessary retries
  2. Runtime Cleanup

    • 3-second timeout balances speed and graceful shutdown
    • Immediate force stop after timeout
    • Minimal resource holding time

Security Considerations

  1. Container Isolation

    • Only stop containers matching specific patterns
    • Validate container ownership before stopping
  2. Error Information

    • Do not expose sensitive container details in logs
    • Use generic error messages for external errors

Logging Strategy

Log Levels

  • INFO: Normal operations, 409 conflicts during startup
  • WARN: Unexpected errors, timeouts
  • ERROR: Critical failures (none expected in this module)
  • DEBUG: Detailed operation flow (for development)

Log Format

// Startup cleanup
info!("🧹 [STARTUP_CLEANUP] 开始清理容器: pattern={}", pattern);
info!("✅ [STARTUP_CLEANUP] 容器清理成功: container_id={}", container_id);
info!("🔄 [STARTUP_CLEANUP] 容器已在删除中,跳过: container_id={}", container_id);
warn!("⚠️ [STARTUP_CLEANUP] 容器清理失败: container_id={}, error={}", container_id, error);

// Runtime cleanup
info!("🔥 [RUNTIME_CLEANUP] 开始停止容器: container_id={}", container_id);
info!("✅ [RUNTIME_CLEANUP] 容器停止成功: container_id={}", container_id);
warn!("⏰ [RUNTIME_CLEANUP] 容器停止超时,强制停止: container_id={}", container_id);
warn!("⚠️ [RUNTIME_CLEANUP] 容器停止失败: container_id={}, error={}", container_id, error);

Migration Strategy

Phase 1: Create New Module

  • Implement container_stop.rs in docker_manager crate
  • Add unit tests
  • Export public API from lib.rs

Phase 2: Update main.rs

  • Replace startup cleanup logic
  • Test service startup with orphaned containers
  • Verify 409 error filtering

Phase 3: Update cleanup_task.rs

  • Replace destroy_docker_container logic
  • Test runtime cleanup behavior
  • Verify 3-second timeout

Phase 4: Update container_manager.rs (if needed)

  • Replace any container stopping logic
  • Test container lifecycle

Phase 5: Cleanup

  • Remove old duplicated code
  • Update documentation
  • Final integration testing

Rollback Plan

If issues are discovered:

  1. Revert changes to calling code (main.rs, cleanup_task.rs, etc.)
  2. Keep new module for future use
  3. Restore original inline implementations
  4. Document issues for future redesign