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§
Sourcefn 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 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 operationresource_type- The FHIR resource typeid- The resource’s logical IDversion_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
Sourcefn 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 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 operationresource_type- The FHIR resource typeid- The resource’s logical IDexpected_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 existStorageError::Concurrency(VersionConflict)- If versions don’t matchStorageError::Concurrency(OptimisticLockFailure)- If update races with anotherStorageError::Tenant- If the tenant doesn’t have update permission
Sourcefn 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 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 operationresource_type- The FHIR resource typeid- The resource’s logical IDexpected_version- The expected current version
§Errors
StorageError::Resource(NotFound)- If the resource doesn’t existStorageError::Concurrency(VersionConflict)- If versions don’t matchStorageError::Tenant- If the tenant doesn’t have delete permission
Sourcefn 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,
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 Methods§
Sourcefn 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,
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 operationresource_type- The FHIR resource typeid- The resource’s logical ID
§Returns
The current version ID, or None if the resource doesn’t exist or is deleted.