Skip to content

Simplify storage trait error API: Accept io::Error from applications, manually convert to StorageError<C> with context #95

@drmingdrmer

Description

@drmingdrmer

Motivation:

Applications implementing storage traits must construct complex StorageError<C> with subject, verb, and metadata. This is unnecessary - applications only need to signal "something went wrong".

However, applications still need to receive structured StorageError<C> FROM Openraft for rich error information, serde support, and debugging.

Proposed Changes:

  1. Storage trait methods return io::Error instead of StorageError<C>:

    // Before
    async fn save_vote(&mut self, vote: &Vote<C>) -> Result<(), StorageError<C>>;
    
    // After  
    async fn save_vote(&mut self, vote: &Vote<C>) -> Result<(), io::Error>;
  2. Openraft manually converts io::Error to StorageError<C> with proper context at call sites:

    // In Openraft's internal code
    self.log_store.save_vote(vote)
        .await
        .map_err(|e| StorageError::write_vote(&e))?;  // Add context here
        
    self.log_store.get_log_state()
        .await
        .map_err(|e| StorageError::read(&e))?;  // Different context
  3. No From<io::Error> trait implementation - manual conversion preserves semantic context (subject/verb)

  4. Openraft continues returning StorageError<C> to applications through RaftError::Fatal(Fatal::StorageError(StorageError<C>))

Benefits:

For application developers:

  • Return simple io::Error - no need to choose subject/verb
  • Significant reduction in boilerplate
  • Direct compatibility with Rust error ecosystem

For Openraft:

  • Full control over error context (knows which operation was called)
  • Maintains structured StorageError<C> with accurate metadata
  • Can add proper subject/verb based on operation

For error consumers:

  • Still receive full StorageError<C> with rich information
  • Serde support unchanged
  • Accurate error context (subject/verb match actual operation)

Implementation Checklist:

  • Update all storage trait method signatures to return Result<T, io::Error>:
    • RaftLogStorage (8 methods)
    • RaftLogReader (4 methods)
    • RaftStateMachine (5 methods)
    • RaftSnapshotBuilder (1 method)
  • Update all Openraft internal call sites to manually convert io::ErrorStorageError<C> with appropriate helper
  • Keep Fatal::StorageError(StorageError<C>) unchanged
  • Update examples (memstore, rocksstore, raft-kv-memstore) to return io::Error
  • Update documentation and migration guide
  • Keep existing StorageError::write_logs() helpers for Openraft's internal use

Example Before/After:

// Before: Application constructs StorageError
impl RaftLogStorage for MyStore {
    async fn save_vote(&mut self, vote: &Vote<C>) -> Result<(), StorageError<C>> {
        self.db.write(vote)
            .await
            .map_err(|e| StorageError::write_vote(&e))?;  // Application adds context
        Ok(())
    }
}

// After: Application returns io::Error, Openraft adds context
impl RaftLogStorage for MyStore {
    async fn save_vote(&mut self, vote: &Vote<C>) -> Result<(), io::Error> {
        self.db.write(vote).await?;  // Simple\!
        Ok(())
    }
}

// In Openraft core:
log_store.save_vote(vote)
    .await
    .map_err(|e| StorageError::write_vote(&e))?;  // Openraft adds context

Files Affected:

  • openraft/src/storage/v2/*.rs - Update trait method signatures (18 methods)
  • openraft/src/core/*.rs - Add .map_err(|e| StorageError::xxx(&e)) at all storage call sites
  • openraft/src/storage/helper.rs - Update call sites
  • stores/memstore/src/*.rs - Return io::Error instead of StorageError<C>
  • examples/*/src/*.rs - Return io::Error instead of StorageError<C>

Migration Impact:

  • Breaking change for all storage implementations
  • Applications must change return types from StorageError<C> to io::Error
  • Migration is straightforward: remove .map_err(|e| StorageError::xxx(&e)) from application code
  • Target for 0.11.0 release

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions