comparison OrthancServer/ServerIndex.cpp @ 269:f6fdf5abe751

recycling up and running
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 07 Dec 2012 14:46:44 +0100
parents 4bc02e2254ec
children e6a4c4329481
comparison
equal deleted inserted replaced
268:4bc02e2254ec 269:f6fdf5abe751
193 } 193 }
194 } 194 }
195 195
196 196
197 ServerIndex::ServerIndex(ServerContext& context, 197 ServerIndex::ServerIndex(ServerContext& context,
198 const std::string& dbPath) : mutex_() 198 const std::string& dbPath) :
199 maximumStorageSize_(0),
200 maximumPatients_(0)
199 { 201 {
200 listener_.reset(new Internals::ServerIndexListener(context)); 202 listener_.reset(new Internals::ServerIndexListener(context));
201 203
202 if (dbPath == ":memory:") 204 if (dbPath == ":memory:")
203 { 205 {
215 { 217 {
216 } 218 }
217 219
218 db_.reset(new DatabaseWrapper(p.string() + "/index", *listener_)); 220 db_.reset(new DatabaseWrapper(p.string() + "/index", *listener_));
219 } 221 }
222
223 // Initial recycling if the parameters have changed since the last
224 // execution of Orthanc
225 StandaloneRecycling();
220 226
221 unsigned int sleep; 227 unsigned int sleep;
222 try 228 try
223 { 229 {
224 std::string sleepString = db_->GetGlobalProperty(GlobalProperty_FlushSleep); 230 std::string sleepString = db_->GetGlobalProperty(GlobalProperty_FlushSleep);
507 513
508 std::string parent = db_->GetPublicId(parentId); 514 std::string parent = db_->GetPublicId(parentId);
509 515
510 switch (type) 516 switch (type)
511 { 517 {
512 case ResourceType_Study: 518 case ResourceType_Study:
513 result["ParentPatient"] = parent; 519 result["ParentPatient"] = parent;
514 break; 520 break;
515 521
516 case ResourceType_Series: 522 case ResourceType_Series:
517 result["ParentStudy"] = parent; 523 result["ParentStudy"] = parent;
518 break; 524 break;
519 525
520 case ResourceType_Instance: 526 case ResourceType_Instance:
521 result["ParentSeries"] = parent; 527 result["ParentSeries"] = parent;
522 break; 528 break;
523 529
524 default: 530 default:
525 throw OrthancException(ErrorCode_InternalError); 531 throw OrthancException(ErrorCode_InternalError);
526 } 532 }
527 } 533 }
528 534
529 // List the children resources 535 // List the children resources
530 std::list<std::string> children; 536 std::list<std::string> children;
540 c.append(*it); 546 c.append(*it);
541 } 547 }
542 548
543 switch (type) 549 switch (type)
544 { 550 {
551 case ResourceType_Patient:
552 result["Studies"] = c;
553 break;
554
555 case ResourceType_Study:
556 result["Series"] = c;
557 break;
558
559 case ResourceType_Series:
560 result["Instances"] = c;
561 break;
562
563 default:
564 throw OrthancException(ErrorCode_InternalError);
565 }
566 }
567
568 // Set the resource type
569 switch (type)
570 {
545 case ResourceType_Patient: 571 case ResourceType_Patient:
546 result["Studies"] = c; 572 result["Type"] = "Patient";
547 break; 573 break;
548 574
549 case ResourceType_Study: 575 case ResourceType_Study:
550 result["Series"] = c; 576 result["Type"] = "Study";
551 break; 577 break;
552 578
553 case ResourceType_Series: 579 case ResourceType_Series:
554 result["Instances"] = c; 580 {
581 result["Type"] = "Series";
582 result["Status"] = ToString(GetSeriesStatus(id));
583
584 int i;
585 if (db_->GetMetadataAsInteger(i, id, MetadataType_Series_ExpectedNumberOfInstances))
586 result["ExpectedNumberOfInstances"] = i;
587 else
588 result["ExpectedNumberOfInstances"] = Json::nullValue;
589
555 break; 590 break;
591 }
592
593 case ResourceType_Instance:
594 {
595 result["Type"] = "Instance";
596
597 FileInfo attachment;
598 if (!db_->LookupAttachment(attachment, id, FileContentType_Dicom))
599 {
600 throw OrthancException(ErrorCode_InternalError);
601 }
602
603 result["FileSize"] = static_cast<unsigned int>(attachment.GetUncompressedSize());
604 result["FileUuid"] = attachment.GetUuid();
605
606 int i;
607 if (db_->GetMetadataAsInteger(i, id, MetadataType_Instance_IndexInSeries))
608 result["IndexInSeries"] = i;
609 else
610 result["IndexInSeries"] = Json::nullValue;
611
612 break;
613 }
556 614
557 default: 615 default:
558 throw OrthancException(ErrorCode_InternalError); 616 throw OrthancException(ErrorCode_InternalError);
559 }
560 }
561
562 // Set the resource type
563 switch (type)
564 {
565 case ResourceType_Patient:
566 result["Type"] = "Patient";
567 break;
568
569 case ResourceType_Study:
570 result["Type"] = "Study";
571 break;
572
573 case ResourceType_Series:
574 {
575 result["Type"] = "Series";
576 result["Status"] = ToString(GetSeriesStatus(id));
577
578 int i;
579 if (db_->GetMetadataAsInteger(i, id, MetadataType_Series_ExpectedNumberOfInstances))
580 result["ExpectedNumberOfInstances"] = i;
581 else
582 result["ExpectedNumberOfInstances"] = Json::nullValue;
583
584 break;
585 }
586
587 case ResourceType_Instance:
588 {
589 result["Type"] = "Instance";
590
591 FileInfo attachment;
592 if (!db_->LookupAttachment(attachment, id, FileContentType_Dicom))
593 {
594 throw OrthancException(ErrorCode_InternalError);
595 }
596
597 result["FileSize"] = static_cast<unsigned int>(attachment.GetUncompressedSize());
598 result["FileUuid"] = attachment.GetUuid();
599
600 int i;
601 if (db_->GetMetadataAsInteger(i, id, MetadataType_Instance_IndexInSeries))
602 result["IndexInSeries"] = i;
603 else
604 result["IndexInSeries"] = Json::nullValue;
605
606 break;
607 }
608
609 default:
610 throw OrthancException(ErrorCode_InternalError);
611 } 617 }
612 618
613 // Record the remaining information 619 // Record the remaining information
614 result["ID"] = publicId; 620 result["ID"] = publicId;
615 MainDicomTagsToJson(result, id); 621 MainDicomTagsToJson(result, id);
696 DicomMap map; 702 DicomMap map;
697 db_->GetMainDicomTags(map, currentId); 703 db_->GetMainDicomTags(map, currentId);
698 704
699 switch (currentType) 705 switch (currentType)
700 { 706 {
701 case ResourceType_Patient: 707 case ResourceType_Patient:
702 patientId = map.GetValue(DICOM_TAG_PATIENT_ID).AsString(); 708 patientId = map.GetValue(DICOM_TAG_PATIENT_ID).AsString();
703 done = true; 709 done = true;
704 break; 710 break;
705 711
706 case ResourceType_Study: 712 case ResourceType_Study:
707 studyInstanceUid = map.GetValue(DICOM_TAG_STUDY_INSTANCE_UID).AsString(); 713 studyInstanceUid = map.GetValue(DICOM_TAG_STUDY_INSTANCE_UID).AsString();
708 currentType = ResourceType_Patient; 714 currentType = ResourceType_Patient;
709 break; 715 break;
710 716
711 case ResourceType_Series: 717 case ResourceType_Series:
712 seriesInstanceUid = map.GetValue(DICOM_TAG_SERIES_INSTANCE_UID).AsString(); 718 seriesInstanceUid = map.GetValue(DICOM_TAG_SERIES_INSTANCE_UID).AsString();
713 currentType = ResourceType_Study; 719 currentType = ResourceType_Study;
714 break; 720 break;
715 721
716 case ResourceType_Instance: 722 case ResourceType_Instance:
717 sopInstanceUid = map.GetValue(DICOM_TAG_SOP_INSTANCE_UID).AsString(); 723 sopInstanceUid = map.GetValue(DICOM_TAG_SOP_INSTANCE_UID).AsString();
718 currentType = ResourceType_Series; 724 currentType = ResourceType_Series;
719 break; 725 break;
720 726
721 default: 727 default:
722 throw OrthancException(ErrorCode_InternalError); 728 throw OrthancException(ErrorCode_InternalError);
723 } 729 }
724 730
725 // If we have not reached the Patient level, find the parent of 731 // If we have not reached the Patient level, find the parent of
726 // the current resource 732 // the current resource
727 if (!done) 733 if (!done)
758 } 764 }
759 765
760 766
761 bool ServerIndex::IsRecyclingNeeded(uint64_t instanceSize) 767 bool ServerIndex::IsRecyclingNeeded(uint64_t instanceSize)
762 { 768 {
769 if (maximumStorageSize_ != 0)
770 {
771 uint64_t currentSize = db_->GetTotalCompressedSize();
772 if (currentSize + instanceSize > maximumStorageSize_)
773 {
774 return true;
775 }
776 }
777
778 if (maximumPatients_ != 0)
779 {
780 uint64_t patientCount = db_->GetResourceCount(ResourceType_Patient);
781 if (patientCount > maximumPatients_)
782 {
783 return true;
784 }
785 }
786
763 return false; 787 return false;
764 } 788 }
765 789
766 790
767 void ServerIndex::Recycle(uint64_t instanceSize, 791 void ServerIndex::Recycle(uint64_t instanceSize,
770 if (!IsRecyclingNeeded(instanceSize)) 794 if (!IsRecyclingNeeded(instanceSize))
771 { 795 {
772 return; 796 return;
773 } 797 }
774 798
775 799 // Check whether other DICOM instances from this patient are
776 //throw OrthancException(ErrorCode_FullStorage); 800 // already stored
801 int64_t patientToAvoid;
802 ResourceType type;
803 bool hasPatientToAvoid = db_->LookupResource(newPatientId, patientToAvoid, type);
804
805 if (hasPatientToAvoid && type != ResourceType_Patient)
806 {
807 throw OrthancException(ErrorCode_InternalError);
808 }
809
810 // Iteratively select patient to remove until there is enough
811 // space in the DICOM store
812 int64_t patientToRecycle;
813 while (true)
814 {
815 // If other instances of this patient are already in the store,
816 // we must avoid to recycle them
817 bool ok = hasPatientToAvoid ?
818 db_->SelectPatientToRecycle(patientToRecycle, patientToAvoid) :
819 db_->SelectPatientToRecycle(patientToRecycle);
820
821 if (!ok)
822 {
823 throw OrthancException(ErrorCode_FullStorage);
824 }
825
826 LOG(INFO) << "Recycling one patient";
827 db_->DeleteResource(patientToRecycle);
828
829 if (!IsRecyclingNeeded(instanceSize))
830 {
831 // OK, we're done
832 break;
833 }
834 }
777 } 835 }
836
837 void ServerIndex::SetMaximumPatientCount(unsigned int count)
838 {
839 boost::mutex::scoped_lock lock(mutex_);
840 maximumPatients_ = count;
841 StandaloneRecycling();
842 }
843
844 void ServerIndex::SetMaximumStorageSize(uint64_t size)
845 {
846 boost::mutex::scoped_lock lock(mutex_);
847 maximumStorageSize_ = size;
848 StandaloneRecycling();
849 }
850
851 void ServerIndex::StandaloneRecycling()
852 {
853 // WARNING: No mutex here, do not include this as a public method
854 std::auto_ptr<SQLite::Transaction> t(db_->StartTransaction());
855 t->Begin();
856 Recycle(0, "");
857 t->Commit();
858 listener_->CommitFilesToRemove();
859 }
778 } 860 }