/src/prod/src/Management/FileStoreService/FileAsyncOperation.cpp

https://github.com/Microsoft/service-fabric · C++ · 355 lines · 298 code · 49 blank · 8 comment · 21 complexity · d0cefc0ea04bc45843ffcb39d392cfd3 MD5 · raw file

  1. // ------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. // Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
  4. // ------------------------------------------------------------
  5. #include "stdafx.h"
  6. using namespace std;
  7. using namespace Common;
  8. using namespace Store;
  9. using namespace Management::FileStoreService;
  10. StringLiteral const TraceComponent("FileAsyncOperation");
  11. StringLiteral const RetryTimerTag("FileAsyncOperation.RetryTimer");
  12. FileAsyncOperation::FileAsyncOperation(
  13. __in RequestManager & requestManager,
  14. bool useSimpleTx,
  15. wstring const & storeRelativePath,
  16. bool useTwoPhaseCommit,
  17. Common::ActivityId const & activityId,
  18. TimeSpan const & timeout,
  19. AsyncCallback const & callback,
  20. AsyncOperationSPtr const & parent)
  21. : AsyncOperation(callback, parent),
  22. ReplicaActivityTraceComponent(requestManager.ReplicaObj.PartitionedReplicaId, activityId),
  23. requestManager_(requestManager),
  24. useSimpleTx_(useSimpleTx),
  25. storeRelativePath_(storeRelativePath),
  26. useTwoPhaseCommit_(useTwoPhaseCommit),
  27. timeoutHelper_(timeout)
  28. {
  29. }
  30. FileAsyncOperation::~FileAsyncOperation()
  31. {
  32. }
  33. ErrorCode FileAsyncOperation::End(Common::AsyncOperationSPtr const & operation)
  34. {
  35. return AsyncOperation::End<FileAsyncOperation>(operation)->Error;
  36. }
  37. void FileAsyncOperation::OnStart(AsyncOperationSPtr const & thisSPtr)
  38. {
  39. this->StartTransitionToIntermediateState(thisSPtr);
  40. }
  41. void FileAsyncOperation::OnCompleted()
  42. {
  43. TimerSPtr snap;
  44. {
  45. AcquireExclusiveLock lock(timerLock_);
  46. snap = retryTimer_;
  47. }
  48. if (snap)
  49. {
  50. snap->Cancel();
  51. }
  52. AsyncOperation::OnCompleted();
  53. }
  54. void FileAsyncOperation::StartTransitionToIntermediateState(AsyncOperationSPtr const & thisSPtr)
  55. {
  56. WriteInfo(
  57. TraceComponent,
  58. TraceId,
  59. "Queueing StoreTransaction for TransitionToIntermediateState. StoreRelativePath:{0}",
  60. storeRelativePath_);
  61. auto intermediateStateOperation = AsyncOperation::CreateAndStart<StoreTransactionAsyncOperation>(
  62. requestManager_,
  63. useSimpleTx_,
  64. [this](StoreTransaction const & storeTx)
  65. {
  66. WriteInfo(
  67. TraceComponent,
  68. TraceId,
  69. "Starting StoreTransaction for TransitionToIntermediateState. StoreRelativePath:{0}",
  70. storeRelativePath_);
  71. return this->TransitionToIntermediateState(storeTx);
  72. },
  73. this->ActivityId,
  74. timeoutHelper_.GetRemainingTime(),
  75. [this](AsyncOperationSPtr const & operation) { this->OnCommitToIntermediateStateCompleted(operation, false); },
  76. thisSPtr);
  77. this->OnCommitToIntermediateStateCompleted(intermediateStateOperation, true);
  78. }
  79. void FileAsyncOperation::OnCommitToIntermediateStateCompleted(AsyncOperationSPtr const & operation, bool expectedCompletedSynchronously)
  80. {
  81. if (operation->CompletedSynchronously != expectedCompletedSynchronously)
  82. {
  83. return;
  84. }
  85. auto error = StoreTransactionAsyncOperation::End(operation);
  86. WriteTrace(
  87. error.IsSuccess() ? LogLevel::Info : LogLevel::Warning,
  88. TraceComponent,
  89. TraceId,
  90. "Ending StoreTransaction for TransitionToIntermediateState. StoreRelativePath:{0}, Error:{1}",
  91. storeRelativePath_,
  92. error);
  93. if (error.IsSuccess())
  94. {
  95. auto fileOperation = this->OnBeginFileOperation(
  96. [this](AsyncOperationSPtr const & operation) { this->OnFileOperationCompleted(operation, false); },
  97. operation->Parent);
  98. this->OnFileOperationCompleted(fileOperation, true);
  99. }
  100. else
  101. {
  102. this->TryComplete(operation->Parent, move(error));
  103. }
  104. }
  105. void FileAsyncOperation::OnFileOperationCompleted(AsyncOperationSPtr const & operation, bool expectedCompletedSynchronously)
  106. {
  107. if (operation->CompletedSynchronously != expectedCompletedSynchronously)
  108. {
  109. return;
  110. }
  111. auto error = this->OnEndFileOperation(operation);
  112. if (error.IsSuccess())
  113. {
  114. this->StartTransitionToReplicatingState(operation->Parent);
  115. }
  116. else
  117. {
  118. this->Rollback(operation->Parent, error);
  119. }
  120. }
  121. void FileAsyncOperation::StartTransitionToReplicatingState(AsyncOperationSPtr const & thisSPtr)
  122. {
  123. WriteInfo(
  124. TraceComponent,
  125. TraceId,
  126. "Queueing StoreTransaction for TransitionToReplicatingState. StoreRelativePath:{0}",
  127. storeRelativePath_);
  128. auto finalStateOperation = AsyncOperation::CreateAndStart<StoreTransactionAsyncOperation>(
  129. requestManager_,
  130. useSimpleTx_,
  131. [this](StoreTransaction const & storeTx)
  132. {
  133. WriteInfo(
  134. TraceComponent,
  135. TraceId,
  136. "Starting StoreTransaction for TransitionToReplicatingState. StoreRelativePath:{0}",
  137. storeRelativePath_);
  138. return this->TransitionToReplicatingState(storeTx);
  139. },
  140. this->ActivityId,
  141. timeoutHelper_.GetRemainingTime(),
  142. [this](AsyncOperationSPtr const & operation) { this->OnCommitToReplicatingStateCompleted(operation, false); },
  143. thisSPtr);
  144. this->OnCommitToReplicatingStateCompleted(finalStateOperation, true);
  145. }
  146. void FileAsyncOperation::OnCommitToReplicatingStateCompleted(AsyncOperationSPtr const & operation, bool expectedCompletedSynchronously)
  147. {
  148. if (operation->CompletedSynchronously != expectedCompletedSynchronously)
  149. {
  150. return;
  151. }
  152. auto const & thisSPtr = operation->Parent;
  153. auto error = StoreTransactionAsyncOperation::End(operation);
  154. WriteTrace(
  155. error.IsSuccess() ? LogLevel::Info : LogLevel::Warning,
  156. TraceComponent,
  157. TraceId,
  158. "Ending StoreTransaction for TransitionToReplicatingState. StoreRelativePath:{0}, Error:{1}",
  159. storeRelativePath_,
  160. error);
  161. if (error.IsSuccess())
  162. {
  163. this->StartTransitionToCommittedState(thisSPtr);
  164. }
  165. else
  166. {
  167. this->Rollback(thisSPtr, error);
  168. }
  169. }
  170. void FileAsyncOperation::StartTransitionToCommittedState(AsyncOperationSPtr const & thisSPtr)
  171. {
  172. if (!useTwoPhaseCommit_)
  173. {
  174. WriteInfo(
  175. TraceComponent,
  176. TraceId,
  177. "skipping two-phase commit: StoreRelativePath:{0}",
  178. storeRelativePath_);
  179. this->TryComplete(thisSPtr, ErrorCodeValue::Success);
  180. }
  181. else
  182. {
  183. WriteInfo(
  184. TraceComponent,
  185. TraceId,
  186. "Queueing StoreTransaction for TransitionToCommittedState. StoreRelativePath:{0}",
  187. storeRelativePath_);
  188. auto operation = AsyncOperation::CreateAndStart<StoreTransactionAsyncOperation>(
  189. requestManager_,
  190. useSimpleTx_,
  191. [this](StoreTransaction const & storeTx)
  192. {
  193. WriteInfo(
  194. TraceComponent,
  195. TraceId,
  196. "Starting StoreTransaction for TransitionToCommittedState. StoreRelativePath:{0}",
  197. storeRelativePath_);
  198. return this->TransitionToCommittedState(storeTx);
  199. },
  200. this->ActivityId,
  201. timeoutHelper_.GetRemainingTime(),
  202. [this](AsyncOperationSPtr const & operation) { this->OnCommitToCommittedStateCompleted(operation, false); },
  203. thisSPtr);
  204. this->OnCommitToCommittedStateCompleted(operation, true);
  205. }
  206. }
  207. void FileAsyncOperation::OnCommitToCommittedStateCompleted(AsyncOperationSPtr const & operation, bool expectedCompletedSynchronously)
  208. {
  209. if (operation->CompletedSynchronously != expectedCompletedSynchronously) { return; }
  210. auto const & thisSPtr = operation->Parent;
  211. auto error = StoreTransactionAsyncOperation::End(operation);
  212. WriteTrace(
  213. error.IsSuccess() ? LogLevel::Info : LogLevel::Warning,
  214. TraceComponent,
  215. TraceId,
  216. "Ending StoreTransaction for TransitionToCommittedState. StoreRelativePath:{0}, Error:{1}",
  217. storeRelativePath_,
  218. error);
  219. if (error.IsSuccess())
  220. {
  221. this->TryComplete(thisSPtr, move(error));
  222. }
  223. else
  224. {
  225. this->Rollback(thisSPtr, move(error));
  226. }
  227. }
  228. void FileAsyncOperation::Rollback(AsyncOperationSPtr const & thisSPtr, ErrorCode const & originalError)
  229. {
  230. WriteWarning(
  231. TraceComponent,
  232. TraceId,
  233. "Performing rollback due to Error:{0}, StoreRelativePath:{1}",
  234. originalError,
  235. storeRelativePath_);
  236. WriteInfo(
  237. TraceComponent,
  238. TraceId,
  239. "Starting StoreTransaction for TransitionToRolledbackState. StoreRelativePath:{0}",
  240. storeRelativePath_);
  241. auto finalStateOperation = AsyncOperation::CreateAndStart<StoreTransactionAsyncOperation>(
  242. requestManager_,
  243. useSimpleTx_,
  244. [this] (StoreTransaction const & storeTx) { return this->TransitionToRolledbackState(storeTx); },
  245. this->ActivityId,
  246. TimeSpan::MaxValue,
  247. [this, originalError] (AsyncOperationSPtr const & operation) { this->OnCommitToRolledbackStateCompleted(operation, false, originalError); },
  248. thisSPtr);
  249. this->OnCommitToRolledbackStateCompleted(finalStateOperation, true, originalError);
  250. }
  251. void FileAsyncOperation::OnCommitToRolledbackStateCompleted(AsyncOperationSPtr const & operation, bool expectedCompletedSynchronously, ErrorCode const & originalError)
  252. {
  253. if (operation->CompletedSynchronously != expectedCompletedSynchronously)
  254. {
  255. return;
  256. }
  257. auto error = StoreTransactionAsyncOperation::End(operation);
  258. WriteTrace(
  259. error.IsSuccess() ? LogLevel::Info : LogLevel::Warning,
  260. TraceComponent,
  261. TraceId,
  262. "Ending StoreTransaction for TransitionToRolledbackState. StoreRelativePath:{0}, Error:{1}",
  263. storeRelativePath_,
  264. error);
  265. if(error.IsSuccess())
  266. {
  267. // Undo file operation only if rollback is successful
  268. this->UndoFileOperation();
  269. // If the operation rolled back, from client's perspective
  270. // the operations has failed
  271. error.Overwrite(originalError);
  272. }
  273. TryComplete(operation->Parent, move(error));
  274. }
  275. ErrorCode FileAsyncOperation::TryScheduleRetry(AsyncOperationSPtr const & thisSPtr, function<void(AsyncOperationSPtr const &)> const & callback)
  276. {
  277. TimeSpan delay = FileStoreServiceConfig::GetConfig().FileOperationBackoffInterval;
  278. if (delay > timeoutHelper_.GetRemainingTime())
  279. {
  280. // Not enough timeout left - just fail early
  281. return ErrorCodeValue::Timeout;
  282. }
  283. {
  284. AcquireExclusiveLock lock(timerLock_);
  285. if (!this->InternalIsCompleted)
  286. {
  287. retryTimer_ = Timer::Create(
  288. RetryTimerTag,
  289. [this, thisSPtr, callback](TimerSPtr const & timer)
  290. {
  291. timer->Cancel();
  292. callback(thisSPtr);
  293. });
  294. retryTimer_->Change(delay);
  295. }
  296. }
  297. return ErrorCodeValue::Success;
  298. }
  299. bool FileAsyncOperation::IsSharingViolationError(Common::ErrorCode const& error)
  300. {
  301. if (error.IsWin32Error(ERROR_SHARING_VIOLATION) || error.IsError(ErrorCodeValue::SharingAccessLockViolation))
  302. {
  303. return true;
  304. }
  305. return false;
  306. }