PACS System 0.1.0
PACS DICOM system library
Loading...
Searching...
No Matches
kcenon::pacs::storage::hsm_storage Class Reference

#include <hsm_storage.h>

Inheritance diagram for kcenon::pacs::storage::hsm_storage:
Inheritance graph
Collaboration diagram for kcenon::pacs::storage::hsm_storage:
Collaboration graph

Public Member Functions

 hsm_storage (std::unique_ptr< storage_interface > hot_tier, std::unique_ptr< storage_interface > warm_tier, std::unique_ptr< storage_interface > cold_tier, const hsm_storage_config &config={})
 Construct HSM storage with three tier backends.
 
 ~hsm_storage () override=default
 Destructor.
 
 hsm_storage (const hsm_storage &)=delete
 Non-copyable (contains mutex and unique_ptr)
 
hsm_storageoperator= (const hsm_storage &)=delete
 
 hsm_storage (hsm_storage &&)=delete
 Non-movable (contains mutex)
 
hsm_storageoperator= (hsm_storage &&)=delete
 
auto store (const core::dicom_dataset &dataset) -> VoidResult override
 Store a DICOM dataset to the hot tier.
 
auto retrieve (std::string_view sop_instance_uid) -> Result< core::dicom_dataset > override
 Retrieve a DICOM dataset by SOP Instance UID.
 
auto remove (std::string_view sop_instance_uid) -> VoidResult override
 Remove a DICOM dataset from all tiers.
 
auto exists (std::string_view sop_instance_uid) const -> bool override
 Check if a DICOM instance exists in any tier.
 
auto find (const core::dicom_dataset &query) -> Result< std::vector< core::dicom_dataset > > override
 Find DICOM datasets matching query criteria across all tiers.
 
auto get_statistics () const -> storage_statistics override
 Get combined storage statistics from all tiers.
 
auto verify_integrity () -> VoidResult override
 Verify storage integrity across all tiers.
 
auto get_tier (std::string_view sop_instance_uid) const -> std::optional< storage_tier >
 Get the current tier of an instance.
 
auto get_tier_metadata (std::string_view sop_instance_uid) const -> std::optional< tier_metadata >
 Get tier metadata for an instance.
 
auto migrate (std::string_view sop_instance_uid, storage_tier target_tier) -> VoidResult
 Manually migrate an instance to a different tier.
 
auto get_migration_candidates (storage_tier from_tier, storage_tier to_tier) const -> std::vector< tier_metadata >
 Get instances eligible for migration.
 
auto run_migration_cycle () -> migration_result
 Run a single migration cycle.
 
auto get_tier_policy () const -> tier_policy
 Get the current tier policy.
 
void set_tier_policy (const tier_policy &policy)
 Set the tier policy.
 
auto get_hsm_statistics () const -> hsm_statistics
 Get HSM-specific statistics.
 
auto get_tier_storage (storage_tier tier) const -> storage_interface *
 Get the storage backend for a specific tier.
 
- Public Member Functions inherited from kcenon::pacs::storage::storage_interface
virtual ~storage_interface ()=default
 Virtual destructor for proper polymorphic destruction.
 
virtual auto store_batch (const std::vector< core::dicom_dataset > &datasets) -> VoidResult
 Store multiple DICOM datasets in a single operation.
 
virtual auto retrieve_batch (const std::vector< std::string > &sop_instance_uids) -> Result< std::vector< core::dicom_dataset > >
 Retrieve multiple DICOM datasets by their SOP Instance UIDs.
 

Private Member Functions

auto find_tier (std::string_view sop_instance_uid) const -> std::optional< storage_tier >
 Find which tier contains an instance.
 
auto get_storage (storage_tier tier) const -> storage_interface *
 Get the storage backend for a tier.
 
void update_metadata (std::string_view sop_instance_uid, storage_tier tier, const core::dicom_dataset &dataset)
 Update tier metadata after store/retrieve.
 
void update_access_time (std::string_view sop_instance_uid)
 Update last access time for an instance.
 
void remove_metadata (std::string_view sop_instance_uid)
 Remove tier metadata.
 
auto migrate_instance (std::string_view uid, storage_tier from_tier, storage_tier to_tier) -> VoidResult
 Migrate a single instance between tiers.
 

Private Attributes

std::unique_ptr< storage_interfacehot_tier_
 Hot tier storage backend.
 
std::unique_ptr< storage_interfacewarm_tier_
 Warm tier storage backend (may be nullptr)
 
std::unique_ptr< storage_interfacecold_tier_
 Cold tier storage backend (may be nullptr)
 
hsm_storage_config config_
 HSM configuration.
 
std::unordered_map< std::string, tier_metadatametadata_index_
 Tier metadata index (SOP Instance UID -> metadata)
 
