Skip to content

Commit 0e95808

Browse files
authored
Merge pull request #245 from qiniu/fix/retry-upload
Fix retry on form upload and part upload
2 parents db03ad2 + e74313a commit 0e95808

File tree

7 files changed

+160
-21
lines changed

7 files changed

+160
-21
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
**Next Release Date**
2+
3+
Next Version
4+
5+
修复:表单直传、分片上传配置 MaxRetryTimes 无效(v7.4.0 引入)
6+
17
**2022-10-26**
28

39
v8.3.0

src/Qiniu/Storage/Config.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ public class Config
4040
/// <summary>
4141
/// 重试请求次数
4242
/// </summary>
43-
public int MaxRetryTimes = 3;
43+
/// 默认值应与 <see cref="PutExtra.MaxRetryTimes"/> 一致
44+
public int MaxRetryTimes { set; get; } = 3;
4445

4546
/// <summary>
4647
/// 获取资源管理域名

src/Qiniu/Storage/FormUploader.cs

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ public HttpResult UploadStream(Stream stream, string key, string token, PutExtra
8181
if (putExtra == null)
8282
{
8383
putExtra = new PutExtra();
84+
putExtra.MaxRetryTimes = config.MaxRetryTimes;
8485
}
8586
if (string.IsNullOrEmpty(putExtra.MimeType )) {
8687
putExtra.MimeType = "application/octet-stream";
@@ -182,17 +183,8 @@ public HttpResult UploadStream(Stream stream, string key, string token, PutExtra
182183
ms.Write(partData2, 0, partData2.Length);
183184
ms.Write(partData3, 0, partData3.Length);
184185

185-
//get upload host
186-
string ak = UpToken.GetAccessKeyFromUpToken(token);
187-
string bucket = UpToken.GetBucketFromUpToken(token);
188-
if (ak == null || bucket == null)
189-
{
190-
return HttpResult.InvalidToken;
191-
}
192-
193-
string uploadHost = this.config.UpHost(ak, bucket);
194186
putExtra.ProgressHandler(stream.Length / 5, stream.Length);
195-
result = httpManager.PostMultipart(uploadHost, ms.ToArray(), boundary, null);
187+
result = PostFormWithRetry(token, ms.ToArray(), boundary, putExtra);
196188
putExtra.ProgressHandler(stream.Length, stream.Length);
197189
if (result.Code == (int)HttpCode.OK)
198190
{
@@ -265,6 +257,31 @@ public static UploadControllerAction DefaultUploadController()
265257
{
266258
return UploadControllerAction.Activated;
267259
}
268-
}
269260

270-
}
261+
private HttpResult PostFormWithRetry(string token, byte[] data, string boundary, PutExtra putExtra)
262+
{
263+
//get upload host
264+
string ak = UpToken.GetAccessKeyFromUpToken(token);
265+
string bucket = UpToken.GetBucketFromUpToken(token);
266+
if (ak == null || bucket == null)
267+
{
268+
return HttpResult.InvalidToken;
269+
}
270+
271+
string uploadHost = this.config.UpHost(ak, bucket);
272+
HttpResult result = httpManager.PostMultipart(uploadHost, data, boundary, null);
273+
274+
int retryTimes = 0;
275+
while (
276+
retryTimes < putExtra.MaxRetryTimes &&
277+
UploadUtil.ShouldRetry(result.Code, result.RefCode)
278+
)
279+
{
280+
result = httpManager.PostMultipart(uploadHost, data, boundary, null);
281+
retryTimes += 1;
282+
}
283+
284+
return result;
285+
}
286+
}
287+
}

src/Qiniu/Storage/PutExtra.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ public class PutExtra
3131
/// <summary>
3232
/// 最大重试次数
3333
/// </summary>
34-
public int MaxRetryTimes { set; get; }
34+
/// 默认值应与 <see cref="Config.MaxRetryTimes"/> 一致
35+
public int MaxRetryTimes { set; get; } = 3;
3536

3637
/// <summary>
3738
/// 块并发上传的线程数量

