Skip to main content

VersionedStorage

Trait VersionedStorage 

Source
pub trait VersionedStorage: ResourceStorage {
    // Required methods
    fn vread<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>(
        &'life0 self,
        tenant: &'life1 TenantContext,
        resource_type: &'life2 str,
        id: &'life3 str,
        version_id: &'life4 str,
    ) -> Pin<Box<dyn Future<Output = StorageResult<Option<StoredResource>>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait,
             'life2: 'async_trait,
             'life3: 'async_trait,
             'life4: 'async_trait;
    fn update_with_match<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>(
        &'life0 self,
        tenant: &'life1 TenantContext,
        resource_type: &'life2 str,
        id: &'life3 str,
        expected_version: &'life4 str,
        resource: Value,
    ) -> Pin<Box<dyn Future<Output = StorageResult<StoredResource>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait,
             'life2: 'async_trait,
             'life3: 'async_trait,
             'life4: 'async_trait;
    fn delete_with_match<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>(
        &'life0 self,
        tenant: &'life1 TenantContext,
        resource_type: &'life2 str,
        id: &'life3 str,
        expected_version: &'life4 str,
    ) -> Pin<Box<dyn Future<Output = StorageResult<()>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait,
             'life2: 'async_trait,
             'life3: 'async_trait,
             'life4: 'async_trait;
    fn list_versions<'life0, 'life1, 'life2, 'life3, 'async_trait>(
        &'life0 self,
        tenant: &'life1 TenantContext,
        resource_type: &'life2 str,
        id: &'life3 str,
    ) -> Pin<Box<dyn Future<Output = StorageResult<Vec<String>>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait,
             'life2: 'async_trait,
             'life3: 'async_trait;

    // Provided method
    fn current_version<'life0, 'life1, 'life2, 'life3, 'async_trait>(
        &'life0 self,
        tenant: &'life1 TenantContext,
        resource_type: &'life2 str,
        id: &'life3 str,
    ) -> Pin<Box<dyn Future<Output = StorageResult<Option<String>>> + Send + 'async_trait>>
       where Self: Sync + 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait,
             'life2: 'async_trait,
             'life3: 'async_trait { ... }
}
Expand description

Storage trait with version-aware operations.

This trait extends ResourceStorage with capabilities for reading specific versions of resources and performing updates with optimistic locking.

§Versioning Model

Each resource has a version ID that is incremented on every update. The version ID is a monotonically increasing string (typically a number). The first version of a resource has version ID “1”.

§Optimistic Locking

The update_with_match method implements HTTP If-Match semantics. The update only succeeds if the current version matches the expected version. This prevents lost updates in concurrent scenarios.

§Example

use helios_persistence::core::VersionedStorage;

async fn example<S: VersionedStorage>(storage: &S) -> Result<(), StorageError> {
    let tenant = TenantContext::new(
        TenantId::new("acme"),
        TenantPermissions::full_access(),
    );

    // Read a specific version
    let v1 = storage.vread(&tenant, "Patient", "123", "1").await?;

    // Update with optimistic locking
    if let Some(current) = storage.read(&tenant, "Patient", "123").await? {
        let new_content = serde_json::json!({"name": [{"family": "Updated"}]});
        let updated = storage.update_with_match(
            &tenant,
            "Patient",
            "123",
            current.version_id(),
            new_content,
        ).await?;
    }

    Ok(())
}

Required Methods§

Source

fn vread<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>( &'life0 self, tenant: &'life1 TenantContext, resource_type: &'life2 str, id: &'life3 str, version_id: &'life4 str, ) -> Pin<Box<dyn Future<Output = StorageResult<Option<StoredResource>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, 'life4: 'async_trait,

Reads a specific version of a resource (vread).

This corresponds to the FHIR vread interaction: GET [base]/[type]/[id]/_history/[vid]

§Arguments
  • tenant - The tenant context for this operation
  • resource_type - The FHIR resource type
  • id - The resource’s logical ID
  • version_id - The version ID to read
§Returns

The stored resource at the specified version, or None if not found. Note that this returns the resource even if it was subsequently deleted, as long as the specific version exists.

§Errors
  • StorageError::Tenant - If the tenant doesn’t have read permission
Source

fn update_with_match<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>( &'life0 self, tenant: &'life1 TenantContext, resource_type: &'life2 str, id: &'life3 str, expected_version: &'life4 str, resource: Value, ) -> Pin<Box<dyn Future<Output = StorageResult<StoredResource>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, 'life4: 'async_trait,

Updates a resource with optimistic locking (If-Match).

The update only succeeds if the current version matches expected_version. This implements HTTP If-Match semantics for concurrent update protection.

§Arguments
  • tenant - The tenant context for this operation
  • resource_type - The FHIR resource type
  • id - The resource’s logical ID
  • expected_version - The expected current version (from ETag/version_id)
  • resource - The new resource content
§Returns

The updated resource with incremented version.

§Errors
  • StorageError::Resource(NotFound) - If the resource doesn’t exist
  • StorageError::Concurrency(VersionConflict) - If versions don’t match
  • StorageError::Concurrency(OptimisticLockFailure) - If update races with another
  • StorageError::Tenant - If the tenant doesn’t have update permission
Source

fn delete_with_match<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>( &'life0 self, tenant: &'life1 TenantContext, resource_type: &'life2 str, id: &'life3 str, expected_version: &'life4 str, ) -> Pin<Box<dyn Future<Output = StorageResult<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, 'life4: 'async_trait,

Deletes a resource with optimistic locking (If-Match).

The delete only succeeds if the current version matches expected_version.

§Arguments
  • tenant - The tenant context for this operation
  • resource_type - The FHIR resource type
  • id - The resource’s logical ID
  • expected_version - The expected current version
§Errors
  • StorageError::Resource(NotFound) - If the resource doesn’t exist
  • StorageError::Concurrency(VersionConflict) - If versions don’t match
  • StorageError::Tenant - If the tenant doesn’t have delete permission
Source

fn list_versions<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, tenant: &'life1 TenantContext, resource_type: &'life2 str, id: &'life3 str, ) -> Pin<Box<dyn Future<Output = StorageResult<Vec<String>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait,

Lists all version IDs for a resource.

§Arguments
  • tenant - The tenant context for this operation
  • resource_type - The FHIR resource type
  • id - The resource’s logical ID
§Returns

A vector of version IDs in ascending order (oldest first).

Provided Methods§

Source

fn current_version<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, tenant: &'life1 TenantContext, resource_type: &'life2 str, id: &'life3 str, ) -> Pin<Box<dyn Future<Output = StorageResult<Option<String>>> + Send + 'async_trait>>
where Self: Sync + 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait,

Gets the current version ID of a resource without reading the full content.

This is more efficient than read when you only need the version.

§Arguments
  • tenant - The tenant context for this operation
  • resource_type - The FHIR resource type
  • id - The resource’s logical ID
§Returns

The current version ID, or None if the resource doesn’t exist or is deleted.

Implementors§