std::shared_mutex mutex_
 Mutex for thread-safe access.
 

Additional Inherited Members

- Protected Member Functions inherited from kcenon::pacs::storage::storage_interface
 storage_interface ()=default
 Protected default constructor for derived classes.
 
 storage_interface (const storage_interface &)=delete
 Non-copyable.
 
storage_interfaceoperator= (const storage_interface &)=delete
 
 storage_interface (storage_interface &&)=default
 Movable.
 
storage_interfaceoperator= (storage_interface &&)=default
 

Detailed Description

Definition at line 87 of file hsm_storage.h.

Constructor & Destructor Documentation

◆ hsm_storage() [1/3]

kcenon::pacs::storage::hsm_storage::hsm_storage ( std::unique_ptr< storage_interface > hot_tier,
std::unique_ptr< storage_interface > warm_tier,
std::unique_ptr< storage_interface > cold_tier,
const hsm_storage_config & config = {} )

Construct HSM storage with three tier backends.

Parameters
hot_tierStorage backend for hot tier (required)
warm_tierStorage backend for warm tier (optional, can be nullptr)
cold_tierStorage backend for cold tier (optional, can be nullptr)
configHSM configuration
Exceptions
std::invalid_argumentif hot_tier is nullptr
Note
At least hot_tier must be provided. If warm_tier is nullptr, migration will skip directly to cold_tier.
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/storage/hsm_storage.h.

Definition at line 40 of file hsm_storage.cpp.

44 : hot_tier_(std::move(hot_tier)),
45 warm_tier_(std::move(warm_tier)),
46 cold_tier_(std::move(cold_tier)),
47 config_(config) {
48 if (!hot_tier_) {
49 throw std::invalid_argument("hot_tier cannot be nullptr");
50 }
51}
std::unique_ptr< storage_interface > warm_tier_
Warm tier storage backend (may be nullptr)
std::unique_ptr< storage_interface > hot_tier_
Hot tier storage backend.
hsm_storage_config config_
HSM configuration.
std::unique_ptr< storage_interface > cold_tier_
Cold tier storage backend (may be nullptr)

References hot_tier_.

◆ ~hsm_storage()

kcenon::pacs::storage::hsm_storage::~hsm_storage ( )
overridedefault

◆ hsm_storage() [2/3]

kcenon::pacs::storage::hsm_storage::hsm_storage ( const hsm_storage & )
delete

Non-copyable (contains mutex and unique_ptr)

◆ hsm_storage() [3/3]

kcenon::pacs::storage::hsm_storage::hsm_storage ( hsm_storage && )
delete

Non-movable (contains mutex)

Member Function Documentation

◆ exists()

auto kcenon::pacs::storage::hsm_storage::exists ( std::string_view sop_instance_uid) const -> bool
nodiscardoverridevirtual

Check if a DICOM instance exists in any tier.

Parameters
sop_instance_uidThe unique identifier to check
Returns
true if the instance exists in any tier

Implements kcenon::pacs::storage::storage_interface.

Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/storage/hsm_storage.h.

Definition at line 137 of file hsm_storage.cpp.

137 {
138 std::shared_lock lock(mutex_);
139 return metadata_index_.contains(std::string(sop_instance_uid));
140}
std::shared_mutex mutex_
Mutex for thread-safe access.
std::unordered_map< std::string, tier_metadata > metadata_index_
Tier metadata index (SOP Instance UID -> metadata)

◆ find()

auto kcenon::pacs::storage::hsm_storage::find ( const core::dicom_dataset & query) -> Result<std::vector<core::dicom_dataset>>
nodiscardoverridevirtual

Find DICOM datasets matching query criteria across all tiers.

Searches all tiers and combines results.

Parameters
queryThe query dataset containing search criteria
Returns
Result containing matching datasets or error information

Implements kcenon::pacs::storage::storage_interface.

Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/storage/hsm_storage.h.

Definition at line 142 of file hsm_storage.cpp.

143 {
144 std::vector<core::dicom_dataset> combined_results;
145
146 // Search all tiers and combine results
147 std::vector<storage_interface*> tiers = {hot_tier_.get(), warm_tier_.get(),
148 cold_tier_.get()};
149
150 for (auto* tier : tiers) {
151 if (tier == nullptr) {
152 continue;
153 }
154
155 auto result = tier->find(query);
156 if (result.is_ok()) {
157 auto& datasets = result.value();
158 combined_results.insert(combined_results.end(), datasets.begin(),
159 datasets.end());
160 }
161 }
162
163 return combined_results;
164}

◆ find_tier()

auto kcenon::pacs::storage::hsm_storage::find_tier ( std::string_view sop_instance_uid) const -> std::optional<storage_tier>
nodiscardprivate

