comparison OrthancFramework/Sources/FileStorage/FilesystemStorage.cpp @ 5729:bdbaccc06e98

Fix extremely rare error when 2 threads are trying to create the same folder in the File Storage at the same time
author Alain Mazy <am@orthanc.team>
date Fri, 26 Jul 2024 16:29:10 +0200
parents f7adfb22e20e
children 264d84c1936a
comparison
equal deleted inserted replaced
5727:843973a0fdfa 5729:bdbaccc06e98
22 **/ 22 **/
23 23
24 24
25 #include "../PrecompiledHeaders.h" 25 #include "../PrecompiledHeaders.h"
26 #include "FilesystemStorage.h" 26 #include "FilesystemStorage.h"
27 #include <boost/thread.hpp>
27 28
28 // 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
29 // http://stackoverflow.com/questions/446358/storing-a-large-number-of-images 30 // http://stackoverflow.com/questions/446358/storing-a-large-number-of-images
30 31
31 #include "../Logging.h" 32 #include "../Logging.h"
133 134
134 if (boost::filesystem::exists(path)) 135 if (boost::filesystem::exists(path))
135 { 136 {
136 // Extremely unlikely case: This Uuid has already been created 137 // Extremely unlikely case: This Uuid has already been created
137 // in the past. 138 // in the past.
138 throw OrthancException(ErrorCode_InternalError); 139 throw OrthancException(ErrorCode_InternalError, "This file UUID already exists");
139 } 140 }
140 141
141 if (boost::filesystem::exists(path.parent_path())) 142 // In very unlikely case (but we've seen it !), 2 threads might enter this same piece
142 { 143 // of code and both try to create the same directory or a thread could be deleting a
143 if (!boost::filesystem::is_directory(path.parent_path())) 144 // directory while another thread needs it -> introduce 3 retries at 1 ms interval
144 { 145 int retryCount = 0;
145 throw OrthancException(ErrorCode_DirectoryOverFile); 146 const int maxRetryCount = 3;
146 } 147
147 } 148 while (retryCount < maxRetryCount)
148 else 149 {
149 { 150 retryCount++;
150 if (!boost::filesystem::create_directories(path.parent_path())) 151 if (retryCount > 1)
151 { 152 {
152 throw OrthancException(ErrorCode_FileStorageCannotWrite); 153 boost::this_thread::sleep(boost::posix_time::milliseconds(2 * retryCount + (rand() % 10)));
153 } 154 LOG(INFO) << "Retrying to create attachment \"" << uuid << "\" of \"" << GetDescriptionInternal(type)
154 } 155 << "\" type";
155 156 }
156 SystemToolbox::WriteFile(content, size, path.string(), fsyncOnWrite_); 157
158 if (boost::filesystem::exists(path.parent_path()))
159 {
160 if (!boost::filesystem::is_directory(path.parent_path()))
161 {
162 throw OrthancException(ErrorCode_DirectoryOverFile); // no need to retry this error
163 }
164 }
165 else
166 {
167 if (!boost::filesystem::create_directories(path.parent_path()))
168 {
169 if (retryCount >= maxRetryCount)
170 {
171 throw OrthancException(ErrorCode_FileStorageCannotWrite);
172 }
173
174 continue; // retry
175 }
176 }
177
178 try
179 {
180 SystemToolbox::WriteFile(content, size, path.string(), fsyncOnWrite_);
181 }
182 catch (OrthancException& ex)
183 {
184 if (retryCount >= maxRetryCount)
185 {
186 throw ex;
187 }
188 }
189 }
190
157 LOG(INFO) << "Created attachment \"" << uuid << "\" (" << timer.GetHumanTransferSpeed(true, size) << ")"; 191 LOG(INFO) << "Created attachment \"" << uuid << "\" (" << timer.GetHumanTransferSpeed(true, size) << ")";
158 } 192 }
159 193
160 194
161 IMemoryBuffer* FilesystemStorage::Read(const std::string& uuid, 195 IMemoryBuffer* FilesystemStorage::Read(const std::string& uuid,