src/Qiniu/Storage/ResumableUploader.cs

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ public HttpResult UploadStream(Stream stream, string key, string upToken, PutExt
8484
if (putExtra == null)
8585
{
8686
putExtra = new PutExtra();
87+
putExtra.MaxRetryTimes = config.MaxRetryTimes;
8788
}
8889
if (putExtra.ProgressHandler == null)
8990
{
@@ -588,7 +589,7 @@ private void processMakeBlocks(Dictionary<long, byte[]> blockDataDict, string up
588589
byte[] blockData = blockDataDict[blockIndex];
589590
ResumeBlocker resumeBlocker = new ResumeBlocker(doneEvent, blockData, blockIndex, upToken, putExtra,
590591
resumeInfo, blockMakeResults, progressLock, uploadedBytesDict, fileSize, encodedObjectName);
591-
ThreadPool.QueueUserWorkItem(new WaitCallback(this.MakeBlock), resumeBlocker);
592+
ThreadPool.QueueUserWorkItem(new WaitCallback(this.MakeBlockWithRetry), resumeBlocker);
592593
}
593594

594595
try
@@ -602,11 +603,37 @@ private void processMakeBlocks(Dictionary<long, byte[]> blockDataDict, string up
602603
}
603604
}
604605

606+
private void MakeBlockWithRetry(object resumeBlockerObj)
607+
{
608+
ResumeBlocker resumeBlocker = (ResumeBlocker)resumeBlockerObj;
609+
ManualResetEvent doneEvent = resumeBlocker.DoneEvent;
610+
Dictionary<long, HttpResult> blockMakeResults = resumeBlocker.BlockMakeResults;
611+
long blockIndex = resumeBlocker.BlockIndex;
612+
PutExtra putExtra = resumeBlocker.PutExtra;
613+
614+
HttpResult result = MakeBlock(resumeBlockerObj);
615+
616+
617+
int retryTimes = 0;
618+
while (
619+
retryTimes < putExtra.MaxRetryTimes &&
620+
UploadUtil.ShouldRetry(result.Code, result.RefCode)
621+
)
622+
{
623+
result = MakeBlock(resumeBlockerObj);
624+
retryTimes += 1;
625+
}
626+
627+
//return the http result
628+
blockMakeResults.Add(blockIndex, result);
629+
doneEvent.Set();
630+
}
631+
605632
/// <summary>
606633
/// 创建块(携带首片数据),v1检查CRC32,v2检查md5
607634
/// </summary>
608635
/// <param name="resumeBlockerObj">创建分片上传的块请求</param>
609-
private void MakeBlock(object resumeBlockerObj)
636+
private HttpResult MakeBlock(object resumeBlockerObj)
610637
{
611638
ResumeBlocker resumeBlocker = (ResumeBlocker)resumeBlockerObj;
612639
ManualResetEvent doneEvent = resumeBlocker.DoneEvent;
@@ -632,7 +659,7 @@ private void MakeBlock(object resumeBlockerObj)
632659
result.RefText += string.Format("[{0}] [ResumableUpload] Info: upload task is aborted, mkblk {1}\n",
633660
DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.ffff"), blockIndex);
634661
blockMakeResults.Add(blockIndex, result);
635-
return;
662+
return result;
636663
}
637664
else
638665
{
@@ -658,7 +685,7 @@ private void MakeBlock(object resumeBlockerObj)
658685
{
659686
result = HttpResult.InvalidToken;
660687
doneEvent.Set();
661-
return;
688+
return result;
662689
}
663690

664691
string uploadHost = this.config.UpHost(ak, bucket);
@@ -793,9 +820,7 @@ private void MakeBlock(object resumeBlockerObj)
793820
}
794821
}
795822

796-
//return the http result
797-
blockMakeResults.Add(blockIndex, result);
798-
doneEvent.Set();
823+
return result;
799824
}
800825

801826
/// <summary>

src/Qiniu/Storage/UploadUtil.cs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using Qiniu.Http;
2+
3+
namespace Qiniu.Storage
4+
{
5+
public static class UploadUtil
6+
{
7+
public static bool ShouldRetry(int code, int refCode)
8+
{
9+
if (code == (int) HttpCode.OK)
10+
{
11+
return false;
12+
}
13+
14+
// allow list
15+
if (
16+
refCode == (int)HttpCode.USER_UNDEF ||
17+
refCode == (int)HttpCode.USER_NEED_RETRY
18+
)
19+
{
20+
return true;
21+
}
22+
23+
// block list
24+
int codeSeries = code / 100;
25+
26+
if (codeSeries == 4 && code != (int)HttpCode.CRC32_CHECK_FAILEd)
27+
{
28+
return false;
29+
}
30+
31+
if (
32+
code == (int)HttpCode.FILE_NOT_EXIST ||
33+
code == (int)HttpCode.FILE_EXISTS ||
34+
code == (int)HttpCode.CALLBACK_FAILED
35+
)
36+
{
37+
return false;
38+
}
39+
40+
// others need retry
41+
return true;
42+
}
43+
}
44+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using NUnit.Framework;
2+
using System.Collections;
3+
using Qiniu.Tests;
4+
5+
namespace Qiniu.Storage.Tests
6+
{
7+
public class UploadUtilTestCases
8+
{
9+
public static IEnumerable ShouldRetryTestCases
10+
{
11+
get
12+
{
13+
// 200 always false
14+
yield return new TestCaseData(200, 200).Returns(false);
15+
16+
// some refCode should retry
17+
yield return new TestCaseData(0, 3).Returns(true);
18+
yield return new TestCaseData(0, 0).Returns(true);
19+
20+
// 4xx shouldn't retry but 406
21+
yield return new TestCaseData(406, 406).Returns(true);
22+
yield return new TestCaseData(400, 400).Returns(false);
23+
24+
// some code shouldn't retry
25+
yield return new TestCaseData(612, 612).Returns(false);
26+
yield return new TestCaseData(614, 614).Returns(false);
27+
yield return new TestCaseData(579, 579).Returns(false);
28+
29+
// any others should retry
30+
yield return new TestCaseData(500, 500).Returns(true);
31+
yield return new TestCaseData(502, 502).Returns(true);
32+
}
33+
}
34+
}
35+
36+
[TestFixture]
37+
public class UploadUtilTests : TestEnv
38+
{
39+
[TestCaseSource(typeof(UploadUtilTestCases), nameof(UploadUtilTestCases.ShouldRetryTestCases))]
40+
public bool ShouldRetryTest(int code, int refCode)
41+
{
42+
return UploadUtil.ShouldRetry(code, refCode);
43+
}
44+
}
45+
}

0 commit comments

Comments
 (0)