Find which tier contains an instance.

Parameters
sop_instance_uidThe SOP Instance UID
Returns
The tier containing the instance, or nullopt if not found
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/storage/hsm_storage.h.

Definition at line 474 of file hsm_storage.cpp.

475 {
476 std::shared_lock lock(mutex_);
477 auto it = metadata_index_.find(std::string(sop_instance_uid));
478 if (it != metadata_index_.end()) {
479 return it->second.current_tier;
480 }
481
482 // Fallback: search each tier directly
483 lock.unlock();
484
485 if (hot_tier_ && hot_tier_->exists(sop_instance_uid)) {
486 return storage_tier::hot;
487 }
488 if (warm_tier_ && warm_tier_->exists(sop_instance_uid)) {
489 return storage_tier::warm;
490 }
491 if (cold_tier_ && cold_tier_->exists(sop_instance_uid)) {
492 return storage_tier::cold;
493 }
494
495 return std::nullopt;
496}
@ hot
Hot tier - Recent, frequently accessed data (SSD/NVMe)
@ cold
Cold tier - Archive, rarely accessed data (S3/Glacier)
@ warm
Warm tier - Older, occasionally accessed data (HDD)

References kcenon::pacs::storage::cold, kcenon::pacs::storage::hot, and kcenon::pacs::storage::warm.

◆ get_hsm_statistics()

auto kcenon::pacs::storage::hsm_storage::get_hsm_statistics ( ) const -> hsm_statistics
nodiscard

Get HSM-specific statistics.

Returns
Statistics broken down by tier
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/storage/hsm_storage.h.

Definition at line 411 of file hsm_storage.cpp.

411 {
412 hsm_statistics stats;
413
414 std::shared_lock lock(mutex_);
415
416 std::set<std::string> hot_studies, hot_series;
417 std::set<std::string> warm_studies, warm_series;
418 std::set<std::string> cold_studies, cold_series;
419
420 for (const auto& [uid, meta] : metadata_index_) {
421 switch (meta.current_tier) {
423 stats.hot.instance_count++;
424 stats.hot.total_bytes += meta.size_bytes;
425 if (!meta.study_instance_uid.empty()) {
426 hot_studies.insert(meta.study_instance_uid);
427 }
428 if (!meta.series_instance_uid.empty()) {
429 hot_series.insert(meta.series_instance_uid);
430 }
431 break;
433 stats.warm.instance_count++;
434 stats.warm.total_bytes += meta.size_bytes;
435 if (!meta.study_instance_uid.empty()) {
436 warm_studies.insert(meta.study_instance_uid);
437 }
438 if (!meta.series_instance_uid.empty()) {
439 warm_series.insert(meta.series_instance_uid);
440 }
441 break;
443 stats.cold.instance_count++;
444 stats.cold.total_bytes += meta.size_bytes;
445 if (!meta.study_instance_uid.empty()) {
446 cold_studies.insert(meta.study_instance_uid);
447 }
448 if (!meta.series_instance_uid.empty()) {
449 cold_series.insert(meta.series_instance_uid);
450 }
451 break;
452 }
453 }
454
455 stats.hot.study_count = hot_studies.size();
456 stats.hot.series_count = hot_series.size();
457 stats.warm.study_count = warm_studies.size();
458 stats.warm.series_count = warm_series.size();
459 stats.cold.study_count = cold_studies.size();
460 stats.cold.series_count = cold_series.size();
461
462 return stats;
463}
std::string_view uid

References kcenon::pacs::storage::cold, kcenon::pacs::storage::hot, metadata_index_, mutex_, uid, and kcenon::pacs::storage::warm.

◆ get_migration_candidates()

auto kcenon::pacs::storage::hsm_storage::get_migration_candidates ( storage_tier from_tier,
storage_tier to_tier ) const -> std::vector<tier_metadata>
nodiscard

Get instances eligible for migration.

Returns instances that should be migrated based on the tier policy.

Parameters
from_tierSource tier to check
to_tierTarget tier
Returns
Vector of tier metadata for eligible instances
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/storage/hsm_storage.h.

Definition at line 290 of file hsm_storage.cpp.

292 {
293 std::vector<tier_metadata> candidates;
294
295 std::shared_lock lock(mutex_);
296 for (const auto& [uid, meta] : metadata_index_) {
297 if (meta.current_tier == from_tier &&
298 meta.should_migrate(config_.policy, to_tier)) {
299 candidates.push_back(meta);
300 }
301 }
302
303 // Sort by age (oldest first)
304 std::sort(candidates.begin(), candidates.end(),
305 [](const tier_metadata& a, const tier_metadata& b) {
306 return a.stored_at < b.stored_at;
307 });
308
309 return candidates;
310}
tier_policy policy
Tier migration policy.
Definition hsm_storage.h:34

