comparison OrthancFramework/Sources/FileStorage/FilesystemStorage.cpp @ 5807:8279eaab0d1d attach-custom-data

merged default -> attach-custom-data
author Alain Mazy <am@orthanc.team>
date Tue, 24 Sep 2024 11:39:52 +0200
parents d7274e43ea7c 2fe77dfe0466
children
comparison
equal deleted inserted replaced
5085:79f98ee4f04b 5807:8279eaab0d1d
1 /** 1 /**
2 * Orthanc - A Lightweight, RESTful DICOM Store 2 * Orthanc - A Lightweight, RESTful DICOM Store
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics 3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4 * Department, University Hospital of Liege, Belgium 4 * Department, University Hospital of Liege, Belgium
5 * Copyright (C) 2017-2022 Osimis S.A., Belgium 5 * Copyright (C) 2017-2023 Osimis S.A., Belgium
6 * Copyright (C) 2021-2022 Sebastien Jodogne, ICTEAM UCLouvain, Belgium 6 * Copyright (C) 2024-2024 Orthanc Team SRL, Belgium
7 * Copyright (C) 2021-2024 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
7 * 8 *
8 * This program is free software: you can redistribute it and/or 9 * This program is free software: you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License 10 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation, either version 3 of 11 * as published by the Free Software Foundation, either version 3 of
11 * the License, or (at your option) any later version. 12 * the License, or (at your option) any later version.
21 **/ 22 **/
22 23
23 24
24 #include "../PrecompiledHeaders.h" 25 #include "../PrecompiledHeaders.h"
25 #include "FilesystemStorage.h" 26 #include "FilesystemStorage.h"
27 #include <boost/thread.hpp>
26 28
27 // http://stackoverflow.com/questions/1576272/storing-large-number-of-files-in-file-system 29 // http://stackoverflow.com/questions/1576272/storing-large-number-of-files-in-file-system
28 // http://stackoverflow.com/questions/446358/storing-a-large-number-of-images 30 // http://stackoverflow.com/questions/446358/storing-a-large-number-of-images
29 31
30 #include "../Logging.h" 32 #include "../Logging.h"
120 void FilesystemStorage::Create(const std::string& uuid, 122 void FilesystemStorage::Create(const std::string& uuid,
121 const void* content, 123 const void* content,
122 size_t size, 124 size_t size,
123 FileContentType type) 125 FileContentType type)
124 { 126 {
127 Toolbox::ElapsedTimer timer;
125 LOG(INFO) << "Creating attachment \"" << uuid << "\" of \"" << GetDescriptionInternal(type) 128 LOG(INFO) << "Creating attachment \"" << uuid << "\" of \"" << GetDescriptionInternal(type)
126 << "\" type (size: " << (size / (1024 * 1024) + 1) << "MB)"; 129 << "\" type";
127 130
128 boost::filesystem::path path; 131 boost::filesystem::path path;
129 132
130 path = GetPath(uuid); 133 path = GetPath(uuid);
131 134
132 if (boost::filesystem::exists(path)) 135 if (boost::filesystem::exists(path))
133 { 136 {
134 // Extremely unlikely case: This Uuid has already been created 137 // Extremely unlikely case: This Uuid has already been created
135 // in the past. 138 // in the past.
136 throw OrthancException(ErrorCode_InternalError); 139 throw OrthancException(ErrorCode_InternalError, "This file UUID already exists");
137 } 140 }
138 141
139 if (boost::filesystem::exists(path.parent_path())) 142 // In very unlikely cases, a thread could be deleting a
140 { 143 // directory while another thread needs it -> introduce 3 retries at 1 ms interval
141 if (!boost::filesystem::is_directory(path.parent_path())) 144 int retryCount = 0;
142 { 145 const int maxRetryCount = 3;
143 throw OrthancException(ErrorCode_DirectoryOverFile); 146
144 } 147 while (retryCount < maxRetryCount)
145 } 148 {
146 else 149 retryCount++;
147 { 150 if (retryCount > 1)
148 if (!boost::filesystem::create_directories(path.parent_path())) 151 {
149 { 152 boost::this_thread::sleep(boost::posix_time::milliseconds(2 * retryCount + (rand() % 10)));
150 throw OrthancException(ErrorCode_FileStorageCannotWrite); 153 LOG(INFO) << "Retrying to create attachment \"" << uuid << "\" of \"" << GetDescriptionInternal(type)
151 } 154 << "\" type";
152 } 155 }
153 156
154 SystemToolbox::WriteFile(content, size, path.string(), fsyncOnWrite_); 157 try
158 {
159 boost::filesystem::create_directories(path.parent_path()); // the function ensures that the directory exists or throws
160 }
161 catch (boost::filesystem::filesystem_error& er)
162 {
163 if (er.code() == boost::system::errc::file_exists // the last element of the parent_path is a file
164 || er.code() == boost::system::errc::not_a_directory) // one of the element of the parent_path is not a directory
165 {
166 throw OrthancException(ErrorCode_DirectoryOverFile, "One of the element of the path is a file"); // no need to retry this error
167 }
168
169 // ignore other errors and retry
170 }
171
172 try
173 {
174 SystemToolbox::WriteFile(content, size, path.string(), fsyncOnWrite_);
175
176 LOG(INFO) << "Created attachment \"" << uuid << "\" (" << timer.GetHumanTransferSpeed(true, size) << ")";
177 return;
178 }
179 catch (OrthancException& ex)
180 {
181 if (retryCount >= maxRetryCount)
182 {
183 throw ex;
184 }
185 }
186 }
155 } 187 }
156 188
157 189
158 IMemoryBuffer* FilesystemStorage::Read(const std::string& uuid, 190 IMemoryBuffer* FilesystemStorage::Read(const std::string& uuid,
159 FileContentType type) 191 FileContentType type)
160 { 192 {
193 Toolbox::ElapsedTimer timer;
161 LOG(INFO) << "Reading attachment \"" << uuid << "\" of \"" << GetDescriptionInternal(type) 194 LOG(INFO) << "Reading attachment \"" << uuid << "\" of \"" << GetDescriptionInternal(type)
162 << "\" content type"; 195 << "\" content type";
163 196
164 std::string content; 197 std::string content;
165 SystemToolbox::ReadFile(content, GetPath(uuid).string()); 198 SystemToolbox::ReadFile(content, GetPath(uuid).string());
199
200 LOG(INFO) << "Read attachment \"" << uuid << "\" (" << timer.GetHumanTransferSpeed(true, content.size()) << ")";
166 201
167 return StringMemoryBuffer::CreateFromSwap(content); 202 return StringMemoryBuffer::CreateFromSwap(content);
168 } 203 }
169 204
170 205
171 IMemoryBuffer* FilesystemStorage::ReadRange(const std::string& uuid, 206 IMemoryBuffer* FilesystemStorage::ReadRange(const std::string& uuid,
172 FileContentType type, 207 FileContentType type,
173 uint64_t start /* inclusive */, 208 uint64_t start /* inclusive */,
174 uint64_t end /* exclusive */) 209 uint64_t end /* exclusive */)
175 { 210 {
211 Toolbox::ElapsedTimer timer;
176 LOG(INFO) << "Reading attachment \"" << uuid << "\" of \"" << GetDescriptionInternal(type) 212 LOG(INFO) << "Reading attachment \"" << uuid << "\" of \"" << GetDescriptionInternal(type)
177 << "\" content type (range from " << start << " to " << end << ")"; 213 << "\" content type (range from " << start << " to " << end << ")";
178 214
179 std::string content; 215 std::string content;
180 SystemToolbox::ReadFileRange( 216 SystemToolbox::ReadFileRange(
181 content, GetPath(uuid).string(), start, end, true /* throw if overflow */); 217 content, GetPath(uuid).string(), start, end, true /* throw if overflow */);
182 218
219 LOG(INFO) << "Read range of attachment \"" << uuid << "\" (" << timer.GetHumanTransferSpeed(true, content.size()) << ")";
183 return StringMemoryBuffer::CreateFromSwap(content); 220 return StringMemoryBuffer::CreateFromSwap(content);
184 } 221 }
185 222
186 223
187 bool FilesystemStorage::HasReadRange() const 224 bool FilesystemStorage::HasReadRange() const