# HG changeset patch # User Sebastien Jodogne # Date 1680276813 -7200 # Node ID 154d37a56500de878ee2f1a08d1a21834d6c2ab2 # Parent e7529e6241d247064a9ffa21f3146652f1b87c91 started implementation of transactions diff -r e7529e6241d2 -r 154d37a56500 OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp --- a/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp Thu Mar 30 21:31:56 2023 +0200 +++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp Fri Mar 31 17:33:33 2023 +0200 @@ -38,55 +38,194 @@ namespace Orthanc { + static void CheckSuccess(PluginsErrorDictionary& errorDictionary, + OrthancPluginErrorCode code) + { + if (code != OrthancPluginErrorCode_Success) + { + errorDictionary.LogError(code, true); + throw OrthancException(static_cast(code)); + } + } + + + static FileInfo Convert(const DatabasePluginMessages::FileInfo& source) + { + return FileInfo(source.uuid(), + static_cast(source.content_type()), + source.uncompressed_size(), + source.uncompressed_hash(), + static_cast(source.compression_type()), + source.compressed_size(), + source.compressed_hash()); + } + + + static ResourceType Convert(DatabasePluginMessages::ResourceType type) + { + switch (type) + { + case DatabasePluginMessages::RESOURCE_PATIENT: + return ResourceType_Patient; + + case DatabasePluginMessages::RESOURCE_STUDY: + return ResourceType_Study; + + case DatabasePluginMessages::RESOURCE_SERIES: + return ResourceType_Series; + + case DatabasePluginMessages::RESOURCE_INSTANCE: + return ResourceType_Instance; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + + + static DatabasePluginMessages::ResourceType Convert(ResourceType type) + { + switch (type) + { + case ResourceType_Patient: + return DatabasePluginMessages::RESOURCE_PATIENT; + + case ResourceType_Study: + return DatabasePluginMessages::RESOURCE_STUDY; + + case ResourceType_Series: + return DatabasePluginMessages::RESOURCE_SERIES; + + case ResourceType_Instance: + return DatabasePluginMessages::RESOURCE_INSTANCE; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + + + static void Execute(DatabasePluginMessages::Response& response, + const OrthancPluginDatabaseV4& database, + const DatabasePluginMessages::Request& request) + { + std::string requestSerialized; + request.SerializeToString(&requestSerialized); + + OrthancPluginMemoryBuffer64 responseSerialized; + CheckSuccess(database.GetErrorDictionary(), database.GetDefinition().operations( + &responseSerialized, database.GetDefinition().backend, + requestSerialized.empty() ? NULL : requestSerialized.c_str(), + requestSerialized.size())); + + bool success = response.ParseFromArray(responseSerialized.data, responseSerialized.size); + + if (responseSerialized.size > 0) + { + free(responseSerialized.data); + } + + if (!success) + { + throw OrthancException(ErrorCode_DatabasePlugin, "Cannot unserialize protobuf originating from the database plugin"); + } + } + + + static void ExecuteDatabase(DatabasePluginMessages::DatabaseResponse& response, + const OrthancPluginDatabaseV4& database, + DatabasePluginMessages::DatabaseOperation operation, + const DatabasePluginMessages::DatabaseRequest& request) + { + DatabasePluginMessages::Request fullRequest; + fullRequest.set_type(DatabasePluginMessages::REQUEST_DATABASE); + fullRequest.mutable_database_request()->CopyFrom(request); + fullRequest.mutable_database_request()->set_operation(operation); + + DatabasePluginMessages::Response fullResponse; + Execute(fullResponse, database, fullRequest); + + response.CopyFrom(fullResponse.database_response()); + } + + class OrthancPluginDatabaseV4::Transaction : public IDatabaseWrapper::ITransaction { private: - OrthancPluginDatabaseV4& that_; + OrthancPluginDatabaseV4& database_; IDatabaseListener& listener_; void* transaction_; + + void ExecuteTransaction(DatabasePluginMessages::TransactionResponse& response, + DatabasePluginMessages::TransactionOperation operation, + const DatabasePluginMessages::TransactionRequest& request) + { + DatabasePluginMessages::Request fullRequest; + fullRequest.set_type(DatabasePluginMessages::REQUEST_TRANSACTION); + fullRequest.mutable_transaction_request()->CopyFrom(request); + fullRequest.mutable_transaction_request()->set_transaction(reinterpret_cast(transaction_)); + fullRequest.mutable_transaction_request()->set_operation(operation); - void CheckSuccess(OrthancPluginErrorCode code) const - { - that_.CheckSuccess(code); + DatabasePluginMessages::Response fullResponse; + Execute(fullResponse, database_, fullRequest); + + response.CopyFrom(fullResponse.transaction_response()); } - public: - Transaction(OrthancPluginDatabaseV4& that, - IDatabaseListener& listener, - TransactionType type) : - that_(that), - listener_(listener) + + void ExecuteTransaction(DatabasePluginMessages::TransactionOperation operation, + const DatabasePluginMessages::TransactionRequest& request) + { + DatabasePluginMessages::TransactionResponse response; // Ignored + ExecuteTransaction(response, operation, request); + } + + + void ExecuteTransaction(DatabasePluginMessages::TransactionOperation operation) { -#if 0 - CheckSuccess(that.database_.startTransaction(that.database_, &transaction_, type)); - if (transaction_ == NULL) - { - throw OrthancException(ErrorCode_DatabasePlugin); - } -#endif + DatabasePluginMessages::TransactionResponse response; // Ignored + DatabasePluginMessages::TransactionRequest request; // Ignored + ExecuteTransaction(response, operation, request); + } + + + public: + Transaction(OrthancPluginDatabaseV4& database, + IDatabaseListener& listener, + void* transaction) : + database_(database), + listener_(listener), + transaction_(transaction) + { } virtual ~Transaction() { -#if 0 - OrthancPluginErrorCode code = that_.database_.destructTransaction(transaction_); - if (code != OrthancPluginErrorCode_Success) { - // Don't throw exception in destructors - that_.errorDictionary_.LogError(code, true); + DatabasePluginMessages::DatabaseRequest request; + request.mutable_finalize_transaction()->set_transaction(reinterpret_cast(transaction_)); + + DatabasePluginMessages::DatabaseResponse response; + ExecuteDatabase(response, database_, DatabasePluginMessages::OPERATION_FINALIZE_TRANSACTION, request); } -#endif } virtual void Rollback() ORTHANC_OVERRIDE { + ExecuteTransaction(DatabasePluginMessages::OPERATION_ROLLBACK); } virtual void Commit(int64_t fileSizeDelta) ORTHANC_OVERRIDE { + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_commit()->set_file_size_delta(fileSizeDelta); + + ExecuteTransaction(DatabasePluginMessages::OPERATION_COMMIT, request); + } } @@ -94,45 +233,137 @@ const FileInfo& attachment, int64_t revision) ORTHANC_OVERRIDE { + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_add_attachment()->set_id(id); + request.mutable_add_attachment()->mutable_attachment()->set_uuid(attachment.GetUuid()); + request.mutable_add_attachment()->mutable_attachment()->set_content_type(attachment.GetContentType()); + request.mutable_add_attachment()->mutable_attachment()->set_uncompressed_size(attachment.GetUncompressedSize()); + request.mutable_add_attachment()->mutable_attachment()->set_uncompressed_hash(attachment.GetUncompressedMD5()); + request.mutable_add_attachment()->mutable_attachment()->set_compression_type(attachment.GetCompressionType()); + request.mutable_add_attachment()->mutable_attachment()->set_compressed_size(attachment.GetCompressedSize()); + request.mutable_add_attachment()->mutable_attachment()->set_compressed_hash(attachment.GetCompressedMD5()); + request.mutable_add_attachment()->set_revision(revision); + + ExecuteTransaction(DatabasePluginMessages::OPERATION_ADD_ATTACHMENT, request); + } } virtual void ClearChanges() ORTHANC_OVERRIDE { + ExecuteTransaction(DatabasePluginMessages::OPERATION_CLEAR_CHANGES); } virtual void ClearExportedResources() ORTHANC_OVERRIDE { + ExecuteTransaction(DatabasePluginMessages::OPERATION_CLEAR_EXPORTED_RESOURCES); } - + virtual void DeleteAttachment(int64_t id, FileContentType attachment) ORTHANC_OVERRIDE { + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_delete_attachment()->set_id(id); + request.mutable_delete_attachment()->set_type(attachment); + + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_DELETE_ATTACHMENT, request); + + listener_.SignalAttachmentDeleted(Convert(response.delete_attachment().deleted_attachment())); + } } virtual void DeleteMetadata(int64_t id, MetadataType type) ORTHANC_OVERRIDE { + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_delete_metadata()->set_id(id); + request.mutable_delete_metadata()->set_type(type); + + ExecuteTransaction(DatabasePluginMessages::OPERATION_DELETE_METADATA, request); + } } virtual void DeleteResource(int64_t id) ORTHANC_OVERRIDE { + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_delete_resource()->set_id(id); + + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_DELETE_RESOURCE, request); + + for (int i = 0; i < response.delete_resource().deleted_attachments().size(); i++) + { + listener_.SignalAttachmentDeleted(Convert(response.delete_resource().deleted_attachments(i))); + } + + for (int i = 0; i < response.delete_resource().deleted_resources().size(); i++) + { + listener_.SignalResourceDeleted(Convert(response.delete_resource().deleted_resources(i).level()), + response.delete_resource().deleted_resources(i).public_id()); + } + + if (response.delete_resource().is_remaining_ancestor()) + { + listener_.SignalRemainingAncestor(Convert(response.delete_resource().remaining_ancestor().level()), + response.delete_resource().remaining_ancestor().public_id()); + } + } } virtual void GetAllMetadata(std::map& target, int64_t id) ORTHANC_OVERRIDE { + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_get_all_metadata()->set_id(id); + + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_ALL_METADATA, request); + + target.clear(); + for (int i = 0; i < response.get_all_metadata().metadata().size(); i++) + { + MetadataType key = static_cast(response.get_all_metadata().metadata(i).type()); + + if (target.find(key) == target.end()) + { + target[key] = response.get_all_metadata().metadata(i).value(); + } + else + { + throw OrthancException(ErrorCode_DatabasePlugin); + } + } + } } virtual void GetAllPublicIds(std::list& target, ResourceType resourceType) ORTHANC_OVERRIDE { + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_get_all_public_ids()->set_resource_type(Convert(resourceType)); + + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_ALL_PUBLIC_IDS, request); + + target.clear(); + for (int i = 0; i < response.get_all_public_ids().ids().size(); i++) + { + target.push_back(response.get_all_public_ids().ids(i)); + } + } } @@ -141,6 +372,21 @@ size_t since, size_t limit) ORTHANC_OVERRIDE { + { + DatabasePluginMessages::TransactionRequest request; + request.mutable_get_all_public_ids_with_limits()->set_resource_type(Convert(resourceType)); + request.mutable_get_all_public_ids_with_limits()->set_since(since); + request.mutable_get_all_public_ids_with_limits()->set_limit(limit); + + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_ALL_PUBLIC_IDS_WITH_LIMITS, request); + + target.clear(); + for (int i = 0; i < response.get_all_public_ids_with_limits().ids().size(); i++) + { + target.push_back(response.get_all_public_ids_with_limits().ids(i)); + } + } } @@ -171,7 +417,12 @@ { } + + virtual void GetLastChange(std::list& target /*out*/) ORTHANC_OVERRIDE + { + } + virtual void GetLastExportedResource(std::list& target /*out*/) ORTHANC_OVERRIDE { } @@ -353,69 +604,13 @@ }; - static void CheckSuccess(PluginsErrorDictionary& errorDictionary, - OrthancPluginErrorCode code) - { - if (code != OrthancPluginErrorCode_Success) - { - errorDictionary.LogError(code, true); - throw OrthancException(static_cast(code)); - } - } - - - static void Execute(DatabasePluginMessages::Response& response, - const _OrthancPluginRegisterDatabaseBackendV4& database, - PluginsErrorDictionary& errorDictionary, - const DatabasePluginMessages::Request& request) - { - std::string requestSerialized; - request.SerializeToString(&requestSerialized); - - OrthancPluginMemoryBuffer64 responseSerialized; - CheckSuccess(errorDictionary, database.operations( - &responseSerialized, database.backend, - requestSerialized.empty() ? NULL : requestSerialized.c_str(), - requestSerialized.size())); - - bool success = response.ParseFromArray(responseSerialized.data, responseSerialized.size); - - if (responseSerialized.size > 0) - { - free(responseSerialized.data); - } - - if (!success) - { - throw OrthancException(ErrorCode_DatabasePlugin, "Cannot unserialize protobuf originating from the database plugin"); - } - } - - static void ExecuteDatabase(DatabasePluginMessages::DatabaseResponse& response, - const _OrthancPluginRegisterDatabaseBackendV4& database, - PluginsErrorDictionary& errorDictionary, - DatabasePluginMessages::DatabaseOperation operation, - const DatabasePluginMessages::DatabaseRequest& request) - { - DatabasePluginMessages::Request fullRequest; - fullRequest.set_type(DatabasePluginMessages::REQUEST_DATABASE); - fullRequest.mutable_database_request()->CopyFrom(request); - fullRequest.mutable_database_request()->set_operation(operation); - - DatabasePluginMessages::Response fullResponse; - Execute(fullResponse, database, errorDictionary, fullRequest); - - response.CopyFrom(fullResponse.database_response()); - } - - OrthancPluginDatabaseV4::OrthancPluginDatabaseV4(SharedLibrary& library, PluginsErrorDictionary& errorDictionary, const _OrthancPluginRegisterDatabaseBackendV4& database, const std::string& serverIdentifier) : library_(library), errorDictionary_(errorDictionary), - database_(database), + definition_(database), serverIdentifier_(serverIdentifier), open_(false), databaseVersion_(0), @@ -425,9 +620,9 @@ CLOG(INFO, PLUGINS) << "Identifier of this Orthanc server for the global properties " << "of the custom database: \"" << serverIdentifier << "\""; - if (database_.backend == NULL || - database_.operations == NULL || - database_.finalize == NULL) + if (definition_.backend == NULL || + definition_.operations == NULL || + definition_.finalize == NULL) { throw OrthancException(ErrorCode_NullPointer); } @@ -436,7 +631,7 @@ OrthancPluginDatabaseV4::~OrthancPluginDatabaseV4() { - database_.finalize(database_.backend); + definition_.finalize(definition_.backend); } @@ -450,13 +645,14 @@ { DatabasePluginMessages::DatabaseRequest request; DatabasePluginMessages::DatabaseResponse response; - ExecuteDatabase(response, database_, errorDictionary_, DatabasePluginMessages::OPERATION_OPEN, request); + ExecuteDatabase(response, *this, DatabasePluginMessages::OPERATION_OPEN, request); } { DatabasePluginMessages::DatabaseRequest request; DatabasePluginMessages::DatabaseResponse response; - ExecuteDatabase(response, database_, errorDictionary_, DatabasePluginMessages::OPERATION_GET_SYSTEM_INFORMATION, request); + ExecuteDatabase(response, *this, DatabasePluginMessages::OPERATION_GET_SYSTEM_INFORMATION, request); + databaseVersion_ = response.get_system_information().database_version(); hasFlushToDisk_ = response.get_system_information().supports_flush_to_disk(); hasRevisionsSupport_ = response.get_system_information().supports_revisions(); } @@ -475,7 +671,7 @@ { DatabasePluginMessages::DatabaseRequest request; DatabasePluginMessages::DatabaseResponse response; - ExecuteDatabase(response, database_, errorDictionary_, DatabasePluginMessages::OPERATION_CLOSE, request); + ExecuteDatabase(response, *this, DatabasePluginMessages::OPERATION_CLOSE, request); } } @@ -504,7 +700,7 @@ { DatabasePluginMessages::DatabaseRequest request; DatabasePluginMessages::DatabaseResponse response; - ExecuteDatabase(response, database_, errorDictionary_, DatabasePluginMessages::OPERATION_FLUSH_TO_DISK, request); + ExecuteDatabase(response, *this, DatabasePluginMessages::OPERATION_FLUSH_TO_DISK, request); } } @@ -517,15 +713,26 @@ throw OrthancException(ErrorCode_BadSequenceOfCalls); } + DatabasePluginMessages::DatabaseRequest request; + switch (type) { case TransactionType_ReadOnly: + request.mutable_start_transaction()->set_type(DatabasePluginMessages::TRANSACTION_READ_ONLY); + break; case TransactionType_ReadWrite: + request.mutable_start_transaction()->set_type(DatabasePluginMessages::TRANSACTION_READ_WRITE); + break; default: throw OrthancException(ErrorCode_InternalError); } + + DatabasePluginMessages::DatabaseResponse response; + ExecuteDatabase(response, *this, DatabasePluginMessages::OPERATION_START_TRANSACTION, request); + + return new Transaction(*this, listener, reinterpret_cast(response.start_transaction().transaction())); } @@ -552,6 +759,7 @@ else { // TODO + throw OrthancException(ErrorCode_NotImplemented); } } diff -r e7529e6241d2 -r 154d37a56500 OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.h --- a/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.h Thu Mar 30 21:31:56 2023 +0200 +++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.h Fri Mar 31 17:33:33 2023 +0200 @@ -38,7 +38,7 @@ SharedLibrary& library_; PluginsErrorDictionary& errorDictionary_; - _OrthancPluginRegisterDatabaseBackendV4 database_; + _OrthancPluginRegisterDatabaseBackendV4 definition_; std::string serverIdentifier_; bool open_; unsigned int databaseVersion_; @@ -55,6 +55,16 @@ virtual ~OrthancPluginDatabaseV4(); + const _OrthancPluginRegisterDatabaseBackendV4& GetDefinition() const + { + return definition_; + } + + PluginsErrorDictionary& GetErrorDictionary() const + { + return errorDictionary_; + } + virtual void Open() ORTHANC_OVERRIDE; virtual void Close() ORTHANC_OVERRIDE; diff -r e7529e6241d2 -r 154d37a56500 OrthancServer/Plugins/Include/orthanc/OrthancDatabasePlugin.proto --- a/OrthancServer/Plugins/Include/orthanc/OrthancDatabasePlugin.proto Thu Mar 30 21:31:56 2023 +0200 +++ b/OrthancServer/Plugins/Include/orthanc/OrthancDatabasePlugin.proto Fri Mar 31 17:33:33 2023 +0200 @@ -114,6 +114,7 @@ OPERATION_FLUSH_TO_DISK = 3; OPERATION_START_TRANSACTION = 4; OPERATION_UPGRADE = 5; + OPERATION_FINALIZE_TRANSACTION = 6; } enum TransactionType { @@ -170,6 +171,13 @@ } } +message FinalizeTransaction { + message Request { + sfixed64 transaction = 1; + } + message Response { + } +} message DatabaseRequest { sfixed64 database = 1; @@ -181,6 +189,7 @@ FlushToDisk.Request flush_to_disk = 103; StartTransaction.Request start_transaction = 104; Upgrade.Request upgrade = 105; + FinalizeTransaction.Request finalize_transaction = 106; } message DatabaseResponse { @@ -190,6 +199,7 @@ FlushToDisk.Response flush_to_disk = 103; StartTransaction.Response start_transaction = 104; Upgrade.Response upgrade = 105; + FinalizeTransaction.Response finalize_transaction = 106; } @@ -254,7 +264,7 @@ message Commit { message Request { - int64 fileSizeDelta = 1; + int64 file_size_delta = 1; } message Response { }