References uid.

◆ get_statistics()

auto kcenon::pacs::storage::hsm_storage::get_statistics ( ) const -> storage_statistics
nodiscardoverridevirtual

Get combined storage statistics from all tiers.

Returns
Storage statistics aggregated from all tiers

Implements kcenon::pacs::storage::storage_interface.

Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/storage/hsm_storage.h.

Definition at line 166 of file hsm_storage.cpp.

166 {
167 storage_statistics stats;
168
169 std::shared_lock lock(mutex_);
170
171 // Aggregate from all tiers
172 std::vector<storage_interface*> tiers = {hot_tier_.get(), warm_tier_.get(),
173 cold_tier_.get()};
174
175 std::set<std::string> studies;
176 std::set<std::string> series;
177 std::set<std::string> patients;
178
179 for (auto* tier : tiers) {
180 if (tier == nullptr) {
181 continue;
182 }
183
184 auto tier_stats = tier->get_statistics();
185 stats.total_instances += tier_stats.total_instances;
186 stats.total_bytes += tier_stats.total_bytes;
187 }
188
189 // Count unique studies/series from metadata
190 for (const auto& [uid, meta] : metadata_index_) {
191 if (!meta.study_instance_uid.empty()) {
192 studies.insert(meta.study_instance_uid);
193 }
194 if (!meta.series_instance_uid.empty()) {
195 series.insert(meta.series_instance_uid);
196 }
197 }
198
199 stats.studies_count = studies.size();
200 stats.series_count = series.size();
201
202 return stats;
203}

References cold_tier_, hot_tier_, metadata_index_, mutex_, uid, and warm_tier_.

◆ get_storage()

auto kcenon::pacs::storage::hsm_storage::get_storage ( storage_tier tier) const -> storage_interface*
nodiscardprivate

Get the storage backend for a tier.

Parameters
tierThe tier
Returns
Pointer to the storage backend
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/storage/hsm_storage.h.

Definition at line 498 of file hsm_storage.cpp.

498 {
499 switch (tier) {
501 return hot_tier_.get();
503 return warm_tier_.get();
505 return cold_tier_.get();
506 }
507 return nullptr;
508}

References kcenon::pacs::storage::cold, kcenon::pacs::storage::hot, and kcenon::pacs::storage::warm.

◆ get_tier()

auto kcenon::pacs::storage::hsm_storage::get_tier ( std::string_view sop_instance_uid) const -> std::optional<storage_tier>
nodiscard

Get the current tier of an instance.

Parameters
sop_instance_uidThe SOP Instance UID
Returns
The storage tier, or nullopt if not found
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/storage/hsm_storage.h.

Definition at line 250 of file hsm_storage.cpp.

251 {
252 std::shared_lock lock(mutex_);
253 auto it = metadata_index_.find(std::string(sop_instance_uid));
254 if (it == metadata_index_.end()) {
255 return std::nullopt;
256 }
257 return it->second.current_tier;
258}

◆ get_tier_metadata()

auto kcenon::pacs::storage::hsm_storage::get_tier_metadata ( std::string_view sop_instance_uid) const -> std::optional<tier_metadata>
nodiscard

Get tier metadata for an instance.

Parameters
sop_instance_uidThe SOP Instance UID
Returns
Tier metadata, or nullopt if not found
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/storage/hsm_storage.h.

Definition at line 260 of file hsm_storage.cpp.

261 {
262 std::shared_lock lock(mutex_);
263 auto it = metadata_index_.find(std::string(sop_instance_uid));
264 if (it == metadata_index_.end()) {
265 return std::nullopt;
266 }
267 return it->second;
268}

◆ get_tier_policy()

auto kcenon::pacs::storage::hsm_storage::get_tier_policy ( ) const -> tier_policy
nodiscard

Get the current tier policy.

Returns
The current tier policy
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/storage/hsm_storage.h.

Definition at line 401 of file hsm_storage.cpp.

401 {
402 std::shared_lock lock(mutex_);
403 return config_.policy;
404}

References config_, mutex_, and kcenon::pacs::storage::hsm_storage_config::policy.

◆ get_tier_storage()

auto kcenon::pacs::storage::hsm_storage::get_tier_storage ( storage_tier tier) const -> storage_interface*
nodiscard

Get the storage backend for a specific tier.

Parameters
tierThe tier to get
Returns
Pointer to the storage backend, or nullptr if tier not configured
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/storage/hsm_storage.h.

Definition at line 465 of file hsm_storage.cpp.

466 {
467 return get_storage(tier);
468}
auto get_storage(storage_tier tier) const -> storage_interface *
Get the storage backend for a tier.

◆ migrate()

auto kcenon::pacs::storage::hsm_storage::migrate ( std::string_view sop_instance_uid,
storage_tier target_tier ) -> VoidResult
nodiscard

Manually migrate an instance to a different tier.

Parameters
sop_instance_uidThe SOP Instance UID to migrate
target_tierThe target tier
Returns
VoidResult Success or error information
Note
Migration to a "hotter" tier (e.g., cold to hot) is allowed for restoring frequently accessed archived data.
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/storage/hsm_storage.h.

Definition at line 270 of file hsm_storage.cpp.

271 {
272 // Find current tier
273 auto current_tier_opt = find_tier(sop_instance_uid);
274 if (!current_tier_opt.has_value()) {
275 return make_error<std::monostate>(
276 kInstanceNotFound,
277 "Instance not found: " + std::string(sop_instance_uid),
278 "hsm_storage");
279 }
280
281 auto current_tier = *current_tier_opt;
282 if (current_tier == target_tier) {
283 // Already in target tier
284 return ok();
285 }
286
287 return migrate_instance(sop_instance_uid, current_tier, target_tier);
288}
auto migrate_instance(std::string_view uid, storage_tier from_tier, storage_tier to_tier) -> VoidResult
Migrate a single instance between tiers.
auto find_tier(std::string_view sop_instance_uid) const -> std::optional< storage_tier >
Find which tier contains an instance.

◆ migrate_instance()

auto kcenon::pacs::storage::hsm_storage::migrate_instance ( std::string_view uid,
storage_tier from_tier,
storage_tier to_tier ) -> VoidResult
nodiscardprivate

Migrate a single instance between tiers.

Parameters
uidThe SOP Instance UID
from_tierSource tier
to_tierTarget tier
Returns
VoidResult Success or error information
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/storage/hsm_storage.h.

Definition at line 542 of file hsm_storage.cpp.

543 {
544 auto* source = get_storage(from_tier);
545 auto* target = get_storage(to_tier);
546
547 if (source == nullptr) {
548 return make_error<std::monostate>(
549 kTierNotAvailable,
550 "Source tier not available: " + std::string(to_string(from_tier)),
551 "hsm_storage");
552 }
553 if (target == nullptr) {
554 return make_error<std::monostate>(
555 kTierNotAvailable,
556 "Target tier not available: " + std::string(to_string(to_tier)),
557 "hsm_storage");
558 }
559
560 // Retrieve from source
561 auto retrieve_result = source->retrieve(uid);
562 if (!retrieve_result.is_ok()) {
563 return make_error<std::monostate>(
564 kMigrationFailed,
565 "Failed to retrieve from source: " +
566 std::string(retrieve_result.error().message),
567 "hsm_storage");
568 }
569
570 auto& dataset = retrieve_result.value();
571
572 // Store to target
573 auto store_result = target->store(dataset);
574 if (!store_result.is_ok()) {
575 return make_error<std::monostate>(
576 kMigrationFailed,
577 "Failed to store to target: " +
578 std::string(store_result.error().message),
579 "hsm_storage");
580 }
581
582 // Verify if configured
584 if (!target->exists(uid)) {
585 return make_error<std::monostate>(
586 kMigrationFailed,
587 "Verification failed: instance not found in target tier",
588 "hsm_storage");
589 }
590 }
591
592 // Remove from source if configured
594 auto remove_result = source->remove(uid);
595 // Ignore remove failure - the instance is already in target tier
596 (void)remove_result;
597 }
598
599 // Update metadata
600 {
601 std::unique_lock lock(mutex_);
602 auto it = metadata_index_.find(std::string(uid));
603 if (it != metadata_index_.end()) {
604 it->second.current_tier = to_tier;
605 }
606 }
607
608 return ok();
609}
const atna_coded_value source
Source Role ID (110153)
auto to_string(annotation_type type) -> std::string
Convert annotation_type to string.
bool verify_after_migration
Whether to verify data integrity after migration.
Definition hsm_storage.h:41
bool delete_after_migration
Whether to remove source after successful migration When false, data is copied (not moved) between ti...
Definition hsm_storage.h:45

References kcenon::pacs::storage::to_string(), and uid.

Here is the call graph for this function:

◆ operator=() [1/2]

hsm_storage & kcenon::pacs::storage::hsm_storage::operator= ( const hsm_storage & )
delete

◆ operator=() [2/2]

hsm_storage & kcenon::pacs::storage::hsm_storage::operator= ( hsm_storage && )
delete

◆ remove()

auto kcenon::pacs::storage::hsm_storage::remove ( std::string_view sop_instance_uid) -> VoidResult
nodiscardoverridevirtual

Remove a DICOM dataset from all tiers.

Removes the instance from whichever tier contains it.

Parameters
sop_instance_uidThe unique identifier for the instance to remove
Returns
VoidResult Success or error information

Implements kcenon::pacs::storage::storage_interface.

Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/storage/hsm_storage.h.

Definition at line 110 of file hsm_storage.cpp.

110 {
111 // Find which tier contains the instance
112 auto tier = find_tier(sop_instance_uid);
113 if (!tier.has_value()) {
114 // Not found is not an error for remove
115 return ok();
116 }
117
118 // Remove from the tier
119 auto* storage = get_storage(*tier);
120 if (storage == nullptr) {
121 return make_error<std::monostate>(
122 kTierNotAvailable, "Tier storage not available", "hsm_storage");
123 }
124
125 auto result = storage->remove(sop_instance_uid);
126 if (!result.is_ok()) {
127 return result;
128 }
129
130 // Remove metadata
131 std::unique_lock lock(mutex_);
132 remove_metadata(sop_instance_uid);
133
134 return ok();
135}
void remove_metadata(std::string_view sop_instance_uid)
Remove tier metadata.

◆ remove_metadata()

void kcenon::pacs::storage::hsm_storage::remove_metadata ( std::string_view sop_instance_uid)
private

Remove tier metadata.

Parameters
sop_instance_uidThe SOP Instance UID
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/storage/hsm_storage.h.

Definition at line 538 of file hsm_storage.cpp.

538 {
539 metadata_index_.erase(std::string(sop_instance_uid));
540}

References metadata_index_.

◆ retrieve()

auto kcenon::pacs::storage::hsm_storage::retrieve ( std::string_view sop_instance_uid) -> Result<core::dicom_dataset>
nodiscardoverridevirtual

Retrieve a DICOM dataset by SOP Instance UID.

Searches all tiers for the instance, starting from hot tier. If track_access_time is enabled, updates the last access timestamp.

Parameters
sop_instance_uidThe unique identifier for the instance
Returns
Result containing the dataset or error information

Implements kcenon::pacs::storage::storage_interface.

Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/storage/hsm_storage.h.

Definition at line 78 of file hsm_storage.cpp.

79 {
80 // Find which tier contains the instance
81 auto tier = find_tier(sop_instance_uid);
82 if (!tier.has_value()) {
83 return make_error<core::dicom_dataset>(
84 kInstanceNotFound,
85 "Instance not found: " + std::string(sop_instance_uid),
86 "hsm_storage");
87 }
88
89 // Retrieve from the tier
90 auto* storage = get_storage(*tier);
91 if (storage == nullptr) {
92 return make_error<core::dicom_dataset>(
93 kTierNotAvailable, "Tier storage not available", "hsm_storage");
94 }
95
96 auto result = storage->retrieve(sop_instance_uid);
97 if (!result.is_ok()) {
98 return result;
99 }
100
101 // Update access time if tracking is enabled
103 std::unique_lock lock(mutex_);
104 update_access_time(sop_instance_uid);
105 }
106
107 return result;
108}
void update_access_time(std::string_view sop_instance_uid)
Update last access time for an instance.
bool track_access_time
Whether to track access times for migration decisions When true, retrieves update the last_accessed t...
Definition hsm_storage.h:38

◆ run_migration_cycle()

auto kcenon::pacs::storage::hsm_storage::run_migration_cycle ( ) -> migration_result
nodiscard

Run a single migration cycle.

Migrates eligible instances according to the tier policy.

Returns
Migration result with statistics
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/storage/hsm_storage.h.

Definition at line 312 of file hsm_storage.cpp.

312 {
313 migration_result result;
314 auto start_time = std::chrono::steady_clock::now();
315
316 // Hot to warm migration
317 if (warm_tier_) {
318 auto candidates =
320
321 for (const auto& meta : candidates) {
322 if (result.instances_migrated >=
324 break;
325 }
326 if (result.bytes_migrated >= config_.policy.max_bytes_per_cycle) {
327 break;
328 }
329
330 auto migrate_result = migrate_instance(
331 meta.sop_instance_uid, storage_tier::hot, storage_tier::warm);
332
333 if (migrate_result.is_ok()) {
334 result.instances_migrated++;
335 result.bytes_migrated += meta.size_bytes;
336 } else {
337 result.failed_uids.push_back(meta.sop_instance_uid);
338 }
339 }
340 }
341
342 // Warm to cold migration
343 if (cold_tier_) {
344 auto candidates =
346
347 for (const auto& meta : candidates) {
348 if (result.instances_migrated >=
350 break;
351 }
352 if (result.bytes_migrated >= config_.policy.max_bytes_per_cycle) {
353 break;
354 }
355
356 auto migrate_result = migrate_instance(
357 meta.sop_instance_uid, storage_tier::warm, storage_tier::cold);
358
359 if (migrate_result.is_ok()) {
360 result.instances_migrated++;
361 result.bytes_migrated += meta.size_bytes;
362 } else {
363 result.failed_uids.push_back(meta.sop_instance_uid);
364 }
365 }
366 }
367
368 // Also check hot to cold (if warm tier is skipped)
369 if (!warm_tier_ && cold_tier_) {
370 auto candidates =
372
373 for (const auto& meta : candidates) {
374 if (result.instances_migrated >=
376 break;
377 }
378 if (result.bytes_migrated >= config_.policy.max_bytes_per_cycle) {
379 break;
380 }
381
382 auto migrate_result = migrate_instance(
383 meta.sop_instance_uid, storage_tier::hot, storage_tier::cold);
384
385 if (migrate_result.is_ok()) {
386 result.instances_migrated++;
387 result.bytes_migrated += meta.size_bytes;
388 } else {
389 result.failed_uids.push_back(meta.sop_instance_uid);
390 }
391 }
392 }
393
394 auto end_time = std::chrono::steady_clock::now();
395 result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
396 end_time - start_time);
397
398 return result;
399}
auto get_migration_candidates(storage_tier from_tier, storage_tier to_tier) const -> std::vector< tier_metadata >
Get instances eligible for migration.
std::size_t max_bytes_per_cycle
Maximum bytes to migrate per cycle Default: 10 GB.
Definition hsm_types.h:127
std::size_t max_instances_per_cycle
Maximum number of instances to migrate per cycle Prevents overwhelming the storage system Default: 10...
Definition hsm_types.h:123

References kcenon::pacs::storage::migration_result::bytes_migrated, kcenon::pacs::storage::cold, kcenon::pacs::storage::migration_result::duration, kcenon::pacs::storage::migration_result::failed_uids, kcenon::pacs::storage::hot, kcenon::pacs::storage::migration_result::instances_migrated, and kcenon::pacs::storage::warm.

◆ set_tier_policy()

void kcenon::pacs::storage::hsm_storage::set_tier_policy ( const tier_policy & policy)

Set the tier policy.

Parameters
policyThe new tier policy
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/storage/hsm_storage.h.

Definition at line 406 of file hsm_storage.cpp.

406 {
407 std::unique_lock lock(mutex_);
408 config_.policy = policy;
409}

References config_, mutex_, and kcenon::pacs::storage::hsm_storage_config::policy.

◆ store()

auto kcenon::pacs::storage::hsm_storage::store ( const core::dicom_dataset & dataset) -> VoidResult
nodiscardoverridevirtual

Store a DICOM dataset to the hot tier.

New data is always stored in the hot tier. Migration to cooler tiers happens based on the configured tier policy.

Parameters
datasetThe DICOM dataset to store
Returns
VoidResult Success or error information

Implements kcenon::pacs::storage::storage_interface.

Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/storage/hsm_storage.h.

Definition at line 57 of file hsm_storage.cpp.

57 {
58 // Extract SOP Instance UID for metadata tracking
59 auto sop_uid = dataset.get_string(core::tags::sop_instance_uid);
60 if (sop_uid.empty()) {
61 return make_error<std::monostate>(
62 kInvalidConfiguration, "Missing SOP Instance UID", "hsm_storage");
63 }
64
65 // Store in hot tier
66 auto result = hot_tier_->store(dataset);
67 if (!result.is_ok()) {
68 return result;
69 }
70
71 // Update metadata
72 std::unique_lock lock(mutex_);
73 update_metadata(sop_uid, storage_tier::hot, dataset);
74
75 return ok();
76}
void update_metadata(std::string_view sop_instance_uid, storage_tier tier, const core::dicom_dataset &dataset)
Update tier metadata after store/retrieve.
constexpr dicom_tag sop_instance_uid
SOP Instance UID.

References kcenon::pacs::storage::hot, and kcenon::pacs::core::tags::sop_instance_uid.

◆ update_access_time()

void kcenon::pacs::storage::hsm_storage::update_access_time ( std::string_view sop_instance_uid)
private

Update last access time for an instance.

Parameters
sop_instance_uidThe SOP Instance UID
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/storage/hsm_storage.h.

Definition at line 531 of file hsm_storage.cpp.

531 {
532 auto it = metadata_index_.find(std::string(sop_instance_uid));
533 if (it != metadata_index_.end()) {
534 it->second.last_accessed = std::chrono::system_clock::now();
535 }
536}

References metadata_index_.

◆ update_metadata()

void kcenon::pacs::storage::hsm_storage::update_metadata ( std::string_view sop_instance_uid,
storage_tier tier,
const core::dicom_dataset & dataset )
private

Update tier metadata after store/retrieve.

Parameters
sop_instance_uidThe SOP Instance UID
tierThe tier where the instance is stored
datasetThe dataset (for extracting metadata)
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/storage/hsm_storage.h.

Definition at line 510 of file hsm_storage.cpp.

512 {
513 std::string uid(sop_instance_uid);
514
515 tier_metadata meta;
516 meta.sop_instance_uid = uid;
517 meta.current_tier = tier;
518 meta.stored_at = std::chrono::system_clock::now();
519 meta.study_instance_uid =
520 dataset.get_string(core::tags::study_instance_uid);
521 meta.series_instance_uid =
522 dataset.get_string(core::tags::series_instance_uid);
523
524 // Try to get size from dataset (if available)
525 // For now, estimate based on pixel data if present
526 meta.size_bytes = 0; // Will be updated by storage backend
527
528 metadata_index_[uid] = std::move(meta);
529}
constexpr dicom_tag study_instance_uid
Study Instance UID.
constexpr dicom_tag series_instance_uid
Series Instance UID.

References kcenon::pacs::storage::tier_metadata::current_tier, kcenon::pacs::core::dicom_dataset::get_string(), metadata_index_, kcenon::pacs::core::tags::series_instance_uid, kcenon::pacs::storage::tier_metadata::series_instance_uid, kcenon::pacs::storage::tier_metadata::size_bytes, kcenon::pacs::storage::tier_metadata::sop_instance_uid, kcenon::pacs::storage::tier_metadata::stored_at, kcenon::pacs::core::tags::study_instance_uid, kcenon::pacs::storage::tier_metadata::study_instance_uid, and uid.

Here is the call graph for this function:

◆ verify_integrity()

auto kcenon::pacs::storage::hsm_storage::verify_integrity ( ) -> VoidResult
nodiscardoverridevirtual

Verify storage integrity across all tiers.

Verifies each tier's integrity and checks tier metadata consistency.

Returns
VoidResult Success if integrity is verified, error otherwise

Implements kcenon::pacs::storage::storage_interface.

Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/storage/hsm_storage.h.

Definition at line 205 of file hsm_storage.cpp.

205 {
206 // Verify each tier
207 std::vector<std::pair<storage_tier, storage_interface*>> tiers = {
211
212 for (const auto& [tier, storage] : tiers) {
213 if (storage == nullptr) {
214 continue;
215 }
216
217 auto result = storage->verify_integrity();
218 if (!result.is_ok()) {
219 return make_error<std::monostate>(
220 kIntegrityError,
221 "Integrity check failed for " + std::string(to_string(tier)) +
222 " tier: " + std::string(result.error().message),
223 "hsm_storage");
224 }
225 }
226
227 // Verify metadata consistency
228 std::shared_lock lock(mutex_);
229 for (const auto& [uid, meta] : metadata_index_) {
230 auto* storage = get_storage(meta.current_tier);
231 if (storage == nullptr) {
232 continue;
233 }
234
235 if (!storage->exists(uid)) {
236 return make_error<std::monostate>(
237 kIntegrityError,
238 "Metadata references non-existent instance: " + uid,
239 "hsm_storage");
240 }
241 }
242
243 return ok();
244}

References kcenon::pacs::storage::cold, kcenon::pacs::storage::hot, kcenon::pacs::storage::to_string(), uid, and kcenon::pacs::storage::warm.

Here is the call graph for this function:

Member Data Documentation

◆ cold_tier_

std::unique_ptr<storage_interface> kcenon::pacs::storage::hsm_storage::cold_tier_
private

Cold tier storage backend (may be nullptr)

Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/storage/hsm_storage.h.

Definition at line 350 of file hsm_storage.h.

Referenced by get_statistics().

◆ config_

hsm_storage_config kcenon::pacs::storage::hsm_storage::config_
private

◆ hot_tier_

std::unique_ptr<storage_interface> kcenon::pacs::storage::hsm_storage::hot_tier_
private

Hot tier storage backend.

Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/storage/hsm_storage.h.

Definition at line 344 of file hsm_storage.h.

Referenced by get_statistics(), and hsm_storage().

◆ metadata_index_

std::unordered_map<std::string, tier_metadata> kcenon::pacs::storage::hsm_storage::metadata_index_
private

◆ mutex_

std::shared_mutex kcenon::pacs::storage::hsm_storage::mutex_
mutableprivate

◆ warm_tier_

std::unique_ptr<storage_interface> kcenon::pacs::storage::hsm_storage::warm_tier_
private

Warm tier storage backend (may be nullptr)

Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/storage/hsm_storage.h.

Definition at line 347 of file hsm_storage.h.

Referenced by get_statistics().


The documentation for this class was generated from the following files: