Compare commits

..

991 Commits

Author SHA1 Message Date
Victor1319
10353bf433 docs(docs): update upgrade 3.5.0 title level.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2025-03-13 10:01:10 +08:00
Victor1319
0cb374c1af docs(docs): add faq upgrade file to sidbar.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2025-03-12 17:24:48 +08:00
Victor1319
06ddf04184 docs(docs): add feature hybridcloud file to sidbar.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2025-03-12 17:00:39 +08:00
Victor1319
605aae1880 docs(docs): correct some issuse abount docs. #23131054
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2025-03-11 16:43:21 +08:00
Victor1319
be01778178 docs(docs): add docs abount 3.5.0. #23131054
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2025-03-11 16:05:59 +08:00
Victor1319
add9913f5f fix(master): use listen port as register consul port for master. #23119229
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2025-03-11 11:32:50 +08:00
Victor1319
329174a879 refactor(client): support config stream reqChan Size. #23114335
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2025-03-11 11:32:50 +08:00
Victor1319
e91f2fb9d9 fix(sdk): force update extent cache after trunc. #22962095
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2025-03-11 11:32:50 +08:00
Wu Huocheng
cd7ce653d4 fix(master): fix the nil pointer when master leader changed.#23070942
Root cause:
The quotaManager is not initilized yet, but it is used in heart beat.

Fix method:
Initilize quotaManager in newVol function.

Signed-off-by: Wu Huocheng <wuhuocheng@oppo.com>
2025-03-11 11:32:50 +08:00
Victor1319
c86610432d fix(client): fix client push commit failed. #22962095
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2025-03-11 11:32:50 +08:00
Victor1319
2d7fdd8664 fix(meta): fix metanode panic when append empty obj extents. #22962095
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2025-03-11 11:32:50 +08:00
Victor1319
51e55b90d9 fix(meta): fix metanode panic when append empty obj extents. #22962095
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2025-03-11 11:32:50 +08:00
baihailong
ec0ae36211 fix(metanode): fix metanode UpdateXAttr incompatibility problem.#22993674
Signed-off-by: baihailong <baihailong@oppo.com>
2025-03-11 11:32:50 +08:00
Victor1319
e6e2ede06a fix(meta): fix batch append exts failed bug. #22962095
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2025-03-11 11:32:50 +08:00
gukaifeng
b7cf74ab5d fix(metanode): fsync the crc file after wirte
@formatter:off

Signed-off-by: gukaifeng <gukaifeng@xiaomi.com>
Signed-off-by: slasher <shenjie1@oppo.com>
2025-03-11 11:32:50 +08:00
zhaochenyang
1e9a31cd20 docs(doc): add cubefs lcnode config
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2025-02-14 10:08:07 +08:00
zhaochenyang
e50cce7aa6 docs(doc): add cubefs lcnode design introduction
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2025-02-13 18:51:48 +08:00
slasher
f8d7059352 chore(github): upgrade action of upload-artifact
see more: https://github.blog/changelog/2024-04-16-deprecation-notice-v3-of-the-artifact-actions/

Signed-off-by: slasher <shenjie1@oppo.com>
2025-02-13 17:46:35 +08:00
leonrayang
a8e58d0141 fix(sdk): The refCnt of the streamer may become inaccurate due to the counting conflicts
close:#22962095

Signed-off-by: leonrayang <chl696@sina.com>
2025-01-08 14:20:11 +08:00
Victor1319
e1d788c29d refactor(master): add crossZone hint message when addAllowedStorageClass failed. #22962157
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2025-01-08 14:20:11 +08:00
Victor1319
dcd89f0cb2 refactor(master): support report datanode status count for different media. #22960364
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2025-01-08 14:20:11 +08:00
Victor1319
b67ac4dec5 refactor(fsck): reduce debug log for fsck gc comand. #22958547
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2025-01-08 14:20:11 +08:00
Victor1319
5fb94fec97 fix(data): fix gc clear data failed bug. #22957485
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2025-01-08 14:20:11 +08:00
zhumingze
6fe5f79490 refactor(fsck): avoid panic when vol not exists #22911241
Signed-off-by: zhumingze <zhumingze@oppo.com>
2025-01-08 14:20:11 +08:00
zhaochenyang
6cdbcd6df1 refactor(lcnode): lcnode response ack to master after save data #22941195
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2025-01-08 14:20:11 +08:00
chihe
091b382a4f fix(doc): modify png path for blobstore
Signed-off-by: chihe <chihe@oppo.com>
(cherry picked from commit e1175bd248)
2025-01-08 14:20:11 +08:00
chihe
81f8dcb3c4 fix(doc): fix sidebarConfig
Signed-off-by: chihe <chihe@oppo.com>
2025-01-08 14:20:11 +08:00
Victor1319
1e55cd23e5 fix(meta): Resolve the issue where log level settings for the EC are ineffective #22921293
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:12 +08:00
Victor1319
a14b9a702c refactor(master): add vol used stats for monitor metrics. #22930922
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:12 +08:00
Victor1319
edd357895d refactor(master): support report monitor stats for cluster different media datanode. #22929430
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:12 +08:00
Victor1319
228cd0a197 refactor(all): refactor code for code scan warn info. #22929123
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:12 +08:00
chihe
bd6da64d8a fix(cli):do not enable trash when excuting operation for quota
close:#22922055

Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 16:00:12 +08:00
Victor1319
08b26f2161 fix(meta): Use a read-write lock to prevent concurrent access to clustername. #22915506
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:12 +08:00
leonrayang
ec8e10a04d fix(metanode): Extend copy logic omitted quota element in the scenario of snapshot being disabled
close:#22788628

Signed-off-by: leonrayang <chl696@sina.com>
2024-12-30 16:00:12 +08:00
Victor1319
d9735ed3d0 fix(meta): avoid panic when new export point. #22911241
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:12 +08:00
Victor1319
ee3172e8cf refactor(fsck): Ignore timestamp validation and support compatibility with older versions. #22911241
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:12 +08:00
Victor1319
bb1eb1ebbe fix(master): ignore abnormal dp when addVolStorageClass. #22908260
enable to skip check mp & dp forbidden status.

Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:12 +08:00
chihe
4d30ac7d4e fix(datanode): trigger disk error when read data from tiny extent
close:#22771554

Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 16:00:12 +08:00
zhumingze
fdb59770c0 fix(data&cmd): Modify the alarm level of logLeftSpaceLimitRatio parameter parsing errors #22904699
Signed-off-by: zhumingze <zhumingze@oppo.com>
2024-12-30 16:00:12 +08:00
chihe
fd90f1835d fix(client): if Current is renanmed, create it again
close:#22900926

Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 16:00:12 +08:00
Victor1319
9c538fb24f fix(data): fix deadlock when load extent header from disk. #22900702
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:12 +08:00
Victor1319
b375b98282 fix(meta): add lock when range metapartitions. #22899488
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:12 +08:00
Victor1319
91e9e6b712 refactor(sdk): to avoid int range overflow in sdk retry logic. #22858505
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:12 +08:00
Victor1319
894cffc2ff refactor(meta): avoid load mp failed when get vol info error. #22858505
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:12 +08:00
Victor1319
e402cf44c3 refactor(util): support delete audit log in log module. #22858505
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:12 +08:00
Victor1319
54c357ddc1 refactor(master): remove useless code. #22890822
eliminate security vulnerabilities in the code.

Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:12 +08:00
zhumingze
6121ffd009 feat(cli): Support cli to view inode detail information by inode id. #22867405
Signed-off-by: zhumingze <zhumingze@oppo.com>
2024-12-30 16:00:12 +08:00
zhumingze
b21ba54597 fix(util): change clean internal of audit log to 10s #22858505
Signed-off-by: zhumingze <zhumingze@oppo.com>
2024-12-30 16:00:12 +08:00
Victor1319
98ee6a31fc fix(meta): update storage class as blobstore when file empty. #22883730
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:12 +08:00
Victor1319
d7255e9151 fix(meta): execute sync func only when error is nil. #22883730
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:12 +08:00
Victor1319
e56d0bd3d1 fix(meta): use local reserved variable to replace var in inode. #22883730
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:12 +08:00
tangdeyi
90fe5104d1 fix(objectnode): optimize the logic of createBucket
with #22863348

Signed-off-by: tangdeyi <tangdeyi@oppo.com>
2024-12-30 16:00:12 +08:00
leonrayang
bb932c609f Simplify calculation process of Quota for accuracy and performance
Close:#22788628

Signed-off-by: leonrayang <chl696@sina.com>
2024-12-30 16:00:12 +08:00
Victor1319
baa5ad8858 fix(sdk): fix client trash concurrent delete bug. #22862286
adjust trash dir lock lease time to 1 hour.

Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:12 +08:00
Victor1319
7639b5dccb refactor(meta): record error when RenewalForbiddenMigration failed. #22871509 2024-12-30 16:00:12 +08:00
chihe
a84abcd405 fix(trash) if rebuild dir failed ,retry next time
close:#22867890

Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 16:00:12 +08:00
chihe
b2896c8522 fix(client): do not return when parent dir is created by other routinues
close:#22867890

Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 16:00:12 +08:00
chihe
42e6d32661 fix(client): start schedule task for trash after metawrapper is initilized
close:#22867890

Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 16:00:12 +08:00
Victor1319
a8fde3ceb4 fix(meta): Adjust the call order of the RegistConsul func to prevent concurrent modify to clustername. #22873788
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:12 +08:00
leonrayang
416b1b3b09 feat(metanode): Optimize the extend memory allocate process in storeExtend, marshal and unmarshal
close:#22788628

Signed-off-by: leonrayang <chl696@sina.com>
2024-12-30 16:00:12 +08:00
Victor1319
0e84cf81bf refactor(meta): add txId in audit log for dentryOp. #22871509
refactor tx retry default cfg when tx is conflicted to over default timeout cfg.

Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:12 +08:00
chihe
7d6327d8ce fix(master): modify logic for reporting metric for diskError
close:#22771554

Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 16:00:12 +08:00
zhumingze
0bb2eeb73d feat(util): Set the default rotate size for audit logs to 1G #22858505
Signed-off-by: zhumingze <zhumingze@oppo.com>
2024-12-30 16:00:12 +08:00
Victor1319
9534c4173e fix(metanode): not close syncAtime chan. #22871509
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:12 +08:00
baihailong
fd94478d9e fix(bcache): No cubefscache directory causes an error when starting bcache.#22870633
Signed-off-by: baihailong <baihailong@oppo.com>
2024-12-30 16:00:12 +08:00
Victor1319
5d2c0f31aa fix(sdk): support refresh dir log automatically. #22862286
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:12 +08:00
Victor1319
976f84bae6 refactor(meta): refactor delete migration eks log. #22869219
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:12 +08:00
Victor1319
09151cb687 feat(sdk): Use dir lock to prevent multiple trash from running concurrently. #22862286
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:12 +08:00
Victor1319
12c73399bb refactor(lcnode): rename variable name writeGen to leaseExpire. #22855673
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:12 +08:00
zhumingze
7559f2dd3f fix(util): Modify clean logic of audit log and add test case #22858505
Signed-off-by: zhumingze <zhumingze@oppo.com>
2024-12-30 16:00:12 +08:00
zhumingze
1726171fad feat(cli): Support cli to display vol used size based on dp media type #22853006
Signed-off-by: zhumingze <zhumingze@oppo.com>
2024-12-30 16:00:12 +08:00
Victor1319
a686e2a394 refactor(meta): refactor client lease logic. #22855673
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:12 +08:00
tangdeyi
bd564efdf4 fix(objectnode): close volume loadOSSMeta task when volume is deleted
with #22861848

Signed-off-by: tangdeyi <tangdeyi@oppo.com>
2024-12-30 16:00:12 +08:00
zhumingze
f6c2e03d1a feat(fsck): Add CheckMP command to detect inconsistency among mp copies #22779997
Signed-off-by: zhumingze <zhumingze@oppo.com>
2024-12-30 16:00:12 +08:00
zhumingze
507b75de30 feat(cmd): Enable pprof configuration for signle-node deployment #22779997
Signed-off-by: zhumingze <zhumingze@oppo.com>
2024-12-30 16:00:12 +08:00
Victor1319
bc0767c257 fix(meta): to avoid write EXTENT_DEL_V2_xx header twice. #22860885
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:12 +08:00
baihailong
e4166b620a fix(libsdk): when start client NewStatistic one time.#22784741
Signed-off-by: baihailong <baihailong@oppo.com>
2024-12-30 16:00:12 +08:00
baihailong
3873817135 fix(sdk): optimize refreshSummary code.#22854234
Signed-off-by: baihailong <baihailong@oppo.com>
2024-12-30 16:00:12 +08:00
baihailong
4e6afbf378 fix(sdk): if not find inode of dentry when refreshSummary, skip it.#22828321
Signed-off-by: baihailong <baihailong@oppo.com>
2024-12-30 16:00:12 +08:00
baihailong
9dd94239eb fix(skd): Add total access file size.#22826411
Signed-off-by: baihailong <baihailong@oppo.com>
2024-12-30 16:00:12 +08:00
baihailong
2b3dd771dd fix(skd): Add directory access file info statistics of storage type.#22789777
Signed-off-by: baihailong <baihailong@oppo.com>
2024-12-30 16:00:12 +08:00
baihailong
36c3880c94 fix(skd): Add directory access file capacity statistics.#22784741
Signed-off-by: baihailong <baihailong@oppo.com>
2024-12-30 16:00:12 +08:00
baihailong
e7b00b6262 fix(sdk): optimize tool, use ReadDirLimit_ll instead of ReadDir_ll when get dentry.#22724074
Signed-off-by: baihailong <baihailong@oppo.com>
2024-12-30 16:00:12 +08:00
baihailong
bf082b2859 fix(sdk): According to the access time statistics directory files.#22724074
Signed-off-by: baihailong <baihailong@oppo.com>
2024-12-30 16:00:12 +08:00
leonrayang
f4d5092148 feat(metanode): Disable the process related to snapshots if snapshots are switched off
close:#22404072

Signed-off-by: leonrayang <chl696@sina.com>
2024-12-30 16:00:12 +08:00
Victor1319
fd5664b598 feat(meta): support return real atime for batchIget api. #22853172
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:12 +08:00
Victor1319
dbf2d43a0d fix(meta): don't block extent delete req when no success exts. #22852635
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:11 +08:00
Victor1319
511e6199f1 fix(master): update bcache report info. #22845037
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:11 +08:00
Victor1319
08b9b7e63f fix(meta): when no error break for loop. #22832968
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:11 +08:00
Victor1319
5b5977807d feat(master): support report whether enable bcache for client. #22845037
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:11 +08:00
Victor1319
88c93e75ff fix(add panic log when invoke getRetryIntervalTimeOut. #22840499):
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:11 +08:00
Victor1319
157c516a84 feat(master): support query all client ip from master. #22834669
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:11 +08:00
zhaochenyang
3445bf50f9 refactor(lcnode): [hybrid cloud] lifecycle rule prefix check #22726594
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 16:00:11 +08:00
Victor1319
f1d91abeae fix(master): check zone type when add datanode node. #22832968
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:11 +08:00
Victor1319
a316c1f72b fix(meta): ignore get dp partitions error when create mp. #22832968
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:11 +08:00
Cloudstriff
76837ed26c feat(rpc): add metrics filter config for auditlog
with #22357724

Signed-off-by: Cloudstriff <chenjiongwendao@qq.com>
2024-12-30 16:00:11 +08:00
slasher
dba756d4c3 fix(blobstore): fix metric to microsecond of response duration
@formatter:off

Signed-off-by: slasher <shenjie1@oppo.com>
2024-12-30 16:00:11 +08:00
slasher
ca9e537d06 perf(common): trace prefer to format track log #22446196
close #3224

Signed-off-by: slasher <mcq.sejust@gmail.com>
2024-12-30 16:00:11 +08:00
slasher
6ae429b4d5 perf(common): limit internal track log of trace span #22446196
close #3223

Signed-off-by: slasher <mcq.sejust@gmail.com>
2024-12-30 16:00:11 +08:00
Victor1319
ce20d0fd27 feat(master&data): add config to control whether vol read direct disk. #22818122
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:11 +08:00
Victor1319
f225ec3a25 refactor(datanode): refactor data read and write performance. #22504692
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:11 +08:00
Victor1319
9609c5784a refactor(meta): refactor dir lock and unlock logic. #22659556
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:11 +08:00
Victor1319
e3d75b007a refactor(master): set forbidWriteOfProtoVer0 as true for new vol. #22825400
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:11 +08:00
Victor1319
1674d75001 fix(master): fix RUnlock failed when invoke AllPartitionForbidVer0. #22822061
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:11 +08:00
baihailong
5ad2840ed3 fix(bcache): Fixed bcache server can start multiple processes.#22808275
Signed-off-by: baihailong <baihailong@oppo.com>
2024-12-30 16:00:11 +08:00
Victor1319
e6dbbd7fae refactor(sdk): for inner req, only support request mp leader . #22818553
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:11 +08:00
baihailong
109bee2d96 fix(client): Optimize bcache switch names and debug logs.#22814877
Signed-off-by: baihailong <baihailong@oppo.com>
2024-12-30 16:00:11 +08:00
Victor1319
6ca664735d test(docker): support run in hybrid way for docker mod. #22816675
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 16:00:11 +08:00
Victor1319
3ead484532 refactor(master): check vol forbidden write type when add vol storage class. #22814514
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:33 +08:00
Victor1319
7a588b123c fix(master): not show Unspecified storage class in allowed storage class. #22812125
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:33 +08:00
Victor1319
c57341a558 refactor(sdk): support limit tiny extent buf size. #22811542
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:33 +08:00
baihailong
aa0f75d4c6 fix(sdk): when enable bcache maybe leadto bad file descriptor.#22812246
Signed-off-by: baihailong <baihailong@oppo.com>
2024-12-30 15:59:33 +08:00
zhaochenyang
8092937450 refactor(lcnode): [hybrid cloud] lc scanner skip trash dir #22797969
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 15:59:33 +08:00
Victor1319
2c0400ffe5 refactor(meta): use bufio to write sanpshot data. #22785311
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:33 +08:00
Victor1319
fecb1488f4 refactor(meta): delete migrate extent key when delete inode. #22785311
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:33 +08:00
Victor1319
9d8b078109 refactor(meta): refactor migrate extent delete performance. #22785311
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:33 +08:00
Victor1319
05510f5ffb refactor(meta): refactor variable name. #22785311
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:33 +08:00
Victor1319
6de11396f6 refactor(meta): reduce cpu cost for mp delete worker. #22785311
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:33 +08:00
zhaochenyang
4f98302544 refactor(lcnode): [hybrid cloud] add auditlog for lc start stop and heartbeat #22771832
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 15:59:33 +08:00
Victor1319
a40d8a5540 feat(meta&sdk&master): support read quoram when mp no leader. #22782472
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:33 +08:00
Victor1319
644759db16 fix(master): support meta follower read. #22782680
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:33 +08:00
Victor1319
569d9fafcd fix(meta): fix some issue about compatiable. #22780321
1. remove useless code.
2. fix compatiable bug abount dataMediatType.

Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:33 +08:00
Victor1319
88b4e96350 refactor(meta): remove useless code. #22716915
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:33 +08:00
chihe
a43321fe23 feat(client): trash can be disabled by sdk
close:#22775703

Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 15:59:33 +08:00
baihailong
77adc9fe9e fix(sdk): enable config bcache only for clod data.#22770439
Signed-off-by: baihailong <baihailong@oppo.com>
2024-12-30 15:59:33 +08:00
baihailong
8c5f86490f fix(client): enableBcache is invalid even if it was specified.#22770449
Signed-off-by: baihailong <baihailong@oppo.com>
2024-12-30 15:59:33 +08:00
zhumingze
70ae442c73 refactor(autofs): Update default log and client path logic #22227137
Signed-off-by: zhumingze <zhumingze@oppo.com>
2024-12-30 15:59:33 +08:00
Victor1319
e3561c3990 fix(meta): return opErr when append migrate extent key. #22771173
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:33 +08:00
Victor1319
4256e73b49 refactor(master): add log for master raft op. #22766129
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:33 +08:00
chihe
f70d9fe287 fix(metanode):remove invalid filed for repsonese of getInode
close:#22768285

Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 15:59:33 +08:00
zhaochenyang
7eeacd18ba refactor(lcnode): [hybrid cloud] avoid heatbeat timeout during adding lcnode #22768923
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 15:59:33 +08:00
Victor1319
3c1440f99f fix(meta): fix unmarshal append inode bug. #22764528
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:33 +08:00
Reliey
21c323407e feat(datanode): Add endpoint for setting enablePid in exporter and handle response building #22257509
Signed-off-by: Reliey <zhumingze@oppo.com>
2024-12-30 15:59:33 +08:00
Reliey
a34fe1ea83 feat(master): Add http interface getOpLog for data processing #22257509
Signed-off-by: Reliey <zhumingze@oppo.com>
2024-12-30 15:59:33 +08:00
Reliey
2070f10fba feat(cli): support reporting and viewing of cluster and vol dimension disk data #22257509
Signed-off-by: Reliey <zhumingze@oppo.com>
2024-12-30 15:59:33 +08:00
Reliey
ae035ed04d feat(master,cli): support datanode report dpOpLog and diskOpLog infomation. #22257509
Signed-off-by: Reliey <zhumingze@oppo.com>
2024-12-30 15:59:33 +08:00
Reliey
a1f1748383 test(stat): add testcase for statistic log when disk is full. #22637219
Signed-off-by: Reliey <616318745@qq.com>
2024-12-30 15:59:32 +08:00
Victor1319
baeffb5f2d fix(meta): fix unmarshal append inode bug. #22764528
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:32 +08:00
chihe
6b829d0064 fix(metanode): modify log level for fsmInternalBatchFreeMigrationExtentKey
close:#22757889

Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 15:59:32 +08:00
Victor1319
3db4c63c14 fix(cli): fix some tiny issues. #22727537
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:32 +08:00
Victor1319
65ae5b4227 refactor(master): support double check master media cfg type. #22727537
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:32 +08:00
Victor1319
b31ed81610 fix(meta): process extent list req as cache for blobstore ino. #22726594
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:32 +08:00
true1064
86eb70df4e feat(meta): [hybrid cloud] metanode quit starting if master not support the API getUpgradeCompatibleSettings
close:#22727537
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:32 +08:00
true1064
8e7ea547ee feat(master): [hybrid cloud] no allowed to start if config legacyDataMediaType not set correctly
close:#22716686
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:32 +08:00
shuqiang-zheng
732f11df5e fix(meta): fix an unlocked error when getting the leader address and master address from the masterclient.
close:#22709182
Signed-off-by: shuqiang-zheng <zhengshuqiang@oppo.com>
2024-12-30 15:59:32 +08:00
chihe
3ca91338ba refactor(metanode): submit inode to free forbidden migration by batch
close:#22701675

Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 15:59:32 +08:00
Victor1319
5cd2a302f0 fix(master): remove self commit. #22735527
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:32 +08:00
Victor1319
b00afa2e33 fix(meta): empty file not check storage class. #22735527
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:32 +08:00
true1064
d1d8f1dfe1 fix(ci): set master config enableDirectDeleteVol as true
close:#22716686
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:32 +08:00
true1064
84abd5d126 refactor(data, meta): [hybrid cloud] optimize some logs related to upgrade compatibility
close:#22716686
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:32 +08:00
Victor1319
1714cc1692 fix(raft): Make the apply and apply snapshot processes mutually exclusive. #22720099
not truncate raft log before raft apply snapshot success.

Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:32 +08:00
Victor1319
78b93610d4 fix(meta): only file check storage class when unmarshal inode. #22735527
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:32 +08:00
Victor1319
a58a7dcfb8 refactor(master): refactor variable name from cap to quota. #22731703
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:32 +08:00
Victor1319
1e94e24198 fix(meta): avoid modify inode info in btree.#22575226
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:32 +08:00
Victor1319
a765cc24c1 feat(master): support replica storage quota limit.#22731703
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:32 +08:00
Victor1319
3f7e65ad4c refactor(meta): refactor metanode inodeGetWithAtime code. #22726594
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:32 +08:00
Victor1319
a1d3c477c5 refactor(master): suppor stat blob storage used. #22623271
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:32 +08:00
true1064
b06a34fc73 feat(master, meta): [hybrid cloud] add LegacyDataMediaType in master cluster values, and metaNode fetch it from master.
close:#22716686
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:32 +08:00
true1064
bc0f1a2347 feat(master,data): [hybrid cloud] datanode quit starting if master returns mediaType not match when register
close:#22727852
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:32 +08:00
Victor1319
d6c7a99fb1 refactor(master): support show vol used space group by media type. #22718093
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:32 +08:00
Victor1319
69150d0e45 refactor(meta): support concurrent delete extent. #22716917
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:32 +08:00
Victor1319
7f1d66e70a fix(metanode): return not exist error when inode not exist. #22712456
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:32 +08:00
Victor1319
9cad5d19d0 fix(metanode): delete extent_del file when reach end of the file. #22706828
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:32 +08:00
Victor1319
e32d2ba624 fix(metanode): recover panic when marshal inode failed. #22720099
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:32 +08:00
Victor1319
d3b7ede7fb refactor(meta): support compatible with v3.4.0. #22711062
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:32 +08:00
true1064
434b843056 fix(master): [hybrid cloud] correctly set legacy zone's dataMediaType by config legacyDataMediaType
#22699128
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:32 +08:00
true1064
a75b58d155 fix(master):[hybrid cloud] when create replica volume and replica-num is less than 3, auto set follower-read as true
close:#22701504
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:32 +08:00
Victor1319
0309a98cbe fix(master): Avoid the Apply and Snapshot processes at the same time. #22660472
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:32 +08:00
true1064
af5764b4f7 fix(fsck): fix nil pointer when get partition info
close:#22709584
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:32 +08:00
zhaochenyang
d0997e9cb1 refactor(lcnode): [hybrid cloud] avoid panic if rule in doing is nil #22706069
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 15:59:32 +08:00
Victor1319
dda6d6bf91 refactor(meta): pass atime when batch sync inodes atime. #22701675
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:32 +08:00
Victor1319
478facf54b refactor(meta): support batch persist inode atime. #22701675
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:32 +08:00
true1064
48e015f54a fix(cli):[hybrid cloud] if create replica volume and replica-num is assigned 1 or 2, cli auto set follower-read as true
close:#22701504
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:32 +08:00
zhaochenyang
b01b302d70 refactor(lcnode): [hybrid cloud] config lc start time and disable expiration #22653422
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 15:59:32 +08:00
true1064
fc48565a01 fix(meta): Increase the timeout when fetch info from master in register and mp start procedure
close:#22677404
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:32 +08:00
chihe
47d679a0c1 fix(master): Add log for persisting apply index
close:#22660472

Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 15:59:32 +08:00
true1064
be050a789e test[java]: improve the operation types provided by TestCfsClient.java
close:#22687828
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:32 +08:00
chihe
b887d20127 fix(client):Client data modification is prohibited if renewal failed
close:#22676203

Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 15:59:32 +08:00
zhaochenyang
d530146c48 refactor(lcnode): [hybrid cloud] admin lcnode auditlog #22675357 #22680253
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 15:59:32 +08:00
zhaochenyang
d07c61b873 refactor(lcnode): [hybrid cloud] check vol delete before lifecycle task start #22666520
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 15:59:32 +08:00
true1064
06fe413c0b fix(master):[hybrid cloud] interface AdminVolAddAllowedStorageClass correctly returns success
clsoe:#22665897
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:32 +08:00
Victor1319
6da6e2d30d fix(sdk): avoid write on tiny extent handler again. #22657199
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:31 +08:00
true1064
76f51b0a61 feat(master): [hybrid cloud] if mediaType is 0 in request of /dataNode/add, set the mediaType as conf item legacyDataMediaType
close:#22395995
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:31 +08:00
true1064
bc8e52faf6 feat(meta,master,cli):[hybrid cloud] migration data usage statistics.
close:#22658297
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:31 +08:00
true1064
e1dbc1c1f1 refactor(sdk):[hybrid cloud] log print dataPartition count of mediaType when refresh and select dataPartition.
close:#22600758
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:31 +08:00
true1064
37816cba46 feat(master):[hybrid cloud] not allow a volume supports both replica storageClass and blobstore
close:#22395995
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:31 +08:00
true1064
acabfe0881 feat(meta):[hybrid cloud] put them into extent delete channel when deleting migration extents of type replica
close:#22605815
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:31 +08:00
Victor1319
333c5ae937 refactor(sdk): support sleep before retry when write datanode failed. #22602256
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:31 +08:00
zhaochenyang
2686afa949 refactor(lcnode): [hybrid cloud] lifecycle validity check #22653422
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 15:59:31 +08:00
Victor1319
a3d94710d9 fix(sdk): set innerReq when init metawrapper. #22645132
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:31 +08:00
true1064
87b3642842 fix(data,meta):[hybrid cloud] use default value if master not support the API AdminGetVolListForbidWriteOpOfProtoVer0.
close:#22644059
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:31 +08:00
Victor1319
b975cd742e feat(lcnode): set inner req true when init lc scanner meta wrapper. #22639641
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:31 +08:00
Victor1319
95a5d16247 feat(meta): not modify atime when get extent list for inner req. #22639641
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:31 +08:00
Victor1319
3b59f0243e feat(meta): return real accessTime for lcnode get req. #22639636
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-12-30 15:59:31 +08:00
true1064
b8138fe2db feat(master):[hybrid cloud] add an API to get volumes those set forbidden write op codes of protocaol version-0
close#22531837
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:31 +08:00
baihailong
3d4a345fe7 fix(sdk): classifiy statistic file size and count for refresher tool.#22490031
Signed-off-by: baihailong <baihailong@oppo.com>
2024-12-30 15:59:31 +08:00
baihailong
8bae4557af fix(libsdk): libsdk support interface for cubefs tools.#22490031
Signed-off-by: baihailong <baihailong@oppo.com>
2024-12-30 15:59:31 +08:00
true1064
71bc017279 feat(master,data,meta):[hybrid cloud] support configuring volume to forbidden write operate codes of lower packet protocol version.
close#22531837
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:31 +08:00
zhaochenyang
0e5e488140 refactor(lcnode): [hybrid cloud] stop task if vol delete #22520059
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 15:59:31 +08:00
true1064
9b0f22056d feat(data,client,master): [hybrid cloud] support configuring datanode and metanode to forbidden write operate codes of lower packet protocol version.
close#22531837
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:31 +08:00
zhaochenyang
54106de10c refactor(lcnode): [hybrid cloud] memory optimization #22401401
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 15:59:31 +08:00
zhaochenyang
40f3e530b6 refactor(lcnode): [hybrid cloud] config use create time and log optimization #22616040
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 15:59:31 +08:00
true1064
18f27ce0a0 fix(meta): [hybrid cloud] not allow truncate if inode's actual storageClass is blob
close:#22571994
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:31 +08:00
true1064
8efc131279 fix(meta): [hybrid cloud] fix panic when print log in function UpdateExtentKeyAfterMigration
close:#22568588
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:31 +08:00
true1064
780e29c928 refactor(meta): [hybrid cloud] add storageClass validation check when inode.storageClass changes
##22542807
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:31 +08:00
true1064
82edc8ba36 feat(master): [hybrid cloud] if crossZone is true, support degrade to create partitions when only one zone available.
close:#22548189
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:31 +08:00
zhaochenyang
97e9884a3c refactor(lcnode): [hybrid cloud] stop scan retry and log optimization #22515436 #22538171 #22543543
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 15:59:31 +08:00
true1064
e40c4449f5 refactor(sdk): [hybrid cloud] function updateExtentKeyAfterMigration print only warn log if inode's lease is occupied by client.
close:#22543543
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:31 +08:00
true1064
66149e9408 fix(master): [hybrid cloud] reset mp replica's usage stat of storageClass if the metanode is not alive.
close:#22490158
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:31 +08:00
true1064
39f1abc83f fix(client): [hybrid cloud] to access the right storageClass when inode has migrated to blobstore
close:#22506168
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:31 +08:00
true1064
8369975ca6 fix(SDK): in function ExtentClient.Read, return if get extents failed.
#22476752
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:31 +08:00
chihe
eb0910110d fix(metanode):if storage class is already the same with request, return directly
close:#22517222

Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 15:59:31 +08:00
true1064
dca9e0ec8c fix(sdk,client): [hybrid cloud] client compatible with older version server modules
#22400600
@formatter:off
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:31 +08:00
zhaochenyang
52925c8a1b fix(lcnode): [hybrid cloud] support start one task and stop one task
#22423763
#22502668
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 15:59:30 +08:00
zhaochenyang
0ff868ae2a fix(lcnode): [hybrid cloud] delete vol done result when start vol task #22496315
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 15:59:30 +08:00
zhaochenyang
fc227055b4 fix(lcnode): [hybrid cloud] support start vol task and stop vol task #22326199
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 15:59:30 +08:00
true1064
dcb0e0f3d6 fix(lcNode):[hybrid cloud] set followerRead as false when create ExtentClient
#22481972
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:30 +08:00
zhaochenyang
d6e0cb7cf9 fix(lcnode): [hybrid cloud] fix panic in setLcMetrics #22466527
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 15:59:30 +08:00
zhaochenyang
be002f2831 fix(lcnode): [hybrid cloud] task restart everyday and sync lc results #22449953 #22438986 #22454917
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 15:59:30 +08:00
true1064
b9f51ef234 refactor(client):[hybrid cloud] print warn log when OpMetaExtentsList returns proto.OpMismatchStorageClass
#22004328
Signed-off-by: tangjingyu <tangjingyu@oppo.com
2024-12-30 15:59:30 +08:00
true1064
2d67d16b1a refactor(cli):[hybrid cloud] add storageClass tips in help messages
#22395995
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:30 +08:00
true1064
0db4f1ef69 refactor(meta):[hybrid cloud]optimize meta log in UpdateExtentKeyAfterMigration()
#22395995
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:30 +08:00
zhaochenyang
d18d38065f fix(lcnode): [hybrid cloud] task continue if lcnode or master restart #22326221 #22322540
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 15:59:30 +08:00
true1064
b773bda52f fix(meta): [hybrid cloud] after deletion migrate extents of an inode, push it back info free list if it is marked delete.
#22313098
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:30 +08:00
true1064
116ab25196 refactor(meta): [hybrid cloud] optimize some error logging
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:30 +08:00
zhaochenyang
9177f90b31 fix(lcnode): [hybrid cloud] new blobstore client only when migrating to blobstore #22404848
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 15:59:30 +08:00
true1064
197ad7765b fix(master): [hybrid cloud] check if volume's zoneName list has the resource when add allowed storageClass to volume.
#22395995
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:30 +08:00
zhaochenyang
fd42546042 fix(lcnode): [hybrid cloud] optimize lcnode scanning #22347010 #22401484
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 15:59:30 +08:00
true1064
7d55a6e7c0 fix(meta): [hybrid cloud] if raft not leader when submit opFSMUpdateExtentKeyAfterMigration, response proto.OpAgain
#22332725
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:30 +08:00
true1064
94ff86a21f feat(meta,lcnode): [hybrid cloud] meta API OpMetaUpdateExtentKeyAfterMigration replies err details to lcNode, lcNode will print it to audit log
#22327519
#22385058
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:30 +08:00
true1064
eeeadbaff0 feat(cli): [hybrid cloud] while creating volume, auto set parameter crossZone as true if assigned more than one zone.
#22376966
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:30 +08:00
true1064
5072cf7216 fix(master): [hybrid cloud] while creating volume, check datapartiton count of specific meidaType.
#22377936
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:30 +08:00
true1064
3a262bbe29 feat(meta): [hybrid cloud] metapartition can start when failed to create blobStore client
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:30 +08:00
true1064
280be08dec feat(client):[hybrid cloud] only create blobStore client when volume's storageClass is blobStore.
#22338879
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:30 +08:00
true1064
c6bc8639b9 feat(master): [hybrid cloud] if crossZone is set but there is only one candidate zone, still create volume in the zone.
#22367107
Signed-off-by: true1064 <tangjingyu@oppo.com>
2024-12-30 15:59:30 +08:00
true1064
499a9396b6 fix(meta): [hybrid cloud] Optimize the error message in audit log of lcNode when OpMetaUpdateExtentKeyAfterMigration failed
#22342470
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:30 +08:00
true1064
81ef38eda7 fix(master): fixed usage of lock dpMissingReplicaMutex in struct warningMetrics
#22295296
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:30 +08:00
true1064
fcf911e822 fix(master): close chan stopc only once, to avoid panic
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:30 +08:00
true1064
6a7cc48b75 fix(master): [hybrid cloud] when zoneName in datanode's conf file changed, make adjustments of topology.
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:30 +08:00
zhaochenyang
0729e9774a fix(lcnode): [hybrid cloud] optimize lcnode auditlog #22295845
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 15:59:30 +08:00
zhaochenyang
9aa4aeb2a1 fix(lcnode): [hybrid cloud] optimize conflict rule prefix #22311091
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 15:59:30 +08:00
zhaochenyang
99bf7fe7ac fix(lcnode): [hybrid cloud] optimize lcnodeInfo #22316398 #22326972
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 15:59:30 +08:00
true1064
8f4f2e15d3 feat(master): [hybrid cloud] support assigning mediaType when manually create datapartiton.
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:30 +08:00
true1064
96ab8cc36b feat(master): [hybrid cloud] support cross zone when creating volume
#22332915
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:30 +08:00
NaturalSelect
8f2d18f4f3 chore(all): format code
@formatter:off

Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-12-30 15:59:30 +08:00
NaturalSelect
9542a34b9b feat(master): [hybrid cloud] support query cluster storage class info
close: #22310366
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-12-30 15:59:30 +08:00
NaturalSelect
bd2ee888b4 feat(cli): [hybrid cloud] support query vol hybrid storage info
close: #22304812 #22304800
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-12-30 15:59:30 +08:00
true1064
58f009931e fix(gosdk): [hybrid cloud] modify gosdk to be compatible with hybrid cloud
#22196087
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:30 +08:00
true1064
fc2e9ae9ca feat(meta): [hybrid cloud] check if sortedExtents is the same when inode's storageClass it the same with UpdateExtentKeyAfterMigrationRequest
@formatter:off
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:30 +08:00
true1064
c313af49fd feat(master): [hybrid cloud] check destination datanode's media type must be the same with the source node's when migration and decommission
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:30 +08:00
true1064
f983783222 fix(lcNode): [hybrid cloud] remove multiple registrations for "/metrics"
#22196488
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:29 +08:00
zhaochenyang
6fe0b83b12 fix(lcnode): [hybrid cloud] fix panic in lcmgr scanning
#22277886
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 15:59:29 +08:00
true1064
c75929b42f fix(master): [hybrid cloud] when manually migrate a datanode, target node's mediaType must be the same with the source node's.
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:29 +08:00
true1064
72b45a228f fix(master): [hybrid cloud] when adding a replica, target datanode's mediaType must be the same with the datapartition's mediaType
#22279050
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:29 +08:00
true1064
53104bf568 fix(client): [hybrid cloud] if volume is cold type and has no datapartition, not print warn log in updateDataPartitionByRsp()
#22260631
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:29 +08:00
true1064
6e5f2023d6 fix(sdk): [hybrid cloud] set VolCacheDpStorageClass when invoking NewExtentClient
#22196488
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:29 +08:00
true1064
05f5d6a96b refactor(master): [hybrid cloud] refactor the check of createVolReq
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:29 +08:00
true1064
2659ace92b fix(ci): [hybrid cloud] fix test case TestCreateVolWithDpCount
#22196488
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:29 +08:00
true1064
d5843bda07 fix(cli): [hybrid cloud] show if the node is set as read-only when get node information
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:29 +08:00
true1064
5c77cd185d fix(meta): [hybrid cloud] when create root inode, set its storageClass as the volume's VolstorageClass
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:29 +08:00
true1064
fa9d24e98a fix(master): [hybrid cloud] fix test cases of creating datapartitions
#22196488
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:29 +08:00
true1064
c2b078b6c2 fix(meta): [hybrid cloud] reduce debug log in function (*metaPartition).deleteWorker()
#22171638
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:29 +08:00
true1064
0f68736564 fix(cli): fix the display of the 'metapartition check' command of the cfs-cli tool
clsoe:#22152841
Signed-off-by: tangjingyu <tangjingyu@oppo.com
2024-12-30 15:59:29 +08:00
chihe
7e952f0e09 fix(metanode): [hybrid cloud] operation for CreateInfo should check storage class for client request
close:#22151482

Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 15:59:29 +08:00
zhaochenyang
72b76103d6 feat(lcnode): [hybrid cloud] add lcnode auditlog (#22067931)
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 15:59:29 +08:00
chihe
ffd593c069 fix(metanode): [hybrid cloud] if storage class for inode is already the same with request for UpdateExtentKeyAfterMigration, return nil
close:#22112938

Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 15:59:29 +08:00
true1064
97474a9f73 fix(meta): [hybrid cloud] check err when invoking (*Inode).UnmarshalInodeValue
close:#22112817
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:29 +08:00
baihailong
2bb47082e5 fix(sdk): revert some modify for sdk.#22148104
Signed-off-by: baihailong <baihailong@oppo.com>
2024-12-30 15:59:29 +08:00
baihailong
d6f01b6d6e fix(sdk): CloseStream leadto panic. #22148104
Signed-off-by: baihailong <baihailong@oppo.com>
2024-12-30 15:59:29 +08:00
baihailong
e38505f05d fix(sdk): [hybrid cloud] revert modify for streamer's multi server and fix in other way
Signed-off-by: baihailong <baihailong@oppo.com>
2024-12-30 15:59:29 +08:00
zhaochenyang
0e293b4145 fix(lcnode): [hybrid cloud] 1. fix LcNodeInfoResponse panic; 2. set snapshot idleNodeCh
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 15:59:29 +08:00
zhaochenyang
fafa35c4a8 fix(lcnode): [hybrid cloud] snapshot apply on lcnode
1. add firstDentry may cause panic if channel close
2. reset snapshot verinfos before add verinfo
3. add TaskResults in GetOneTask to avoid adding tasks repeatedly
4. support notify multi snapshot tasks
5. delete snapshot failed retry

Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 15:59:29 +08:00
chihe
f0a1c6545c fix(metanode): [hybrid cloud] when ek is nil but mek is not nil, no replacement occurs.
close:#22037699

Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 15:59:29 +08:00
chihe
e31cfd1998 fix(metanode): [hybrid cloud] reset the flag of inode when migration ek is deleted
close:#21959956

Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 15:59:29 +08:00
baihailong
28dd04623f fix(sdk): [hybrid cloud] waitForFlush blocked leadto deadlock.
Signed-off-by: baihailong <baihailong@oppo.com>
2024-12-30 15:59:29 +08:00
baihailong
2bd66739f6 fix(sdk): [hybrid cloud] add sdk debug log
Signed-off-by: baihailong <baihailong@oppo.com>
2024-12-30 15:59:29 +08:00
true1064
d8ca2169a7 fix(meta): [hybrid cloud] If the inode is a directory, it is not allowed to set migrate extents in the UpdateExtentKeyAfterMigration operation
close:#22076233
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:29 +08:00
true1064
dcddcf32fd fix(meta): [hybrid cloud] if the storageClass of the inode is the same as the requested storageClass in the function UpdateExtentKeyAfterMigration, then return err.
close:#22067659
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:29 +08:00
true1064
04f5b51c73 fix(meta): [hybrid cloud] function TxCreateInode should reply the storageClass of the inode.
close:#22060598
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:29 +08:00
zhaochenyang
ed8783bd06 fix(lcnode): [hybrid cloud] NewEbsClient when ebsAddr is not nil (#22049286)
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 15:59:29 +08:00
baihailong
9f5304e8c8 fix(sdk): [hybrid cloud] one streamer has multi server maybe leadto bad file descriptor.#21989411
Signed-off-by: baihailong <baihailong@oppo.com>
2024-12-30 15:59:29 +08:00
true1064
e835422064 fix(meta): [hybrid cloud] to correctly determine whether the inode should be deleted when unlink
close:#22037078
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:29 +08:00
true1064
62ad8294e5 fix(meta): [hybrid cloud] in function MarshalInodeValue, write length 0 to buff if migrate sortedEks is nil
close:#22031037
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:29 +08:00
true1064
005f4855a5 fix(meta): [hybrid cloud] set migrate storageClass in function fsmInternalDeleteMigrationExtentKey
close:#22029476
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:29 +08:00
chihe
a1aee5c757 fix(client): [hybrid cloud] assign open flags when opening file
close:#21969712

Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 15:59:29 +08:00
chihe
d6daf93a82 fix(client): [hybrid cloud] fix migrate check condition
close:#21969712

Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 15:59:29 +08:00
chihe
576ef2b8d7 fix(client): [hybrid cloud] create ebs reader or writer when storage class is changed
close:#21969712

Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 15:59:29 +08:00
chihe
015a0a40b4 feat(client): [hybrid cloud] if storage class has been changed, do not raise err when reading file
close:#21969712

Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 15:59:29 +08:00
true1064
0214a4fb2a feat(fsck): [hybrid cloud] support scanning migrate-extent garbage
Signed-off-by: true1064 <true1063@163.com>
2024-12-30 15:59:28 +08:00
chihe
1e3c15fb66 bugfix(client): [hybrid cloud] if file is stored in ebs, do not fetch extentkey list from metanode in lookup operation
Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 15:59:28 +08:00
chihe
3cc8f49051 fix(client,metanode): [hybrid cloud] remove duplicated logs for deleteMarkedInodes
Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 15:59:28 +08:00
chihe
b971b5fcf4 fix(metanode,client): [hybrid cloud] if encountering storage class mismatch error, do not retry
Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 15:59:28 +08:00
chihe
c170d8a09e enhance(metanode): [hybrid cloud] remove unnecessary code
Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 15:59:28 +08:00
chihe
b31f8fe011 fix(metanode): [hybrid cloud] Save list element.prev pointer when the element is removed
Signed-off-by: chihe <chi.he@oppo.com>
2024-12-30 15:59:28 +08:00
zhaochenyang
b42fe327dd fix(lcnode): [hybrid cloud] multipart migration to blobstore
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 15:59:28 +08:00
chihe
b9d762045c fix(client): [hybrid cloud] update file node cache if storage class had been changed
Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 15:59:28 +08:00
chihe
f10c805776 feat(metanode): [hybrid cloud] inode marshall support empty file
Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 15:59:28 +08:00
chihe
a629477876 fix(metanode): [hybrid cloud] support empty file migration
Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 15:59:28 +08:00
zhaochenyang
ecf98727d1 fix(lcnode): [hybrid cloud] multipart migration to blobstore
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 15:59:28 +08:00
baijiaruo
811f7910ca fix(data): [hybrid cloud] properly set the param isBackupWrite when invoking ExtentStore.Write()
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:28 +08:00
chihe
8ca429d5fc fix(metanode): [hybrid cloud] modify the logic of checking deferred deletion of migration
extent key

Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 15:59:28 +08:00
zhaochenyang
e36eb02b3c fix(lcnode): [hybrid cloud] set delayDelMinute after migration
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 15:59:28 +08:00
chihe
03d9a65a25 fix(metanode): [hybrid cloud] notify follower to delete migration ek if update migration ek failed
Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 15:59:28 +08:00
chihe
64f2c7b02c fix(metanode): [hybrid cloud] Correct the type conversion error for logCurrentExtentKeys
function

Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 15:59:28 +08:00
zhaochenyang
82f2d95fc9 fix(lcnode): [hybrid cloud] DeleteMigrationExtentKey before migration
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 15:59:28 +08:00
true1064
8c9f486b88 fix(master): [hybrid cloud] set ebsblockSize when add blobStore to volume's allowedStorageClass
Signed-off-by: true1064 <true1063@163.com>
2024-12-30 15:59:28 +08:00
true1064
0fecbe198d fix(master): [hybrid cloud] While creating a volume, if blobStore is in req.allowedStorageClass but req.ebsBlockSize is not assigned, set it to default value.
Signed-off-by: true1064 <true1063@163.com>
2024-12-30 15:59:28 +08:00
chihe
56fb90e9ab feature(metanode): [hybrid cloud] Optimize extentkey logging logic of fsmUpdateExtentKeyAfterMigration function
Signed-off-by: chihe <chi.he@oppo.com>
2024-12-30 15:59:28 +08:00
true1064
03aad1831f feat(master): [hybrid cloud] Support querying the inode count and used size of volumes and metapartitions by storage class.
Signed-off-by: true1064 <true1063@163.com>
2024-12-30 15:59:28 +08:00
chihe
c6067e6c8a feature(metanode): [hybrid cloud] Support for deleting discard migration data
Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 15:59:28 +08:00
chihe
215e9185fe feature(metanode): [hybrid cloud] Delaying deletion of data before migration
Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 15:59:28 +08:00
chihe
7a2410b052 feature(metanode): [hybrid cloud] The deletion of inode support hybridcloud
Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 15:59:28 +08:00
true1064
237171beb0 fix(master): [hybrid cloud] When updating a volume, ebsBlockSize can be modified if allowedStorageClass contains blobstore.
Signed-off-by: true1064 <true1063@163.com>
2024-12-30 15:59:28 +08:00
chihe
41ffaa03f3 bugfix(client): [hybrid cloud] update openforwrite for reused streamer
Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 15:59:28 +08:00
zhaochenyang
f4c0e92cc8 enhance(lcnode): [hybrid cloud] add start time in heartbeat
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 15:59:28 +08:00
chihe
2e798b0437 enhance(client,metanode): [hybrid cloud] add debug log to check opMetaExtentsList value
Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 15:59:28 +08:00
chihe
d853613d65 bugfix(metanode): [hybrid cloud] copy storage class from HybridCouldExtentsMigration of inode when executing migration
Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 15:59:28 +08:00
chihe
910dd648c1 bugfix(metanode): [hybrid cloud] Copy WriteGeneration and ForbiddenMigration when getting inode from btree
Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 15:59:28 +08:00
chihe
4b041c8de3 bugfix(metanode): [hybrid cloud] fix writeGen rollback to 0
Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 15:59:27 +08:00
zhaochenyang
98580f4edf enhance(lcnode): [hybrid cloud] fix lcnode log
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 15:59:27 +08:00
chihe
c4b467074c feature(metanode): [hybrid cloud] add audit log for migration
Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 15:59:27 +08:00
true1064
dd224a1cde fix(client): [hybrid cloud] Fix some places where the original condition "vol allowedStorage supports blobstore"
should be changed to "vol storageClass is blobstore".
Adds field volStorageClass to ExtentClient for this purpose.
Remove field volumeType from ExtentClient by the way.

Signed-off-by: true1064 <tangjingyu@oppo.com>
2024-12-30 15:59:27 +08:00
true1064
e875d2f7e8 feat(meta): [hybrid cloud] provide an API to modify inode's ctime, for debug/test purpose.
Signed-off-by: true1064 <tangjingyu@oppo.com>
2024-12-30 15:59:27 +08:00
zhaochenyang
f48cbe09d5 enhance(lcnode): [hybrid cloud] lifecycle transition
1. add debug service

Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 15:59:27 +08:00
true1064
6eb300e3e5 feat(meta): [hybrid cloud] handle compatibility when old version client send createInode request without field StorageClass
Signed-off-by: true1064 <tangjingyu@oppo.com>
2024-12-30 15:59:27 +08:00
true1064
6559caf259 feat(master): [hybrid cloud] handle compatibility of master when older version upgrade to hybrid cloud:
(1) auto set mediaType of datanode, zone, datapartiton by config legacy "legacyDataMediaType" and persist;
(2) auto set storageClass of volume by config legacy "legacyDataMediaType" and persist;
(3) if need to rollback master to older version, need to do nothing, just replace to old master and restart. because hybrid cloud master only add new fields to meta by not changed old fields.
Signed-off-by: true1064 <tangjingyu@oppo.com>
2024-12-30 15:59:01 +08:00
true1064
a365ed6841 fix(master): [hybrid cloud] if vol.volStorageClass is blobStore, add allowedStorageClass is forbidden.
Signed-off-by: true1064 <tangjingyu@oppo.com>
2024-12-30 15:59:01 +08:00
true1064
6e8317a4b0 fix(master): [hybrid cloud] amend the way judging if the volume has snapshot version, in update allowedStorageClass procedure.
Signed-off-by: true1064 <tangjingyu@oppo.com>
2024-12-30 15:59:01 +08:00
true1064
9d201ba999 bugfix(metanode): [hybrid cloud] assign migration hdd ek in UpdateExtentKeyAfterMigration
Signed-off-by: chihe <chihe@oppo.com>
2024-12-30 15:59:01 +08:00
zhaochenyang
c3fbed5812 enhance(lcnode): [hybrid cloud] lifecycle transition
1. close extent client
2. use StorageClass_Replica_HDD and StorageClass_BlobStore
3. scanner init AllowedStorageClass
4. enhance lifecycle parameter check
5. add extent client for write

Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 15:59:01 +08:00
chihe
9ca94340f1 enhance(metanode): [hybrid cloud]1.Simplify the logic of updating extent key after migration
2. reset inode reserved when excute marshall operation
3. initial ebs client in mp
4. remove cold vol or hot vol check in client and objectnode

Signed-off-by: chihe <chi.he@oppo.com>
2024-12-30 15:59:00 +08:00
chihe
d4fbfd19a4 bugfix(metanode): [hybrid cloud] fix bug when update extent key after migration
Signed-off-by: chihe <chi.he@oppo.com>
2024-12-30 15:59:00 +08:00
chihe
e018991d9b enhance(metanode): [hybrid cloud] add api for get full inode infomation include eks
Signed-off-by: chihe <chi.he@oppo.com>
2024-12-30 15:59:00 +08:00
chihe
c65774f7bd bugfix(metanode):[hybrid cloud] check sortedEks is nil when excute fsmAppendExtentsWithCheck
Signed-off-by: chihe <chi.he@oppo.com>
2024-12-30 15:59:00 +08:00
chihe
e3c1a3dbc6 enhance(client): [hybrid cloud] only file opened with write request needs to forbidden migration
Signed-off-by: chihe <chi.he@oppo.com>
2024-12-30 15:59:00 +08:00
zhaochenyang
f7f709afa1 feat(lcnode): [hybrid cloud] lifecycle transition
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-12-30 15:59:00 +08:00
chihe
c4ee3f7e05 feature(metanode,client):[hybrid cloud] support hybridcloud data migration
Signed-off-by: chihe <chi.he@oppo.com>
2024-12-30 15:59:00 +08:00
true1064
d0c840b50a feat(master): [hybrid cloud] forbidden AdminCreateVersion API because hybrid cloud not support snapshot version yet
Signed-off-by: true1064 <tangjingyu@oppo.com>
2024-12-30 15:59:00 +08:00
true1064
c0288be9ea feat(master):[hybrid cloud] make the snapshot version and multiple allowedStorageClass mutually exclusive in a volume.
Because now multiple allowedStorageClass can not support snapshot version.

Signed-off-by: true1064 <tangjingyu@oppo.com>
2024-12-30 15:59:00 +08:00
true1064
126ce01612 feat(master, cli): [hybrid cloud] add API to add storageClass to volume's allowedStorageClass list
Signed-off-by: true1064 <tangjingyu@oppo.com>
2024-12-30 15:59:00 +08:00
true1064
3e8770796c fix(master): [hybrid cloud] in create vol procedure, update datapartition view cache right after the creation of datapartitions
Signed-off-by: true1064 <tangjingyu@oppo.com>
2024-12-30 15:59:00 +08:00
true1064
28a7c80f6c fix(master): [hybrid cloud] amend some logs
Signed-off-by: true1064 <tangjingyu@oppo.com>
2024-12-30 15:59:00 +08:00
chihe
cad4867c65 bugfix(client): [hybrid cloud] only clear dp when volume storage class is blobstore
Signed-off-by: chihe <chi.he@oppo.com>
2024-12-30 15:59:00 +08:00
true1064
07d763808a fix(master): [hybrid cloud] In create volume procedure, sort allowedStorageClass after check if append volStorageClass info it.
Signed-off-by: true1064 <tangjingyu@oppo.com>
2024-12-30 15:59:00 +08:00
true1064
0c1d715162 fix(master): [hybrid cloud] when updateVol, check that req.VolStorageClass should be in vol.allowedStorageClass
Signed-off-by: true1064 <tangjingyu@oppo.com>
2024-12-30 15:59:00 +08:00
true1064
0594f19ddb fix(master): [hybrid cloud] check if the cluster has resource to support req.VolStorageClass when create vol.
Signed-off-by: true1064 <tangjingyu@oppo.com>
2024-12-30 15:59:00 +08:00
true1064
fe5dd281a7 fix(master):[hybrid cloud] some fix about datapartition creation:
(1) while creating dp in vol creation, check dp count by mediaType to judge if success.
(2) when background periodical check if need to create dp, skip volumes whose createTime is too recently, to avoid background create dp while volume is creating.
(3) display mediaType of dp in cfs-cli cmd "volume info -d"

Signed-off-by: true1064 <tangjingyu@oppo.com>
2024-12-30 15:59:00 +08:00
true1064
7b7fb214c3 feat(master): [hybrid cloud] sort AllowedStorageClass[] while creating volume
Signed-off-by: true1064 <tangjingyu@oppo.com>
2024-12-30 15:59:00 +08:00
chihe
e1cb86abf1 bugfix(metanode): [hybrid cloud] fix log panic in function of deleteMarkedEBSInodes
Signed-off-by: chihe <chi.he@oppo.com>
2024-12-30 15:59:00 +08:00
true1064
be565fe8e7 fix(master): [hybrid cloud] not set zoneName as default value if not assigned in the create vol request.
Signed-off-by: true1064 <tangjingyu@oppo.com>
2024-12-30 15:59:00 +08:00
chihe
8ec66005d4 bugfix(client): [hybrid cloud] fix bug about checking media type of dp
Signed-off-by: chihe <chi.he@oppo.com>
2024-12-30 15:59:00 +08:00
true1064
369b0dea7c fix(master): [hybrid cloud] not use var pointer dataNode if not got it from cluster, to avoid panic
Signed-off-by: true1064 <tangjingyu@oppo.com>
2024-12-30 15:59:00 +08:00
true1064
ab81ddc134 feat(master, cli): [hybrid cloud] vol.volStorageClass can be changed by "cfs-cli volume update"
Signed-off-by: true1064 <tangjingyu@oppo.com>
2024-12-30 15:59:00 +08:00
chihe
61ae29445f enhance(client,metanode): [hybrid cloud] check inode/vol storage class when checking vol type
Signed-off-by: chihe <chi.he@oppo.com>
2024-12-30 15:59:00 +08:00
true1064
17bc5fcfb3 feat(master,client,preload): [hybrid cloud] add property CacheDpStorageClass to volume.
CacheDpStorageClass is used to inform SDK which storageClass to use when access cache dp.

Signed-off-by: true1064 <tangjingyu@oppo.com>
2024-12-30 15:59:00 +08:00
true1064
a55c501725 feat(master): [hybrid cloud] volume supports multi mediaType based on the refactored codes from v3.4.0
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:00 +08:00
true1064
8331987489 feat(master, client, cli): [hybrid cloud] add property mediaType to datapartition
(1)master: when creating vol, master creates dps of each mediaType according to vol.allowedStorageClass
(2)client: when select dp to do append write, choose dp with spcific mediaType according to inode's storageClass

Signed-off-by: true1064 <tangjingyu@oppo.com>
2024-12-30 15:59:00 +08:00
true1064
b94bb0b6f2 feat(master, client, cli): [hybrid cloud] add property volStorageClass and allowedStorageClass to volume
now volType is not uesed while creating volume, but is maintained for compability
Signed-off-by: true1064 <tangjingyu@oppo.com>
2024-12-30 15:59:00 +08:00
true1064
e65280a381 feat(master): [hybrid cloud] add mediaType property to zone
(1) zone mediaType is set as the first add-in datanode's mediaType
(2) once zone mediaType is set, all datanodes in the zone must be the same mediaType.
(3) the cmd 'cfs-cli zone info' shows zone mediaType.
Signed-off-by: true1064 <tangjingyu@oppo.com>
2024-12-30 15:59:00 +08:00
chihe
3ddfc7fd04 feature(client,metanode):[hybrid cloud] support change inode storageclass when updating ek
Signed-off-by: chihe <chi.he@oppo.com>
2024-12-30 15:59:00 +08:00
chihe
9d85683cdf feature(client): [hybrid cloud] supports the option to choose SSD or HDD data partition
Signed-off-by: chihe <chi.he@oppo.com>
2024-12-30 15:59:00 +08:00
chihe
6edde6d574 feature(client,metanode):[hybrid cloud] support renewal inode forbidden migration
Signed-off-by: chihe <chi.he@oppo.com>
2024-12-30 15:59:00 +08:00
chihe
e643ac8713 feature(metanod,client): [hybrid cloud] 1. inode supprot hybrid-clound 2.client read/write support hybrid-clound
Signed-off-by: chihe <chi.he@oppo.com>
2024-12-30 15:59:00 +08:00
true1064
e0148a506c feat(master/data/cli): [hybrid cloud] add datanode's mediaType for module master and datanode; the cmd 'cfs-cli datanode info' can show datanode's mediaType
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-12-30 15:59:00 +08:00
Victor1319
8f6866a586 oppo self commit
#22179575
Signed-off-by: Victor1319 <834863182@qq.com>
2024-12-30 15:56:47 +08:00
chihe
01560a0e91 fix(master): canAllocDp compatible with 3.4.0 before
Signed-off-by: chihe <chihe@oppo.com>
2024-11-22 14:09:59 +08:00
chihe
7c39bdd987 fix(metanode): InodeOnce marshall compatible with meta 3.3.2
Signed-off-by: chihe <chihe@oppo.com>
2024-11-22 14:09:59 +08:00
baihailong
12b4359dee fix(client): enableBcache is invalid even if it was specified.
Signed-off-by: baihailong <baihailong@oppo.com>
2024-11-22 14:09:59 +08:00
leonrayang
9581a8b037 feat(metanode): Optimize quota infomation storage to reduce usage of memory
Signed-off-by: leonrayang <chl696@sina.com>
2024-11-22 14:09:59 +08:00
leonrayang
e7c26c8ff4 feat(metanode): Optimize memory usage of metanode for multi snapshit
Signed-off-by: leonrayang <chl696@sina.com>
2024-11-22 14:09:59 +08:00
chihe
1ef6290388 fix(client): goroutine for trash to build parent dir would not quit
Signed-off-by: chihe <chihe@oppo.com>
2024-11-22 14:09:59 +08:00
Victor1319
22f6380e35 refactor(client): if subdir is empty from cfg file, use cfg from option.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-11-22 14:09:59 +08:00
chihe
84fa5c665a fix(doc): fix path for png in security practice
Signed-off-by: chihe <chihe@oppo.com>
2024-11-11 20:13:12 +08:00
chihe
6afdcdc327 fix(doc): fix path for cfs-arch-ec.png
Signed-off-by: chihe <chihe@oppo.com>
2024-11-11 17:36:16 +08:00
chihe
de5b75c6cc fix(master): It is essential to check whether meta-restore need to be executed.
Signed-off-by: chihe <chihe@oppo.com>
2024-11-11 17:12:24 +08:00
chihe
e34acd0d61 fix(doc): modify the path for object-system-structure.png
Signed-off-by: chihe <chihe@oppo.com>
2024-11-11 17:12:24 +08:00
chihe
b2ece23ec7 fix(doc): modify the path for object-system-structure.png
Signed-off-by: chihe <chihe@oppo.com>
2024-11-06 14:22:40 +08:00
chihe
ee346d86b6 feat(doc): update doc for v3.4.0
Signed-off-by: chihe <chihe@oppo.com>
2024-10-31 11:01:58 +08:00
chihe
de8867da5d feat(doc): update changelog for v3.4.0
Signed-off-by: chihe <chihe@oppo.com>
(cherry picked from commit cdfc7677841de0887e80d09d35867a62e9ddd549)
2024-10-31 11:01:58 +08:00
leonrayang
eda0d2492b feat(doc): Add rule for Peripheral Projects and describe the rule of joining and achriving
Signed-off-by: leonrayang <chl696@sina.com>
(cherry picked from commit 206d5ddadf)
2024-10-31 11:01:58 +08:00
slasher
ea3478ad30 docs(access): fix limit of access upload and download
Signed-off-by: slasher <shenjie1@oppo.com>

(cherry picked from commit d18001a133)
2024-10-31 11:01:58 +08:00
Reliey
845be7229b docs(docs): Modify the details of the documentation in the dev-guide section
Signed-off-by: Reliey <616318745@qq.com>

(cherry picked from commit a28ac40474)
2024-10-31 11:01:58 +08:00
Reliey
48aa6dff12 docs(docs): Modify the layout and text description of security_practice.md
Signed-off-by: Reliey <616318745@qq.com>

(cherry picked from commit db8c58074a)
2024-10-31 11:01:58 +08:00
Reliey
374fa68b2d docs(docs): Modifie the log.md and config.md in the ops chapter to be easier to understand
Signed-off-by: Reliey <616318745@qq.com>

(cherry picked from commit 64e7afaba2)
2024-10-31 11:01:58 +08:00
Reliey
a9eb837acc docs(docs): Revise capacity.md and zone.md to be easier to understand
Signed-off-by: Reliey <616318745@qq.com>

(cherry picked from commit 09806ae0b2)
2024-10-31 11:01:58 +08:00
mingwei
f3a3764999 docs(ops): add auto-ops docs
Signed-off-by: mingwei <gongmingwei@oppo.com>

(cherry picked from commit cdbbfa5656)
2024-10-31 11:01:58 +08:00
Reliey
32e7d7fc97 docs(docs): Add some details and problem solutions to cluster-deploy documentation
Add some details to cluster-deploy.md

Signed-off-by: Reliey <616318745@qq.com>
(cherry picked from commit 13b92e50d7)
2024-10-31 11:01:58 +08:00
Reliey
8c169a3d08 docs(docs): Modify the description of the trash document to be more accurate and add some details
Signed-off-by: Reliey <616318745@qq.com>

(cherry picked from commit b8815f9423)
2024-10-31 11:01:58 +08:00
Reliey
72940d10ed docs(docs): Correct inaccuracies and typos in feature-cache documentation
commit message

Signed-off-by: Reliey <616318745@qq.com>

(cherry picked from commit 3923fbe5f5)
2024-10-31 11:01:58 +08:00
leonrayang
9ee5a193fa feat(doc): Update the information of the maintainer members
Signed-off-by: leonrayang <chl696@sina.com>
(cherry picked from commit 6e8066dcd5)
2024-10-31 11:01:58 +08:00
Reliey
22aed18ada docs(docs): correct the inaccuracies and typos in the module design section
commit message

signed-off-by: Reliey <616318745@qq.com>

(cherry picked from commit 208451d4c6)
2024-10-31 11:01:58 +08:00
leonrayang
6ac2dc1275 feat(doc): Update governance documentation to improve the accuracy of vendor-neutrality
Signed-off-by: leonrayang <chl696@sina.com>
(cherry picked from commit 4dde46d30b)
2024-10-31 11:01:58 +08:00
Reliey
200ffbaabd feat(doc): Add more details on the single deployment for better governance
commit message

signed-off-by: Reliey <616318745@qq.com>

(cherry picked from commit 0b83e56790)
2024-10-31 11:01:58 +08:00
Reliey
59161dea1e feat(doc): Add instructions for yum deployment under arm architecture
commit message

Signed-off-by: Reliey <616318745@qq.com>

(cherry picked from commit 65bd5eb51f)
2024-10-31 11:01:58 +08:00
leonrayang
dbc37b4fbe feat(doc): Update OpenSSF Best Practices to silver badge
Signed-off-by: leonrayang <chl696@sina.com>
(cherry picked from commit dbeab8ddbb)
2024-10-31 11:01:58 +08:00
leonrayang
ab6f2b8262 feat(doc): Add CODEOWNERS to improve control over code merge approval priorities
Signed-off-by: leonrayang <chl696@sina.com>
(cherry picked from commit e053fa3f07)
2024-10-31 11:01:58 +08:00
chihe
af5fcf4cec enhance(doc): Organize documents of ecology
Signed-off-by: chihe <chihe@oppo.com>
2024-10-31 11:01:58 +08:00
leonrayang
b8136d3730 feat(doc): Update OWNERS.md
Signed-off-by: leonrayang <changliang@oppo.com>
2024-10-31 11:01:58 +08:00
chihe
0d64969074 feat(docs): Update Governance related documentation
1. Clarify the responsibilities of the TSC, maintainers, and committers.
2. Fix the issue in security reporting as incorrect email address for reporting vulnerabilities.
3. Clarify the governace of SIGs
4. Add a link to the governance section in README.md for emphasis.

Signed-off-by: chihe <chihe@oppo.com>
2024-10-31 11:01:58 +08:00
leonrayang
35f2cbdfa2 feat(doc): Add TestCase Guidelines for contributors
Signed-off-by: leonrayang <chl696@sina.com>
2024-10-31 11:01:58 +08:00
leonrayang
997014bce3 feat(doc): Add CubeFS-self-assessment.md
Signed-off-by: leonrayang <chl696@sina.com>
(cherry picked from commit 83d122e6dc)
2024-10-31 11:01:58 +08:00
leonrayang
766fa89262 feat(doc): Add more rules on the sub project for better governance
Signed-off-by: leonrayang <chl696@sina.com>
(cherry picked from commit 3576d88889)
2024-10-31 11:01:58 +08:00
leonrayang
c938ec2d58 feat(doc): Update some maintainer's Affiliation information
Signed-off-by: leonrayang <chl696@sina.com>
2024-10-31 11:01:58 +08:00
leonrayang
5d7c5ffed0 feat(doc): Update the governance documentation
1.Clearify the promotion and exit rules for committers.
2.Add the Expectations for maintainer and committers
3.Clearify the core maintainers

Signed-off-by: leonrayang <chl696@sina.com>
2024-10-31 11:01:58 +08:00
leonrayang
c3479b2cbb feat(doc): Update the release documentation and add docker image info
Signed-off-by: leonrayang <chl696@sina.com>
2024-10-31 11:01:58 +08:00
chihe
78eadfb5e4 fix(master): check quotaManager for vol when executing ListQuota
Signed-off-by: chihe <chihe@oppo.com>
2024-10-29 11:22:59 +08:00
Victor1319
cd22976e7f fix(metanode): delete extent_del file when reach end of the file.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-29 11:22:59 +08:00
Victor1319
a5b8e64719 fix(meta): reset reserved flag when marshal inode.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-29 11:22:59 +08:00
Victor1319
88fb77be4f fix(meta): consider snapshot ver when unmarshal extents
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-29 11:22:59 +08:00
Victor1319
2edac6569b fix(meta): inode unmarshal logic is compatible with subsequent versions.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-29 11:22:59 +08:00
Victor1319
7bdefc9325 fix(meta): check error when unmarshal inode value.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-29 11:22:59 +08:00
leonrayang
11c9d0f51d fix(docker): Update docker-compose to docker compose to adapt to github unbuntu env change
Signed-off-by: leonrayang <chl696@sina.com>
(cherry picked from commit cde492bba6)
2024-10-18 17:05:25 +08:00
chihe
b145724909 fix(metanode): Do not update Accesstime when excuting getInodeTopLayer
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 17:05:25 +08:00
chihe
4c7ad60549 fix(metanod): if clusterEnableSnapshot is not enbale, do not apply VersionOp
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 17:05:25 +08:00
chihe
67f9a425ab fix(datanode): check initPartitionSize when calculating progress
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:15 +08:00
Victor1319
865dbdfa61 refactor(raft): use decimal format to print number in raft log.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:38:15 +08:00
Victor1319
3e43d5020b refactor(raft): use decimal format to print number in raft log.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:38:15 +08:00
cuishuang
587183f09a fix: fix slice init length
Signed-off-by: cuishuang <imcusg@gmail.com>
2024-10-18 09:38:15 +08:00
leonrayang
28d1e06beb feat(metanode): Enable metanode with version 3.4.0 compatible with master 3.3.2
Signed-off-by: leonrayang <chl696@sina.com>
2024-10-18 09:38:15 +08:00
leonrayang
91c82f6578 fix(client): Enable Datanode with version 3.3.2 compatible with client 3.4.0
Signed-off-by: leonrayang <chl696@sina.com>
2024-10-18 09:38:15 +08:00
leonrayang
8046974d73 fix(client): Enable Client with version 3.4.0 compatible with 3.3.2
Signed-off-by: leonrayang <chl696@sina.com>
2024-10-18 09:38:14 +08:00
chihe
2c3743b634 fix(master):Compatible with 3.3.2 metanode(used for memory)
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:14 +08:00
chihe
cbb3b1aa26 fix(master): only check offline with old version datanode
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:14 +08:00
chihe
12c69848b0 fix(datanode): If dpBackupTimeout is not set, then do not delete back up directories.
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:14 +08:00
leonrayang
bca680f460 feat(datanode&metanode): Cluster switch on snapshot take effect on module handle protocal process
Signed-off-by: leonrayang <chl696@sina.com>
2024-10-18 09:38:14 +08:00
leonrayang
e471798855 feat(metanode): Enable rollback from 3.4.0 to 3.3.2
1) Add protocal opFSMSentToChanWithVer, enable compatible during update version because []proto.ExtentKey has no version info
2) Update inode marshal and unmarshal process, do not persist version 3 flag if not eable snapshot

Signed-off-by: leonrayang <chl696@sina.com>
2024-10-18 09:38:13 +08:00
baihailong
b03501cc6f fix(client): failure to mount other directories when the mount point is abnormal.
Signed-off-by: baihailong <baihailong@oppo.com>
2024-10-18 09:38:13 +08:00
baihailong
f73eb824d8 fix(client): alarm the mount point has been mounted incorrectly.
Signed-off-by: baihailong <baihailong@oppo.com>
2024-10-18 09:38:13 +08:00
leonrayang
39b2d22679 fix(master): Set the cluster's stop channel to nil upon receiving the signal to prevent it from running again
Signed-off-by: leonrayang <chl696@sina.com>
2024-10-18 09:38:13 +08:00
chihe
1594864cb1 fix(datanode):Iterating through the cache of dps and disks when stopping space manager
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:13 +08:00
leonrayang
e799a1307f fix(master): Some goroutines are stateless and do not require waiting for their transfer to a stopped state
Signed-off-by: leonrayang <chl696@sina.com>
2024-10-18 09:38:13 +08:00
leonrayang
a93a6a045c fix(master): Enable master exit process more ellgantly
Signed-off-by: leonrayang <chl696@sina.com>
2024-10-18 09:38:13 +08:00
chihe
7a8d7f15e1 fix(client): When creating extent failed for disk full, remove dp from selector
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:12 +08:00
chihe
de9a90bc85 fix(datanode): Launch sechedule for raft log before staring raft when loading dp
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:12 +08:00
chihe
469e8048ca fix(master):If the replica being decommissioned is the one intended for decommissioning, no error is reported.
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:12 +08:00
chihe
3b900c9304 fix(raft): If the previous member change did not return, reject the submission of the next member change
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:12 +08:00
chihe
4d592f213f fix(master): delete decommission disk from rocksdb when datanode is decommissioned
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:12 +08:00
Victor1319
7f43f724ec test(data): add testcase for datanode op log when disk is full.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:38:12 +08:00
leonrayang
a5214f0478 fix(client): Client's extents cache should not be forcefully updated that may ignore the ek on flight in some scenarios
Signed-off-by: leonrayang <chl696@sina.com>
2024-10-18 09:38:11 +08:00
baihailong
ed081f205d fix(sdk): client update extents from meta and drop extents in cache leadto ek conflict.
Signed-off-by: baihailong <baihailong@oppo.com>
2024-10-18 09:38:11 +08:00
chihe
74ebfad902 fix(datanode): If reading applyid fails when loading dp, then delete from disk cache
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:11 +08:00
chihe
f9c3c2b509 fix(datanode): use lock for getPartitionsAPI
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:11 +08:00
chihe
cddfba1bc9 fix(master):release token before resetting decommission dst
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:11 +08:00
chihe
a84868aeec fix(master): If the decommission fails and requires retry, set the status to 'mark' only at the end of the decommission function
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:11 +08:00
chihe
7b34b6d754 feat(datanode):Increasing retry interval when DataNode connects to Master during startup.
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:10 +08:00
leonrayang
9f6e415350 feat(master): Improve the exit process in multiple goroutines to ensure proper resource cleanup and graceful shutdown.
Signed-off-by: leonrayang <chl696@sina.com>
2024-10-18 09:38:10 +08:00
chihe
ca9e9b57cd fix(mater):delete decommission disk record form rocksdb
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:10 +08:00
chihe
68b4335405 fix(master): deadlock for datanode and nodeset
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:10 +08:00
chihe
6572d578f7 fix(master):Exiting the decommmission goroutine when leader is changed
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:10 +08:00
baihailong
1d759b5325 fix(client): check whether the mountpoint has been mounted when mounting.
Signed-off-by: baihailong <baihailong@oppo.com>
2024-10-18 09:38:09 +08:00
chihe
2a64f3f997 fix(master): fix deadlock for deleteMissingDp
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:09 +08:00
chihe
4592d418c0 fix(data): donot persist applyId by raftForce
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:09 +08:00
chihe
51f9f93d86 fix(master):The dp's zone selection logic supports decommission operations.
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:09 +08:00
chihe
e57e66bec8 fix(master): When setting a RestoreReplica operation to fail, check if another replica is currently undergoing the offline process.
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:09 +08:00
chihe
7e45916a4e fix(master):use lock to set dp markDecommission
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:09 +08:00
leonrayang
729e53e7ac feat(datanode): Enhance the preformace of the stat for disk opertion
Signed-off-by: leonrayang <chl696@sina.com>
2024-10-18 09:38:09 +08:00
W9068822
47e4e81358 feat(stat): datanode disk and dp operation log
Signed-off-by: W9068822 <v-lijianrong1@oppo.com>
2024-10-18 09:38:08 +08:00
leonrayang
ae97b8943e feat(client&master): Enable clients to report their version and flow information to the master
Signed-off-by: leonrayang <chl696@sina.com>
2024-10-18 09:38:08 +08:00
leonrayang
c23b847714 Clean the libsdk.so file, which is the output of the compilation.
Signed-off-by: leonrayang <chl696@sina.com>
2024-10-18 09:38:08 +08:00
leonrayang
1fe820283f feat(client): Clean target class of java interface
Signed-off-by: leonrayang <chl696@sina.com>
2024-10-18 09:38:08 +08:00
chihe
c96c750262 fix(master):Traverse all nodesets to check if dp is decommissioning
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:08 +08:00
chihe
92d38b5152 fix(master): do not check size of datanode when retrying to decommission special replica
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:08 +08:00
chihe
49fa10d1f4 fix(cli): remove unnecessary commands for cli
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:07 +08:00
baihailong
dba21bbb4b fix(sdk): client update extents from meta and drop extents in cache leadto ek conflict.
Signed-off-by: baihailong <baihailong@oppo.com>
2024-10-18 09:38:07 +08:00
Victor1319
c09ef0d94d fix(datanode): use dp leader real size as to be repaired size.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:38:07 +08:00
chihe
7e3ff6ca77 fix(datanode): return error if receive OpDeleteDataPartition when disks are not loaded
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:07 +08:00
chihe
d373350345 fix(sdk): return err if getting extents failed in function ExtentClient
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:06 +08:00
leonrayang
993844ae85 feat(master): Enhance the effectiveness of log output
Signed-off-by: leonrayang <chl696@sina.com>
2024-10-18 09:38:06 +08:00
leonrayang
87c29a8fca fix(master): Use the largest volume ID in load vols process
Signed-off-by: leonrayang <chl696@sina.com>
2024-10-18 09:38:06 +08:00
chihe
8651e849a5 fix(datanode): modifying the regular expression for backup directory:
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:06 +08:00
chihe
4a1bcde6ea fix(master): fix deadlock for getDataPartitionsView
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:06 +08:00
chihe
9d64cd3db4 feat(cli): support raftForce for datanode decommission by cfs-cli
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:05 +08:00
Victor1319
ed3663f7d0 refactor(all): resolve conflict when pick commit from 3.3.2 to 3.4.0.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:38:05 +08:00
Victor1319
8add9cfd31 fix(data): check response reqId to avoid data painc.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:38:05 +08:00
Victor1319
96d65045c9 fix(master): reset bad dp report info after dp recover.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:38:05 +08:00
Victor1319
453202e389 refactor(master): report datanode count which can alloc dp.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:38:05 +08:00
Victor1319
e4b45d83aa refactor(master): add replica num label when report dp no leader metrics.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:38:04 +08:00
Victor1319
205db02303 feat(master): add vol total count metric info.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:38:04 +08:00
Victor1319
34d6e4a19e fix(data): fix delete gc data failed bug.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:38:04 +08:00
Victor1319
e8912c39c8 fix(metanode): fix tx bug in concurrent use case.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:38:04 +08:00
Victor1319
ca1a57a141 refactor(master): reduce dp&mp detail report info. ,
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:38:04 +08:00
Victor1319
05bc990c83 refactor(master): reduce prometheus metrics count for master.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:38:04 +08:00
chihe
f6863ca033 fix(metanode): only sync at among replicas when inode is not nil
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:03 +08:00
chihe
fb4f2ee762 fix(cli): use macro value instead of hard code
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:03 +08:00
Victor1319
22434af2cd fix(master): fix the bug that reset transaction info when update vol info.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:38:03 +08:00
Victor1319
0e24e23cc0 fix(meta): fix panic bug when delete empty dentry info. ,
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:38:02 +08:00
Victor1319
debba855ad refactor(cli): refactor cli hint message when update transaction info.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:38:02 +08:00
Victor1319
01343671d8 fix(master): return no leader for request before master change leader success.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:38:02 +08:00
Victor1319
6f65314ee4 fix(master): fix http pool test case fail bug.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:38:02 +08:00
chihe
0ae60f0d02 feat(master): cli support setting for persistting accessTime
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:02 +08:00
chihe
ad2fee1e2d fix(master):persist AccessTimeInterval and EnablePersistAccessTime
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:01 +08:00
chihe
ce2fbe8829 feat(master):support set trashInterval with cli
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:01 +08:00
Victor1319
f235a6de05 refactor(server): refactor http connect pool when comminute with master. ,
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:38:01 +08:00
leonrayang
da344a7d74 feat(client): Reduce the massive getVol request from Quota routine
Signed-off-by: leonrayang <chl696@sina.com>
2024-10-18 09:38:01 +08:00
chihe
281cb64a38 feat(master): api for enable/disbale persist for accesstime
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:01 +08:00
chihe
eb14e94355 feat(master): api for setting accesstime vaild interval by vol
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:00 +08:00
chihe
2ca0ae27ec feat(metanode): add sdk for querying accesstime of inode
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:00 +08:00
chihe
114b1c0cba feat(metanode):persist accesstime by raft
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:38:00 +08:00
leonrayang
3ccbe392a4 fix(master): Simplify the process of FolloweRead jugement and resolve issues
Signed-off-by: leonrayang <chl696@sina.com>
2024-10-18 09:38:00 +08:00
Victor1319
4aa22112e5 fix(master): fix some potential deadlock issues.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:38:00 +08:00
leonrayang
68d6fd7229 feat(master): Ease the pressure caused by volumeView cache synchronization
Signed-off-by: leonrayang <chl696@sina.com>
2024-10-18 09:37:59 +08:00
Victor1319
7b8641d719 fix(meta): fix delete dentry conflict with tx bug.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:59 +08:00
chihe
d348b0304a fix(datanode): check if host[0] exist when creating repair task
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:59 +08:00
chihe
4b6476cf77 fix(datanode): stop raft if replica is deleted when StartRaftAfterRepair is finished
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:59 +08:00
chihe
1da88a7983 fix(master): remove replica on decommission dst by force when rolling back
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:59 +08:00
chihe
147cd795a1 fix(master): get disk path for replica when getting info for datanode
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:59 +08:00
chihe
2e98ea1c4a fix(datanode): use localServerAddr instead of replica[0]
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:58 +08:00
chihe
854b4ccd11 fix(master):reset IgnoreDecommissionDps when marking disk decommission
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:58 +08:00
shuqiang-zheng
36c87e9342 enhance(cli): update instructions for freezing clusters.
@formatter:off
Signed-off-by: shuqiang-zheng <zhengshuqiang@oppo.com>
2024-10-18 09:37:58 +08:00
leonrayang
a1f10afed0 feat(project-structure): Reorganize the module directory in the root to a suitable subdirectory
Signed-off-by: leonrayang <chl696@sina.com>
2024-10-18 09:37:58 +08:00
true1064
e57433c888 fix(raft): set no leader for the application layer when raft stopped by panic.
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-10-18 09:37:57 +08:00
chihe
33309627db fix(master): reset RestoreReplica when resetting decommission status
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:57 +08:00
chihe
682522e67a fix(master):fix dead lock by getting copy of dp map
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:57 +08:00
chihe
0faeb9c32f fix(master): donot reset decommission status for datanode
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:56 +08:00
chihe
724da33e7a feat(master):add a new schedule task for checking meta for replica meta
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:56 +08:00
chihe
6a3eeef0ed fix(master): remove redundate replica from partition.replicas on master
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:56 +08:00
chihe
85a68ab4c2 feat(master): auto delete backup directories
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:56 +08:00
tangdeyi
8ebed45e1b fix(objectnode): separate read and write buffers
with

Signed-off-by: tangdeyi <tangdeyi@oppo.com>
2024-10-18 09:37:55 +08:00
chihe
34b78f26be fix(datanode): delete backup directories by async
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:55 +08:00
chihe
a0e58714aa fix(master): do not persist disk status when querying progress
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:55 +08:00
true1064
4d033bd644 fix(data): correctly determine the extent type in function handleBatchMarkDeletePacket
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-10-18 09:37:55 +08:00
leonrayang
afadbe8a34 fix(util): Improve the code style according to warnings from code check 2024-10-18 09:37:55 +08:00
chihe
53b46c414e fix(master): return timeout error for setting restore replica status
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:54 +08:00
chihe
0113fe981d feat(master): api for removing backup directories on disk
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:54 +08:00
chihe
6b61f011cf fix(datanode):reduce the locking time when building heart beat
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:53 +08:00
chihe
ab24e0e6d2 fix(master): only show running and failed decommisison disk when executing queryAllDecommissionDisk
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:53 +08:00
leonrayang
7ed6fb6224 fix(datanode): Write process skip check between offset and dataSize while do repair
Signed-off-by: leonrayang <chl696@sina.com>
2024-10-18 09:37:53 +08:00
leonrayang
6d349761e6 feat(master): Add the switch for function of volume snapshot management
Signed-off-by: leonrayang <chl696@sina.com>
2024-10-18 09:37:53 +08:00
leonrayang
35b44f9cfb fix(master): Simplify the process of FolloweRead jugement and resolve issues
Signed-off-by: leonrayang <chl696@sina.com>
2024-10-18 09:37:53 +08:00
leonrayang
d79ae21bdf feat(master): Ease the pressure caused by volumeView cache synchronization
Signed-off-by: leonrayang <chl696@sina.com>
2024-10-18 09:37:52 +08:00
chihe
6ea80cc8c2 fix(datanode):to accelerate the traversal of dp by goroutines
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:52 +08:00
chihe
816abe3df8 feat(master): add disk info when executing queryDecommissionToken
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:52 +08:00
chihe
f1b87468b8 fix(master): special replica dp with raftForce hungs in checking status of new replica
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:52 +08:00
chihe
4c801f337f fix(datanode): remove extent from extentInfoMap if extent is deleted
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:52 +08:00
chihe
012edf947f feat(master): save previous decommission error msg when decommissioning another replica
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:52 +08:00
chihe
b07f678351 fix(datanode): remove dp 0 from disk error set when recovering disk err
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:51 +08:00
chihe
2681144e6c fix(master): enable disk when excuting datnode cancel decommission
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:51 +08:00
chihe
ddb41ce3a4 fix(master): when rolling back special replica dp, only need to delete decommission src if new replica is recovered
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:51 +08:00
baihailong
f45cf4a1ed fix(fuse): if vol name not match regexp when mount, return failed immediately.
Signed-off-by: baihailong <baihailong@oppo.com>
2024-10-18 09:37:51 +08:00
chihe
1986293fce fix(master):initialize reportTime for datanode when leader changed
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:51 +08:00
shuqiang-zheng
31b37ea52e fix(datanode): Remove the gc operation to reduce the time required to create a dp.
Signed-off-by: shuqiang-zheng <zhengshuqiang@oppo.com>
2024-10-18 09:37:50 +08:00
chihe
ca62a6e2a3 fix(master): show ignore decommission dp when displaying decommission progress for dataNode
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:49 +08:00
chihe
2e30c06933 fix(master): do not delete decommission disk from list when recover task is submitted
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:49 +08:00
chihe
78f0420e08 fix(master): optimize the calculation of datanode decommission progress
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:49 +08:00
chihe
a271d0bb2e fix(datanode):reload dp by parallelism during recovering bad disk
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:48 +08:00
chihe
ee9022c444 fix(datanode): only print root path in log
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:48 +08:00
chihe
4f7c928772 fix(datanode):optimize the process of constructing heartbeat packets.
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:48 +08:00
chihe
9fc517335e fix(master): modify logic of updateDecommissionStatus for datanode
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:48 +08:00
chihe
b65c113de0 feat(master):add requestID for sending admin task
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:48 +08:00
Victor1319
6cb95a47bf fix(metanode): fix the issue that can't clear data when delete inode.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:47 +08:00
baihailong
9992a91f4f fix(client): if vol not exists, mount return failed immediately.
Signed-off-by: baihailong <baihailong@oppo.com>
2024-10-18 09:37:47 +08:00
chihe
69363d4775 fix(master): remove disk from decommission list if it is recoverd
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:47 +08:00
chihe
3616da0896 fix(master): do not decommission disk with cancel status
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:47 +08:00
chihe
62b9aaab18 fix(datanode): reset diskErrorCnt when recovering bad disk
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:47 +08:00
baihailong
3b24528c0d fix(client): when volume is deleted, cfs-client need to exit.
Signed-off-by: baihailong <baihailong@oppo.com>
2024-10-18 09:37:46 +08:00
chihe
2e3430f315 fix(datanode): fix panic raised by function of ExistDir
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:46 +08:00
Victor1319
c943160c2a refactor(client): reduce buffer chan size to reduce memory when client idle.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:46 +08:00
Victor1319
d6d815dd29 fix(client): fix the issuse of continuous memory growth on client side.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:46 +08:00
chihe
c34d6a64a9 fix(master): skip auto add replica when dp lost leader
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:46 +08:00
chihe
2dd9b3af65 feat(master): add api for recovering bad disk
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:45 +08:00
baihailong
1610440fde fix(sdk): access nil pointer leadto cfs-client exit.
Signed-off-by: baihailong <baihailong@oppo.com>
2024-10-18 09:37:45 +08:00
Victor1319
d7a098f79a refactor(client): add config to control stream op max timeout.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:45 +08:00
Victor1319
684867eae8 refactor(client): amplify retry interval once overwrite failed to avoid disk io busy.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:45 +08:00
chihe
9eec4a82cd fix(master): mark disk as disabled when decommissioning disk
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:45 +08:00
Victor1319
507bcb3684 refactor(data): use tryRun to limit delete io. ,
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:44 +08:00
Victor1319
b4b299770e refactor(client): refactor random write retry strategy. ,
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:44 +08:00
NaturalSelect
17a523f7af feat(data): limit memory used
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:44 +08:00
NaturalSelect
c523bb4e0c fix(master): cluster info show readable time
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:44 +08:00
leonrayang
d2e54ae09e refactor(master): Refactor the code of vol struct and cluster struct
:

Signed-off-by: leonrayang <chl696@sina.com>
2024-10-18 09:37:43 +08:00
NaturalSelect
806daa4081 fix(data): return limited io if failed to delete batch extents
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:43 +08:00
NaturalSelect
c374043f8d feat(master): persist datanode bad disk
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:43 +08:00
chihe
84bd57b9a4 fix(master):if all replica is unvaliable, mark dp as failed
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:42 +08:00
chihe
886a3510a2 feat(datanode): add debug log for heartbeat
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:42 +08:00
NaturalSelect
bced471273 feat(master): support config decommission settings
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:42 +08:00
NaturalSelect
966fd88428 feat(master): parallelism check dp meta
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:41 +08:00
true1064
6dac6107b4 fix(master): only choose from the specific zone in function canWriteForNode
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-10-18 09:37:41 +08:00
chihe
5d67e540e1 fix(master):clean decommission list when leader change
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:41 +08:00
chihe
623f17300f fix(master): wait for setting RestoreReplica of dp during MarkDecommissionStatus
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:41 +08:00
chihe
424cedfd44 fix(datanode): rename root dir of dp to dir with backup prefix when execute decommisison with raftForce
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:41 +08:00
chihe
87c66fa39d fix(master): Calculating the decommission progress of the disk includes ignoring dps.
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:40 +08:00
chihe
764bfb0c6e fix(master):execute recoverReplicaMeta when replica is IOError
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:40 +08:00
baihailong
f84e75e889 fix(libsdk): add IsDir and IsRegular for java.
Signed-off-by: baihailong <baihailong@oppo.com>
2024-10-18 09:37:39 +08:00
chihe
cc5217c939 fix(datanode): check disk path for replica with master when executing attachPartition
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:39 +08:00
chihe
8d6f83e812 fix(master): decommission concel ignore decommission running
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:39 +08:00
NaturalSelect
2fe8a8cc4d feat(meta): async inode del file recycle
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:39 +08:00
NaturalSelect
c0cab9f621 feat(meta): adjust delete INODE_DEL file water mark
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:38 +08:00
NaturalSelect
f6705e18a2 fix(meta): fix INODE_DEL recycle
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:38 +08:00
NaturalSelect
0a00b4679d fix(meta): limit the count of metanode inode audit log
Signed-off-by: NaturalSelect <2145973003@qq.com>
2024-10-18 09:37:38 +08:00
true1064
9c0f52933d fix(data): When loading the disk, strengthen the checking before removing the duplicated data partition directories.
Signed-off-by: true1064 <tangjingyu@oppo.com>
2024-10-18 09:37:38 +08:00
leonrayang
b2ef12bec6 refactor(master): refactor the code of zone moudule
Signed-off-by: leonrayang <chl696@sina.com>
2024-10-18 09:37:38 +08:00
chihe
9e78f6087a feat(master):recover replica deleted by raftForce
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:37 +08:00
chihe
e146c4c4ef fix(master): remove the status of DecommissionNeedManualFix
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:37 +08:00
chihe
3bdb185f38 fix(master): fix deadlock for progress of WarnMissingDp
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:37 +08:00
chihe
efb17f0ab7 feature(datanode): do not remove root dir for dp deleted by raftForce with auto decommmission mode
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:36 +08:00
Victor1319
5bcc291c47 refactor(master): add log when over datanode dp cnt limit.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:36 +08:00
Victor1319
ee0b910840 fix(meta): add gen log when append extent key.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:36 +08:00
shuqiang-zheng
e59ce22a7e feat(client): cleaning up the log that may be printed to standard output during write process.
Signed-off-by: shuqiang-zheng <zhengshuqiang@oppo.com>
2024-10-18 09:37:36 +08:00
Victor1319
f91640acf2 fix(data): fix reload datapartition error.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:36 +08:00
NaturalSelect
3f0f3ac351 fix(meta): fix INODE_DEL recycle
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:35 +08:00
shuqiang-zheng
88c1fecf09 fix(master): Fix the problem of loading volDelayDeleteTimeHour from rocksdb without determining whether the value is greater than 0 or not.
@formatter:off

Signed-off-by: shuqiang-zheng <zhengshuqiang@oppo.com>
2024-10-18 09:37:35 +08:00
Victor1319
69243510d2 refactor(master): reload data from rocksdb once leader changed.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:35 +08:00
NaturalSelect
d7022c2114 fix(master): avoid warningMetrics deadlock
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:35 +08:00
leonrayang
cbef541a9d fix(datanode): The contents on which the CRC check depends are inconsistent with each other
Update the crc check output from error to warn, the inconsistent may be false report and require
more output and make a jugement.

The applyId and crc cann't be atomic under the architecture and are not necessary for performance.

Signed-off-by: leonrayang <chl696@sina.com>
2024-10-18 09:37:35 +08:00
leonrayang
d699119b41 fix(datanode):Refactor the paramter of storage write at datanode
Signed-off-by: leonrayang <chl696@sina.com>
2024-10-18 09:37:34 +08:00
baihailong
2caee176fc fix(sdk): add gosdk.
Signed-off-by: baihailong <baihailong@oppo.com>
2024-10-18 09:37:34 +08:00
NaturalSelect
b211b84591 fix(data): avoid flush extent when it is not dirty
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:34 +08:00
chihe
470c7dc5e3 fix(datanode): donot raise panic when ApplyMemberChange trigger disk error
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:34 +08:00
chihe
4c17d9b2c5 fix(master): reset decommission type when dp decommission fail
close;

Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:34 +08:00
chihe
d586c4e55f fix(master): remove excessive peer during checkReplicaMeta
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:33 +08:00
chihe
068531205a fix(datanode): remove expired peers with expired nodeID
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:33 +08:00
chihe
207fe1063f fix(datanode): remove expired root of dp during loading dp
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:33 +08:00
chihe
8eb0ad8cc5 fix(master):if dp has emtpy replicas when execute decommission,return error
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:33 +08:00
chihe
509b10fec1 fix(master): if raft is already started when repair for dp is finished, return error directly
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:32 +08:00
chihe
5ff65cbe43 fix(master): add lock for accessing missingMpAddrSet
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:32 +08:00
chihe
647d617ed9 fix(master): change error msg for multiple replica are executing decommissioning
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:32 +08:00
NaturalSelect
a84b5b2d7d fix(util): refactor audit log remove
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:32 +08:00
NaturalSelect
7dd5d98602 feat(master): support set dp heartbeat timeout
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:31 +08:00
shuqiang-zheng
00fe9b7b44 feat(log): Add support for standard output in the log module.
Signed-off-by: shuqiang-zheng <zhengshuqiang@oppo.com>
2024-10-18 09:37:31 +08:00
baihailong
b0c9407586 fix(sdk): add cfs_IsDir,cfs_IsRegular for libsdk.
Signed-off-by: baihailong <baihailong@oppo.com>
2024-10-18 09:37:31 +08:00
NaturalSelect
05ba93432f feat(master): feat support vol level dp meta repair
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:30 +08:00
NaturalSelect
2002638ffe fix(data): ensure that truncate index is less than applied index
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:30 +08:00
NaturalSelect
8af898a02b fix(master): update dp cache after create vol
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:30 +08:00
NaturalSelect
2ede1811ca fix(data): adjust extent flush period to 5min
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:30 +08:00
baihailong
17f02b2f5a fix(sdk): add statistical metrics for libsdk.
Signed-off-by: baihailong <baihailong@oppo.com>
2024-10-18 09:37:29 +08:00
NaturalSelect
d3d2d1c61d fix(meta): avoid leak EXTENT_DEL fd
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:29 +08:00
baihailong
4efd74e18a fix(client): remove the wrong code in function write.
Signed-off-by: baihailong <baihailong@oppo.com>
2024-10-18 09:37:29 +08:00
baihailong
b6bbdae272 fix(fuse): print goroutine info when cfs-client exiting normally.
Signed-off-by: baihailong <baihailong@oppo.com>
2024-10-18 09:37:29 +08:00
baihailong
fd79fefc53 fix(sdk): add debug log for remove dp and select dp.
Signed-off-by: baihailong <baihailong@oppo.com>
2024-10-18 09:37:29 +08:00
chihe
5d49789edf fix(master):redundant replica not participate int the decommission progress when decommissioning mutiple replicas of dp
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:29 +08:00
NaturalSelect
45431e78a3 fix(data): avaliable space calculate consider decommissioned disks
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:28 +08:00
W9068822
005cf8e5db feat(metrics): report version of client, metanode, datanode, authnode, lcnode, objectnode
Signed-off-by: W9068822 <v-lijianrong1@oppo.com>
2024-10-18 09:37:28 +08:00
baihailong
240723edad fix(fuse): report error when read size not expected.
Signed-off-by: baihailong <baihailong@oppo.com>
2024-10-18 09:37:28 +08:00
Victor1319
aa527dba9e refactor(data): remove autoComputeCrc func from snapshot process. ,
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:28 +08:00
NaturalSelect
47d93a6462 feat(master): support enable or disable auto dp meta repair
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:27 +08:00
NaturalSelect
d15925e4df feat(meta): batch delete extents in delete extent channel
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:27 +08:00
W9068822
267d580297 fix(dp): sendErrReply @formatter:off
Signed-off-by: W9068822 <v-lijianrong1@oppo.com>
2024-10-18 09:37:27 +08:00
chihe
f519327c5c fix(master):modify condition for raftForce when executing checkReplicaMeta
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:27 +08:00
chihe
68b6913ad9 fix(master): add log to debug add disabled disk failed
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:27 +08:00
chihe
0c9f0c43f0 fix(master): do not change error msg for decommission failed dp when master reboot
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:26 +08:00
chihe
0e5278b4cf fix(master):the DecommissionNeedManualFix status should be included in the progress statistics for dataNode
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:26 +08:00
chihe
18198bbaa2 fix(master): delete new replica when performing rollback in AutoAddReplica mode
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:26 +08:00
NaturalSelect
5b26029acd fix(master): support config dp repiar timeout
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:25 +08:00
NaturalSelect
448c924c89 fix(master): carry weight node selector ignore mp limit
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:25 +08:00
NaturalSelect
91013f9864 feat(master): nodeset info return can alloc metanode/datanode cnt
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:24 +08:00
NaturalSelect
537cbd349d fix(client): avoid remove dp concurrent
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:24 +08:00
NaturalSelect
469ce5ed6c chore(master): fix ci test
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:24 +08:00
Victor1319
b06f9f5d98 refactor(data): datanode only process random write op before node start.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:24 +08:00
NaturalSelect
88e1e0c15f chore(master): fix ci tests
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:24 +08:00
NaturalSelect
255db6dc57 fix(master): load decommission disk limit when load cluster
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:23 +08:00
NaturalSelect
36bbcd73df fix(client): fix appendExtentKey extent conflict
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:23 +08:00
Victor1319
9c1c8b1401 fix(sdk): revert code to fix eh flush failed error.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:23 +08:00
chihe
513f0de70b fix(master): only set RestoreReplica of dp to RestoreReplicaMetaStop when removed from decommission list
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:23 +08:00
chihe
bae6e473f9 fix(master): use raft force to delete redundant peers when 1-replica rollback failed
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:23 +08:00
chihe
e1c072e6b8 feat(client): update pom.xml for maven
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:22 +08:00
NaturalSelect
53b9bbded9 fix(master): when stop master wait 10 sec for background tasks to exit
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:22 +08:00
NaturalSelect
8f8dbebebf fix(master): assess replica[0] need dp lock
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:22 +08:00
wangxiaodong1
d8818f1f00 feat(master): add replicas laber for dp and mp noleader metrics
Signed-off-by: wangxiaodong1 <wangxiaodong1@oppo.com>
2024-10-18 09:37:22 +08:00
chihe
15ecac6bcd fix(cli): decommission disk for cli tool add migrationType param
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:22 +08:00
chihe
ae30b2774f feat(master):report the list of dps hold token
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:22 +08:00
W9068822
ba2b8672ad fix(cli): fix DataNodeMigrate, @formatter:off
Signed-off-by: W9068822 <v-lijianrong1@oppo.com>
2024-10-18 09:37:21 +08:00
W9068822
4c21557d33 feat(master): add getAllDataNodes and getAllMetaNodes apis
Signed-off-by: W9068822 <v-lijianrong1@oppo.com>
2024-10-18 09:37:21 +08:00
NaturalSelect
202d9035be fix(data): tiny extent snapshot off fix to 128M
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:21 +08:00
W9068822
aa10b15f2c fix(dp): add dp count limitation
Signed-off-by: W9068822 <v-lijianrong1@oppo.com>
2024-10-18 09:37:21 +08:00
NaturalSelect
0661a0854a chore(data): change get store used size log level
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:21 +08:00
NaturalSelect
41e69dafb7 fix(data): load extents consider snapshot
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:21 +08:00
NaturalSelect
0604d5d46b fix(master): fix abortDecommissionDisk interface
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:20 +08:00
NaturalSelect
164c090bb3 feat(master): support reset dp restore status
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:20 +08:00
chihe
21ffe79b91 feat(master): audit log for try decommission disk
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:20 +08:00
chihe
bbffd60601 fix(master):if RestoreReplica for dp is RestoreReplicaMetaRunning, reset it to RestoreReplicaMetaStop when master reboot or change leader.
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:20 +08:00
NaturalSelect
ac1a19c35a feat(master): return can alloc partition when query node info
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:20 +08:00
baihailong
d766f13cc7 fix(client): revert reports file's metadata and data inconsistencies.
Signed-off-by: baihailong <baihailong@oppo.com>
2024-10-18 09:37:20 +08:00
Victor1319
47fa33f8f3 fix(data): remove rlock to avoid deadlock.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:19 +08:00
NaturalSelect
a6cca07cd8 fix(master): avoid panic when master exit
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:19 +08:00
chihe
3caab90cd4 feat(master): add audit log for decommission datapartition
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:19 +08:00
chihe
8f4136dd71 feat(master): add audit log for decommission disk
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:19 +08:00
chihe
ebbe955170 feat(master): excute checkReplicaMeta when mark dp decommission
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:19 +08:00
chihe
f0e76414db feature(master):mutual exclusion between the decommission progress and restoreReplicaMeta progress
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:19 +08:00
chihe
59af081a03 fedat(master):auto add missing repplica
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:18 +08:00
chihe
d85d3ee610 fix(master): the DecommissionNeedManualFix status should be included in the progress statistics
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:18 +08:00
baihailong
3cb72baf0a fix(fuse): when client starting, reports last exit info.
Signed-off-by: baihailong <baihailong@oppo.com>
2024-10-18 09:37:18 +08:00
Victor1319
d32017cf23 refactor(all): reslove merge conflicts between 3.3.x and 3.4.0.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:16 +08:00
Victor1319
daae931f4a refactor(meta): skip discard dp when delete extent failed.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:16 +08:00
NaturalSelect
1bc3f27f2e fix(meta): correct inode size calculate
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:16 +08:00
NaturalSelect
7335ae7b26 feat(data): rewrite space manager stop function
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:16 +08:00
NaturalSelect
fb254c76b3 fix(data): avoid panic during apply rand write after store close
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:16 +08:00
Victor1319
c10b23bb77 fix(client): avoid block flush when clean up stream.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:16 +08:00
NaturalSelect
f0e10f7b4f refactor(data): revert data node dp delay load
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:16 +08:00
NaturalSelect
50c14ce570 fix(data): avoid write extent after store closed
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:15 +08:00
NaturalSelect
9830a6eddb test(master): fix unit test
@formatter:off

Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:15 +08:00
Victor1319
35cb0a377c fix(client): fix ek conflict error when invoke closeOpenHandler.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>

,
2024-10-18 09:37:15 +08:00
NaturalSelect
e0c97a346e test(util): fix unit test
@formatter:off

Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:15 +08:00
NaturalSelect
f5989ce885 fix(client): remove unused import
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:15 +08:00
Victor1319
f82dd2e788 docs(docs): update rpm version from 3.3.0 to 3.3.2.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:15 +08:00
Victor1319
2ac08104f7 docs(docs): update docs about volume for release-3.3.1
1. add volume forbidden docs.
2. add volume audit-log docs.
3. add volume delay deletion docs.

Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:15 +08:00
Victor1319
e0f0177379 docs(docs): add docs about v3.3.2.
1. update change log.
2. add doc for volume delay deletion.

Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:14 +08:00
Victor1319
789abab556 test(data): fix bad testcase for data, meta, master.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:14 +08:00
Victor1319
c9fc21a9cb test(data): fix bad testcase for data, meta, master.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:14 +08:00
NaturalSelect
d75d3dd9af fix(client): if we meet limited io, recover directly
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:14 +08:00
NaturalSelect
8b6855f786 fix(client): release all conns, if we meet an EOF
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:14 +08:00
baihailong
857146b36b fix(sdk): fix ek conflict caused by traverse.
Signed-off-by: baihailong <baihailong@oppo.com>
2024-10-18 09:37:14 +08:00
leonrayang
6c17c03271 feat(master): Volume update enable crossZone
Signed-off-by: leonrayang <changliang@oppo.com>
2024-10-18 09:37:13 +08:00
leonrayang
a5c980f67a fix(master): go routine for qos leak while raft change but forget close
Signed-off-by: leonrayang <changliang@oppo.com>
2024-10-18 09:37:13 +08:00
shuqiang-zheng
d35cbb85f4 fix(pprof): Fix for pprof endpoint exposures.
Signed-off-by: shuqiang-zheng <zhengshuqiang@oppo.com>
2024-10-18 09:37:13 +08:00
NaturalSelect
83038f361d feat(cli): support set trash interval
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:13 +08:00
NaturalSelect
a538e3d313 feat(data): speed data node restart
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:12 +08:00
Victor1319
421eb1cfb0 fix(data): Add to partitions map only after start raft success. ,
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:12 +08:00
chihe
4b6de6ae6b feature(master): Compatible with old version trash
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:12 +08:00
chihe
295fdda372 fix(client): Don't remove non-empty directories when trash enable
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:12 +08:00
Victor1319
ad50f9454b feat(data): Start the external service port first, then start Raft.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:12 +08:00
NaturalSelect
1f96fcd802 fix(master): update decommission limit when load nodeset
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:12 +08:00
Victor1319
b175459e3d fix(meta): Print error log when remove raft member failed.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:12 +08:00
NaturalSelect
f982a0fddd feat(data): flush extent cache in timer
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:11 +08:00
Victor1319
3bdef9cd89 fix(data): Automatic cleanup of channels to avoid deadlock issues.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:11 +08:00
Victor1319
682359a6cb fix(master): Support asynchronous deletion of volume persistent information to avoid occupation of locks.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:11 +08:00
Victor1319
1945d80bf2 fix(master): Avoid concurrent access exceptions when responding to HTTP requests with a map.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:11 +08:00
Victor1319
5ba9a81437 refactor(master): Optimize filecheck-related logs for easier issue troubleshooting.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:11 +08:00
Victor1319
f93f43943c refactor(cli): Optimize the print output results of fileInCore.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:11 +08:00
Victor1319
60fec091d8 feat(data): Support asynchronous deletion of expired partition lists.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:11 +08:00
NaturalSelect
9917ddb14a feat(meta): audit log print millisecond
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:11 +08:00
S9054862
33f2e03607 feat(data): load extent info on demand
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:11 +08:00
NaturalSelect
d19e0094e5 feat(meta): delete dentry support inode audit
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:10 +08:00
Victor1319
5a6c6d1bb8 refactor(data): refactor print log whith req id when send or read follower failed.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:10 +08:00
Victor1319
a03c2794d1 feat(fsck): support unlock target dp.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:10 +08:00
Victor1319
93fc0f77f7 fix(client): To prevent write and flush operations from timing out and failing to respond to interrupt.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:10 +08:00
Victor1319
1af666bada feat(fsck): support batch persistence of bad extents.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:10 +08:00
Victor1319
4837a493f1 fix(fsck): fix block bug when request same node. 2024-10-18 09:37:10 +08:00
Victor1319
65aaea2ea1 feat(fsck): Limit the deletion of only 128 files at once.
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-10-18 09:37:09 +08:00
Victor1319
227319cacd feat(fsck): support gc extent mark and delete at the same time. ,
1. support mark gc extents and delete gc extents.
2. after delete gc extents success, rename extents to xx.succ.

Signed-off-by: Victor1319 <834863182@qq.com>
2024-10-18 09:37:09 +08:00
tangdeyi
fefd2522e4 fix(object): listobjectv2 IsTruncated required
Signed-off-by: tangdeyi <tangdeyi@oppo.com>
2024-10-18 09:37:09 +08:00
S9054862
bfe101236b fix(data): speed up initBaseFileID
Signed-off-by: S9054862 <huangzhibin1@oppo.com>
2024-10-18 09:37:09 +08:00
chihe
081452d6d5 feat(datnode): support read enableExtentRepairReadLimit from conf and query dp holded read extent token
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:09 +08:00
chihe
dd26f32000 feat(datanode): Support the functionality of allowing only one extent data read at a time per disk
Signed-off-by: chihe <chihe@oppo.com>
2024-10-18 09:37:09 +08:00
S9054862
b30e582d81 feat(data): limit io current on a disk when load/stop dp
Signed-off-by: S9054862 <huangzhibin1@oppo.com>
2024-10-18 09:37:08 +08:00
W9068822
11d3ddb011 feat(master): compress client/partitions response data
Signed-off-by: W9068822 <v-lijianrong1@oppo.com>
2024-10-18 09:37:08 +08:00
true1064
3ac38b75a2 enhance(master/cli): Reduce too much dp info printing about replica file count and size differ:
(1)master: not record replica file count and size differ if dp is in decommission
(2)cli: only print number of dp with replica file count and size differ in default; and add an argument to control whether print such dp info

Signed-off-by: true1064 <tangjingyu@oppo.com>
2024-10-18 09:37:08 +08:00
true1064
b24a9af406 fix(data): datapartition status should be readonly when disk status is read-only
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-10-18 09:37:08 +08:00
true1064
dcc30f7f33 fix(master): correct the determination of DataNode writability
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-10-18 09:37:08 +08:00
NaturalSelect
0a527d2749 enhance(meta): delete inode file support rolling
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:08 +08:00
NaturalSelect
f3b63d59fe fix(data): rewrite disk selector
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-10-18 09:37:08 +08:00
yhjiango
82ed79b0e4 fix(object): fix request parts check in complete multipart
Signed-off-by: yhjiango <jiangyunhua@oppo.com>
2024-10-18 09:37:08 +08:00
yhjiango
2827606cd3 fix(object): fix tagging valid verification
Signed-off-by: yhjiango <jiangyunhua@oppo.com>
2024-10-18 09:37:07 +08:00
shuqiang-zheng
b28b2ad3b5 fix(master): Printing load metadata information to the output log.
Signed-off-by: shuqiang-zheng <zhengshuqiang@oppo.com>
2024-10-18 09:36:56 +08:00
NaturalSelect
9da4c90a37 fix(util): recycle audit logs when rolling
close: #3178
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-05-20 16:25:50 +08:00
yhjiango
af4212ccd3 fix(object): partNumber must not be greater than 10000
Signed-off-by: yhjiango <jiangyunhua@oppo.com>
(cherry picked from commit 234d30ec11)
2024-05-20 16:25:50 +08:00
yhjiango
787346f192 fix(object): adjust STS auth process
Signed-off-by: yhjiango <jiangyunhua@oppo.com>

(cherry picked from commit 1f23b3f620)
Signed-off-by: yhjiango <jiangyunhua@oppo.com>
2024-05-20 16:25:50 +08:00
NaturalSelect
480d297781 fix(master): use DeleteRange to clear rocksdb
close: #3152

Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-05-20 16:25:50 +08:00
true1064
8244569279 fix(data): when traversing the replicas slice, avoid panics caused by changes in the slice length
Signed-off-by: true1064 <true1063@163.com>
2024-05-20 16:25:50 +08:00
NaturalSelect
14e83e2125 fix(master): avoid cross device link when apply snapshot
close: #3145

Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-05-20 16:25:50 +08:00
true1064
fdedd37714 fix(metadata): Taking a local snapshot or applying raft snapshot in MP persist the UniqID field
when loading a local snapshot, if the UniqID is found to be 0, set it to a larger value.

Signed-off-by: true1064 <true1063@163.com>
2024-05-20 16:25:50 +08:00
Victor1319
391f0bd793 feat(data): when extent is mark delete, write op not failed.
Signed-off-by: Victor1319 <834863182@qq.com>
2024-05-20 16:25:50 +08:00
Victor1319
575a8ca3cc feat(fsck): support print volume name after calc garbage extent.
Signed-off-by: Victor1319 <834863182@qq.com>
2024-05-20 16:25:50 +08:00
Victor1319
b1b845285f feat(fsck): not fatal when get one dp info failed.
Signed-off-by: Victor1319 <834863182@qq.com>
2024-05-20 16:25:50 +08:00
Victor1319
9f0e10b79d feat(fsck): support calcuate bad extent concurrently.
Signed-off-by: Victor1319 <834863182@qq.com>
2024-05-20 16:25:50 +08:00
true1064
c71d88d967 enhance(master): not calculate preload dp capacity every time in newSimpleView,to reduce CPU usage
Signed-off-by: true1064 <tangjingyu@oppo.com>
2024-05-20 16:25:50 +08:00
Victor1319
f6b1bc349f fix(fsck): return error when get connect failed.
Signed-off-by: Victor1319 <834863182@qq.com>
2024-05-20 16:25:50 +08:00
true1064
05f62c22dd fix(data): function startEvict() should not panic when failed to get volume info from master
Signed-off-by: true1064 <true1063@163.com>
2024-05-20 16:25:50 +08:00
baihailong
049931458a fix(libsdk): rename file A to file B twice, file B not exist while open B
Signed-off-by: baihailong <baihailong@oppo.com>
2024-05-20 16:25:49 +08:00
baihailong
1d92c687f4 fix(libsdk): libsdk support cfs_link, cfs_symlink
Signed-off-by: baihailong <baihailong@oppo.com>
2024-05-20 16:25:49 +08:00
Victor1319
068cabe3a7 feat(meta): Enhance the allocation and validation of txId to avoid potential duplicates.
Signed-off-by: Victor1319 <834863182@qq.com>
2024-05-20 16:25:49 +08:00
Victor1319
43363ac12c fix(data): cancel extent count limit when create extent.
Cancel the limit of "maxCount + 10" extents to avoid no available data partitions.

Signed-off-by: Victor1319 <834863182@qq.com>
2024-05-20 16:25:49 +08:00
shuqiang-zheng
804e47e3cb fix(master):Change the logic of checkReplicaNum in the update volume process.
Signed-off-by: shuqiang-zheng <zhengshuqiang@oppo.com>
2024-05-20 16:25:49 +08:00
shuqiang-zheng
a8c2cccf38 enhance(log):Modify the threshold for triggering log cleanup to logLeftSpaceLimitRatio of the total disk space.
Signed-off-by: shuqiang-zheng <zhengshuqiang@oppo.com>
2024-05-20 16:25:49 +08:00
shuqiang-zheng
efb42fc872 enhance(cli):Modifying Volume Deletion Related Error Messages.
Signed-off-by: shuqiang-zheng <zhengshuqiang@oppo.com>
2024-05-20 16:25:07 +08:00
baihailong
ea6c059edf fix(libsdk,meta): support dir lock
Signed-off-by: baihailong <baihailong@oppo.com>
2024-05-20 16:25:07 +08:00
shuqiang-zheng
ef21e9c328 fix(master):Fixed the problem that volumes marked for deletion but still in the freeze period could not restore the dp and mp replicas after reboot and migration was not supported.
Signed-off-by: shuqiang-zheng <zhengshuqiang@oppo.com>
2024-05-20 16:25:07 +08:00
Victor1319
c357ab9a2f refactor(cli): remove pasue flag in transaction flag hint.
Signed-off-by: Victor1319 <834863182@qq.com>
2024-05-20 16:25:07 +08:00
Victor1319
875b894e45 fix(master&client&cli): Add a new variable EnableTransactionV1 to avoid compatibility issues
Signed-off-by: Victor1319 <834863182@qq.com>
2024-05-20 16:25:07 +08:00
Victor1319
b6172b681c fix(master): only new vol enable rename atomic operation default, and support close.
Signed-off-by: Victor1319 <834863182@qq.com>
2024-05-20 16:25:07 +08:00
baihailong
3d8defe393 fix(sdk): not check result of InodeGet_ll leadto panic in Truncate.
Signed-off-by: baihailong <baihailong@oppo.com>
2024-05-20 16:25:07 +08:00
Victor1319
7a18f07342 feat(master): set rename atomic switch open default.
Signed-off-by: Victor1319 <834863182@qq.com>
2024-05-20 16:25:07 +08:00
leonrayang
6152faa5eb fix(master):MaxDpCntLimit in load datanode and band with configure
Signed-off-by: leonrayang <chl696@sina.com>
2024-05-20 16:25:07 +08:00
leonrayang
64f47b8143 enhance(client):tiny extent update 128KB to 1MB
Signed-off-by: leonrayang <chl696@sina.com>
2024-05-20 16:25:07 +08:00
shuqiang-zheng
16c8423e2d feat(master): update the volume freeze feature to make it unreadable when the volume is frozen.
Signed-off-by: shuqiang-zheng <zhengshuqiang@oppo.com>
2024-05-20 16:25:07 +08:00
baihailong
415827798a fix(libsdk): fix bug, cfs_getattr return ENOENT after cfs_unlink, cfs_open,cfs_write.
Signed-off-by: baihailong <baihailong@oppo.com>
2024-05-20 16:25:07 +08:00
shuqiang-zheng
04825e545c feat(master): Open the volume deletion interface to freeze the volume when performing a volume deletion and then wait 48 hours before deleting the volume.
Signed-off-by: shuqiang-zheng <zhengshuqiang@oppo.com>
2024-05-20 16:25:07 +08:00
yhjiango
aa391f63a6 feat(objectnode): s3 sts and signature auth
1. sts federation token
2. signature auth code refactor

Signed-off-by: yhjiango <jiangyunhua@oppo.com>

(cherry picked from commit 4401624f96)
Signed-off-by: yhjiango <jiangyunhua@oppo.com>
2024-05-20 16:25:07 +08:00
NaturalSelect
6922da8960 feat(master): add forbid vol feature
Signed-off-by: NaturalSelect <2145973003@qq.com>
2024-05-20 16:25:07 +08:00
baihailong
deb7d0d738 feature(libsdk): cfs_rename support param overwritten
Signed-off-by: baihailong <baihailong@oppo.com>
2024-05-20 16:25:07 +08:00
baihailong
f288386dc3 fix(libsdk): multi-thread call cfs_rename leadto same ino
Signed-off-by: baihailong <baihailong@oppo.com>
2024-05-20 16:25:07 +08:00
baihailong
480f49e351 fix(libsdk): fix cfs_mkdirs bug when multi-thread call
Signed-off-by: baihailong <baihailong@oppo.com>
2024-05-20 16:25:07 +08:00
baihailong
24f9e527d3 fix(libsdk): support cfs_truncate
Signed-off-by: baihailong <baihailong@oppo.com>
2024-05-20 16:25:07 +08:00
Victor1319
5b399ce7a5 🐞 fix(fsck): remove clean switch flag from fsck.
Signed-off-by: Victor1319 <834863182@qq.com>
2024-05-20 16:25:07 +08:00
Victor1319
3bdd141c39 🐞 fix(data): when read locked extent, only print error log not return error.
Signed-off-by: Victor1319 <834863182@qq.com>
2024-05-20 16:25:07 +08:00
chihe
43eb875af8 feature(doc): update the content of doc to version 3.3.1
Signed-off-by: chihe <chi.he@oppo.com>
2024-05-20 16:25:07 +08:00
leonrayang
5b4aac4b11 enhance(client): Log path check too strict to startup if have softlink in the path
Signed-off-by: leonrayang <chl696@sina.com>
2024-05-20 16:25:07 +08:00
Victor1319
7bbd55495e feat(data): refactor delete logic to check opcode first
Signed-off-by: Victor1319 <834863182@qq.com>
2024-05-20 16:25:07 +08:00
Victor1319
9e23cd2c43 🐞 fix(fsck): close clean switch before online.
Signed-off-by: Victor1319 <834863182@qq.com>
2024-05-20 16:25:06 +08:00
leonrayang
67b1b83d1c enhance(security document): Add note for user report private security issues with groups.io
Signed-off-by: leonrayang <chl696@sina.com>
2024-05-20 16:25:06 +08:00
leonrayang
56b4cecca2 fix(master):qos.Lock of assignClientsNewQos forget release and trigger deadlock
Signed-off-by: leonrayang <chl696@sina.com>
2024-05-20 16:25:06 +08:00
slasher
83809eea8c feat(build): build with goreleaser
Signed-off-by: slasher <shenjie1@oppo.com>
2024-05-20 16:25:06 +08:00
NaturalSelect
0e4c4acd48 refactor(util): remove lock from auditlog
Signed-off-by: NaturalSelect <2145973003@qq.com>
2024-05-20 16:25:06 +08:00
NaturalSelect
41b2bf7093 feat(master): enable audit log by default
NOTE: Please be careful when cherry-picking this commit.

Signed-off-by: NaturalSelect <2145973003@qq.com>
2024-05-20 16:25:06 +08:00
zhaochenyang
6bb73e1908 feat(ci): add sast tools gosec and semgrep
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-05-20 16:25:06 +08:00
shuqiang-zheng
fa7b9b61bc fix(master):fix the problem of TestCreateColdVol
Signed-off-by: shuqiang-zheng <zhengshuqiang@oppo.com>
2024-05-20 16:25:06 +08:00
Victor1319
1762b64dc8 🐞 fix(fsck): add lock to protect host map
Signed-off-by: Victor1319 <834863182@qq.com>
2024-05-20 16:25:06 +08:00
Victor1319
f9f709828f 🐞 fix(data): not check before time when lock extent to avoid fail.
Signed-off-by: Victor1319 <834863182@qq.com>
2024-05-20 16:25:06 +08:00
baihailong
868ce62389 fix(auditlog): fix multithread logAudit bug
Signed-off-by: baihailong <baihailong@oppo.com>
2024-05-20 16:25:06 +08:00
Victor1319
619b2527ef 🐞 fix(fsck&data): support repeat delete extent
Signed-off-by: Victor1319 <834863182@qq.com>
2024-05-20 16:25:06 +08:00
Victor1319
85d0112838 🐞 fix(fsck): fix rename old dir bug
Signed-off-by: Victor1319 <834863182@qq.com>
2024-05-20 16:25:06 +08:00
Victor1319
1c6b7767a3 feat(fsck & data): refacotr gc logic
1. "getAllExtent?id=xx&beforeTime=xx" supports get extent GC flags.
2. When deleting extent, also check for GC flags. If there are no GC flags, return an error.
3. Optimize related log printing by outputting relationship information and execution time, reduce unnecessary logs, and improve performance.
4. Optimize the implementation of the "getExtents" list interface and the performance of batch locking interfaces. Read all the latest information of extents at once to avoid accessing the disk for each extent, reducing timeout calls.
5. Support outputting profiles for easy performance analysis.
6. "cleanBadExtents" and "rollbackBadExtents" support a "clean" parameter to control whether to perform data cleanup and overwrite.
7. Use a task pool to support multi-threaded concurrent tasks.
8. Limit the concurrent task number to 3 for each node.
9. Back up the execution result of the previous command each time it is executed for easy tracing.
10. Remove "from-dp" from "getMpExtents" and "getDpExtents" and directly support concurrent retrieval of full volume information.
11. When obtaining MP information, take the maximum value among the three nodes as the reference.
12. When persisting MP information, use a buffer to optimize performance and avoid reading and writing to disk each time.
13. Analyze performance bottlenecks and optimize the process of obtaining MP extents.
14. If there is an exception during the MP retrieval process, exit directly.

Signed-off-by: Victor1319 <834863182@qq.com>
2024-05-20 16:25:06 +08:00
chihe
40aecf53c1 enhance(client): ignore exist error when creating parent dir in transaction mode
Signed-off-by: chihe <chi.he@oppo.com>
2024-05-20 16:25:06 +08:00
huyao2
e340878b67 feat(fsck): Optimize fsck gc features, including:
1. The before of getDpextent must be at least 3 hours smaller than the current time.
2. The use of locks in the lock extent is changed from mutual exclusion locks to read-write locks.
3. Add a time judgment when locking extent. If the time is greater than before time, it means it has been changed and it will fail.
4. Support concurrency when cleaning bad extent
5. Add verification of master and volume names

Signed-off-by: huyao2 <huyao2@oppo.com>
2024-05-20 16:25:06 +08:00
chihe
7a784561eb enhance(client): ingnore exist error when rebuild parent dir
Signed-off-by: chihe <chi.he@oppo.com>
2024-05-20 16:25:06 +08:00
baihailong
9d2408bd43 fix(util): fix audit log bug, not handle shiftFiles() returned error 2024-05-20 16:25:06 +08:00
NaturalSelect
ce23550eb8 fix(cli): fix ui error occured by cherry-pick
Signed-off-by: NaturalSelect <2145973003@qq.com>
2024-05-20 16:25:06 +08:00
NaturalSelect
3ff05df741 fix(util): avoid lock in audit log
Signed-off-by: NaturalSelect <2145973003@qq.com>
2024-05-20 16:25:06 +08:00
NaturalSelect
59467aedd8 fix(util): audit log performance
Signed-off-by: NaturalSelect <2145973003@qq.com>
2024-05-20 16:25:06 +08:00
NaturalSelect
64fcb5d929 feat(metanode): merge GetDataPartitionsView RPCs and GetVolumeSimpleInfo RPCs in a volume into one RPC
close: #1908

Signed-off-by: NaturalSelect <2145973003@qq.com>
2024-05-20 16:25:06 +08:00
NaturalSelect
5dbf8d0aad feat(metanode): audit log support master control
Signed-off-by: NaturalSelect <2145973003@qq.com>
2024-05-20 16:25:06 +08:00
chihe
2a460d731e feature(client): trash supports random interval to prevent a large number of concurrent deletions
Signed-off-by: chihe <chi.he@oppo.com>
2024-05-20 16:25:06 +08:00
chihe
b2c9aec0d4 bugfix(ci): fix some test case error
Signed-off-by: chihe <chi.he@oppo.com>
2024-05-20 16:25:06 +08:00
baijiaruo
ff66d662c8 enhance(cli): Optimize get mp extent. If an mp exception is encountered, the request fails.
Signed-off-by: baijiaruo <baijiaruo@126.com>
2024-05-20 16:25:06 +08:00
baijiaruo
0a089b106e fix(util): fix btree remove item
Signed-off-by: baijiaruo <baijiaruo@126.com>
2024-05-20 16:25:06 +08:00
chihe
1be4c4d856 bugfix(client): trash will ignore nil inodeInfo when remove expired dir
Signed-off-by: chihe <chi.he@oppo.com>
2024-05-20 16:25:06 +08:00
baijiaruo
2fdb6105ae fix(datanode): Fix the problem of extent lock error when the extent is copied in the scene
Signed-off-by: baijiaruo <baijiaruo@126.com>
2024-05-20 16:25:06 +08:00
chihe
837380be10 fix(client): trash support back-end audit 2024-05-20 16:25:06 +08:00
NaturalSelect
e48fa7f313 feat(object): support full path audit log
Signed-off-by: NaturalSelect <2145973003@qq.com>
2024-05-20 16:25:06 +08:00
NaturalSelect
fc76139b63 feat(meta): support back-end audit log
close: #2625

Signed-off-by: NaturalSelect <2145973003@qq.com>
2024-05-20 16:25:06 +08:00
chihe
89b8072a49 enchance(client): modify ParentDirPrefix
Signed-off-by: chihe <chi.he@oppo.com>
2024-05-20 16:25:06 +08:00
chihe
3bcd150a18 bugfix(client): if file is not find in Current when setXattr, then try find it in expired dirs
Signed-off-by: chihe <chi.he@oppo.com>
2024-05-20 16:25:06 +08:00
chihe
57d36e8c76 bugfix(client):1.Launch new deleteWorker when deleteInterval is changed 2.Change the setxattr operation to synchronous.
Signed-off-by: chihe <chi.he@oppo.com>
2024-05-20 16:25:06 +08:00
baijiaruo
1a4313aaad enhance(cli): add fsck gc tool
Signed-off-by: baijiaruo <baijiaruo@126.com>
2024-05-20 16:25:06 +08:00
chihe
e95c1c4c40 bugfix(client):1. use ReadDir_ll when remove expired data 2.long name add uuid subfix
Signed-off-by: chihe <chi.he@oppo.com>
2024-05-20 16:25:06 +08:00
chihe
be250bc49d enhance(client): trash read current directory in segments
Signed-off-by: chihe <chi.he@oppo.com>
2024-05-20 16:25:06 +08:00
Victor1319
3238abf919 tmp code
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-05-20 16:24:12 +08:00
chihe
28227ebb26 bugfix(client):1.fix inode leak for trash 2. save long file name in xattr
Signed-off-by: chihe <chi.he@oppo.com>
2024-05-20 16:22:20 +08:00
chihe
8f21c2fc81 bugfix(client):add dir cache when delete expired trash dir
Signed-off-by: chihe <chi.he@oppo.com>
2024-05-20 16:22:20 +08:00
chihe
daf8bb87e9 feature(client): support trash
Signed-off-by: chihe <chi.he@oppo.com>
2024-05-20 16:22:20 +08:00
slasher
c5747c8c06 chore(ci): run ci when merging to release
Signed-off-by: slasher <shenjie1@oppo.com>
2024-05-20 16:22:20 +08:00
NaturalSelect
3a7a9f261e fix(master): avoid metrics panic
close: #21938887

Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-05-20 14:34:04 +08:00
NaturalSelect
cae9127f94 feat(master): support set decommission disk limit
close: #21938887
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-05-20 14:34:04 +08:00
chihe
19edbdee97 fix(master): initial WarnMetrics before schedual tasks for master
close:#22200304

Signed-off-by: chihe <chihe@oppo.com>
2024-05-20 14:34:04 +08:00
chihe
3551ba3955 fix(master): when marking dp to start decommission, no need to concerned about the rollback times
close:#22192825

Signed-off-by: chihe <chihe@oppo.com>
2024-05-20 14:34:04 +08:00
chihe
9a76e5ed8a fix(master): delete decommission dst from hosts of master by force if rollback failed
close:#22192825

Signed-off-by: chihe <chihe@oppo.com>
2024-05-20 14:34:04 +08:00
chihe
6f7532d328 fix(master): do not set new replica status if new replica is not found
close:#22192835

Signed-off-by: chihe <chihe@oppo.com>
2024-05-20 14:34:04 +08:00
chihe
2c6da484e0 feat(master): enhace audit log for master
close:#22139330
Signed-off-by: chihe <chihe@oppo.com>
2024-05-20 14:34:04 +08:00
shuqiang-zheng
79bce821f4 fix(log):Fix problems that may be caused by concurrent flushing of logs.
@formatter:off

Signed-off-by: shuqiang-zheng <zhengshuqiang@oppo.com>
2024-05-20 11:09:26 +08:00
shuqiang-zheng
0f0211b28c enhance(log):Modify the threshold for triggering log cleanup to logLeftSpaceLimitRatio of the total disk space.
@formatter:off

Signed-off-by: shuqiang-zheng <zhengshuqiang@oppo.com>
2024-05-20 11:02:39 +08:00
leonrayang
52067ffcde enhance(metanode): Reduce log output and memory cost
close:#22193002

Signed-off-by: leonrayang <chl696@sina.com>
2024-05-16 17:03:28 +08:00
chihe
f0f0a63f67 fix(datanode): remove root dir for disk error dp not loaded
close:#22185410

Signed-off-by: chihe <chihe@oppo.com>
2024-05-16 14:31:23 +08:00
chihe
5512ac71f7 fix(master): retry auto disk decommission when bad replica is removed
close:#22185410

Signed-off-by: chihe <chihe@oppo.com>
2024-05-16 14:31:23 +08:00
chihe
8d317f1f4a fix(datanode):remove peers from the monitor for raft after raftFsm is stopped
close:#22179209

Signed-off-by: chihe <chihe@oppo.com>
2024-05-16 14:31:23 +08:00
NaturalSelect
a764db1f28 fix(client): release all conns, if we meet an EOF
close: #22110495

Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-05-16 11:02:56 +08:00
baihailong
d77a51acce fix(client): client reports file's metadata and data inconsistencies.#21937872
Signed-off-by: baihailong <baihailong@oppo.com>
2024-05-15 18:51:31 -07:00
NaturalSelect
69c118a492 fix(raft): check raft wal when rebuild log index
close: #22148428
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-05-15 17:09:49 +08:00
NaturalSelect
6b2b646e22
refactor(master): change audit log format
close: #21938887

Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-05-15 13:07:39 +08:00
zhaochenyang
ca861f4017 fix(lcnode): fix lcnode panic and reduce task allocation time
#21938894
Signed-off-by: zhaochenyang <zhaochenyang@oppo.com>
2024-05-13 17:30:54 +08:00
chihe
09b82910fd fix(datanode):add debug log for reading extent repair packet
close:#21938887

Signed-off-by: chihe <chihe@oppo.com>
2024-05-13 17:06:20 +08:00
baihailong
72505f67df fix(sdk): fix streamer's multi server problem.#21989411
Signed-off-by: baihailong <baihailong@oppo.com>
2024-05-13 16:18:31 +08:00
NaturalSelect
0c2fc80459
test(master): fix unit tests
close: #21938887
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-05-13 14:48:36 +08:00
NaturalSelect
5214ea0257
chore(all): gofmt code
close:#21938887
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-05-13 10:44:04 +08:00
chihe
4cdc1eb68b fix(master): if special dp is decommissioned by raftForce, disk manager can check recovery progress for new replica
close:#21938887

Signed-off-by: chihe <chihe@oppo.com>
2024-05-13 10:32:21 +08:00
chihe
368a0d23df feat(master): enhance for replica mata
close:#22120226
Signed-off-by: chihe <chihe@oppo.com>
2024-05-13 10:32:20 +08:00
chihe
cf0d8efdc8 feat(cli): add decommissioned disks info for datanode node info display
close:##21938887

Signed-off-by: chihe <chihe@oppo.com>
2024-05-13 10:32:20 +08:00
chihe
605cab15ff fix(master): validate the existence of the disk and node when performing disk decommission
close:#22153015

Signed-off-by: chihe <chihe@oppo.com>
2024-05-13 10:32:20 +08:00
chihe
6b5a204cc8 feat(master):do not check decommission condition when raftForce is setted
close:#21938887
Signed-off-by: chihe <chihe@oppo.com>
2024-05-13 10:32:19 +08:00
chihe
131a23986d fix(cli): cli can display dps encountering IO errors during loading
close:#22157324

Signed-off-by: chihe <chihe@oppo.com>
2024-05-13 10:32:19 +08:00
chihe
d698349f69 feat(master): exclude decommission src when acquiring token
close:#21938887

Signed-off-by: chihe <chihe@oppo.com>
2024-05-13 10:32:19 +08:00
chihe
f7b16771cc fix(datanode): raise disk error when dp persist applied id
close:#22153809

Signed-off-by: chihe <chihe@oppo.com>
2024-05-13 10:32:18 +08:00
chihe
baed7fcba2 feat(master): decommission all dp from disk if threshold for bad dp on disk is not set
close:#21938887

Signed-off-by: chihe <chihe@oppo.com>
2024-05-13 10:32:02 +08:00
chihe
4b5c01f758 fix(datanode): replica for dp can only be updated by raft member change
close:#22120226
Signed-off-by: chihe <chihe@oppo.com>
2024-05-13 10:30:07 +08:00
Victor1319
7de30df4c9 fix(data): persist dp meta only when create dp to avoid reset apply id 0.
close #22125333, #22125350

Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-05-13 10:30:07 +08:00
NaturalSelect
2aa1b08bab
feat(master): support abort disk decommission
close: #21938887
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-05-09 09:51:37 +08:00
NaturalSelect
7422d00b9f feat(master): support query decommission failed disks
close: #22140475

Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-05-08 20:32:42 +08:00
NaturalSelect
14c91ab07a feat(data): limit rw dp decrease count at once
close: #21996578

Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-05-08 11:35:34 +08:00
NaturalSelect
8e8fdad39c
feat(master): support max mp cnt limit
close: #22074239

@formatter:off

Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-05-08 09:54:41 +08:00
NaturalSelect
e3f6c27968 test(master): fix unit tests
close: #22151341

Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-05-08 09:53:36 +08:00
shuqiang-zheng
cb769e5def feature(master): If the dpCount is less than 10 when creating a volume, change it to 10.
close:#22086548

Signed-off-by: shuqiang-zheng <zhengshuqiang@oppo.com>

@formatter:off
2024-05-06 11:54:25 +08:00
W9068822
e885171996 feat(ci): fix format failed. #22022943
Signed-off-by: W9068822 <v-lijianrong1@oppo.com>
2024-04-30 16:51:33 +08:00
NaturalSelect
d74805918d feat(master): if all disks decommissioned, not allow to alloc dp on datanode
close: #22121082

Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-04-30 16:45:54 +08:00
chihe
88669f52d0 feat(master): if special replica dp has reduant replica when marking
decommission, return failed

close:#21938887
Signed-off-by: chihe <chihe@oppo.com>
2024-04-30 16:35:22 +08:00
chihe
0195901b46 fix(datanode): enhance for raft panic
close:#22125195

Signed-off-by: chihe <chihe@oppo.com>
2024-04-30 16:35:22 +08:00
NaturalSelect
ebaa4532cf fix(data): refactor snapshot extent deletion
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-04-30 16:32:58 +08:00
NaturalSelect
eadd4466f6 feat(master): support master decomm audit log
close: #22139330

Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-04-30 16:05:15 +08:00
chihe
a54031a56f fix(mater,datanode): check nodeID when adding raft member, return failed if nodeID is different from meta of replica
close:#21980258

Signed-off-by: chihe <chihe@oppo.com>
2024-04-29 14:42:10 +08:00
chihe
1486d9f8fc feat(master): delete dp from badparitionIds when exceeding maximum rollback attempts.
close:#21938887

Signed-off-by: chihe <chihe@oppo.com>
2024-04-29 11:25:25 +08:00
chihe
feaf1d5187 fix(datanode): if raft trigger disk io when persisting wal logs, do not
raise data server panic
close:#22125195

Signed-off-by: chihe <chihe@oppo.com>
2024-04-29 11:21:14 +08:00
NaturalSelect
007d4373e8 fix(data): batch delete normal extent not punch del, if disable snap
close: #22038262

Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-04-29 09:51:02 +08:00
Victor1319
8287ef215a fix(data): When a disk operation error occurs, only stop the current raft. #22054398
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-04-26 11:31:11 +08:00
chihe
47d3477df2 feat(master): support query for auto decommission progress
close:#21938887

Signed-off-by: chihe <chihe@oppo.com>
2024-04-26 11:13:34 +08:00
chihe
24f8a69cf8 feat(master):enhance for auto decommission
close:#21938887

Signed-off-by: chihe <chihe@oppo.com>
2024-04-26 09:02:22 +08:00
chihe
8c5e15438f feat(matser,datanode,cli): add report for disk error dp replica for cli
close:#21938887

Signed-off-by: chihe <chihe@oppo.com>
2024-04-26 08:52:26 +08:00
chihe
cdf2c7002e feat(master,datanode): support recover meta for data replica
close:#22120226

Signed-off-by: chihe <chihe@oppo.com>
2024-04-25 16:51:18 +08:00
chihe
b228714f7f fix(master):check peers length and content for replica
close:#22104077

Signed-off-by: chihe <chihe@oppo.com>
2024-04-25 15:12:27 +08:00
chihe
53b9b18a9d fix(master): don't set new created replica status to unavailable for specail dp
close:#22106330

Signed-off-by: chihe <chihe@oppo.com>
2024-04-25 14:21:34 +08:00
chihe
8fb9b3627c fix(datnode): validata pair.Size in FormatSize
close:#22105056,#22106330

Signed-off-by: chihe <chihe@oppo.com>
2024-04-23 11:11:17 +08:00
chihe
ba12fe3d57 feat(master): add api for checking meta for dp replica with meta for dp keeped in master
close:#22104077

Signed-off-by: chihe <chihe@oppo.com>
2024-04-22 16:33:56 +08:00
chihe
cd53758b7b fix(datanode): only remove redundant raft memeber when single replicum dp is noleader
close:#22077917

Signed-off-by: chihe <chihe@oppo.com>
2024-04-22 15:21:40 +08:00
chihe
f6bd807d9a fix(master): when special replicum dp retry decommission, do not check
recover flag

close:#22089724

Signed-off-by: chihe <chihe@oppo.com>
2024-04-22 14:59:16 +08:00
chihe
7705642d13 fix(datanode): when datanode remove raft memeber, use local nodeID
close:#22089724

Signed-off-by: chihe <chihe@oppo.com>
2024-04-22 10:18:52 +08:00
NaturalSelect
43b41b488d
fix(master): persist decommission error message
close: #22093468

Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-04-18 15:55:14 +08:00
chihe
abaa6aba2a fix(master):don't delete excess replica for the dp excuting decommission operation
close:#22089724

Signed-off-by: chihe <chihe@oppo.com>
2024-04-18 09:38:19 +08:00
Victor1319
9c2e96455e fix(data): Use usedCap as totalCap when usedCap bigger than totalCap. #22082582
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-04-15 20:10:56 +08:00
chihe
6e2393a65b fix(master): A leaderless single replica can self-recover into a state with a leader
close:#22077917

Signed-off-by: chihe <chihe@oppo.com>
2024-04-15 09:20:59 +08:00
chihe
aac1738be5 fix(master): remove replica by force when reaching max rollback retry
close:#22065162

Signed-off-by: chihe <chihe@oppo.com>
2024-04-12 09:54:13 +08:00
chihe
8073e66ae4 fix(master): when reaching retry max, don't reset decommission dst
close:#22065162

Signed-off-by: chihe <chihe@oppo.com>
2024-04-11 16:54:47 +08:00
NaturalSelect
1ada0055b2
fix(master): data race may happens when background goroutine update decommission status
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-04-11 14:33:06 +08:00
chihe
b4e48cef14 fix(master): set dp as rollback failed when the maximum number of token retrieval failures is reached
close:#22065162

Signed-off-by: chihe <chihe@oppo.com>
2024-04-11 14:22:22 +08:00
W9068822
c43c3d12d9 enchance(master): limit parameter count to 10 each time MP is created
close:#22068350

Signed-off-by: W9068822 <v-lijianrong1@oppo.com>
2024-04-11 12:09:01 +08:00
NaturalSelect
04b53dd152 fix(data): avoid panic when remove dp
close: #22060356
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-04-11 10:27:59 +08:00
chihe
686a485097 feature(master): QueryDataNodeDecoProgress supports error message
Signed-off-by: chihe <chihe@oppo.com>
2024-04-10 15:38:23 +08:00
chihe
5372c3f90d fix(master): release token before reset deocmmission dst
Signed-off-by: chihe <chihe@oppo.com>
2024-04-10 14:33:45 +08:00
NaturalSelect
c2179b8cf3 refactor(master): check dp when sync update
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-04-09 17:40:32 +08:00
NaturalSelect
131afc8729 feat(master): check datapartition status when set dp discard
close: #22053502

Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-04-09 17:40:32 +08:00
NaturalSelect
33c17368dd feat(master): if dp is discard, decommission will success directly
close: #22053070

Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-04-09 17:40:32 +08:00
NaturalSelect
fba83d7490 fix(client): close tmpConn correctly
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-04-09 15:30:03 +08:00
NaturalSelect
ab5d45336c fix(client): use tmp conn to avoid reorder packet
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-04-09 15:30:03 +08:00
S9054862
4fb6b5712c fix(client): treat try again error as a special error
close: #21953822 #21964458
Signed-off-by: S9054862 <huangzhibin1@oppo.com>
2024-04-09 15:30:03 +08:00
true1064
d00b02ef3b refactor(master/client): Add a new field in the client's response of retrieving the partition view, indicating whether the volume is read-only.
close:#21938435
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-04-09 10:50:23 +08:00
chihe
8a06d82190 fix(master):if dp with sepcical replicum delete decommission src raft member failed, don't reset decommission src
close:#22054632

Signed-off-by: chihe <chihe@oppo.com>
2024-04-09 09:23:15 +08:00
chihe
9b6c0052aa fix(datanode): single dp use delete new raft member with raftForce when rolling back
close:#22050712

Signed-off-by: chihe <chihe@oppo.com>
2024-04-08 15:17:20 +08:00
chihe
1ebc1c624a fix(master): restore the decommission progress of dp with special replica number when master restart
close:#22045153

Signed-off-by: chihe <chihe@oppo.com>
2024-04-03 17:08:30 +08:00
NaturalSelect
a3c54902a9
fix(cli): foramt decommission dp info
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-04-03 16:36:45 +08:00
NaturalSelect
cfd730a7c7
feat(master): support auto decommission disk and config
close: #21938887

Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-04-03 10:57:23 +08:00
chihe
b33568d3fd fix(master): display progress when decommission failed datanode restart
close:#22038283
Signed-off-by: chihe <chihe@oppo.com>
2024-04-03 10:35:55 +08:00
NaturalSelect
4ff31a49f0
fix(cli): datapartition query-progress
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-04-02 10:53:28 +08:00
true1064
b12137fd8a fix(meta): To correctly determine whether the inode should be deleted when unlink
close:#22037078
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-04-01 15:19:38 +08:00
true1064
41960159cc fix(meta): check if inode exists in function ExtentsList
close:#22031400
Signed-off-by: tangjingyu <tangjingyu@oppo.com>
2024-03-29 19:50:52 +08:00
S9054862
07d7ba805f feat(data): repair block size support master config
close: #21989384

Signed-off-by: S9054862 <huangzhibin1@oppo.com>
2024-03-29 16:58:22 +08:00
BillXiang
3c23efd620 fix(client): write qos of hot volume
Signed-off-by: BillXiang <xiangwencheng@gmail.com>
2024-03-29 14:52:06 +08:00
leonrayang
89c6b48673 feat(master): Optimize logic of uid calculate for better performance
close:#22024738

1.use channel to instead of lock and isolate with main heartbeat routine
2.calculate in an aysnchoronus way periodically instead of real time
3.move heartbeat outside the goroutine, the startup of goroutine may delay the response time

Signed-off-by: leonrayang <changliang@oppo.com>
2024-03-29 14:52:06 +08:00
chihe
60b0abafdd enhance(master): if host 0 is down for 10 minutes during repairing, mark dp decomission failed
close##22031300

Signed-off-by: chihe <chihe@oppo.com>
2024-03-29 14:36:56 +08:00
chihe
770a8cd418 fix(datnode): get copy of dp replica when excuting updateMaxMinAppliedID
close:#22015827

Signed-off-by: chihe <chihe@oppo.com>
2024-03-28 16:42:06 +08:00
chihe
f99aeeaf22 fix(master): put success or failed dp to decommission list when reloading meta
adjust debug level

Signed-off-by: chihe <chihe@oppo.com>
2024-03-28 16:42:06 +08:00
唐经宇
4b62cd5b7e 火眼平台 Merge branch lily/develop-v3.4.0 -> develop-v3.4.0 2024-03-28 06:50:31 +00:00
W9068822
0cc19f46d0 fix(master): fix get DP compression data, update cached data when needsUpdate is true
Signed-off-by: W9068822 <v-lijianrong1@oppo.com>
2024-03-28 12:17:08 +08:00
shuqiang-zheng
624843c99f feat(reconstruct): gofmt by gofumpt.
Signed-off-by: shuqiang-zheng <zhengshuqiang@oppo.com>
2024-03-28 11:46:35 +08:00
chihe
674a6b95c2 debug(master): add debug log
close:#21980258

Signed-off-by: chihe <chihe@oppo.com>
2024-03-27 14:25:00 +08:00
chihe
5638315c4b fix(master):reset decommissoin dst if decommissioning dp failed
close:#22015827,#22015229
Signed-off-by: chihe <chihe@oppo.com>
2024-03-27 14:25:00 +08:00
chihe
ef167a3acd enhance(datanode): add log for extent repair speed
close:#22000940

Signed-off-by: chihe <chihe@oppo.com>
2024-03-27 14:25:00 +08:00
chihe
81fcb3e898 fix(datanode): packerror if get extent token failed
close:#21999366
Signed-off-by: chihe <chihe@oppo.com>
2024-03-27 14:25:00 +08:00
Victor1319
af50845179 fix(data): fix nil poniter bug when invoke partition api for repair replica. #21998205
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-03-27 14:25:00 +08:00
Victor1319
2df0fdba47 feat(master): Optimizing Volume Deletion Performance and Speed. #22001647
Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-03-27 14:25:00 +08:00
shuqiang-zheng
7fd5f2ab68 fix(master): Modify the execution order of deletion after freezing.
Signed-off-by: shuqiang-zheng <zhengshuqiang@oppo.com>
2024-03-27 14:19:17 +08:00
shuqiang-zheng
513d71dbe0 fix(master):Fix the problem of updating a cold volume when cachecap is 0 and dp replicaNum is also 0.
Signed-off-by: shuqiang-zheng <zhengshuqiang@oppo.com>
2024-03-27 14:19:17 +08:00
shuqiang-zheng
3e7d119f54 enhance(cli):Modifying Volume Deletion Related Error Messages.
Signed-off-by: shuqiang-zheng <zhengshuqiang@oppo.com>
2024-03-27 14:19:17 +08:00
shuqiang-zheng
dcc43ad142 fix(master):Fixed the problem that volumes marked for deletion but still in the freeze period could not restore the dp and mp replicas after reboot and migration was not supported.
Signed-off-by: shuqiang-zheng <zhengshuqiang@oppo.com>
2024-03-27 14:19:17 +08:00
shuqiang-zheng
743c6dbbaf enhance(cli):.Dynamically configure volDeletionDelayTime through the interface.
Signed-off-by: shuqiang-zheng <zhengshuqiang@oppo.com>
2024-03-27 14:19:17 +08:00
shuqiang-zheng
111cba1798 enhance(cli):Updated the volume information query interface to show the current delayed deletion time of the volume.
Signed-off-by: shuqiang-zheng <zhengshuqiang@oppo.com>
2024-03-27 14:19:17 +08:00
shuqiang-zheng
ef0c089bf6 fix(master):Fix the problem that when the configured delayDeletionTime is changed, the long freeze period volume that is deleted first will block the short freeze period volume that is deleted later.
Signed-off-by: shuqiang-zheng <zhengshuqiang@oppo.com>
2024-03-27 14:19:17 +08:00
shuqiang-zheng
6d4412d017 feat(master): Add a configuration item to determine if freezing before volume deletion, and set the state of the volume to markDelete when it freezes .
Signed-off-by: shuqiang-zheng <zhengshuqiang@oppo.com>
2024-03-27 14:19:17 +08:00
shuqiang-zheng
f2f3521e5a feat(master): update the volume freeze feature to make it unreadable when the volume is frozen.
Signed-off-by: shuqiang-zheng <zhengshuqiang@oppo.com>
2024-03-27 14:19:17 +08:00
shuqiang-zheng
f16360b533 feat(master): Open the volume deletion interface to freeze the volume when performing a volume deletion and then wait 48 hours before deleting the volume.
Signed-off-by: shuqiang-zheng <zhengshuqiang@oppo.com>
2024-03-27 14:19:17 +08:00
leonrayang
77b2f75bd9 feat(reconstruct): gofmt by gofumpt
Signed-off-by: leonrayang <changliang@oppo.com>
2024-03-27 11:11:38 +08:00
leonrayang
4006356ee8 fix(code merge): Raft Partition mock be missed before
Signed-off-by: leonrayang <changliang@oppo.com>
2024-03-27 11:11:00 +08:00
leonrayang
18fa2a055b reconstruct(metanode): reconstruct dir snapshot deletion subprocess
Signed-off-by: leonrayang <changliang@oppo.com>
2024-03-27 11:11:00 +08:00
leonrayang
fc2963c9e6 fix(datanode): Don't use zero for indicating completion in the load partition and Raft status to avoid conflicts with the initial value
close:#21991650

Signed-off-by: leonrayang <changliang@oppo.com>
2024-03-27 11:11:00 +08:00
leonrayang
32c3f957ca fix(datanode): dp may on decommision while doing auto compute crc, so change it to warn
close:#21979158

Signed-off-by: leonrayang <changliang@oppo.com>
2024-03-27 11:11:00 +08:00
NaturalSelect
aa8363526c
test(master): fix decommission unit test
Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-03-27 10:23:37 +08:00
chihe
4873e6d6d3 feature(master):support reset decommission dp status for datanode with no dp left
close#21938887
Signed-off-by: chihe <chihe@oppo.com>
2024-03-26 09:24:10 +08:00
chihe
bdfdf9ed39 fix(master): restore dp host if adding replica failed
close:#22010510
Signed-off-by: chihe <chihe@oppo.com>
2024-03-25 20:20:37 +08:00
shuqiang-zheng
bdc3d4e251 debug(master): Print task-specific information about master's processing of dataNode heartbeat responses.
close: #22000400

Signed-off-by: shuqiang-zheng <zhengshuqiang@oppo.com>
2024-03-25 15:49:35 +08:00
chihe
42efe2bb9e fix(master): fix complie error for last commit
close:#21938898
Signed-off-by: chihe <chihe@oppo.com>
2024-03-25 14:35:26 +08:00
chihe
0082d54cf6 enhance(master): decommission failed dp without reaching rollback retry maximun can be put into decommission list
close:#21938898

Signed-off-by: chihe <chihe@oppo.com>
2024-03-25 12:52:08 +08:00
chihe
25316c24b6 enhance(datanode): remove mutex for extent repair read
close:#21999366

Signed-off-by: chihe <chihe@oppo.com>
2024-03-25 11:12:50 +08:00
chihe
5ee039ea43 fix(datanode): if get extent repair read token for disk failed, release read token for datanoe
close:#21999366,#21999177,#21999095

Signed-off-by: chihe <chihe@oppo.com>
2024-03-25 11:12:47 +08:00
Victor1319
5efb2639e9 feat(data): Optimizing Network Connections for datanode. #21971464
1. fix dirty net connection problem.
2. not user connection pool when enableSumxPool

Signed-off-by: Victor1319 <zengxuewei@oppo.com>
2024-03-25 11:12:43 +08:00
chihe
aba331ac83 fix(master): a rollback is still required, even if the rollback conditions have not been triggered, when reached retry max
close:#21938898

Signed-off-by: chihe <chihe@oppo.com>
2024-03-25 10:44:00 +08:00
chihe
79fca176b8 fix(datanode):Don't delete datapartition when removing raft member
close:#21952262

Signed-off-by: chihe <chihe@oppo.com>
2024-03-22 16:00:17 +08:00
NaturalSelect
c9646470cc feat(master): show error message when decommission dp fails
close: #21952214

Signed-off-by: NaturalSelect <huangzhibin1@oppo.com>
2024-03-22 15:35:52 +08:00
chihe
a01c9084d1 feat(master): retry decommission for failed dp first
close:##21952214

Signed-off-by: chihe <chihe@oppo.com>
2024-03-22 11:00:14 +08:00
chihe
15afa2d3a8 enhance: auto reduce redundant replica if rolling back
close:#21993948

Signed-off-by: chihe <chihe@oppo.com>
2024-03-22 09:03:23 +08:00
chihe
26a52966e8 feature(master): Decommission datapartition operation for cli tool is supported
close:#21975923

Signed-off-by: chihe <chihe@oppo.com>
2024-03-22 09:03:19 +08:00
S9054862
44accfd761 fix(master): avoid to pull old vol view from other followers
close: #21970720
Signed-off-by: S9054862 <huangzhibin1@oppo.com>
2024-03-20 16:13:42 +08:00
S9054862
b052e535b2
fix(data): avoid panic when normal extent hole repair read
close: #21995943
Signed-off-by: S9054862 <huangzhibin1@oppo.com>
2024-03-20 15:25:02 +08:00
baihailong
c6e1cec0d5 fix(libsdk): cfs_close dir print error "stream is not opened yet".
Signed-off-by: baihailong <baihailong@oppo.com>
2024-03-18 23:55:54 -07:00
baihailong
5a1ba775f3 fix(sdk): when the stream's residual server coroutine exits, others stream maybe deleted
Signed-off-by: baihailong <baihailong@oppo.com>
2024-03-18 23:55:07 -07:00
baihailong
15db0098d2 fix(sdk): when the stream's residual server coroutine exits, others stream maybe deleted.#2827
Signed-off-by: baihailong <baihailong@oppo.com>
2024-03-18 23:53:33 -07:00
baihailong
f735517d61 fix(sdk): flush operation appeared bad file descriptor
Signed-off-by: baihailong <baihailong@oppo.com>
2024-03-18 23:52:43 -07:00
baihailong
e9e7e68493 fix(sdk): GetStreamer drop request in channel leadto deadlock
Signed-off-by: baihailong <baihailong@oppo.com>
2024-03-18 23:51:18 -07:00
baihailong
aac8682b77 fix(fuse): MountOption add parameter DisableMountSubtype 2024-03-18 20:36:25 -07:00
leonrayang
30f156b410 fix(datanode): snapshot mod append should not allocate extent id in follower
close:#21938895

Signed-off-by: leonrayang <changliang@oppo.com>
2024-03-19 10:12:01 +08:00
leonrayang
95cb6b5853 feat(datanode): add testcase for repair routine
close:#21938895

Signed-off-by: leonrayang <changliang@oppo.com>
2024-03-19 10:12:01 +08:00
leonrayang
1c5634ff54 feat(mock): Move raft mock to uitl for all module usage
close:#21938895

Signed-off-by: leonrayang <changliang@oppo.com>
2024-03-19 10:12:01 +08:00
leonrayang
be9ab639b9 fix(datanode): normal extent repair need punch hole in senario of snapshot
close:#21938895

Signed-off-by: leonrayang <changliang@oppo.com>
2024-03-19 10:12:01 +08:00
S9054862
709d685acd
fix(util): disable gohook by default
close: #21985685

Signed-off-by: S9054862 <huangzhibin1@oppo.com>
2024-03-18 10:54:33 +08:00
chihe
3f56baa23a feat(master): some enhanced functionalities for checking datanode decommission status
close:#21975944

Signed-off-by: chihe <chihe@oppo.com>
2024-03-14 02:14:30 -07:00
chihe
5c1e869423 feat(master): If the service on the node where the new replica is created stops,attempt other nodes
close:#21967801
Signed-off-by: chihe <chihe@oppo.com>
2024-03-12 19:48:15 +08:00
chihe
96885e94e9 bugfix(datanode): remove logic of fetching replica from master after adding raft member
Signed-off-by: chihe <chi.he@oppo.com>
2024-03-08 15:02:46 +08:00
chihe
f44822614e bugfix(datanode): Apply will be initialized when new datapartition is created
Signed-off-by: chihe <chi.he@oppo.com>
2024-03-08 15:00:10 +08:00
chihe
a01b1c9a87 bugfix(master): 1.datanode with no disk reset to initial 2. restore dp replica if rollback failed
Signed-off-by: chihe <chi.he@oppo.com>
2024-03-08 14:59:53 +08:00
chihe
7b78e7eceb bugfix(raft): When the length of the data is 0, return directly without further decoding
Signed-off-by: chihe <chi.he@oppo.com>
2024-03-08 14:52:24 +08:00
chihe
2cbdfa46e2 feat(datnode): 1.support read enableExtentRepairReadLimit from conf 2.query dp holded token
Signed-off-by: chihe <chihe@oppo.com>
2024-03-08 14:18:00 +08:00
chihe
91f7f914bd feat(datanode): Support the functionality of allowing only one extent data read at a time per disk
Signed-off-by: chihe <chihe@oppo.com>
2024-03-08 14:18:00 +08:00
chihe
1e1cd44c54 feat(datanode): support api handler for reloading data partition
Signed-off-by: chihe <chihe@oppo.com>
2024-03-08 14:18:00 +08:00
chihe
28b7e486af feat(datanode): add log for extent repair speed
Signed-off-by: chihe <chi.he@oppo.com>
2024-03-08 14:18:00 +08:00
chihe
53ae50591b enhance(datanode): add debug log for datanode repair
Signed-off-by: chihe <chi.he@oppo.com>
2024-03-08 14:18:00 +08:00
chihe
b0d110cb59 fix(master): if vol for cache dp is deleted, mark dp decommission status as success directly
Signed-off-by: chihe <chihe@oppo.com>
2024-03-07 09:28:46 +08:00
chihe
400a53f3b1 fix(master): start to check decommission datanode or disk when meta data is ready
Signed-off-by: chihe <chihe@oppo.com>
2024-03-07 09:28:46 +08:00
chihe
92d15d587c feature(master): support specify count for datanode decomission
Signed-off-by: chihe <chihe@oppo.com>
2024-03-07 09:28:46 +08:00
chihe
ffe4d14a6f debug(master): to debug length of dp.Replicas equals to 0
Signed-off-by: chihe <chihe@oppo.com>
2024-03-07 09:28:46 +08:00
2783 changed files with 57550 additions and 298478 deletions

3
.gitattributes vendored
View File

@ -2,6 +2,3 @@
* text=auto eol=lf
# Do not modify line endings for binary files
*.png binary
*.pdf binary
*.tar.gz binary
*.jpg binary

View File

@ -1,110 +1,17 @@
<!-- Thanks for sending the pull request! -->
<!-- Thanks for sending a pull request! -->
<!--
### Contribution Checklist
**What this PR does / why we need it**:
- PR title format should be *type(scope): subject*. For details, see *[Pull Request Title](https://github.com/cubefs/cubefs/blob/master/.github/workflows/check_pull_request.yml)*.
**Which issue this PR fixes**:
<!-- *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close that issue when PR gets merged)*: -->
fixes #
- Each pull request should address only one issue, not mix up code from multiple issues.
**Special notes for your reviewer**:
- Each commit in the pull request has a meaningful commit message. For details, see *[Commit Message](https://github.com/cubefs/cubefs/blob/master/.github/workflows/check_pull_request.yml)*.
- Fill out the template below to describe the changes contributed by the pull request. That will give reviewers the context they need to do the review.
- Once all items of the checklist are addressed, remove the above text and this checklist, leaving only the filled out template below.
**Release note**:
<!-- Steps to write your release note:
1. Use the release-note-* labels to set the release note state (if you have access)
2. Enter your extended release note in the below block; leaving it blank means using the PR title as the release note. If no release note is required, just write `NONE`.
-->
<!-- Either this PR fixes an issue, -->
Fixes: #xyz
<!-- or this PR is one task of an issue. -->
Main Issue: #xyz
### Motivation
<!-- Explain here the context, and why you're making that change. What is the problem you're trying to solve. -->
blaaaaa
### Modifications
<!-- Describe the modifications you've done. -->
``` text
blaaaaa
```release-note
```
### Types of changes
<!-- Show in a checkbox-style, the expected types of changes your project is supposed to have: -->
<!-- _Put an `x` in the boxes that apply_ -->
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Bugfix (non-breaking change which fixes an issue)
- [ ] Documentation Update (if none of the other choices apply)
- [ ] So on...
### Verifying this change
<!-- Please pick either of the following options. -->
- [ ] Make sure that the change passes the testing checks.
This change is a trivial rework / code cleanup without any test coverage.
*(or)*
This change is already covered by existing tests, such as *(please describe tests)*.
*(or)*
This change added tests and can be verified as follows:
*(example:)*
- *This can be verified in development debugging*
- *This can be realized in a mocked environment, like a test cluster consisting in docker*
*(or)*
This change `MUST` reappear in online clusters, or occur in that specific scenarios.
### Does this pull request potentially affect one of the following parts:
<!-- Which of the following parts are affected by this change? -->
- [ ] Master
- [ ] MetaNode
- [ ] DataNode
- [ ] ObjectNode
- [ ] AuthNode
- [ ] LcNode
- [ ] Blobstore
- [ ] Client
- [ ] Cli
- [ ] SDK
- [ ] Other Tools
- [ ] Common Packages
- [ ] Dependencies
- [ ] Anything that affects deployment
### Documentation
<!-- Is there a chinese and english document modification? -->
- [ ] `doc` <!-- Your PR contains doc changes. -->
- [ ] `doc-required` <!-- Your PR changes impact docs and you will update later -->
- [ ] `doc-not-needed` <!-- Your PR changes do not impact docs -->
- [ ] `doc-complete` <!-- Docs have been already added -->
### Review Expection
<!-- How long would you like the team to be completed in your contributing? -->
- [ ] `in-two-days`
- [ ] `weekly`
- [ ] `free-time`
- [ ] `whenever`
### Matching PR in forked repository
<!-- enter the url if has PR in forked repository. -->
PR in forked repository: <!-- ENTER URL HERE -->
<!-- Thanks for contributing, best days! -->

View File

@ -0,0 +1,23 @@
version: 1
env:
- CGO_ENABLED=0
flags:
- -trimpath
goos: linux
goarch: amd64
# (Optional) Entrypoint to compile.
main: ./preload/preload.go
binary: cfs-preload-{{ .Os }}-{{ .Arch }}
ldflags:
- "-X github.com/cubefs/cubefs/proto.Version={{ .Env.VERSION }}"
- "-X github.com/cubefs/cubefs/proto.CommitID={{ .Env.COMMIT_ID }}"
- "-X github.com/cubefs/cubefs/proto.BranchName={{ .Env.BRANCH_NAME }}"
- "-X github.com/cubefs/cubefs/proto.BuildTime={{ .Env.BUILD_TIME }}"
- "-X github.com/cubefs/cubefs/blobstore/util/version.version={{ .Env.BRANCH_NAME }}/{{ .Env.COMMIT_ID }}"
- "-w -s"

36
.github/workflows/blobstore_checks.yml vendored Normal file
View File

@ -0,0 +1,36 @@
name: BlobStore-Checks
on:
push:
paths:
- 'blobstore/**.go'
pull_request:
types: [opened, synchronize, reopened]
paths:
- 'blobstore/**'
permissions:
contents: read
jobs:
GolangFormat:
name: format
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- name: Go code format with gofumpt
run: |
docker/run_docker.sh --bsgofumpt
GolangCI-Lint:
name: lint
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- name: run golangci-lint
run: |
docker/run_docker.sh --bsgolint

View File

@ -8,9 +8,6 @@ on:
- reopened
- synchronize
permissions:
contents: read
jobs:
check-pr-title:
name: Check Pull Request Title
@ -48,41 +45,31 @@ jobs:
check-commit-message:
name: Check Commit Message
runs-on: ubuntu-latest
env:
JOB_COMMIT_FILE: '/tmp/commits.json'
steps:
- name: Get PR Commits
id: 'get-pr-commits'
uses: sejust/get-pr-commits@21ca1696fc716fa9423291cddc3d7a82668cfbc2 # v1.3.2
uses: tim-actions/get-pr-commits@3efc1387ead42029a0d488ab98f24b7452dc3cde # v1.3.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
output-file: ${{ env.JOB_COMMIT_FILE }}
- name: Check Title
uses: sejust/commit-message-checker-with-regex@5bedef5c21ee29bb438572bdb715ad65159264f8 # v0.3.3
uses: tim-actions/commit-message-checker-with-regex@094fc16ff83d04e2ec73edb5eaf6aa267db33791 # v0.3.2
with:
commits: ${{ env.JOB_COMMIT_FILE }}
pattern: '^[a-z]+\([a-z0-9_\-\.]+\): .+\n(\n.*)*$'
commits: ${{ steps.get-pr-commits.outputs.commits }}
pattern: '^[a-z]+\([a-z]+\): .+\n(\n.*)*$'
error: 'Title likes `<type>(<scope>): <subject>`'
- name: Check Title Space
uses: sejust/commit-message-checker-with-regex@5bedef5c21ee29bb438572bdb715ad65159264f8 # v0.3.3
with:
commits: ${{ env.JOB_COMMIT_FILE }}
pattern: '^[^ ]+(?: [^ ]+)*\n(\n.*)*$'
error: 'Title has consecutive spaces'
- name: Check Subject Line Length
uses: sejust/commit-message-checker-with-regex@5bedef5c21ee29bb438572bdb715ad65159264f8 # v0.3.3
uses: tim-actions/commit-message-checker-with-regex@094fc16ff83d04e2ec73edb5eaf6aa267db33791 # v0.3.2
with:
commits: ${{ env.JOB_COMMIT_FILE }}
commits: ${{ steps.get-pr-commits.outputs.commits }}
pattern: '^.{0,100}\n(\n.*)*$'
error: 'Subject too long (max 100)'
- name: Check Body Line Length
uses: sejust/commit-message-checker-with-regex@5bedef5c21ee29bb438572bdb715ad65159264f8 # v0.3.3
uses: tim-actions/commit-message-checker-with-regex@094fc16ff83d04e2ec73edb5eaf6aa267db33791 # v0.3.2
with:
commits: ${{ env.JOB_COMMIT_FILE }}
commits: ${{ steps.get-pr-commits.outputs.commits }}
pattern: '^.+\n(\n.{0,100})*$'
error: 'Body line too long (max 100)'

View File

@ -16,8 +16,12 @@ on:
- release-*
- develop-*
- blobstore-*
# paths-ignore:
paths-ignore:
# - 'blobstore/**'
# - '.github/**'
# - 'docs/**'
# - 'docs-zh/**'
# - '**.md'
permissions:
contents: read
@ -29,7 +33,7 @@ jobs:
- name: Checkout repo
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- name: Find changed files of document
- name: Find changed files
id: changed-files
uses: tj-actions/changed-files@87697c0dca7dd44e37a2b79a79489332556ff1f3 # v37.6.0
with:
@ -39,42 +43,13 @@ jobs:
docs-zh/**
**.md
- name: Find changed files of blobstore
id: changed-blobs
uses: tj-actions/changed-files@87697c0dca7dd44e37a2b79a79489332556ff1f3 # v37.6.0
with:
files: |
blobstore/**
- name: All changed documents
if: steps.changed-files.outputs.only_changed == 'true'
env:
CI_ALL_CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
run: |
for file in ${CI_ALL_CHANGED_FILES}; do
echo "<$file> was changed"
done
- name: Check gofmt
if: steps.changed-files.outputs.only_changed != 'true'
run: |
docker/run_docker.sh --format
- name: Unit test for blobstore
if: steps.changed-blobs.outputs.only_changed == 'true'
run: |
docker/run_docker.sh --testblobstore
- name: Unit test for cubefs
if: ${{ (steps.changed-files.outputs.only_changed != 'true') &&
(steps.changed-blobs.outputs.any_changed != 'true') }}
run: |
docker/run_docker.sh --testcubefs
- name: Unit test for all
if: ${{ (steps.changed-files.outputs.only_changed != 'true') &&
(steps.changed-blobs.outputs.only_changed != 'true') &&
(steps.changed-blobs.outputs.any_changed == 'true') }}
- name: Unit tests
if: steps.changed-files.outputs.only_changed != 'true'
run: |
docker/run_docker.sh --test

View File

@ -41,21 +41,11 @@ jobs:
strategy:
fail-fast: false
matrix:
include:
- language: go
build-mode: autobuild
- language: java-kotlin
build-mode: none # This mode only analyzes Java. Set this to 'autobuild' or 'manual' to analyze Kotlin too.
- language: python
build-mode: none
# CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift'
# Use `c-cpp` to analyze code written in C, C++ or both
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
language: [ 'java', 'python' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby', 'swift' ]
# Use only 'java' to analyze code written in Java, Kotlin or both
# Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
- name: Checkout repository
@ -63,10 +53,9 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # v3.28.9
uses: github/codeql-action/init@a09933a12a80f87b87005513f0abb1494c27a716 # v2.21.4
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
@ -74,6 +63,12 @@ jobs:
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@a09933a12a80f87b87005513f0abb1494c27a716 # v2.21.4
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@ -85,6 +80,6 @@ jobs:
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # v3.28.9
uses: github/codeql-action/analyze@a09933a12a80f87b87005513f0abb1494c27a716 # v2.21.4
with:
category: "/language:${{matrix.language}}"

View File

@ -14,7 +14,7 @@ jobs:
- name: analysis
uses: actions-cool/issues-similarity-analysis@8f46978e3e8b79d736997a225c95d27d9029f294 # v1.3.1
with:
filter-threshold: 0.8
filter-threshold: 0.6
comment-title: '### See'
comment-body: '${index}. ${similarity} #${number}'
show-footer: false

View File

@ -32,12 +32,12 @@ jobs:
steps:
- name: "Checkout code"
uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1
uses: ossf/scorecard-action@08b4669551908b1024bb425080c797723083c031 # v2.2.0
with:
results_file: results.sarif
results_format: sarif
@ -67,6 +67,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@4355270be187e1b672a7a1c7c7bae5afdc1ab94a # v3.24.10
uses: github/codeql-action/upload-sarif@0ba4244466797eb048eb91a6cd43d5c03ca8bd05 # v2.21.2
with:
sarif_file: results.sarif

5
.gitignore vendored
View File

@ -20,8 +20,3 @@ java/src/main/resources/*.so
/cover.output
/cubefs_unittest.output
.version
.cache
.clangd
blobstore/cpp/build
blobstore/cpp/**/*.pb.cc
blobstore/cpp/**/*.pb.h

View File

@ -110,6 +110,24 @@ builds:
- -X {{.Env.PROTO}}.BuildTime={{.Date}}
- -X {{.Env.VV}}={{.Branch}}/{{.Date}}
- -w -s
- id: "preload"
main: ./preload
binary: cfs-preload
env:
- CGO_ENABLED=0
goos:
- linux
goarch:
- amd64
flags:
- -trimpath
ldflags:
- -X {{.Env.PROTO}}.Version={{.Version}}
- -X {{.Env.PROTO}}.CommitID={{.FullCommit}}
- -X {{.Env.PROTO}}.BranchName={{.Branch}}
- -X {{.Env.PROTO}}.BuildTime={{.Date}}
- -X {{.Env.VV}}={{.Branch}}/{{.Date}}
- -w -s
- id: "server"
main: ./cmd

View File

@ -8,14 +8,11 @@ rules:
- '*.go'
patterns:
- pattern-regex: '(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)'
- pattern-not-regex: '127\.0\.0\.\d+'
- pattern-not-regex: '10\.\d+\.\d+.\d+'
- pattern-not-regex: '192\.168\.\d+.\d+'
- pattern-not-regex: '192\.0\.2\.\d+' # 192.0.2.0/24 (TEST-NET-1, rfc5737)
- pattern-not-regex: '198\.51\.100\.\d+' # 198.51.100.0/24 (TEST-NET-2, rfc5737)
- pattern-not-regex: '203\.0\.113\.\d+' # 203.0.113.0/24 (TEST-NET-3, rfc5737)
- pattern-not-regex: '172\.16\.\d+\.\d+' # 172.16.0.0/12
- pattern-not-regex: '169\.254\.\d+\.\d+'# 169.254.0.0/16
severity: WARNING
- id: rfc-3849-ip-address
languages:
@ -25,5 +22,5 @@ rules:
include:
- '*.go'
patterns:
- pattern-regex: '(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7})|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))'
severity: WARNING
- pattern-regex: '(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))'
severity: WARNING

View File

@ -1,2 +0,0 @@
depends/
vendor/

View File

@ -14,7 +14,6 @@ This page contains a list of organizations who are using CubeFS in production or
- **[LinkSure Network](https://cn.wifi.com)**: LinkSure uses CubeFS to store application logs running inside container environments as well as nginx logs. Also, they use CubeFS as the backend storage for Elasticsearch.
- **[Reconova](http://www.reconova.com):** Reconova uses CubeFS to store massive small files in the production environment. It currently uses one CubeFS volume to store more than 80 million small files and each file around 40 kilobytes in size.
- **[BIGO](https://www.bigo.sg/):** BIGO uses CubeFS to store logs for AI platform applications that running in the container environment, because CubeFS have excellent concurrent processing capability.
- **[Vipshop](https://www.vip.com/):** Cubefs is used in the offline hybrid deployment scenario of YARN on Kubernetes.After some adjustments, it has become quite stable.
## Adopters
@ -30,19 +29,11 @@ This page contains a list of organizations who are using CubeFS in production or
| [LinkSure Network](https://cn.wifi.com) | ![production](https://img.shields.io/badge/-production-blue?style=flat) |
| [BIGO LIVE](https://www.bigo.tv/cn/) | ![production](https://img.shields.io/badge/-production-blue?style=flat) |
| [Xiaomi](https://www.mi.com/global/) | ![production](https://img.shields.io/badge/-production-blue?style=flat) |
| [shengwang.cn](https://www.shengwang.cn/) | ![production](https://img.shields.io/badge/-production-blue?style=flat) |
| [Vipshop](https://www.vip.com/) | ![production](https://img.shields.io/badge/-production-blue?style=flat) |
| [CreditEase](https://www.creditease.com/) | ![production](https://img.shields.io/badge/-production-blue?style=flat) |
| [TD Tech](https://www.td-tech.com/) | ![production](https://img.shields.io/badge/-production-blue?style=flat) |
| [Digtal guangdong](https://www.digitalgd.com.cn/) | ![production](https://img.shields.io/badge/-production-blue?style=flat) |
| [PITS Global Data Recovery Services](https://www.pitsdatarecovery.net/) | ![testing](https://img.shields.io/badge/-testing-green?style=flat) |
| [Da-Jiang Innovations](https://www.dji.com/cn) | ![testing](https://img.shields.io/badge/-testing-green?style=flat) |
| [Yanhuang Data](https://yanhuangdata.com/) | ![testing](https://img.shields.io/badge/-testing-green?style=flat) |
| [Sinosoft](http://www.sinosoft.com.cn) | ![testing](https://img.shields.io/badge/-testing-green?style=flat) |
| [DADA](https://about.imdada.cn) | ![testing](https://img.shields.io/badge/-testing-green?style=flat) |
| [Club Factory](https://www.wholeeprime.com/) | ![testing](https://img.shields.io/badge/-testing-green?style=flat) |
| [Nanjing University](https://www.nju.edu.cn/en/) | ![testing](https://img.shields.io/badge/-testing-green?style=flat) |
| [PSBC](https://www.psbc.com/) | ![testing](https://img.shields.io/badge/-testing-green?style=flat) |
| [DeepMirror](https://deepmirror.vercel.app/) | ![testing](https://img.shields.io/badge/-testing-green?style=flat) |
| [shoppe](https://shopee.com/) | ![testing](https://img.shields.io/badge/-testing-green?style=flat) |
| [houdutech](https://www.houdutech.cn) | ![testing](https://img.shields.io/badge/-testing-green?style=flat) |
| [cecloud](https://www.cecloud.com/) | ![testing](https://img.shields.io/badge/-testing-green?style=flat) |

View File

@ -1,159 +1,3 @@
## Release v3.5.3 - 2025/12/31
### **UPGRAGDE NOTICE**
If you are using a CubeFS version earlier than v3.5.0, please refer to the UPGRADE NOTICE in version v3.5.0 for detailed upgrade steps and upgrade to v3.5.0 first.
Upgrade nodes in this order: flshnode → master → datanode → metanode → objectnode → lcnode → cli → client.
Upgrade lcnode and flashnode when needed.
Deploy flashgroupmanager when needed
Clients should use versions later than 3.2.0. Older versions need to be upgraded promptly; otherwise, there will be a risk of compromising stability.
### **Main Feature**
#### High-throughput LLM/MLLM training with 8ms+ computestorage latency tolerance
+ `client`: Support asynchronous flush for extent handler to improve write performance. Write speed exceeds 1.2 GB/s; on a high-spec H20 training node, a single client can achieve 10+ GB/s aggregate throughput with 10 concurrent large-file writes.(#3973,@bboyCH4)
+ `client`: Optimize the client read-ahead mechanism and memory footprint; single-file read speeds exceed 2 GB/s. (#3982,@bboyCH4)
+ `client`: Metadata cache acceleration for small-file prewarm (#3995,@Victor1319)
Note: Refer to the latest community documentation for enabling and tuning.
#### Distributed cache can run as an independent service
+ `flashgroupmanager`: Introduce flashgroupmanager node and topology to support flashnode cluster management. (@bboyCH4)
+ `flashnode`: Support block-level data read and write operations. (#3977, @clinx)
+ `tools`: Add `rctest` (benchmark) and `rcconfig` (config) tools for remote cache system. (#3981,@bboyCH4,@clinx)
+ `client`: Provide SDK for FlashNode object storage data block upload/download service #3985,@bboyCH4,@clinx
+ `client`: Implement NearRead strategy to prioritize reading from the nearest replica to reduce latency. (#3976,@zhumingze1108)
### **Enhance**
+ `client`: Fuse library supports parallel processing of FUSE requests to improve concurrency. (#3974,@Victor1319)
+ `client`: Optimize metadata cache performance. (#3974,@Victor1319)
+ `client`: Add `tcpAliveTime` parameter for better TCP connection management. (#3974,@Victor1319)
+ `master`: Support DP decommission status evolution history query. (#3987,@shuqiang-zheng)
+ `master`: Add `TryDecommissionRunningDiskIgnoreDps` to support differentiated strategies for disk decommission based on different reasons. (#3975,@shuqiang-zheng)
+ `master`: Add audit logs for `migrateMetaPartition` and record reasons for DP migration/rollback. (#3975,@shuqiang-zheng)
+ `datanode`: Support reason passthrough for DP migration. (#3975,@shuqiang-zheng)
+ `flashnode`: Optimize cache operation opcodes and processing logic. (#3988,@clinx,@bboyCH4)
### **Bugfix**
* `master`: Decommission token consumed twice on restart during two-replica DP decommissioning(#3978,@shuqiang-zheng)
* `client`: Fix `ltp iogen01` test failure when pre-reading (ahead read) is enabled. (#3980,@clinx)
* `client`: Offset calculation error during client readahead with partial hits(#3979,@bboyCH4)
* `master`: Some DPs remain in decommission queue when disk offline marking fails, affecting subsequent decommissions (#3983,@shuqiang-zheng)
* `master`: Incorrect disk/node decommission progress display for 2-replica DPs due to leader change(#3984,@Victor1319)
## Release v3.5.2 - 2025/07/31
### **UPGRAGDE NOTICE**
If you are using a CubeFS version earlier than v3.5.0, please refer to the UPGRADE NOTICE in version v3.5.0 for detailed upgrade steps and upgrade to v3.5.0 first.
Upgrade nodes in this order: master → metanode → datanode → objectnode → cli → client.
Upgrade lcnode and deploy flashnode when needed.
Clients should use versions later than 3.2.0. Older versions need to be upgraded promptly; otherwise, there will be a risk of compromising stability.
### **Main Feature**
#### For large language models (LLMs) and multimodal LLM (MLLM) training, delivers high throughput (LLM checkpoints) and tolerates high-latency computestorage separation (8 ms+), achieving training durations comparable to public cloud deployments in the same region
+ `master/lcnode`: Lifecycle adds filtering rule based on file size. (#3893, @Victor1319)
+ `master`: dp decommission support priority&concurrency control. (#3891, @shuqiang-zheng)
+ `master`: Add master replica abnormality alarm. (#3882, @zhumingze1108)
+ `master`: Add disk decommission success alarm. (#3886, @zhumingze1108)
+ `meta`: Support mp reload capability. (#3894, @leonrayang)
+ `meta`: Volume file size distribution statistics. (#3884, @M1eyu2018, @zhumingze1108)
+ `client`: Implement client pre-reading function. (#3889, @yanbin027, @Victor1319)
+ `client`: Client delay monitoring statistics. (#3885, @M1eyu2018, @aaronwu2010)
+ `data`: Bad disk detection and lost disk discovery. (#3878, @zhumingze1108)
+ `data`: Support asynchronous limitio restrictions. (#3881, @zhumingze1108)
+ `master/data/meta/cli`: dp and mp read-only reasons display. (#3880, @zhumingze1108)
### **Enhance**
+ `all`: Remove the part of the code that uses datanode as cache. (#3888, @Victor1319)
+ `master`: DataNode&Disk&dp Decommission logic optimization. (#3891, @shuqiang-zheng, @zhumingze1108)
+ `meta`: Optimize metanode memory usage. (#3892, @Victor1319)
+ `data`: Optimize datanode memory usage. (#3887, @aaronwu2010)
+ `data`: Optimize and repair the process of tinyDeleteRecord synchronization logic. (#3890, @Victor1319)
+ `cli`: cli datapartition check display optimization. (#3879, @zhumingze1108)
### **Bugfix**
* `master/data`: Repair process blocked by host0 replica. (@shuqiang-zheng)
* `master/raft`: Fixed the issue of dp no leader caused by failure to add raft members during decommission process. (@shuqiang-zheng)
## Release v3.5.1 - 2025/05/28
### **UPGRAGDE NOTICE**
If you are using a CubeFS version earlier than v3.5.0, please refer to the UPGRADE NOTICE in version v3.5.0 for detailed upgrade steps and upgrade to v3.5.0 first.
### **Main Feature**
+ `all`: flash cache in cluster. #2943 @bboyCH4, @longerfly, @slasher, @shuqiang-zheng, @clinx
+ `flash`: scale out the cache layer by adding more cache nodes to handle increased read traffic.
+ `master`: save the FlashNode topology state and push FlashNode topology data to the client.
+ `client`: data reads are routed to the appropriate cache node based on consistent hashing.
+ `cli`: use CLI commands to query the current cache status and control its behavior.
### **Enhance**
+ `data/meta`: support for dynamic adjustment of gogc. (#3816, @shuqiang-zheng)
+ `client`: actively release part of the client's memory to reduce memory footprint. (@bboyCH4)
+ `client`: support reading data with quorum consistency. (@zhumingze1108)
### **Bugfix**
* `meta`: tune the retry mechanism for failed volume creation to minimize the impact on volume deletion performance. (@bboyCH4)
* `data`: no longer allow single replica dp raftForce deletion. (@zhumingze1108)
* `data`: add CRC check for extent ID allocation. (@leonrayang)
* `client`: monitor already contains grouping label commit. (@zhumingze1108)
* `client`: failure to update the local extent cache generation resulted in an LTP failure.(@bboyCH4)
* `flash`: Removing an fn immediately after a single 200ms timeout on origin fetch is too sensitive. A better approach would be to remove it only after multiple consecutive timeouts. (@longerfly)
* `object`: copy data between different buckets. (@clinx)
## Release v3.5.0 - 2025/03/13
### **UPGRAGDE NOTICE**
If you are using a CubeFS version earlier than v3.4.0, please refer to the UPGRADE NOTICE in version v3.4.0 for detailed upgrade steps and upgrade to v3.4.0 first.
### **Attention:**
If you deployed the cluster based on an older version, please refer to the documentation [upgrade 3.5.0](https://github.com/cubefs/cubefs/wiki/CubeFS-v3.5.0-upgrade-manual) for upgrade steps to version 3.5.+
### **Main Feature**
+ `all`: Supports the capability to manage different storage media. #3603@bboyCH4, @true1064, @Victor1319
+ `master/lcnode`: Supports automatic migration of cold data via lifecycle management . (#3604 @bboyCH4, @honeyvinnie, @Victor1319)
+ `master/client`: Support querying all client versions and IP information. (#3606 , @Victor1319)
+ `datanode`: Support for direct I/O read operations with volume. (#3630, @Victor1319)
### **Enhance**
+ `bcache`: bcache supports configuration switches that work for non-SSD types. (#3607, @longerfly)
+ `sdk`: Support the ability of the tool to count the size of the directory by access time. (#3608, @longerfly)
+ `sdk`: Use distributed locks to prevent concurrent deletions from multiple clients in the recycle bin. (#3610, @Victor1319)
+ `sdk/datanode`: Optimize data read path performance for storage and compute separation scenarios. (#3631, @Victor1319)
+ `sdk/metanode`: Support metadata reads in a leaderless environment using quorum mode and meta follower mode. (#3632, @Victor1319)
### **Bugfix**
+ `raft`: Conflicts between Raft metadata and WAL logs (#3605, @Victor1319
+ `sdk`: Deleting files can cause client panic when the recycle bin is enabled (#3609, @bboyCH4)
+ `all`: Fix bugs related to historical version faults and anomalies. ( @Victor1319
## Release v3.4.0 - 2024/10/28
### **UPGRAGDE NOTICE**
@ -343,7 +187,7 @@ If your Blobstore version is v1.1.0 or before which built with cubefs-blobstore
### **Bugfix**
* `master`: master snapshot recover not reset local rocksdb info (#1522, @wuchunhuan )
* `master`:Memory cost too fast during restart in case of data partition's count is magnity (#1774 , @leonrayang)
* `master`:Memory cost too fast during restart in case of data partitions count is magnity (#1774 , @leonrayang)
* `client`: Readonly dp can still accept write request from client (#1779, @bboyCH4)
* `metanode`: Metanode should not establish connection to blobstore for cold volume (#1781, @bboyCH4)
* `client`: blockcache service may be oom when the client caches many large files concurrently (#1783, @zhangtianjiong)
@ -628,7 +472,7 @@ https://zhuanlan.zhihu.com/p/28417779
faultDomainGrpBatchCntdefault count:3can also set 2 or 1
If zone is unavaliable caused by network partition interruptioncreate nodeset group according to usable zone
Set "faultDomainBuildAsPossible" true, default is false
Set “faultDomainBuildAsPossible” true, default is false
The distribution of nodesets under the number of different faultDomainGrpBatchCnt
3 zone1 nodeset per zone
@ -665,7 +509,7 @@ Set "faultDomainBuildAsPossible" true, default is false
**3. Note**
**1) After the fault domain is enabled, all devices in the new zone will join the fault domain**
**2) The created volume will preferentially select the resources of the original zone**
**3) Need add configuration items to use domain resources when creating a new volume according to the table below. By default, the original zone resources are used first if it's avaliable**
**3) Need add configuration items to use domain resources when creating a new volume according to the table below. By default, the original zone resources are used first if its avaliable**
| Cluster:faultDomain | Vol:crossZone | Vol:defaultPriority | Rules for volume to use domain |
h| ------ | ------ | ------ |------ |
@ -681,14 +525,16 @@ example :` curl "http://10.177.200.119:17010/admin/createVol?name=vol_cross5&cap
### **Content Summary**
**1. Purpose**
In order to query the content summary information of a directory efficiently, e.g. total file size, total files and total directories, v2.5 stores such information as the parent directory's xattr.
In order to query the content summary information of a directory efficiently, e.g. total file size, total files and total directories, v2.5 stores such information as the parent directorys xattr.
The parent directory stores the files, directories and total file size of the current directory. Then only need to make recursive of the sub directories, and accumulate the information stored by the directories to query the content summary information of a directory.
**2. Configuration**
Client config file: fuse.json
**1) Enable XAttr**
"enableXattr":"true"
”enableXattr”:”true”
**2) Enable Summary**
”enableSummary”:”true”
Both of xattr and summay have to be set if you want to mount a volume to the local disk.
Set summary is enough if you want to access the volume via libsdk.so.
@ -701,7 +547,7 @@ The parent directory stores the files, directories and total file size of the cu
cfs_getsummary (libsdk/libsdk.go)
**4. Note**
1)The incremental files' summary information will be held by their parent directories. But the old files will not. Use cfs_refreshsummary
1)The incremental files summary information will be held by their parent directories. But the old files will not. Use cfs_refreshsummary
(libsdk/libsdk.go) interface to rebuild the content summary information.
2)The files, directories and total file size are updated asynchronously in the background. Users are not aware of these operations, but it does
increase the requests to meta servers (usually doubled). You are recommended to evaluate the impact to your cluster before using this
@ -945,7 +791,7 @@ Release2.1.0 did a lot of work to optimize memory usage.
* `object`: Change from hard link to soft link in **CopyObject** action. [#563](https://github.com/cubefs/cubefs/pull/563)
* `object`: Solved **parallel-safety** issue; Clean up useless data on failure in **upload** part. [#553](https://github.com/cubefs/cubefs/pull/553)
* `object`: Fixed a problem in listing multipart uploads. [#595](https://github.com/cubefs/cubefs/pull/595)
* `object`: Solve the problem that back-end report "NotExistErr" error when uploading files with the same key in parallel. [#685](https://github.com/cubefs/cubefs/pull/685)
* `object`: Solve the problem that back-end report “NotExistErr” error when uploading files with the same key in parallel. [#685](https://github.com/cubefs/cubefs/pull/685)
* `fuse`: Evict inode cache when dealing with forget. [#523](https://github.com/cubefs/cubefs/pull/523)
### Document

0
GOVERNANCE_CN.md Normal file → Executable file
View File

View File

@ -1,63 +1,63 @@
# Technical Steering Committee(TSC)
| Name | Email | Organization |
| ---------------------------------------------------------- | --------------------------------------------------------------- | ------------ |
| Haifeng Liu ([@bladehliu](https://github.com/bladehliu)) | [bladehliu@gmail.com](mailto:bladehliu@gmail.com) | [Individual] |
| Weilong Guo ([@awzhgw](https://github.com/awzhgw)) | [guowl18702995996@gmail.com](mailto:guowl18702995996@gmail.com) | [JD.com] |
| Xiaochun He ([@xiaochunhe](https://github.com/xiaochunhe)) | [626148589@qq.com](mailto:626148589@qq.com) | [OPPO] |
| Mofei Zhang ([@mervinkid](https://github.com/mervinkid)) | [mofei2816@gmail.com](mailto:mofei2816@gmail.com) | [JD.com] |
| Liang Chang ([@leonrayang](https://github.com/leonrayang)) | [chl696@sina.com](mailto:chl696@sina.com) | [OPPO] |
| Name | Email | Organization |
|------------------------------------------------------------------------|-----------------------------------------------------------------|--------------|
| Haifeng Liu ([@bladehliu](https://github.com/bladehliu)) | [bladehliu@qq.com](mailto:bladehliu@qq.com) | [Individual] |
| Weilong Guo ([@awzhgw](https://github.com/awzhgw)) | [guowl18702995996@gmail.com](mailto:guowl18702995996@gmail.com) | [JD.com] |
| Xiaochun He ([@xiaochunhe](https://github.com/xiaochunhe)) | [626148589@qq.com](mailto:626148589@qq.com) | [OPPO] |
| Mofei Zhang ([@mervinkid](https://github.com/mervinkid)) | [mofei2816@gmail.com](mailto:mofei2816@gmail.com) | [JD.com] |
| Liang Chang ([@leonrayang](https://github.com/leonrayang)) | [chl696@sina.com](mailto:chl696@sina.com) | [OPPO] |
# Maintainers
| Name | Email | Organization |
| ---------------------------------------------------------------- | --------------------------------------------------------------- | ------------ |
| Haifeng Liu ([@bladehliu](https://github.com/bladehliu)) | [bladehliu@gmail.com](mailto:bladehliu@gmail.com) | [Individual] |
| Weilong Guo ([@awzhgw](https://github.com/awzhgw)) | [guowl18702995996@gmail.com](mailto:guowl18702995996@gmail.com) | [JD.com] |
| Shuoran Liu ([@shuoranliu](https://github.com/shuoranliu)) | [shuoranliu@gmail.com](mailto:shuoranliu@gmail.com) | [BEIKE] |
| Xiaochun He ([@xiaochunhe](https://github.com/xiaochunhe)) | [626148589@qq.com](mailto:626148589@qq.com) | [OPPO] |
| Mofei Zhang ([@mervinkid](https://github.com/mervinkid)) | [mofei2816@gmail.com](mailto:mofei2816@gmail.com) | [JD.com] |
| Liang Chang ([@leonrayang](https://github.com/leonrayang)) | [chl696@sina.com](mailto:chl696@sina.com) | [OPPO] |
| Xuewei Zeng ([@Victor1319](https://github.com/Victor1319)) | [834863182@qq.com](mailto:834863182@qq.com) | [OPPO] |
| Dr. Wei Ding ([@wding109](https://github.com/wding109)) | [wding109@gmail.com](mailto:wding109@gmail.com) | [ByteDance] |
| Dr. Junyuan Zeng ([@jzeng4](https://github.com/jzeng4)) | [jzeng04@gmail.com](mailto:jzeng04@gmail.com) | [LinkedIn] |
| hooklee2000 ([@hooklee2000](https://github.com/hooklee2000)) | [hooklee2000@gmail.com](mailto:hooklee2000@gmail.com) | [XFusion] |
| Zhendong Li ([@lizhendong666](https://github.com/lizhendong666)) | [lizhendong666@gmail.com](mailto:lizhendong666@gmail.com) | [JD.com] |
| Xiaobo Yu ([@cessory](https://github.com/cessory)) | [yxbstorm@gmail.com](mailto:yxbstorm@gmail.com) | [OPPO] |
| Cloudstriff ([@Cloudstriff](https://github.com/Cloudstriff)) | [chenjiongwendao@qq.com](mailto:chenjiongwendao@qq.com) | [OPPO] |
| slasher ([@sejust](https://github.com/sejust)) | [mcq.sejust@gmail.com](mailto:mcq.sejust@gmail.com) | [OPPO] |
| Name | Email | Organization |
|------------------------------------------------------------------------|-----------------------------------------------------------------|--------------|
| Haifeng Liu ([@bladehliu](https://github.com/bladehliu)) | [bladehliu@qq.com](mailto:bladehliu@qq.com) | - |
| Weilong Guo ([@awzhgw](https://github.com/awzhgw)) | [guowl18702995996@gmail.com](mailto:guowl18702995996@gmail.com) | [JD.com] |
| Shuoran Liu ([@shuoranliu](https://github.com/shuoranliu)) | [shuoranliu@gmail.com](mailto:shuoranliu@gmail.com) | [BEIKE] |
| Xiaochun He ([@xiaochunhe](https://github.com/xiaochunhe)) | [626148589@qq.com](mailto:626148589@qq.com) | [OPPO] |
| Mofei Zhang ([@mervinkid](https://github.com/mervinkid)) | [mofei2816@gmail.com](mailto:mofei2816@gmail.com) | [JD.com] |
| Liang Chang ([@leonrayang](https://github.com/leonrayang)) | [chl696@sina.com](mailto:chl696@sina.com) | [OPPO] |
| Xuewei Zeng ([@Victor1319](https://github.com/Victor1319)) | [834863182@qq.com](mailto:834863182@qq.com) | [OPPO] |
| Dr. Wei Ding ([@wding109](https://github.com/wding109)) | [wding109@gmail.com](mailto:wding109@gmail.com) | [ByteDance] |
| Dr. Junyuan Zeng ([@jzeng4](https://github.com/jzeng4)) | [jzeng04@gmail.com](mailto:jzeng04@gmail.com) | [LinkedIn] |
| hooklee2000 ([@hooklee2000](https://github.com/hooklee2000)) | [hooklee2000@gmail.com](mailto:hooklee2000@gmail.com) | [XFusion] |
| Zhendong Li ([@lizhendong666](https://github.com/lizhendong666)) | [lizhendong666@gmail.com](mailto:lizhendong666@gmail.com) | [JD.com] |
| Xiaobo Yu ([@cessory](https://github.com/cessory)) | [yxbstorm@gmail.com](mailto:yxbstorm@gmail.com) | [OPPO] |
| Cloudstriff ([@Cloudstriff](https://github.com/Cloudstriff)) | [chenjiongwendao@qq.com](mailto:chenjiongwendao@qq.com) | [OPPO] |
| slasher ([@sejust](https://github.com/sejust)) | [mcq.sejust@gmail.com](mailto:mcq.sejust@gmail.com) | [OPPO] |
# Committers
| Name | Email | Organization |
| ------------------------------------------------------------------------------ | --------------------------------------------------------------- | ---------------------------------------------- |
| Hongyan Wang ([@jadewang198510](https://github.com/jadewang198510)) | [741773046@qq.com](mailto:741773046@qq.com) | [OPPO] |
| Chi He ([bboyCH4](https://github.com/bboyCH4)) | [hechi1014@126.com](mailto:hechi1014@126.com) | [OPPO] |
| Jianxing Zhao ([@znlstar](https://github.com/znlstar)) | [znlstar@163.com](mailto:znlstar@163.com) | [JD.com] |
| Yong Sheng ([@shyodx](https://github.com/shyodx)) | [shengyong2021@gmail.com](mailto:shengyong2021@gmail.com) | [BEIKE] |
| Zhengyi Zhu ([@zhuzhengyi](https://github.com/wding109)) | [zhengyi.zhu.hust@gmail.com](mailto:zhengyi.zhu.hust@gmail.com) | [BEIKE] |
| Lei Yin ([@yinlei-jinan](https://github.com/yinlei-jinan)) | [297155992@qq.com](mailto:297155992@qq.com) | [JD.com] |
| Liying Zhang ([@Vivian7755](https://github.com/Vivian7755)) | [zly7755@163.com](mailto:zly7755@163.com) | [JD.com] |
| Xihao Xu ([@xxscott](https://github.com/xxscott)) | [xxscott@163.com](mailto:xxscott@163.com) | [JD.com] |
| Wenjia Wu ([@wenjia322](https://github.com/wenjia322)) | [buaa1214wwj@126.com](mailto:buaa1214wwj@126.com) | [JD.com] |
| pengtianyue ([@pengtianyue025](https://github.com/pengtianyue025)) | [pengtianyue025@gmail.com](mailto:pengtianyue025@gmail.com) | [ByteDance] |
| baijiaruo ([@baijiaruo](https://github.com/baijiaruo)) | [505892459@qq.com](mailto:505892459@qq.com) | [China United Telecommunications Co] |
| Tianjiong Zhang ([@tianjiongzhang](https://github.com/tianjiongzhang)) | [236556116@qq.com](mailto:236556116@qq.com) | [Sangfor] |
| Zongchao Hu ([@JasonHu520](https://github.com/JasonHu520)) | [hastyjason500@gmail.com](mailto:hastyjason500@gmail.com) | [OPPO] |
| Tianpeng Li ([@Skypigltp](https://github.com/skypigltp)) | [skypigltp@gmail.com](mailto:skypigltp@gmail.com) | [VIVO] |
| Hongyin Zhu ([@zhuhyc](https://github.com/zhuhyc)) | [zzhniy.163.niy@163.com](mailto:zzhniy.163.niy@163.com) | [JD.com] |
| Zhixiang Tang ([@xiangcai1215](https://github.com/xiangcai1215)) | [505892459@qq.com](mailto:505892459@qq.com) | [Xiaohongshu] |
| Yubo Li ([@yuboLee](https://github.com/yuboLee)) | [pangbolee@gmail.com](mailto:pangbolee@gmail.com) | [JD.com] |
| Tao Li ([@tomscut](https://github.com/tomscut)) | [tomleescut@gmail.com](mailto:tomleescut@gmail.com) | [BIGO] |
| Junhao Guo ([@M1eyu2018](https://github.com/M1eyu2018)) | [857037797@qq.com](mailto:857037797@qq.com) | [BIGO] |
| Bingxing Liu ([@liubingxing](https://github.com/liubingxing)) | [liubbingxing@gmail.com](mailto:liubbingxing@gmail.com) | [BIGO] |
| Xiang Li ([@lixiang](https://github.com/lixiang)) | [960754123@qq.com](mailto:960754123@qq.com) | [Sangfor] |
| Qing Li ([@qingli](https://github.com/liqingqiya)) | [liqing.qiya@gmail.com](mailto:liqing.qiya@gmail.com) | [ByteDance] |
| Zhihao Wang ([@Cresc](https://github.com/zhihao-wang)) | [wzh07@hotmail.com](mailto:liqing.qiya@gmail.com) | [ByteDance] |
| NaturalSelect ([@NaturalSelect](https://github.com/NaturalSelect)) | [2145973003@qq.com](mailto:2145973003@qq.com) | [Chengdu University of Information Technology] |
| setcy ([@setcy](https://github.com/setcy)) | [asetcy@gmail.com](mailto:asetcy@gmail.com) | [Hangzhou Dianzi University] |
| Shuqiang Zheng ([@shuqiang-zheng](https://github.com/shuqiang-zheng)) | [782879301@qq.com](mailto:782879301@qq.com) | [OPPO] |
| Chuanqing Zhang ([@zhangchuanqing5658](https://github.com/zhangchuanqing5658)) | [zhang691753540@gmail.com](mailto:zhang691753540@gmail.com) | [JD.com] |
| Name | Email | Organization |
|---------------------------------------------------------------------------------|------------------------------------------------------------------|--------------|
| Hongyan Wang ([@jadewang198510](https://github.com/jadewang198510)) | [741773046@qq.com](mailto:741773046@qq.com) | [OPPO] |
| Chi He ([bboyCH4](https://github.com/bboyCH4)) | [hechi1014@126.com](mailto:hechi1014@126.com) | [OPPO] |
| Jianxing Zhao ([@znlstar](https://github.com/znlstar)) | [znlstar@163.com](mailto:znlstar@163.com) | [JD.com] |
| Yong Sheng ([@shyodx](https://github.com/shyodx)) | [shengyong2021@gmail.com](mailto:shengyong2021@gmail.com) | [BEIKE] |
| Zhengyi Zhu ([@zhuzhengyi](https://github.com/wding109)) | [zhengyi.zhu.hust@gmail.com](mailto:zhengyi.zhu.hust@gmail.com) | [BEIKE] |
| Lei Yin ([@yinlei-jinan](https://github.com/yinlei-jinan)) | [297155992@qq.com](mailto:297155992@qq.com) | [JD.com] |
| Liying Zhang ([@Vivian7755](https://github.com/Vivian7755)) | [zly7755@163.com](mailto:zly7755@163.com) | [JD.com] |
| Xihao Xu ([@xxscott](https://github.com/xxscott)) | [xxscott@163.com](mailto:xxscott@163.com) | [JD.com] |
| Wenjia Wu ([@wenjia322](https://github.com/wenjia322)) | [buaa1214wwj@126.com](mailto:buaa1214wwj@126.com) | [JD.com] |
| pengtianyue ([@pengtianyue025](https://github.com/pengtianyue025)) | [pengtianyue025@gmail.com](mailto:pengtianyue025@gmail.com) | [ByteDance] |
| baijiaruo ([@baijiaruo](https://github.com/baijiaruo)) | [505892459@qq.com](mailto:505892459@qq.com) | [China United Telecommunications Co] |
| Tianjiong Zhang ([@tianjiongzhang](https://github.com/tianjiongzhang)) | [236556116@qq.com](mailto:236556116@qq.com) | [Sangfor] |
| Zongchao Hu ([@JasonHu520](https://github.com/JasonHu520)) | [hastyjason500@gmail.com](mailto:hastyjason500@gmail.com) | [OPPO] |
| Tianpeng Li ([@Skypigltp](https://github.com/skypigltp)) | [skypigltp@gmail.com](mailto:skypigltp@gmail.com) | [VIVO] |
| Hongyin Zhu ([@zhuhyc](https://github.com/zhuhyc)) | [zzhniy.163.niy@163.com](mailto:zzhniy.163.niy@163.com) | [JD.com] |
| Zhixiang Tang ([@xiangcai1215](https://github.com/xiangcai1215)) | [505892459@qq.com](mailto:505892459@qq.com) | [Xiaohongshu]|
| Yubo Li ([@yuboLee](https://github.com/yuboLee)) | [pangbolee@gmail.com](mailto:pangbolee@gmail.com) | [JD.com] |
| Tao Li ([@tomscut](https://github.com/tomscut)) | [tomleescut@gmail.com](mailto:tomleescut@gmail.com) | [BIGO] |
| Junhao Guo ([@M1eyu2018](https://github.com/M1eyu2018)) | [857037797@qq.com](mailto:857037797@qq.com) | [BIGO] |
| Bingxing Liu ([@liubingxing](https://github.com/liubingxing)) | [liubbingxing@gmail.com](mailto:liubbingxing@gmail.com) | [BIGO] |
| Xiang Li ([@lixiang](https://github.com/lixiang)) | [960754123@qq.com](mailto:960754123@qq.com) | [Sangfor] |
| Qing Li ([@qingli](https://github.com/liqingqiya)) | [liqing.qiya@gmail.com](mailto:liqing.qiya@gmail.com) | [ByteDance] |
| Zhihao Wang ([@Cresc](https://github.com/zhihao-wang)) | [wzh07@hotmail.com](mailto:liqing.qiya@gmail.com) | [ByteDance] |
| NaturalSelect ([@NaturalSelect](https://github.com/NaturalSelect)) | [2145973003@qq.com](mailto:2145973003@qq.com) | [Chengdu University of Information Technology] |
| setcy ([@setcy](https://github.com/setcy)) | [asetcy@gmail.com](mailto:asetcy@gmail.com) | [Hangzhou Dianzi University] |
| Shuqiang Zheng ([@shuqiang-zheng](https://github.com/shuqiang-zheng)) | [782879301@qq.com](mailto:782879301@qq.com) | [OPPO] |
| Chuanqing Zhang ([@zhangchuanqing5658](https://github.com/zhangchuanqing5658)) | [zhang691753540@gmail.com](mailto:zhang691753540@gmail.com) | [JD.com] |
[OPPO]: https://www.oppo.com/en/

View File

@ -8,8 +8,8 @@ default: all
phony := all
all: build
phony += build server authtool client cli libsdkpre libsdk fsck fdstore bcache blobstore deploy
build: server authtool client cli libsdk fsck fdstore bcache blobstore deploy
phony += build server authtool client cli libsdkpre libsdk fsck fdstore preload bcache blobstore deploy
build: server authtool client cli libsdk fsck fdstore preload bcache blobstore deploy
server:
@build/build.sh server $(GOMOD) --threads=$(threads)
@ -22,9 +22,6 @@ deploy:
blobstore:
@build/build.sh blobstore $(GOMOD) --threads=$(threads)
blobstoredialtest:
@build/build.sh blobstoredialtest $(GOMOD) --threads=$(threads)
client:
@build/build.sh client $(GOMOD) --threads=$(threads)
@ -46,15 +43,12 @@ libsdk:
fdstore:
@build/build.sh fdstore $(GOMOD) --threads=$(threads)
preload:
@build/build.sh preload $(GOMOD) --threads=$(threads)
bcache:
@build/build.sh bcache $(GOMOD) --threads=$(threads)
rctest:
@build/build.sh rctest $(GOMOD) --threads=$(threads)
rcconfig:
@build/build.sh rcconfig $(GOMOD) --threads=$(threads)
phony += clean
clean:
@$(RM) -rf build/bin
@ -67,13 +61,9 @@ phony += test
test:
@build/build.sh test $(GOMOD) --threads=$(threads)
phony += testcover testcovercubefs testcoverblobstore
phony += testcover
testcover:
@build/build.sh testcover $(GOMOD) --threads=$(threads)
testcovercubefs:
@build/build.sh testcovercubefs $(GOMOD) --threads=$(threads)
testcoverblobstore:
@build/build.sh testcoverblobstore $(GOMOD) --threads=$(threads)
phony += mock
mock:

View File

@ -1,6 +0,0 @@
code approvers:
- maintainers
code reviewers:
- contributors && maintainers
docs:
- sig-docs

View File

@ -1,6 +1,6 @@
# CubeFS
[![CNCF Status](https://img.shields.io/badge/cncf%20status-graduated-blue.svg)](https://www.cncf.io/projects)
[![CNCF Status](https://img.shields.io/badge/cncf%20status-incubating-blue.svg)](https://www.cncf.io/projects)
[![Build Status](https://github.com/cubefs/cubefs/actions/workflows/ci.yml/badge.svg)](https://github.com/cubefs/cubefs/actions/workflows/ci.yml)
[![LICENSE](https://img.shields.io/github/license/cubefs/cubefs.svg)](https://github.com/cubefs/cubefs/blob/master/LICENSE)
[![Language](https://img.shields.io/badge/Language-Go-blue.svg)](https://golang.org/)
@ -14,7 +14,6 @@
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fcubefs%2Fcubefs.svg?type=shield&issueType=security)](https://app.fossa.com/projects/git%2Bgithub.com%2Fcubefs%2Fcubefs?ref=badge_shield)
[![Release](https://img.shields.io/github/v/release/cubefs/cubefs.svg?color=161823&style=flat-square&logo=smartthings)](https://github.com/cubefs/cubefs/releases)
[![Tag](https://img.shields.io/github/v/tag/cubefs/cubefs.svg?color=ee8936&logo=fitbit&style=flat-square)](https://github.com/cubefs/cubefs/tags)
[![Gurubase](https://img.shields.io/badge/Gurubase-Ask%20CubeFS%20Guru-006BFF)](https://gurubase.io/g/cubefs)
|<img src="https://user-images.githubusercontent.com/5708406/91202310-31eaab80-e734-11ea-84fc-c1b1882ae71c.png" height="24"/>&nbsp;Community Meeting|
|------------------|
@ -26,14 +25,13 @@
## Overview
CubeFS ("储宝" in Chinese) is an open-source cloud-native distributed file & object storage system, hosted by the [Cloud Native Computing Foundation](https://cncf.io) (CNCF) as a [graduated](https://www.cncf.io/projects/) project.
CubeFS ("储宝" in Chinese) is an open-source cloud-native file storage system, hosted by the [Cloud Native Computing Foundation](https://cncf.io) (CNCF) as an [incubating](https://www.cncf.io/projects/) project.
## What can you build with CubeFS
* As an open-source distributed storage, CubeFS can serve as your datacenter filesystem, data lake storage infra, and private or hybrid cloud storage.
* Moreover, it can be run in public cloud services, providing cache acceleration and file system semantics on top of public cloud storage such as S3.
* In particular, CubeFS enables the separation of storage/compute architecture for databases, search systems, and AI/ML applications.
As an open-source distributed storage, CubeFS can serve as your datacenter filesystem, data lake storage infra, and private or hybrid cloud storage.
In particular, CubeFS enables the separation of storage/compute architecture for databases and AI/ML applications.
Some key features of CubeFS include:
@ -45,7 +43,7 @@ Some key features of CubeFS include:
- Flexible storage policies, high-performance replication or low-cost erasure coding
<div width="100%" style="text-align:center;"><img alt="CubeFS Architecture" src="https://raw.githubusercontent.com/cubefs/cubefs/master/docs/source/overview/pic/cfs-arch-ec.png"/></div>
<div width="100%" style="text-align:center;"><img alt="CubeFS Architecture" src="https://raw.githubusercontent.com/cubefs/cubefs/master/docs/source/pic/cfs-arch-ec.png"/></div>
## Documents

0
RELEASE.md Normal file → Executable file
View File

View File

@ -1,12 +1,4 @@
# Roadmap of 2026
# Roadmap of 2024
### Release Scheduled
https://github.com/cubefs/cubefs/issues/3064
| Feature | Type | Version | Status | Development Branch | Scheduled Release Date | Details |
|:--|:--|:--|:--|:--|:--|:--|
| Hybrid Cloud Support & Metadata Cost Reduction | Feature | Release-3.6.0 | System Testing | develop-v3.6.0 | July | 1) Public Cloud Data Writeback<br>2) RocksDB metadata supports RocksDB persistence (Learner priority)<br>3) Learner capability: Raft group supports Learner capability<br>4) MP&&DP multi-region distribution and directed automatic migration |
| System Operations Automation and Stability Enhancement | Feature | Release-3.6.1 | In Development | develop-v3.6.1 | November | 1) DP supports capacity and quantity balancing<br>2) Master & Datanode IO tiered flow control + adaptive load<br>3) Rack balancing, NodeSet balancing<br>4) Cache node massive small files support and performance optimization<br>5) Cache prefetch optimization |
CubeFS will prioritize performance and feature requirements for AI and similar scenarios, and may adjust release content accordingly.

View File

@ -29,6 +29,10 @@ import (
"github.com/cubefs/cubefs/util/log"
)
const (
nodeType = "auth"
)
func (m *Server) getTicket(w http.ResponseWriter, r *http.Request) {
var (
plaintext []byte
@ -39,7 +43,7 @@ func (m *Server) getTicket(w http.ResponseWriter, r *http.Request) {
message string
)
if !m.metaReady {
if m.metaReady == false {
log.LogWarnf("action[handlerWithInterceptor] leader meta has not ready")
http.Error(w, m.leaderInfo.addr, http.StatusBadRequest)
}
@ -75,6 +79,7 @@ func (m *Server) getTicket(w http.ResponseWriter, r *http.Request) {
}
sendOkReply(w, r, newSuccessHTTPAuthReply(message))
return
}
func (m *Server) raftNodeOp(w http.ResponseWriter, r *http.Request) {
@ -140,14 +145,21 @@ func (m *Server) raftNodeOp(w http.ResponseWriter, r *http.Request) {
}
sendOkReply(w, r, newSuccessHTTPAuthReply(message))
return
}
func (m *Server) handleAddRaftNode(raftNodeInfo *proto.AuthRaftNodeInfo) (err error) {
return m.cluster.addRaftNode(raftNodeInfo.ID, raftNodeInfo.Addr)
if err = m.cluster.addRaftNode(raftNodeInfo.ID, raftNodeInfo.Addr); err != nil {
return
}
return
}
func (m *Server) handleRemoveRaftNode(raftNodeInfo *proto.AuthRaftNodeInfo) (err error) {
return m.cluster.removeRaftNode(raftNodeInfo.ID, raftNodeInfo.Addr)
if err = m.cluster.removeRaftNode(raftNodeInfo.ID, raftNodeInfo.Addr); err != nil {
return
}
return
}
func genAuthRaftNodeOpResp(req *proto.APIAccessReq, ts int64, key []byte, msg string) (message string, err error) {
@ -277,18 +289,28 @@ func (m *Server) apiAccessEntry(w http.ResponseWriter, r *http.Request) {
}
sendOkReply(w, r, newSuccessHTTPAuthReply(message))
return
}
func (m *Server) handleCreateKey(keyInfo *keystore.KeyInfo) (res *keystore.KeyInfo, err error) {
return m.cluster.CreateNewKey(keyInfo.ID, keyInfo)
if res, err = m.cluster.CreateNewKey(keyInfo.ID, keyInfo); err != nil {
return
}
return
}
func (m *Server) handleDeleteKey(keyInfo *keystore.KeyInfo) (res *keystore.KeyInfo, err error) {
return m.cluster.DeleteKey(keyInfo.ID)
if res, err = m.cluster.DeleteKey(keyInfo.ID); err != nil {
return
}
return
}
func (m *Server) handleGetKey(keyInfo *keystore.KeyInfo) (res *keystore.KeyInfo, err error) {
return m.getSecretKeyInfo(keyInfo.ID)
if res, err = m.getSecretKeyInfo(keyInfo.ID); err != nil {
return
}
return
}
func (m *Server) handleAddCaps(keyInfo *keystore.KeyInfo) (res *keystore.KeyInfo, err error) {
@ -415,6 +437,7 @@ func (m *Server) osCapsOp(w http.ResponseWriter, r *http.Request) {
}
sendOkReply(w, r, newSuccessHTTPAuthReply(message))
return
}
func (m *Server) genTicket(key []byte, serviceID string, IP string, caps []byte) (ticket cryptoutil.Ticket) {
@ -657,6 +680,7 @@ func send(w http.ResponseWriter, r *http.Request, reply []byte) {
return
}
log.LogInfof("URL[%v],remoteAddr[%v],response ok", r.URL, r.RemoteAddr)
return
}
func keyNotFound(name string) (err error) {
@ -676,4 +700,5 @@ func sendErrReply(w http.ResponseWriter, r *http.Request, HTTPAuthReply *proto.H
if _, err = w.Write(reply); err != nil {
log.LogErrorf("fail to write http reply[%s] len[%d].URL[%v],remoteAddr[%v] err:[%v]", string(reply), len(reply), r.URL, r.RemoteAddr, err)
}
return
}

View File

@ -36,7 +36,7 @@ func (m *Server) handleLeaderChange(leader uint64) {
log.LogWarnf("action[handleLeaderChange] change leader to [%v] ", m.leaderInfo.addr)
m.authProxy = m.newAuthProxy() // TODO no lock?
if !m.metaReady {
if m.metaReady == false {
if err := m.cluster.loadKeystore(); err != nil {
panic(err)
}
@ -57,7 +57,7 @@ func (m *Server) handlePeerChange(confChange *proto.ConfChange) (err error) {
msg = fmt.Sprintf("action[handlePeerChange] clusterID[%v] nodeAddr[%v] is invalid", m.clusterName, addr)
break
}
m.raftStore.AddNodeWithPort(confChange.Peer.ID, arr[0], m.config.heartbeatPort, m.config.replicaPort)
m.raftStore.AddNodeWithPort(confChange.Peer.ID, arr[0], int(m.config.heartbeatPort), int(m.config.replicaPort))
AddrDatabase[confChange.Peer.ID] = string(confChange.Context)
msg = fmt.Sprintf("clusterID[%v] peerID:%v,nodeAddr[%v] has been add", m.clusterName, confChange.Peer.ID, addr)
case proto.ConfRemoveNode:
@ -73,4 +73,5 @@ func (m *Server) handlePeerChange(confChange *proto.ConfChange) (err error) {
func (m *Server) handleApplySnapshot() {
log.LogInfof("clusterID[%v] peerID:%v action[handleApplySnapshot]", m.clusterName, m.id)
m.fsm.restore()
return
}

View File

@ -57,7 +57,10 @@ var action2PathMap = map[string]string{
OSGetCaps: proto.OSGetCaps,
}
var flaginfo flagInfo
var (
cflag string
flaginfo flagInfo
)
type ticketFlag struct {
key string
@ -361,7 +364,7 @@ func accessAuthServer() {
panic(err)
}
} else {
if _, err = resp.KeyInfo.DumpJSONStr(resp.AuthIDKey); err != nil {
if res, err = resp.KeyInfo.DumpJSONStr(resp.AuthIDKey); err != nil {
panic(err)
}
}

View File

@ -57,8 +57,8 @@ func newCluster(name string, leaderInfo *LeaderInfo, fsm *KeystoreFsm, partition
c.cfg = cfg
c.fsm = fsm
c.partition = partition
c.fsm.keystore = make(map[string]*keystore.KeyInfo)
c.fsm.accessKeystore = make(map[string]*keystore.AccessKeyInfo)
c.fsm.keystore = make(map[string]*keystore.KeyInfo, 0)
c.fsm.accessKeystore = make(map[string]*keystore.AccessKeyInfo, 0)
return
}
@ -66,6 +66,10 @@ func (c *Cluster) scheduleTask() {
c.scheduleToCheckHeartbeat()
}
func (c *Cluster) authAddr() (addr string) {
return c.leaderInfo.addr
}
func (c *Cluster) scheduleToCheckHeartbeat() {
go func() {
for {

View File

@ -42,8 +42,8 @@ const (
type clusterConfig struct {
peers []raftstore.PeerAddress
peerAddrs []string
heartbeatPort int
replicaPort int
heartbeatPort int64
replicaPort int64
}
// AddrDatabase is a map that stores the address of a given host (e.g., the leader)
@ -76,7 +76,7 @@ func (cfg *clusterConfig) parsePeers(peerStr string) error {
if err != nil {
return err
}
cfg.peers = append(cfg.peers, raftstore.PeerAddress{Peer: proto.Peer{ID: id}, Address: ip, HeartbeatPort: cfg.heartbeatPort, ReplicaPort: cfg.replicaPort})
cfg.peers = append(cfg.peers, raftstore.PeerAddress{Peer: proto.Peer{ID: id}, Address: ip, HeartbeatPort: int(cfg.heartbeatPort), ReplicaPort: int(cfg.replicaPort)})
address := fmt.Sprintf("%v:%v", ip, port)
syslog.Println(address)
AddrDatabase[id] = address

View File

@ -37,9 +37,3 @@ const (
akAcronym = "ak"
akPrefix = keySeparator + akAcronym + keySeparator
)
// TODO: unused
var (
_ = opSyncGetKey
_ = opSyncGetCaps
)

View File

@ -54,6 +54,7 @@ func (m *Server) startHTTPService() {
}
}
}()
return
}
func (m *Server) newAuthProxy() *AuthProxy {
@ -121,6 +122,7 @@ func (m *Server) handleFunctions() {
http.Handle(proto.OSAddCaps, m.handlerWithInterceptor())
http.Handle(proto.OSDeleteCaps, m.handlerWithInterceptor())
http.Handle(proto.OSGetCaps, m.handlerWithInterceptor())
return
}
func (m *Server) handlerWithInterceptor() http.Handler {

View File

@ -30,6 +30,7 @@ func (mf *KeystoreFsm) DeleteKey(id string) {
mf.ksMutex.Lock()
defer mf.ksMutex.Unlock()
delete(mf.keystore, id)
return
}
func (mf *KeystoreFsm) PutAKInfo(akInfo *keystore.AccessKeyInfo) {
@ -54,4 +55,5 @@ func (mf *KeystoreFsm) DeleteAKInfo(accessKey string) {
mf.aksMutex.Lock()
defer mf.aksMutex.Unlock()
delete(mf.accessKeystore, accessKey)
return
}

View File

@ -37,6 +37,8 @@ type raftLeaderChangeHandler func(leader uint64)
type raftPeerChangeHandler func(confChange *proto.ConfChange) (err error)
type raftCmdApplyHandler func(cmd *RaftCmd) (err error)
type raftApplySnapshotHandler func()
// KeystoreFsm represents the finite state machine of a keystore

View File

@ -118,7 +118,7 @@ func (c *Cluster) syncPutAccessKeyInfo(opType uint32, accessKeyInfo *keystore.Ac
}
func (c *Cluster) loadKeystore() (err error) {
ks := make(map[string]*keystore.KeyInfo)
ks := make(map[string]*keystore.KeyInfo, 0)
log.LogInfof("action[loadKeystore]")
result, err := c.fsm.store.SeekForPrefix([]byte(ksPrefix))
if err != nil {
@ -143,8 +143,14 @@ func (c *Cluster) loadKeystore() (err error) {
return
}
func (c *Cluster) clearKeystore() {
c.fsm.ksMutex.Lock()
defer c.fsm.ksMutex.Unlock()
c.fsm.keystore = nil
}
func (c *Cluster) loadAKstore() (err error) {
aks := make(map[string]*keystore.AccessKeyInfo)
aks := make(map[string]*keystore.AccessKeyInfo, 0)
log.LogInfof("action[loadAccessKeystore]")
result, err := c.fsm.store.SeekForPrefix([]byte(akPrefix))
if err != nil {
@ -169,6 +175,12 @@ func (c *Cluster) loadAKstore() (err error) {
return
}
func (c *Cluster) clearAKstore() {
c.fsm.aksMutex.Lock()
defer c.fsm.aksMutex.Unlock()
c.fsm.accessKeystore = nil
}
func (c *Cluster) addRaftNode(nodeID uint64, addr string) (err error) {
peer := proto.Peer{ID: nodeID}
_, err = c.partition.ChangeMember(proto.ConfAddNode, peer, []byte(addr))

View File

@ -109,8 +109,8 @@ func (m *Server) checkConfig(cfg *config.Config) (err error) {
if m.id, err = strconv.ParseUint(cfg.GetString(ID), 10, 64); err != nil {
return fmt.Errorf("%v,err:%v", proto.ErrInvalidCfg, err.Error())
}
m.config.heartbeatPort = cfg.GetInt(heartbeatPortKey)
m.config.replicaPort = cfg.GetInt(replicaPortKey)
m.config.heartbeatPort = cfg.GetInt64(heartbeatPortKey)
m.config.replicaPort = cfg.GetInt64(replicaPortKey)
if m.config.heartbeatPort <= 1024 {
m.config.heartbeatPort = raftstore.DefaultHeartbeatPort
}
@ -162,8 +162,8 @@ func (m *Server) createRaftServer(cfg *config.Config) (err error) {
NodeID: m.id,
RaftPath: m.walDir,
NumOfLogsToRetain: m.retainLogs,
HeartbeatPort: m.config.heartbeatPort,
ReplicaPort: m.config.replicaPort,
HeartbeatPort: int(m.config.heartbeatPort),
ReplicaPort: int(m.config.replicaPort),
TickInterval: m.tickInterval,
ElectionTick: m.electionTick,
}
@ -213,7 +213,7 @@ func (m *Server) Start(cfg *config.Config) (err error) {
return fmt.Errorf("action[Start] failed %v,err: auth root Key invalid=%s", proto.ErrInvalidCfg, AuthRootKey)
}
if cfg.GetBool(EnableHTTPS) {
if cfg.GetBool(EnableHTTPS) == true {
m.cluster.PKIKey.EnableHTTPS = true
if m.cluster.PKIKey.AuthRootPublicKey, err = os.ReadFile("/app/server.crt"); err != nil {
return fmt.Errorf("action[Start] failed,err[%v]", err)

4
blobstore/Dockerfile Normal file → Executable file
View File

@ -1,7 +1,5 @@
FROM golang:1.18.10@sha256:50c889275d26f816b5314fc99f55425fa76b18fcaf16af255f5d57f09e1f48da
FROM golang:1.17.13@sha256:87262e4a4c7db56158a80a18fefdc4fee5accc41b59cde821e691d05541bbb18
RUN sed -i 's/deb.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list && \
sed -i 's/security.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list
ENV JAVA_HOME=bin/jdk1.8.0_321
ENV CLASSPATH=$CLASSPATH:$JAVA_HOME/lib

View File

@ -34,7 +34,7 @@ INSTALL=CGO_ENABLED=0 $(BUILD)
CGOINSTALL=CGO_ENABLED=1 $(BUILD)
PROJECTMOD=github.com/cubefs/cubefs/blobstore
CMDDIR=$(PROJECTMOD)/cmd
TARGETS=clustermgr blobnode access scheduler proxy cli shardnode
TARGETS=clustermgr blobnode access scheduler proxy cli
.PHONY: clean all $(TARGETS)
all:$(TARGETS)
@ -63,10 +63,6 @@ cli:
@echo "building blobstore-cli"
@$(CGOINSTALL) -o $(BINDIR)/blobstore-cli $(PROJECTMOD)/cli/cli
shardnode:
@echo "building shardnode"
@$(CGOINSTALL) $(CMDDIR)/shardnode
clean:
@go clean -i ./...
@rm -f $(BINDIR)/*

View File

@ -1,16 +1,15 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/cubefs/cubefs/blobstore/access/stream (interfaces: StreamHandler)
// Source: github.com/cubefs/cubefs/blobstore/access (interfaces: StreamHandler,Limiter)
// Package mocks is a generated GoMock package.
package mocks
// Package access is a generated GoMock package.
package access
import (
context "context"
io "io"
reflect "reflect"
access "github.com/cubefs/cubefs/blobstore/api/access"
shardnode "github.com/cubefs/cubefs/blobstore/api/shardnode"
access0 "github.com/cubefs/cubefs/blobstore/api/access"
codemode "github.com/cubefs/cubefs/blobstore/common/codemode"
proto "github.com/cubefs/cubefs/blobstore/common/proto"
gomock "github.com/golang/mock/gomock"
@ -54,10 +53,10 @@ func (mr *MockStreamHandlerMockRecorder) Admin() *gomock.Call {
}
// Alloc mocks base method.
func (m *MockStreamHandler) Alloc(arg0 context.Context, arg1 uint64, arg2 uint32, arg3 proto.ClusterID, arg4 codemode.CodeMode) (*proto.Location, error) {
func (m *MockStreamHandler) Alloc(arg0 context.Context, arg1 uint64, arg2 uint32, arg3 proto.ClusterID, arg4 codemode.CodeMode) (*access0.Location, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Alloc", arg0, arg1, arg2, arg3, arg4)
ret0, _ := ret[0].(*proto.Location)
ret0, _ := ret[0].(*access0.Location)
ret1, _ := ret[1].(error)
return ret0, ret1
}
@ -68,38 +67,8 @@ func (mr *MockStreamHandlerMockRecorder) Alloc(arg0, arg1, arg2, arg3, arg4 inte
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Alloc", reflect.TypeOf((*MockStreamHandler)(nil).Alloc), arg0, arg1, arg2, arg3, arg4)
}
// AllocSlice mocks base method.
func (m *MockStreamHandler) AllocSlice(arg0 context.Context, arg1 *access.AllocSliceArgs) (shardnode.AllocSliceRet, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "AllocSlice", arg0, arg1)
ret0, _ := ret[0].(shardnode.AllocSliceRet)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// AllocSlice indicates an expected call of AllocSlice.
func (mr *MockStreamHandlerMockRecorder) AllocSlice(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AllocSlice", reflect.TypeOf((*MockStreamHandler)(nil).AllocSlice), arg0, arg1)
}
// CreateBlob mocks base method.
func (m *MockStreamHandler) CreateBlob(arg0 context.Context, arg1 *access.CreateBlobArgs) (*proto.Location, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateBlob", arg0, arg1)
ret0, _ := ret[0].(*proto.Location)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// CreateBlob indicates an expected call of CreateBlob.
func (mr *MockStreamHandlerMockRecorder) CreateBlob(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateBlob", reflect.TypeOf((*MockStreamHandler)(nil).CreateBlob), arg0, arg1)
}
// Delete mocks base method.
func (m *MockStreamHandler) Delete(arg0 context.Context, arg1 *proto.Location) error {
func (m *MockStreamHandler) Delete(arg0 context.Context, arg1 *access0.Location) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Delete", arg0, arg1)
ret0, _ := ret[0].(error)
@ -112,22 +81,8 @@ func (mr *MockStreamHandlerMockRecorder) Delete(arg0, arg1 interface{}) *gomock.
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockStreamHandler)(nil).Delete), arg0, arg1)
}
// DeleteBlob mocks base method.
func (m *MockStreamHandler) DeleteBlob(arg0 context.Context, arg1 *access.DelBlobArgs) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DeleteBlob", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// DeleteBlob indicates an expected call of DeleteBlob.
func (mr *MockStreamHandlerMockRecorder) DeleteBlob(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteBlob", reflect.TypeOf((*MockStreamHandler)(nil).DeleteBlob), arg0, arg1)
}
// Get mocks base method.
func (m *MockStreamHandler) Get(arg0 context.Context, arg1 io.Writer, arg2 proto.Location, arg3, arg4 uint64) (func() error, error) {
func (m *MockStreamHandler) Get(arg0 context.Context, arg1 io.Writer, arg2 access0.Location, arg3, arg4 uint64) (func() error, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Get", arg0, arg1, arg2, arg3, arg4)
ret0, _ := ret[0].(func() error)
@ -141,53 +96,23 @@ func (mr *MockStreamHandlerMockRecorder) Get(arg0, arg1, arg2, arg3, arg4 interf
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockStreamHandler)(nil).Get), arg0, arg1, arg2, arg3, arg4)
}
// GetBlob mocks base method.
func (m *MockStreamHandler) GetBlob(arg0 context.Context, arg1 *access.GetBlobArgs) (*proto.Location, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetBlob", arg0, arg1)
ret0, _ := ret[0].(*proto.Location)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetBlob indicates an expected call of GetBlob.
func (mr *MockStreamHandlerMockRecorder) GetBlob(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBlob", reflect.TypeOf((*MockStreamHandler)(nil).GetBlob), arg0, arg1)
}
// ListBlob mocks base method.
func (m *MockStreamHandler) ListBlob(arg0 context.Context, arg1 *access.ListBlobArgs) (shardnode.ListBlobRet, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ListBlob", arg0, arg1)
ret0, _ := ret[0].(shardnode.ListBlobRet)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// ListBlob indicates an expected call of ListBlob.
func (mr *MockStreamHandlerMockRecorder) ListBlob(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListBlob", reflect.TypeOf((*MockStreamHandler)(nil).ListBlob), arg0, arg1)
}
// Put mocks base method.
func (m *MockStreamHandler) Put(arg0 context.Context, arg1 io.Reader, arg2 int64, arg3 access.HasherMap, arg4 proto.ClusterID, arg5 codemode.CodeMode) (*proto.Location, error) {
func (m *MockStreamHandler) Put(arg0 context.Context, arg1 io.Reader, arg2 int64, arg3 access0.HasherMap) (*access0.Location, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Put", arg0, arg1, arg2, arg3, arg4, arg5)
ret0, _ := ret[0].(*proto.Location)
ret := m.ctrl.Call(m, "Put", arg0, arg1, arg2, arg3)
ret0, _ := ret[0].(*access0.Location)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Put indicates an expected call of Put.
func (mr *MockStreamHandlerMockRecorder) Put(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call {
func (mr *MockStreamHandlerMockRecorder) Put(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Put", reflect.TypeOf((*MockStreamHandler)(nil).Put), arg0, arg1, arg2, arg3, arg4, arg5)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Put", reflect.TypeOf((*MockStreamHandler)(nil).Put), arg0, arg1, arg2, arg3)
}
// PutAt mocks base method.
func (m *MockStreamHandler) PutAt(arg0 context.Context, arg1 io.Reader, arg2 proto.ClusterID, arg3 proto.Vid, arg4 proto.BlobID, arg5 int64, arg6 access.HasherMap) error {
func (m *MockStreamHandler) PutAt(arg0 context.Context, arg1 io.Reader, arg2 proto.ClusterID, arg3 proto.Vid, arg4 proto.BlobID, arg5 int64, arg6 access0.HasherMap) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "PutAt", arg0, arg1, arg2, arg3, arg4, arg5, arg6)
ret0, _ := ret[0].(error)
@ -200,16 +125,93 @@ func (mr *MockStreamHandlerMockRecorder) PutAt(arg0, arg1, arg2, arg3, arg4, arg
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutAt", reflect.TypeOf((*MockStreamHandler)(nil).PutAt), arg0, arg1, arg2, arg3, arg4, arg5, arg6)
}
// SealBlob mocks base method.
func (m *MockStreamHandler) SealBlob(arg0 context.Context, arg1 *access.SealBlobArgs) error {
// MockLimiter is a mock of Limiter interface.
type MockLimiter struct {
ctrl *gomock.Controller
recorder *MockLimiterMockRecorder
}
// MockLimiterMockRecorder is the mock recorder for MockLimiter.
type MockLimiterMockRecorder struct {
mock *MockLimiter
}
// NewMockLimiter creates a new mock instance.
func NewMockLimiter(ctrl *gomock.Controller) *MockLimiter {
mock := &MockLimiter{ctrl: ctrl}
mock.recorder = &MockLimiterMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockLimiter) EXPECT() *MockLimiterMockRecorder {
return m.recorder
}
// Acquire mocks base method.
func (m *MockLimiter) Acquire(arg0 string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SealBlob", arg0, arg1)
ret := m.ctrl.Call(m, "Acquire", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// SealBlob indicates an expected call of SealBlob.
func (mr *MockStreamHandlerMockRecorder) SealBlob(arg0, arg1 interface{}) *gomock.Call {
// Acquire indicates an expected call of Acquire.
func (mr *MockLimiterMockRecorder) Acquire(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SealBlob", reflect.TypeOf((*MockStreamHandler)(nil).SealBlob), arg0, arg1)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Acquire", reflect.TypeOf((*MockLimiter)(nil).Acquire), arg0)
}
// Reader mocks base method.
func (m *MockLimiter) Reader(arg0 context.Context, arg1 io.Reader) io.Reader {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Reader", arg0, arg1)
ret0, _ := ret[0].(io.Reader)
return ret0
}
// Reader indicates an expected call of Reader.
func (mr *MockLimiterMockRecorder) Reader(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Reader", reflect.TypeOf((*MockLimiter)(nil).Reader), arg0, arg1)
}
// Release mocks base method.
func (m *MockLimiter) Release(arg0 string) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "Release", arg0)
}
// Release indicates an expected call of Release.
func (mr *MockLimiterMockRecorder) Release(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Release", reflect.TypeOf((*MockLimiter)(nil).Release), arg0)
}
// Status mocks base method.
func (m *MockLimiter) Status() Status {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Status")
ret0, _ := ret[0].(Status)
return ret0
}
// Status indicates an expected call of Status.
func (mr *MockLimiterMockRecorder) Status() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Status", reflect.TypeOf((*MockLimiter)(nil).Status))
}
// Writer mocks base method.
func (m *MockLimiter) Writer(arg0 context.Context, arg1 io.Writer) io.Writer {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Writer", arg0, arg1)
ret0, _ := ret[0].(io.Writer)
return ret0
}
// Writer indicates an expected call of Writer.
func (mr *MockLimiterMockRecorder) Writer(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Writer", reflect.TypeOf((*MockLimiter)(nil).Writer), arg0, arg1)
}

View File

@ -12,7 +12,7 @@
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package stream
package access
import (
"fmt"
@ -43,22 +43,3 @@ func (c CodeModePairs) SelectCodeMode(size int64) codemode.CodeMode {
panic(fmt.Sprintf("no codemode policy to be selected by size %d, %+v", size, c))
}
// Verify select codemode
func (c CodeModePairs) VerifySelectCodeMode(selectCodeMode codemode.CodeMode) bool {
if !selectCodeMode.IsValid() {
return false
}
for codeMode, pair := range c {
policy := pair.Policy
if !policy.Enable {
continue
}
if selectCodeMode == codeMode {
return true
}
}
return false
}

View File

@ -12,20 +12,20 @@
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package stream_test
package access_test
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/cubefs/cubefs/blobstore/access/stream"
"github.com/cubefs/cubefs/blobstore/access"
"github.com/cubefs/cubefs/blobstore/common/codemode"
)
func TestAccessStreamCodeModePairs(t *testing.T) {
m := stream.CodeModePairs{
codemode.EC6P6: stream.CodeModePair{
m := access.CodeModePairs{
codemode.EC6P6: access.CodeModePair{
Policy: codemode.Policy{
ModeName: codemode.EC6P6.Name(),
MinSize: 1 << 10,
@ -34,7 +34,7 @@ func TestAccessStreamCodeModePairs(t *testing.T) {
},
Tactic: codemode.EC6P6.Tactic(),
},
codemode.EC6P10L2: stream.CodeModePair{
codemode.EC6P10L2: access.CodeModePair{
Policy: codemode.Policy{
ModeName: codemode.EC6P10L2.Name(),
MinSize: 1 << 30,

View File

@ -12,19 +12,17 @@
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package stream
package access
const (
defaultMaxBlobSize uint32 = 1 << 22 // 4MB
defaultDiskPunishIntervalS int = 60
defaultServicePunishIntervalS int = 60
defaultAllocRetryTimes int = 3
defaultAllocRetryIntervalMS int = 100
defaultEncoderConcurrency int = 1000
defaultMinReadShardsX int = 1
defaultShardnodeRetryTimes int = 3
defaultShardnodeRetryIntervalMS int = 200
defaultDiskPunishIntervalS int = 60
defaultServicePunishIntervalS int = 60
defaultAllocRetryTimes int = 3
defaultAllocRetryIntervalMS int = 100
defaultEncoderConcurrency int = 1000
defaultMinReadShardsX int = 1
// client timeout ms
defaultTimeoutClusterMgr int64 = 1000 * 3

View File

@ -38,9 +38,6 @@ import (
"github.com/cubefs/cubefs/blobstore/util/log"
)
// just for cli write to readonly initialised cluster.
var ForceWriteReadonlyCluster = false
// AlgChoose algorithm of choose cluster
type AlgChoose uint32
@ -55,17 +52,14 @@ const (
maxAlg
)
type keyClient struct {
key string
cli *cmapi.Client
}
var cachedClient = struct {
mu sync.Mutex
cache map[proto.ClusterID]*keyClient
}{cache: make(map[proto.ClusterID]*keyClient)}
cache map[string]*cmapi.Client
}{
cache: make(map[string]*cmapi.Client),
}
func getClusterClient(ctx context.Context, clusterID proto.ClusterID, conf cmapi.Config) *cmapi.Client {
func getClusterClient(conf cmapi.Config) *cmapi.Client {
hosts := make([]string, len(conf.Hosts))
copy(hosts, conf.Hosts[:])
sort.Strings(hosts)
@ -73,20 +67,13 @@ func getClusterClient(ctx context.Context, clusterID proto.ClusterID, conf cmapi
cachedClient.mu.Lock()
defer cachedClient.mu.Unlock()
keyCli, ok := cachedClient.cache[clusterID]
if !ok {
cli := cmapi.New(&conf)
cachedClient.cache[clusterID] = &keyClient{key: key, cli: cli}
cli, ok := cachedClient.cache[key]
if ok {
return cli
}
if keyCli.key != key {
span := trace.SpanFromContextSafe(ctx)
span.Warnf("change cluster(%d) clustermgr hosts %s -> %s", clusterID, keyCli.key, key)
cli := cmapi.New(&conf)
keyCli.key = key
*keyCli.cli = *cli
}
return keyCli.cli
cli = cmapi.New(&conf)
cachedClient.cache[key] = cli
return cli
}
// IsValid returns valid algorithm or not.
@ -128,8 +115,6 @@ type ClusterController interface {
GetConfig(ctx context.Context, key string) (string, error)
// ChangeChooseAlg change alloc algorithm
ChangeChooseAlg(alg AlgChoose) error
// GetShardController return IShardController in specified cluster
GetShardController(clusterID proto.ClusterID) (IShardController, error)
}
// ClusterConfig cluster config
@ -141,15 +126,13 @@ type ClusterConfig struct {
Region string `json:"region"`
RegionMagic string `json:"region_magic"`
ClusterReloadSecs int `json:"cluster_reload_secs"`
ShardReloadSecs int `json:"shard_reload_secs"`
ServiceReloadSecs int `json:"service_reload_secs"`
CMClientConfig cmapi.Config `json:"clustermgr_client_config"`
ServiceConfig
VolumeConfig
ServicePunishThreshold uint32 `json:"service_punish_threshold"`
ServicePunishValidIntervalS int `json:"service_punish_valid_interval_s"`
ConsulAgentAddr string `json:"consul_agent_addr"`
ConsulToken string `json:"consul_token"`
ConsulTokenFile string `json:"consul_token_file"`
Clusters []Cluster `json:"clusters"`
}
@ -157,7 +140,6 @@ type ClusterConfig struct {
type Cluster struct {
ClusterID proto.ClusterID `json:"cluster_id"`
Hosts []string `json:"hosts"`
Space SpaceConf `json:"space"` // one space - one cluster
}
type cluster struct {
@ -178,8 +160,7 @@ type clusterControllerImpl struct {
available atomic.Value // available clusters
serviceMgrs sync.Map
volumeGetters sync.Map
shardMgrs sync.Map // cid -> shardController
roundRobinCount uint64 // a count for round robin
roundRobinCount uint64 // a count for round robin
proxy proxy.Cacher
stopCh <-chan struct{}
@ -192,15 +173,9 @@ func NewClusterController(cfg *ClusterConfig, proxy proxy.Cacher, stopCh <-chan
consulConf := api.DefaultConfig()
consulConf.Address = cfg.ConsulAgentAddr
if cfg.ConsulTokenFile != "" {
consulConf.TokenFile = cfg.ConsulTokenFile
}
if cfg.ConsulToken != "" {
consulConf.Token = cfg.ConsulToken
}
var client *api.Client
var err error
if cfg.ConsulAgentAddr != "" {
if consulConf.Address != "" {
client, err = api.NewClient(consulConf)
if err != nil {
return nil, fmt.Errorf("new consul client failed, err: %v", err)
@ -255,7 +230,7 @@ func (c *clusterControllerImpl) loadWithConfig() error {
for _, cs := range c.config.Clusters {
conf := c.config.CMClientConfig
conf.Hosts = cs.Hosts
cmCli := getClusterClient(ctx, cs.ClusterID, conf)
cmCli := getClusterClient(conf)
stat, err := cmCli.Stat(ctx)
if err != nil {
@ -264,13 +239,10 @@ func (c *clusterControllerImpl) loadWithConfig() error {
}
clusterInfo := &cmapi.ClusterInfo{}
clusterInfo.ClusterID = cs.ClusterID
clusterInfo.Capacity = stat.BlobNodeSpaceStat.TotalSpace
clusterInfo.Available = stat.BlobNodeSpaceStat.WritableSpace
clusterInfo.Capacity = stat.SpaceStat.TotalSpace
clusterInfo.Available = stat.SpaceStat.WritableSpace
clusterInfo.Nodes = cs.Hosts
clusterInfo.Readonly = stat.ReadOnly
if clusterInfo.Readonly && ForceWriteReadonlyCluster {
clusterInfo.Readonly = false
}
allClusters[cs.ClusterID] = &cluster{client: cmCli, clusterInfo: clusterInfo}
@ -282,6 +254,7 @@ func (c *clusterControllerImpl) loadWithConfig() error {
span.Debug("readonly or no available cluster", clusterInfo.ClusterID)
}
}
return c.deal(ctx, available, allClusters, totalAvailable)
}
@ -332,12 +305,7 @@ func (c *clusterControllerImpl) loadWithConsul() error {
return c.deal(ctx, available, allClusters, totalAvailable)
}
func (c *clusterControllerImpl) deal(ctx context.Context,
available []*cmapi.ClusterInfo, allClusters clusterMap, totalAvailable int64,
) error {
if len(allClusters) == 0 {
return nil
}
func (c *clusterControllerImpl) deal(ctx context.Context, available []*cmapi.ClusterInfo, allClusters clusterMap, totalAvailable int64) error {
span := trace.SpanFromContextSafe(ctx)
sort.Slice(available, func(i, j int) bool {
@ -345,14 +313,9 @@ func (c *clusterControllerImpl) deal(ctx context.Context,
})
newClusters := make([]*cmapi.ClusterInfo, 0, len(allClusters))
for clusterID, cluster := range allClusters {
for clusterID := range allClusters {
if _, ok := c.serviceMgrs.Load(clusterID); !ok {
newClusters = append(newClusters, cluster.clusterInfo)
} else {
// update cluster client if hosts changed
conf := c.config.CMClientConfig
conf.Hosts = cluster.clusterInfo.Nodes[:]
getClusterClient(ctx, clusterID, conf)
newClusters = append(newClusters, allClusters[clusterID].clusterInfo)
}
}
@ -362,7 +325,7 @@ func (c *clusterControllerImpl) deal(ctx context.Context,
if allClusters[clusterID].client == nil {
conf := c.config.CMClientConfig
conf.Hosts = newCluster.Nodes
allClusters[clusterID].client = getClusterClient(ctx, clusterID, conf)
allClusters[clusterID].client = getClusterClient(conf)
}
cmCli := allClusters[clusterID].client
@ -380,39 +343,26 @@ func (c *clusterControllerImpl) deal(ctx context.Context,
}
}
serviceConfig := c.config.ServiceConfig
serviceConfig.ClusterID = clusterID
serviceConfig.IDC = c.config.IDC
serviceController, err := NewServiceController(serviceConfig, cmCli, c.proxy, c.stopCh)
serviceController, err := NewServiceController(ServiceConfig{
ClusterID: clusterID,
IDC: c.config.IDC,
ReloadSec: c.config.ServiceReloadSecs,
ServicePunishThreshold: c.config.ServicePunishThreshold,
ServicePunishValidIntervalS: c.config.ServicePunishValidIntervalS,
}, cmCli, c.proxy, c.stopCh)
if err != nil {
removeThisCluster()
span.Warn("new service manager failed", clusterID, err)
continue
}
volumeConfig := c.config.VolumeConfig
volumeConfig.ClusterID = clusterID
volumeGetter, err := NewVolumeGetter(volumeConfig, serviceController, c.proxy, c.stopCh)
volumeGetter, err := NewVolumeGetter(clusterID, serviceController, c.proxy, -1)
if err != nil {
removeThisCluster()
span.Warn("new volume getter failed", clusterID, err)
continue
}
space := c.getSpaceConf(clusterID)
span.Debugf("get space config valid:%t, cluster:%d", space.IsValid(), clusterID)
if space.IsValid() { // need shard node, meta system
shardMgr, err := NewShardController(shardCtrlConf{
clusterID: clusterID,
reloadSecs: c.config.ShardReloadSecs,
space: space,
}, cmCli, serviceController, c.stopCh)
if err != nil {
span.Fatalf("new shard controller failed, clusterID=%d, err=%+v", clusterID, err)
}
c.shardMgrs.Store(clusterID, shardMgr)
}
c.serviceMgrs.Store(clusterID, serviceController)
c.volumeGetters.Store(clusterID, volumeGetter)
span.Debug("loaded new cluster", clusterID)
@ -525,22 +475,3 @@ func (c *clusterControllerImpl) GetConfig(ctx context.Context, key string) (ret
}
return
}
func (c *clusterControllerImpl) GetShardController(clusterID proto.ClusterID) (IShardController, error) {
if shardMgr, exist := c.shardMgrs.Load(clusterID); exist {
if controller, ok := shardMgr.(IShardController); ok {
return controller, nil
}
return nil, fmt.Errorf("not shard controller for %d", clusterID)
}
return nil, fmt.Errorf("no shard controller of %d", clusterID)
}
func (c *clusterControllerImpl) getSpaceConf(clusterID proto.ClusterID) SpaceConf {
for _, cs := range c.config.Clusters {
if clusterID == cs.ClusterID {
return cs.Space
}
}
return SpaceConf{}
}

View File

@ -146,7 +146,7 @@ func stat(w http.ResponseWriter, req *http.Request) {
info := &clustermgr.StatInfo{
LeaderHost: hostAddr,
ReadOnly: false,
BlobNodeSpaceStat: clustermgr.SpaceStatInfo{
SpaceStat: clustermgr.SpaceStatInfo{
TotalSpace: 1 << 40,
WritableSpace: 1 << 20,
},

View File

@ -17,12 +17,12 @@ package controller_test
import (
"context"
"math/rand"
"sync"
"testing"
"time"
"github.com/golang/mock/gomock"
bnapi "github.com/cubefs/cubefs/blobstore/api/blobnode"
cmapi "github.com/cubefs/cubefs/blobstore/api/clustermgr"
"github.com/cubefs/cubefs/blobstore/api/proxy"
"github.com/cubefs/cubefs/blobstore/common/codemode"
@ -48,11 +48,10 @@ var (
cmcli cmapi.APIAccess
proxycli proxy.Cacher
dataMu sync.Mutex
dataCalled map[proto.Vid]int
dataNodes map[string]cmapi.ServiceInfo
dataVolumes map[proto.Vid]cmapi.VolumeInfo
dataDisks map[proto.DiskID]cmapi.BlobNodeDiskInfo
dataDisks map[proto.DiskID]bnapi.DiskInfo
)
func init() {
@ -64,15 +63,15 @@ func init() {
dataVolumes[1] = cmapi.VolumeInfo{
VolumeInfoBase: cmapi.VolumeInfoBase{Vid: 1, CodeMode: codemode.EC6P10L2},
Units: []cmapi.Unit{
{Vuid: 1011, DiskID: 1021},
{Vuid: 1012, DiskID: 1022},
{Vuid: 1011, DiskID: 1021, Host: "1031"},
{Vuid: 1012, DiskID: 1022, Host: "1032"},
},
}
dataVolumes[9] = cmapi.VolumeInfo{
VolumeInfoBase: cmapi.VolumeInfoBase{Vid: 9, CodeMode: codemode.EC16P20L2},
Units: []cmapi.Unit{
{Vuid: 9011, DiskID: 9021},
{Vuid: 9012, DiskID: 9022},
{Vuid: 9011, DiskID: 9021, Host: "9031"},
{Vuid: 9012, DiskID: 9022, Host: "9032"},
},
}
dataVolumes[vid404] = cmapi.VolumeInfo{VolumeInfoBase: cmapi.VolumeInfoBase{Vid: vid404}}
@ -85,24 +84,20 @@ func init() {
},
}
dataDisks = make(map[proto.DiskID]cmapi.BlobNodeDiskInfo)
dataDisks[10001] = cmapi.BlobNodeDiskInfo{
DiskInfo: cmapi.DiskInfo{
ClusterID: 1,
Idc: idc,
Host: "blobnode-1",
},
DiskHeartBeatInfo: cmapi.DiskHeartBeatInfo{
dataDisks = make(map[proto.DiskID]bnapi.DiskInfo)
dataDisks[10001] = bnapi.DiskInfo{
ClusterID: 1,
Idc: idc,
Host: "blobnode-1",
DiskHeartBeatInfo: bnapi.DiskHeartBeatInfo{
DiskID: 10001,
},
}
dataDisks[10002] = cmapi.BlobNodeDiskInfo{
DiskInfo: cmapi.DiskInfo{
ClusterID: 1,
Idc: idc,
Host: "blobnode-2",
},
DiskHeartBeatInfo: cmapi.DiskHeartBeatInfo{
dataDisks[10002] = bnapi.DiskInfo{
ClusterID: 1,
Idc: idc,
Host: "blobnode-2",
DiskHeartBeatInfo: bnapi.DiskHeartBeatInfo{
DiskID: 10002,
},
}
@ -117,31 +112,26 @@ func init() {
return cmapi.ServiceInfo{}, errNotFound
})
cli.EXPECT().ListDisk(A, A).AnyTimes().Return(cmapi.ListDiskRet{}, nil)
cli.EXPECT().ListShardNodeDisk(A, A).AnyTimes().Return(cmapi.ListShardNodeDiskRet{}, nil)
cmcli = cli
pcli := mocks.NewMockProxyClient(C(&testing.T{}))
pcli.EXPECT().GetCacheVolume(A, A, A).AnyTimes().DoAndReturn(
func(ctx context.Context, _ string, args *proxy.CacheVolumeArgs) (*cmapi.VolumeInfo, error) {
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
}
func(_ context.Context, _ string, args *proxy.CacheVolumeArgs) (*proxy.VersionVolume, error) {
volume := new(proxy.VersionVolume)
vid := args.Vid
dataMu.Lock()
dataCalled[vid]++
dataMu.Unlock()
if val, ok := dataVolumes[vid]; ok {
if vid == vid404 {
return nil, errcode.ErrVolumeNotExist
}
return &val, nil
volume.VolumeInfo = val
volume.Version = volume.GetVersion()
return volume, nil
}
return nil, errNotFound
})
pcli.EXPECT().GetCacheDisk(A, A, A).AnyTimes().DoAndReturn(
func(_ context.Context, _ string, args *proxy.CacheDiskArgs) (*cmapi.BlobNodeDiskInfo, error) {
func(_ context.Context, _ string, args *proxy.CacheDiskArgs) (*bnapi.DiskInfo, error) {
if val, ok := dataDisks[args.DiskID]; ok {
return &val, nil
}

View File

@ -24,6 +24,7 @@ import (
"golang.org/x/sync/singleflight"
"github.com/cubefs/cubefs/blobstore/api/blobnode"
"github.com/cubefs/cubefs/blobstore/api/clustermgr"
"github.com/cubefs/cubefs/blobstore/api/proxy"
"github.com/cubefs/cubefs/blobstore/common/proto"
@ -34,8 +35,12 @@ import (
)
const (
_primaryDisk = "_disk_"
_primaryShardnodeDisk = "_sddisk_"
_diskHostServicePrefix = "diskhost"
// default service punish check valid interval
defaultServicePinishValidIntervalS int = 30
// default service punish check threshold
defaultServicePinishThreshold uint32 = 3
)
// HostIDC item of host with idc
@ -63,15 +68,6 @@ type ServiceController interface {
// PunishDiskWithThreshold will punish a disk host for
// an punishTimeSec interval if disk host failed times satisfied with threshold
PunishDiskWithThreshold(ctx context.Context, diskID proto.DiskID, punishTimeSec int)
// GetShardnodeHost return shardnode host
GetShardnodeHost(ctx context.Context, diskID proto.DiskID) (hostIDC *HostIDC, err error)
// PunishShardnode will punish a shardnode disk host for an punishTimeSec interval
PunishShardnode(ctx context.Context, diskID proto.DiskID, punishTimeSec int)
// PunishShardnodeDiskWithThreshold will punish a disk host for
// an punishTimeSec interval if disk host failed times satisfied with threshold
PunishShardnodeDiskWithThreshold(ctx context.Context, diskID proto.DiskID, punishTimeSec int)
// IsPunishShardnode return shardnode disk is punish
IsPunishShardnode(diskID proto.DiskID) bool
}
type (
@ -90,8 +86,6 @@ type hostItem struct {
lastModifyTime int64
// failedTimes record the service host failed times during some interval
failedTimes uint32
createAt time.Time
}
func (h *hostItem) isPunish() bool {
@ -100,24 +94,19 @@ func (h *hostItem) isPunish() bool {
// ServiceConfig service config
type ServiceConfig struct {
ClusterID proto.ClusterID `json:"-"`
IDC string `json:"-"`
ServiceReloadSecs int `json:"service_reload_secs"`
LoadDiskIntervalS int `json:"load_disk_interval_s"`
DiskPunishThreshold uint32 `json:"disk_punish_threshold"`
DiskPunishValidIntervalS int `json:"disk_punish_valid_interval_s"`
DiskMemoryExpirationS int `json:"disk_memory_expiration_s"` // <= 0 means no expiration
ServicePunishThreshold uint32 `json:"service_punish_threshold"`
ServicePunishValidIntervalS int `json:"service_punish_valid_interval_s"`
ClusterID proto.ClusterID
IDC string
ReloadSec int
LoadDiskInterval int
ServicePunishThreshold uint32
ServicePunishValidIntervalS int
}
type serviceControllerImpl struct {
// allServices hold all disk/service host map, use for quickly find out
allServices sync.Map
serviceHosts serviceMap
brokenDisks sync.Map
sdBrokenDisks sync.Map // shard node broken disks
allServices sync.Map
serviceHosts serviceMap
brokenDisks sync.Map
group singleflight.Group
serviceLocks map[string]*sync.RWMutex
@ -128,15 +117,12 @@ type serviceControllerImpl struct {
}
// NewServiceController returns a service controller
func NewServiceController(cfg ServiceConfig,
cmCli clustermgr.APIAccess, proxy proxy.Cacher, stopCh <-chan struct{},
) (ServiceController, error) {
defaulter.IntegerLessOrEqual(&cfg.ServiceReloadSecs, 10)
defaulter.IntegerLessOrEqual(&cfg.LoadDiskIntervalS, 300)
defaulter.IntegerEqual(&cfg.DiskPunishThreshold, 3)
defaulter.IntegerLessOrEqual(&cfg.DiskPunishValidIntervalS, 30)
defaulter.IntegerEqual(&cfg.ServicePunishThreshold, 3)
defaulter.IntegerLessOrEqual(&cfg.ServicePunishValidIntervalS, 30)
func NewServiceController(cfg ServiceConfig, cmCli clustermgr.APIAccess, proxy proxy.Cacher,
stopCh <-chan struct{}) (ServiceController, error) {
defaulter.Equal(&cfg.ServicePunishThreshold, defaultServicePinishThreshold)
defaulter.LessOrEqual(&cfg.ServicePunishValidIntervalS, defaultServicePinishValidIntervalS)
defaulter.LessOrEqual(&cfg.LoadDiskInterval, int(300))
defaulter.LessOrEqual(&cfg.ReloadSec, int(10))
controller := &serviceControllerImpl{
serviceHosts: serviceMap{
@ -159,7 +145,7 @@ func NewServiceController(cfg ServiceConfig,
return controller, nil
}
go func() {
tick := time.NewTicker(time.Duration(cfg.ServiceReloadSecs) * time.Second)
tick := time.NewTicker(time.Duration(cfg.ReloadSec) * time.Second)
defer tick.Stop()
for {
select {
@ -173,12 +159,13 @@ func NewServiceController(cfg ServiceConfig,
}
}()
go func() {
tick := time.NewTicker(time.Duration(cfg.LoadDiskIntervalS) * time.Second)
controller.loadBrokenDisks()
tick := time.NewTicker(time.Duration(cfg.LoadDiskInterval) * time.Second)
defer tick.Stop()
for {
controller.loadBrokenDisks()
select {
case <-tick.C:
controller.loadBrokenDisks()
case <-stopCh:
return
}
@ -209,7 +196,7 @@ func (s *serviceControllerImpl) load(cid proto.ClusterID, idc string) error {
}
if len(hostItems) > 0 {
for _, item := range hostItems {
s.allServices.Store(s.getServiceKey(serviceName, item.host), item)
s.allServices.Store(serviceName+item.host, item)
span.Debugf("store node %+v", item)
}
s.serviceHosts[serviceName].Store(hostItems)
@ -218,65 +205,29 @@ func (s *serviceControllerImpl) load(cid proto.ClusterID, idc string) error {
}
func (s *serviceControllerImpl) loadBrokenDisks() {
_, ctx := trace.StartSpanFromContext(context.Background(), "access_cluster_load_disks")
fnBlobnode := func(ctx context.Context, args *clustermgr.ListOptionArgs, diskMap map[proto.DiskID]struct{}) error {
list, err := s.cmClient.ListDisk(ctx, args)
if err != nil {
return err
}
for _, disk := range list.Disks {
diskMap[disk.DiskID] = struct{}{}
}
args.Marker = list.Marker
return nil
}
fnShardnode := func(ctx context.Context, args *clustermgr.ListOptionArgs, diskMap map[proto.DiskID]struct{}) error {
list, err := s.cmClient.ListShardNodeDisk(ctx, args)
if err != nil {
return err
}
for _, disk := range list.Disks {
diskMap[disk.DiskID] = struct{}{}
}
args.Marker = list.Marker
return nil
}
s.processBrokenDisks(ctx, fnBlobnode, &s.brokenDisks)
s.processBrokenDisks(ctx, fnShardnode, &s.sdBrokenDisks)
}
func (s *serviceControllerImpl) processBrokenDisks(
ctx context.Context,
fn func(context.Context, *clustermgr.ListOptionArgs, map[proto.DiskID]struct{}) error,
disks *sync.Map,
) {
span := trace.SpanFromContextSafe(ctx)
span, ctx := trace.StartSpanFromContext(context.Background(), "access_cluster_load_disks")
brokenDiskIDs := make(map[proto.DiskID]struct{})
for _, st := range []proto.DiskStatus{proto.DiskStatusBroken} {
for _, st := range []proto.DiskStatus{proto.DiskStatusBroken, proto.DiskStatusRepairing} {
span.Debugf("to load disks of cluster %d %s", s.config.ClusterID, st.String())
args := &clustermgr.ListOptionArgs{Status: st, Count: 1 << 10}
for {
err := fn(ctx, args, brokenDiskIDs)
args := &clustermgr.ListOptionArgs{Status: st, Marker: 1, Count: 1 << 10}
for args.Marker > proto.InvalidDiskID {
list, err := s.cmClient.ListDisk(ctx, args)
if err != nil {
span.Errorf("load disks of cluster %d, err:%+v", s.config.ClusterID, err)
span.Errorf("load disks of cluster %d %s", s.config.ClusterID, err.Error())
return
}
if args.Marker <= proto.InvalidDiskID {
break
for _, disk := range list.Disks {
brokenDiskIDs[disk.DiskID] = struct{}{}
}
args.Marker = list.Marker
}
}
// clean cached disks, ignore cases when concurrency getting disk.
disks.Range(func(key, value any) bool {
disks.Delete(key)
s.brokenDisks.Range(func(key, value interface{}) bool {
s.brokenDisks.Delete(key)
return true
})
if len(brokenDiskIDs) == 0 {
@ -284,7 +235,7 @@ func (s *serviceControllerImpl) processBrokenDisks(
}
span.Warnf("load disks of cluster %d broken %v", s.config.ClusterID, brokenDiskIDs)
for diskID := range brokenDiskIDs {
disks.Store(diskID, struct{}{})
s.brokenDisks.Store(diskID, struct{}{})
}
}
@ -379,19 +330,16 @@ func (s *serviceControllerImpl) GetDiskHost(ctx context.Context, diskID proto.Di
_, broken := s.brokenDisks.Load(diskID)
v, ok := s.allServices.Load(s.getServiceKey(_primaryDisk, diskID))
v, ok := s.allServices.Load(_diskHostServicePrefix + (diskID.ToString()))
if ok {
item := v.(*hostItem)
expiration := time.Second * time.Duration(s.config.DiskMemoryExpirationS)
if expiration <= 0 || time.Since(item.createAt) < expiration {
return &HostIDC{
Host: item.host,
IDC: item.idc,
Punished: broken || item.isPunish(),
}, nil
}
return &HostIDC{
Host: item.host,
IDC: item.idc,
Punished: broken || item.isPunish(),
}, nil
}
ret, err, _ := s.group.Do("get-diskinfo-"+diskID.ToString(), func() (any, error) {
ret, err, _ := s.group.Do("get-diskinfo-"+diskID.ToString(), func() (interface{}, error) {
hosts, err := s.GetServiceHosts(ctx, proto.ServiceNameProxy)
if err != nil {
return nil, err
@ -410,48 +358,10 @@ func (s *serviceControllerImpl) GetDiskHost(ctx context.Context, diskID proto.Di
span.Error("can't get disk host from proxy", err)
return nil, errors.Base(err, "get disk info", diskID)
}
diskInfo := ret.(*clustermgr.BlobNodeDiskInfo)
item := &hostItem{host: diskInfo.Host, idc: diskInfo.Idc, createAt: time.Now()}
s.allServices.Store(s.getServiceKey(_primaryDisk, diskInfo.DiskID), item)
return &HostIDC{
Host: item.host,
IDC: item.idc,
Punished: broken || item.isPunish(),
}, nil
}
func (s *serviceControllerImpl) GetShardnodeHost(ctx context.Context, diskID proto.DiskID) (hostIDC *HostIDC, err error) {
span := trace.SpanFromContextSafe(ctx)
_, broken := s.sdBrokenDisks.Load(diskID)
v, ok := s.allServices.Load(s.getServiceKey(_primaryShardnodeDisk, diskID))
if ok {
item := v.(*hostItem)
return &HostIDC{
Host: item.host,
IDC: item.idc,
Punished: broken || item.isPunish(),
}, nil
}
ret, err, _ := s.group.Do("get-shardnode-diskinfo-"+diskID.ToString(), func() (any, error) {
// todo: support proxy get disk host, next version
info, err := s.cmClient.ShardNodeDiskInfo(ctx, diskID)
if err != nil {
return nil, err
}
return info, nil
})
if err != nil {
span.Error("can't get shardnode disk host from cm", err)
return nil, errors.Base(err, "get shardnode disk info", diskID)
}
diskInfo := ret.(*clustermgr.ShardNodeDiskInfo)
diskInfo := ret.(*blobnode.DiskInfo)
item := &hostItem{host: diskInfo.Host, idc: diskInfo.Idc}
s.allServices.Store(s.getServiceKey(_primaryShardnodeDisk, diskInfo.DiskID), item)
s.allServices.Store(_diskHostServicePrefix+(diskInfo.DiskID.ToString()), item)
return &HostIDC{
Host: item.host,
IDC: item.idc,
@ -461,9 +371,9 @@ func (s *serviceControllerImpl) GetShardnodeHost(ctx context.Context, diskID pro
// PunishService will punish an service host for an punishTimeSec interval
func (s *serviceControllerImpl) PunishService(ctx context.Context, service, host string, punishTimeSec int) {
v, ok := s.allServices.Load(s.getServiceKey(service, host))
v, ok := s.allServices.Load(service + host)
if !ok {
panic(fmt.Sprintf("can't find host in all services map, %s", s.getServiceKey(service, host)))
panic(fmt.Sprintf("can't find host in all services map, %s-%s", service, host))
}
item := v.(*hostItem)
@ -473,63 +383,28 @@ func (s *serviceControllerImpl) PunishService(ctx context.Context, service, host
// PunishDisk will punish a disk host for an punishTimeSec interval
func (s *serviceControllerImpl) PunishDisk(ctx context.Context, diskID proto.DiskID, punishTimeSec int) {
s.PunishService(ctx, _primaryDisk, diskID.ToString(), punishTimeSec)
}
// PunishShardnode will punish a shardnode disk host for an punishTimeSec interval
func (s *serviceControllerImpl) PunishShardnode(ctx context.Context, diskID proto.DiskID, punishTimeSec int) {
s.PunishService(ctx, _primaryShardnodeDisk, diskID.ToString(), punishTimeSec)
}
// PunishShardnodeDiskWithThreshold will punish a disk host for
// an punishTimeSec interval if disk host failed times satisfied with threshold
func (s *serviceControllerImpl) PunishShardnodeDiskWithThreshold(ctx context.Context, diskID proto.DiskID, punishTimeSec int) {
s.punishWith(ctx, _primaryShardnodeDisk, diskID.ToString(), punishTimeSec,
s.config.DiskPunishThreshold, s.config.DiskPunishValidIntervalS)
}
func (s *serviceControllerImpl) IsPunishShardnode(diskID proto.DiskID) bool {
return s.isPunishService(_primaryShardnodeDisk, diskID.ToString())
}
func (s *serviceControllerImpl) isPunishService(primary, secondary string) bool {
v, ok := s.allServices.Load(s.getServiceKey(primary, secondary))
if !ok {
panic(fmt.Sprintf("can't find host in all services map, %s", s.getServiceKey(primary, secondary)))
}
item := v.(*hostItem)
return item.isPunish()
s.PunishService(ctx, _diskHostServicePrefix, diskID.ToString(), punishTimeSec)
}
// PunishDiskWithThreshold will punish a disk host for
// an punishTimeSec interval if disk host failed times satisfied with threshold
func (s *serviceControllerImpl) PunishDiskWithThreshold(ctx context.Context, diskID proto.DiskID, punishTimeSec int) {
s.punishWith(ctx, _primaryDisk, diskID.ToString(), punishTimeSec,
s.config.DiskPunishThreshold, s.config.DiskPunishValidIntervalS)
s.PunishServiceWithThreshold(ctx, _diskHostServicePrefix, diskID.ToString(), punishTimeSec)
}
// PunishServiceWithThreshold will punish an service host for
// an punishTimeSec interval if service failed times satisfied with threshold
func (s *serviceControllerImpl) PunishServiceWithThreshold(ctx context.Context, service, host string, punishTimeSec int) {
s.punishWith(ctx, service, host, punishTimeSec, s.config.ServicePunishThreshold, s.config.ServicePunishValidIntervalS)
}
func (s *serviceControllerImpl) punishWith(ctx context.Context,
primary, secondary string, punishTimeSec int,
threshold uint32, interval int,
) {
serviceKey := s.getServiceKey(primary, secondary)
v, ok := s.allServices.Load(serviceKey)
v, ok := s.allServices.Load(service + host)
if !ok {
panic(fmt.Sprintf("can't load host in all services map, %s", serviceKey))
panic(fmt.Sprintf("can't can host in all services map, %s-%s", service, host))
}
item := v.(*hostItem)
new := atomic.AddUint32(&item.failedTimes, 1)
// failedTimes larger than threshold, then check the lastModifyTime
if new >= threshold {
if time.Since(time.Unix(atomic.LoadInt64(&item.lastModifyTime), 0)) < time.Duration(interval)*time.Second {
s.PunishService(ctx, primary, secondary, punishTimeSec)
if new >= s.config.ServicePunishThreshold {
if time.Since(time.Unix(atomic.LoadInt64(&item.lastModifyTime), 0)) < time.Duration(s.config.ServicePunishValidIntervalS)*time.Second {
s.PunishService(ctx, service, host, punishTimeSec)
return
}
atomic.AddUint32(&item.failedTimes, -(new - 1))
@ -540,7 +415,3 @@ func (s *serviceControllerImpl) punishWith(ctx context.Context,
func (s *serviceControllerImpl) getServiceLock(name string) *sync.RWMutex {
return s.serviceLocks[name]
}
func (s *serviceControllerImpl) getServiceKey(primary string, secondary any) string {
return fmt.Sprintf("%s/%v", primary, secondary)
}

View File

@ -23,6 +23,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/cubefs/cubefs/blobstore/access/controller"
bnapi "github.com/cubefs/cubefs/blobstore/api/blobnode"
cmapi "github.com/cubefs/cubefs/blobstore/api/clustermgr"
"github.com/cubefs/cubefs/blobstore/api/proxy"
"github.com/cubefs/cubefs/blobstore/common/proto"
@ -56,7 +57,7 @@ func TestAccessServiceNew(t *testing.T) {
}
{
sc, err := controller.NewServiceController(
controller.ServiceConfig{IDC: idc + "x", ServiceReloadSecs: 1}, cmcli, proxycli, nil)
controller.ServiceConfig{IDC: idc + "x", ReloadSec: 1}, cmcli, proxycli, nil)
require.NoError(t, err)
_, err = sc.GetServiceHost(serviceCtx, serviceName)
@ -66,7 +67,7 @@ func TestAccessServiceNew(t *testing.T) {
func TestAccessServiceGetServiceHost(t *testing.T) {
sc, err := controller.NewServiceController(
controller.ServiceConfig{IDC: idc, ServiceReloadSecs: 1}, cmcli, proxycli, nil)
controller.ServiceConfig{IDC: idc, ReloadSec: 1}, cmcli, proxycli, nil)
require.NoError(t, err)
keys := make(hostSet)
@ -86,7 +87,7 @@ func TestAccessServicePunishService(t *testing.T) {
stop := closer.New()
defer stop.Close()
sc, err := controller.NewServiceController(
controller.ServiceConfig{IDC: idc, ServiceReloadSecs: 1}, cmcli, proxycli, stop.Done())
controller.ServiceConfig{IDC: idc, ReloadSec: 1}, cmcli, proxycli, stop.Done())
require.NoError(t, err)
{
@ -137,7 +138,7 @@ func TestAccessServicePunishServiceWithThreshold(t *testing.T) {
sc, err := controller.NewServiceController(
controller.ServiceConfig{
IDC: idc,
ServiceReloadSecs: 1,
ReloadSec: 1,
ServicePunishThreshold: threshold,
ServicePunishValidIntervalS: 2,
}, cmcli, proxycli, stop.Done())
@ -189,7 +190,7 @@ func TestAccessServicePunishServiceWithThreshold(t *testing.T) {
func TestAccessServiceGetDiskHost(t *testing.T) {
sc, err := controller.NewServiceController(
controller.ServiceConfig{IDC: idc, ServiceReloadSecs: 1}, cmcli, proxycli, nil)
controller.ServiceConfig{IDC: idc, ReloadSec: 1}, cmcli, proxycli, nil)
require.NoError(t, err)
{
@ -203,24 +204,11 @@ func TestAccessServiceGetDiskHost(t *testing.T) {
}
}
func TestAccessServiceGetDiskHostExpired(t *testing.T) {
sc, err := controller.NewServiceController(
controller.ServiceConfig{IDC: idc, ServiceReloadSecs: 1, DiskMemoryExpirationS: 1}, cmcli, proxycli, nil)
require.NoError(t, err)
host, err := sc.GetDiskHost(serviceCtx, proto.DiskID(10001))
require.NoError(t, err)
require.True(t, host.Host == "blobnode-1")
time.Sleep(time.Second)
_, err = sc.GetDiskHost(serviceCtx, proto.DiskID(10001))
require.NoError(t, err)
}
func TestAccessServiceGetBrokenDiskHost(t *testing.T) {
brokenRet := cmapi.ListDiskRet{}
brokenRet.Disks = make([]*cmapi.BlobNodeDiskInfo, 2)
brokenRet.Disks[0] = &cmapi.BlobNodeDiskInfo{}
brokenRet.Disks[1] = &cmapi.BlobNodeDiskInfo{}
brokenRet.Disks = make([]*bnapi.DiskInfo, 2)
brokenRet.Disks[0] = &bnapi.DiskInfo{}
brokenRet.Disks[1] = &bnapi.DiskInfo{}
cli := mocks.NewMockClientAPI(C(t))
cli.EXPECT().GetService(A, A).Times(5).DoAndReturn(
@ -230,12 +218,11 @@ func TestAccessServiceGetBrokenDiskHost(t *testing.T) {
}
return cmapi.ServiceInfo{}, errNotFound
})
cli.EXPECT().ListDisk(A, A).Times(3).Return(brokenRet, nil)
cli.EXPECT().ListShardNodeDisk(A, A).Times(3).Return(cmapi.ListShardNodeDiskRet{}, nil)
cli.EXPECT().ListDisk(A, A).Times(6).Return(brokenRet, nil)
pcli := mocks.NewMockProxyClient(C(t))
pcli.EXPECT().GetCacheDisk(A, A, A).AnyTimes().DoAndReturn(
func(_ context.Context, _ string, args *proxy.CacheDiskArgs) (*cmapi.BlobNodeDiskInfo, error) {
func(_ context.Context, _ string, args *proxy.CacheDiskArgs) (*bnapi.DiskInfo, error) {
if val, ok := dataDisks[args.DiskID]; ok {
return &val, nil
}
@ -245,7 +232,7 @@ func TestAccessServiceGetBrokenDiskHost(t *testing.T) {
stop := closer.New()
defer stop.Close()
sc, err := controller.NewServiceController(
controller.ServiceConfig{IDC: idc, ServiceReloadSecs: 1, LoadDiskIntervalS: 1}, cli, pcli, stop.Done())
controller.ServiceConfig{IDC: idc, ReloadSec: 1, LoadDiskInterval: 1}, cli, pcli, stop.Done())
require.NoError(t, err)
{
@ -283,7 +270,6 @@ func TestAccessServiceGetBrokenDiskHost(t *testing.T) {
brokenRet.Disks[0].DiskID = 10000
brokenRet.Disks[1].DiskID = 10000
cli.EXPECT().ListDisk(A, A).Times(1).Return(brokenRet, errors.New("list error"))
cli.EXPECT().ListShardNodeDisk(A, A).Times(1).Return(cmapi.ListShardNodeDiskRet{}, errors.New("list error"))
time.Sleep(time.Second)
{
host, err := sc.GetDiskHost(serviceCtx, 10001)
@ -295,8 +281,7 @@ func TestAccessServiceGetBrokenDiskHost(t *testing.T) {
}
brokenRet.Disks = brokenRet.Disks[:0]
cli.EXPECT().ListDisk(A, A).Times(1).Return(brokenRet, nil)
cli.EXPECT().ListShardNodeDisk(A, A).Times(1).Return(cmapi.ListShardNodeDiskRet{}, nil)
cli.EXPECT().ListDisk(A, A).Times(2).Return(brokenRet, nil)
time.Sleep(time.Second)
{
host, err := sc.GetDiskHost(serviceCtx, 10001)
@ -312,7 +297,7 @@ func TestAccessServicePunishDisk(t *testing.T) {
stop := closer.New()
defer stop.Close()
sc, err := controller.NewServiceController(
controller.ServiceConfig{IDC: idc, ServiceReloadSecs: 1}, cmcli, proxycli, stop.Done())
controller.ServiceConfig{IDC: idc, ReloadSec: 1}, cmcli, proxycli, stop.Done())
require.NoError(t, err)
{

View File

@ -1,728 +0,0 @@
// Copyright 2024 The CubeFS Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package controller
import (
"context"
"fmt"
"math/rand"
"sync"
"time"
"github.com/google/btree"
"golang.org/x/sync/singleflight"
acapi "github.com/cubefs/cubefs/blobstore/api/access"
"github.com/cubefs/cubefs/blobstore/api/clustermgr"
"github.com/cubefs/cubefs/blobstore/api/shardnode"
"github.com/cubefs/cubefs/blobstore/cli/common"
errcode "github.com/cubefs/cubefs/blobstore/common/errors"
"github.com/cubefs/cubefs/blobstore/common/proto"
"github.com/cubefs/cubefs/blobstore/common/sharding"
"github.com/cubefs/cubefs/blobstore/common/trace"
"github.com/cubefs/cubefs/blobstore/util/defaulter"
"github.com/cubefs/cubefs/blobstore/util/errors"
)
const (
defaultBTreeDegree = 16
defaultShardReloadSecs = 120
)
var (
errCatalogInvalid = errors.New("invalid catalog")
errCatalogNoLeader = errors.New("catalog item no leader")
errShardInvalid = errors.New("invalid shard info")
)
type IShardController interface {
GetShard(ctx context.Context, shardKeys []string) (Shard, error)
GetShardByID(ctx context.Context, shardID proto.ShardID) (Shard, error)
GetShardByRange(ctx context.Context, shardRange sharding.Range) (Shard, error)
GetFisrtShard(ctx context.Context) (Shard, error)
GetNextShard(ctx context.Context, shardRange sharding.Range) (Shard, error)
GetSpaceID() proto.SpaceID
UpdateRoute(ctx context.Context) error
UpdateShard(ctx context.Context, ss shardnode.ShardStats) error
GetShardSubRangeCount(ctx context.Context) int
}
type shardCtrlConf struct {
clusterID proto.ClusterID
reloadSecs int
space SpaceConf
}
type SpaceConf struct {
Name string `json:"name"`
AK string `json:"ak"`
SK string `json:"sk"`
}
func (c *SpaceConf) IsValid() bool {
return c.Name != "" && c.AK != "" && c.SK != ""
}
func NewShardController(conf shardCtrlConf, cmCli clustermgr.ClientAPI, punishCtrl ServiceController, stopCh <-chan struct{}) (IShardController, error) {
defaulter.Equal(&conf.reloadSecs, defaultShardReloadSecs)
s := &shardControllerImpl{
shards: make(map[proto.ShardID]*shard),
ranges: btree.New(defaultBTreeDegree),
conf: conf,
cmCli: cmCli,
stopCh: stopCh,
punishCtrl: punishCtrl,
}
span, ctx := trace.StartSpanFromContext(context.Background(), "")
span.Debugf("start new shard controller, conf:%+v", conf)
err := s.initSpace(ctx)
if err != nil {
return nil, err
}
err = s.initRoute(ctx)
// if err is nil, errCatalogNoLeader: OK. we can get leader from sn when write leader node ; else : FATAL
if err != nil && !errors.Is(err, errCatalogNoLeader) {
return nil, err
}
sd, err := s.GetFisrtShard(ctx)
if err != nil {
return nil, err
}
s.subRangeCnt = len(sd.(*shard).rangeExt.Subs)
go s.incrementalRoute()
span.Debugf("success to new shard controller, clusterID:%d, space:%s", conf.clusterID, conf.space.Name)
return s, nil
}
type shardControllerImpl struct {
shards map[proto.ShardID]*shard
ranges *btree.BTree
version proto.RouteVersion
spaceID proto.SpaceID
groupRun singleflight.Group
subRangeCnt int
sync.RWMutex // todo: I will optimize locker in the next version
conf shardCtrlConf
cmCli clustermgr.ClientAPI
punishCtrl ServiceController
stopCh <-chan struct{}
}
func (s *shardControllerImpl) GetShard(ctx context.Context, shardKeys []string) (Shard, error) {
// shard_1 ranges [1, 100) , shard 2: [100, 200), shard 3: [200, 300) ...
// if compare shard keys=20, it belong to shard 1 ; if keys=100, it belong to shard 2 ; keys=220, belong to shard 3
// if keys=120, will walk [shard 2, shard end]
s.RLock()
defer s.RUnlock()
span := trace.SpanFromContextSafe(ctx)
ci := sharding.NewCompareItem(sharding.RangeType_RangeTypeHash, shardKeys)
var si *shard
pivot := &compareItem{ci: *ci}
s.ranges.AscendGreaterOrEqual(pivot, func(i btree.Item) bool {
si = i.(*shard)
// span.Debugf("shardID=%d, max boundary=%d, compare=%d, shard=%+v", si.shardID, si.rangeExt.MaxBoundary(), ci.GetBoundary(), *si)
if si.belong(ci) {
return false
}
si = nil
return true
})
if si == nil { // not found expect shard
span.Errorf("not find shard. name:%s, shard len:%d, key boundary:%s", shardKeys, s.ranges.Len(), ci.GetBoundary())
return nil, errcode.ErrAccessNotFoundShard
}
return si, nil
}
func (s *shardControllerImpl) GetShardByID(ctx context.Context, shardID proto.ShardID) (Shard, error) {
sd, ok := s.getShardByID(shardID)
if ok {
return sd, nil
}
// not found expect shard
return nil, errcode.ErrAccessNotFoundShard
}
func (s *shardControllerImpl) GetFisrtShard(ctx context.Context) (Shard, error) {
s.RLock()
defer s.RUnlock()
span := trace.SpanFromContextSafe(ctx)
min := s.ranges.Min()
if min == nil { // not found expect shard
span.Errorf("not find shard. name:%s, shard len:%d", s.ranges.Len())
return nil, errcode.ErrAccessNotFoundShard
}
return min.(*shard), nil
}
func (s *shardControllerImpl) GetShardByRange(ctx context.Context, shardRange sharding.Range) (Shard, error) {
s.RLock()
defer s.RUnlock()
span := trace.SpanFromContextSafe(ctx)
var si *shard
pivot := &shard{rangeExt: shardRange}
// todo: shard ranges split, old shard range cant find
s.ranges.DescendLessOrEqual(pivot, func(i btree.Item) bool {
si = i.(*shard)
if si.contain(&shardRange) {
return false
}
si = nil
return true
})
if si == nil { // not found expect shard
span.Errorf("not find shard. range:%s, shard len:%d", common.RawString(shardRange), s.ranges.Len())
return nil, errcode.ErrAccessNotFoundShard
}
return si, nil
}
func (s *shardControllerImpl) GetNextShard(ctx context.Context, shardRange sharding.Range) (Shard, error) {
s.RLock()
defer s.RUnlock()
span := trace.SpanFromContextSafe(ctx)
// range is the end, the last one
if s.ranges.Max().(*shard).contain(&shardRange) {
return nil, nil
}
var si *shard
pivot := &shard{rangeExt: shardRange}
// todo: If two shard merge, it is possible that the shard queried contains the shard range
s.ranges.AscendGreaterOrEqual(pivot, func(i btree.Item) bool {
si = i.(*shard)
if !si.contain(&shardRange) {
return false
}
si = nil
return true
})
if si == nil { // not found expect shard
span.Errorf("not find shard. range:%s, shard len:%d", common.RawString(shardRange), s.ranges.Len())
return nil, errcode.ErrAccessNotFoundShard
}
return si, nil
}
func (s *shardControllerImpl) GetSpaceID() proto.SpaceID {
return s.spaceID
}
func (s *shardControllerImpl) UpdateRoute(ctx context.Context) error {
// Aggregation blob operations which comes from upper-layer
_, err, _ := s.groupRun.Do("updateRoute", func() (interface{}, error) {
// there is only one updateRoute, the same time. no concurrence
err1 := s.updateRoute(ctx)
return nil, err1
})
return err
}
// UpdateShard update leader disk id and units info
func (s *shardControllerImpl) UpdateShard(ctx context.Context, sd shardnode.ShardStats) error {
span := trace.SpanFromContextSafe(ctx)
span.Debugf("will update shard, leaderDiskID=%d, LeaderSuid=%d, version=%d, suid=%d", sd.LeaderDiskID, sd.LeaderSuid, sd.RouteVersion, sd.Suid)
_, err, _ := s.groupRun.Do("shardID-"+sd.Suid.ShardID().ToString(), func() (interface{}, error) {
if isInvalidShardStat(sd) {
span.Errorf("invalid shard get from shard node. shard info:%+v", sd)
return nil, errShardInvalid
}
// todo: optimize lock at next version; will split smaller Lock or do a copy update
s.Lock()
defer s.Unlock()
// shard exist
oldShard, exist := s.getShardNoLock(sd.Suid.ShardID())
if !exist {
span.Warnf("dont need update shard, exist:%t, current shard:%+v, replace shard:%+v", exist, oldShard, sd)
return nil, errcode.ErrAccessNotFoundShard
}
// only update leader diskID/suid ; sd.LeaderDiskID must is in units
// don't need to judge or change RouteVersion, when switch the primary shardNode. only update version in cm GetCatalogChanges
if sd.LeaderSuid.Epoch() >= oldShard.units[sd.LeaderSuid.Index()].Suid.Epoch() {
oldShard.leaderDiskID = sd.LeaderDiskID
oldShard.leaderSuid = sd.LeaderSuid
return nil, nil
} else {
span.Warnf("skip update shard, leader suid epoch is less than old. old:%d, new:%d", oldShard.leaderSuid.Epoch(), sd.LeaderSuid.Epoch())
return nil, errCatalogNoLeader
}
})
return err
}
func (s *shardControllerImpl) GetShardSubRangeCount(ctx context.Context) int {
return s.subRangeCnt
}
func (s *shardControllerImpl) initSpace(ctx context.Context) error {
token, err := clustermgr.EncodeAuthInfo(&clustermgr.AuthInfo{
AccessKey: s.conf.space.AK,
SecretKey: s.conf.space.SK,
})
if err != nil {
return err
}
err = s.cmCli.AuthSpace(ctx, &clustermgr.AuthSpaceArgs{
Name: s.conf.space.Name,
Token: token,
})
if err != nil {
return err
}
ret, err := s.cmCli.GetSpaceByName(ctx, &clustermgr.GetSpaceByNameArgs{
Name: s.conf.space.Name,
})
if err != nil {
return err
}
s.spaceID = ret.SpaceID
return nil
}
func (s *shardControllerImpl) initRoute(ctx context.Context) error {
return s.updateRoute(ctx)
}
func (s *shardControllerImpl) incrementalRoute() {
tk := time.NewTicker(time.Second * time.Duration(s.conf.reloadSecs))
defer tk.Stop()
for {
span, ctx := trace.StartSpanFromContext(context.Background(), "")
select {
case <-tk.C:
// there is only one updateRoute, the same time. no concurrence
err := s.UpdateRoute(ctx)
span.Debugf("loop update catalog route, err:%+v", err)
case <-s.stopCh:
span.Info("exit shard controller")
return
}
}
}
// called by period task, or read/write fail, init route
// if err is nil, errCatalogNoLeader: OK. we can get leader from sn when write leader node ; else : FATAL
func (s *shardControllerImpl) updateRoute(ctx context.Context) error {
span := trace.SpanFromContextSafe(ctx)
s.RLock()
version := s.version
s.RUnlock()
// $RouteVersion is 0: means full catalog route ; RouteVersion is greater than 0: means fetch incremental route
// full catalog route: all type is CatalogChangeItemAddShard (may contain init item and modify update item)
// incremental route: all type is ItemUpdateShard at 1.5.0 branch; If splitting features are supported, there may be multiple types
// todo: shard ranges split, incremental route will handles the add and update types
ret, err := s.cmCli.GetCatalogChanges(ctx, &clustermgr.GetCatalogChangesArgs{
RouteVersion: version,
})
if err != nil {
span.Errorf("fail to get catalog from clusterMgr. err:%+v", err)
return err
}
// skip
if version >= ret.RouteVersion || len(ret.Items) == 0 {
span.Debugf("skip get catalog changes, version=%d, items=%+v", version, *ret)
return nil
}
// todo: optimize lock at next version; will split smaller Lock or do a copy update
s.Lock()
defer s.Unlock()
// Try to process the correct item in this batch, skip error item and wait for the next fetch catalog, or fetch from sn
// 1. catalog normal: leader disk is not zero, and it is in units
// 2. leader disk is 0: in the election: shardNode is restart, or leader disk is broken re_election
// 3. leader disk not zero, and not in units
succ, wrong, itemErr := 0, 0, error(nil)
for _, item := range ret.Items {
switch item.Type {
case proto.CatalogChangeItemAddShard:
itemErr = s.handleShardAdd(ctx, item)
case proto.CatalogChangeItemUpdateShard:
itemErr = s.handleShardUpdate(ctx, item)
default:
itemErr = fmt.Errorf("not expected catalog")
}
// Skip the item that failed. and then fetch from sn, or wait for the next cm catalog
if errors.Is(itemErr, errCatalogNoLeader) {
wrong++
err = errCatalogNoLeader
continue
}
if itemErr != nil {
span.Errorf("update shard catalog error:%+v, item:%+v", err, item)
return itemErr
}
succ++
}
s.version = ret.RouteVersion
span.Debugf("success to update catalog, version from %d to %d, correct:%d, wrong:%d, local range min:%s, max:%s",
version, ret.RouteVersion, succ, wrong, s.ranges.Min().(*shard).String(), s.ranges.Max().(*shard).String())
return err
}
func (s *shardControllerImpl) handleShardAdd(ctx context.Context, item clustermgr.CatalogChangeItem) error {
span := trace.SpanFromContextSafe(ctx)
val := clustermgr.CatalogChangeShardAdd{}
err := val.Unmarshal(item.Item.Value)
if err != nil {
span.Warnf("catalog json unmarshal failed. type=%d, version=%d, err=%+v", item.Type, item.RouteVersion, err)
return err
}
// check invalid item
leaderIdx, err := findAndCheckCatalogShardAdd(val)
if err != nil && !errors.Is(err, errCatalogNoLeader) {
return err
}
sh := &shard{
shardID: val.ShardID,
version: val.RouteVersion,
leaderDiskID: val.Units[leaderIdx].DiskID,
leaderSuid: val.Units[leaderIdx].Suid,
rangeExt: val.Units[leaderIdx].Range,
units: convertShardUnitInfo(val.Units),
punishCtrl: s.punishCtrl,
}
s.addShardNoLock(sh)
span.Debugf("handle one catalog item add :%+v", val)
// insert a no leader item. because we need shardID. and will fetch correct item from sn
if errors.Is(err, errCatalogNoLeader) {
span.Warnf("catalog handle item add, no leader disk, item:%+v", val)
return errCatalogNoLeader
}
return nil
}
func (s *shardControllerImpl) handleShardUpdate(ctx context.Context, item clustermgr.CatalogChangeItem) error {
span := trace.SpanFromContextSafe(ctx)
val := clustermgr.CatalogChangeShardUpdate{}
if err := val.Unmarshal(item.Item.Value); err != nil {
span.Warnf("catalog json unmarshal failed. type=%d, version=%d, err=%+v", item.Type, item.RouteVersion, err)
return err
}
// shard id not exist
info, exist := s.shards[val.ShardID]
if !exist {
return errCatalogInvalid
}
// RouteVersion must be monotonically increasing. The version is too small, less than expected, should discard invalid item
// In general, cm make sure that the version is correct, and will not happen here
if val.RouteVersion < info.version {
span.Warnf("catalog skip invalid version item update, item:%+v", val)
return errCatalogInvalid
}
// skip invalid item
err := checkCatalogShardUpdate(val)
if errors.Is(err, errCatalogInvalid) {
span.Warnf("catalog skip invalid item update, item:%+v", val)
return errCatalogInvalid
}
if val.Unit.Suid.Epoch() <= info.units[val.Unit.Suid.Index()].Suid.Epoch() {
span.Warnf("catalog skip invalid item update, old:%+v, new:%+v", info.units[val.Unit.Suid.Index()], val)
return errCatalogInvalid
}
// fix leader disk is 0: use new disk unit as leaderDisk, and we will fetch the correct leader later from sn
if errors.Is(err, errCatalogNoLeader) {
span.Warnf("catalog handle item update, no leader disk, item:%+v", val)
val.Unit.LeaderDiskID = val.Unit.DiskID
}
// we will update shard, after fix catalog val
s.setShardByID(ctx, info, &val)
span.Debugf("handle one catalog item update:%+v, shard:%+v", val, *info)
return err
}
func (s *shardControllerImpl) delShardNoLock(si *shard) {
sd, ok := s.shards[si.shardID]
if ok {
s.ranges.Delete(sd)
delete(s.shards, si.shardID)
}
}
func (s *shardControllerImpl) addShardNoLock(si *shard) {
s.shards[si.shardID] = si
s.ranges.ReplaceOrInsert(si)
}
func (s *shardControllerImpl) getShardNoLock(id proto.ShardID) (*shard, bool) {
sd, ok := s.shards[id]
return sd, ok
}
func (s *shardControllerImpl) getShardByID(shardID proto.ShardID) (*shard, bool) {
s.RLock()
defer s.RUnlock()
info, ok := s.shards[shardID]
return info, ok
}
func (s *shardControllerImpl) setShardByID(ctx context.Context, info *shard, val *clustermgr.CatalogChangeShardUpdate) {
info.version = val.RouteVersion
// info.rangeExt = val.Unit.Range // todo: will update range next version
idx := val.Unit.Suid.Index()
info.units[idx] = clustermgr.ShardUnit{
Suid: val.Unit.Suid,
DiskID: val.Unit.DiskID,
Learner: val.Unit.Learner, // most time, $learner is false
}
// update leader disk id and suid ; leader disk may not in units ; leader disk may not val.disk
for _, unit := range info.units {
if unit.DiskID == val.Unit.LeaderDiskID {
info.leaderDiskID = unit.DiskID
info.leaderSuid = unit.Suid
return
}
}
// leader disk not in units, fix it
span := trace.SpanFromContextSafe(ctx)
span.Infof("leader disk not in units. old leader:(%d, %d), new leader:%d, units:%+v",
info.leaderDiskID, info.leaderSuid, val.Unit.LeaderDiskID, info.units)
info.leaderDiskID = info.units[0].DiskID
info.leaderSuid = info.units[0].Suid
}
// ShardOpInfo for upper level(stream) use, get ShardOpHeader information
type ShardOpInfo struct {
DiskID proto.DiskID
Suid proto.Suid
RouteVersion proto.RouteVersion
}
type Shard interface {
GetShardID() proto.ShardID
GetRange() sharding.Range
GetMember(context.Context, acapi.GetShardMode, map[proto.DiskID]struct{}) (ShardOpInfo, error)
}
// shard implement btree.Item interface, shard route information
type shard struct {
shardID proto.ShardID
leaderDiskID proto.DiskID
leaderSuid proto.Suid
version proto.RouteVersion
rangeExt sharding.Range
units []clustermgr.ShardUnit
punishCtrl ServiceController
}
func (i *shard) Less(item btree.Item) bool {
switch than := item.(type) {
case *shard:
return i.rangeExt.MaxBoundary().Less(than.rangeExt.MaxBoundary())
case *compareItem:
return i.rangeExt.MaxBoundary().Less(than.ci.GetBoundary())
default:
return false
}
}
func (i *shard) String() string {
return i.rangeExt.String()
}
func (i *shard) GetShardID() proto.ShardID {
return i.shardID
}
func (i *shard) GetRange() sharding.Range {
return i.rangeExt
}
func (i *shard) GetMember(ctx context.Context, mode acapi.GetShardMode, exclude map[proto.DiskID]struct{}) (ShardOpInfo, error) {
span := trace.SpanFromContextSafe(ctx)
span.Debugf("get shard member, mode:%d, exclude:%d, shard:%+v", mode, exclude, *i)
// 1. get member exclude disk id
if len(exclude) != 0 {
return i.getMemberExcluded(ctx, exclude)
}
// 2. get member by mode
if mode == acapi.GetShardModeLeader {
return i.getMemberLeader(ctx)
}
return i.getMemberRandom(ctx, nil)
}
func (i *shard) getMemberExcluded(ctx context.Context, exclude map[proto.DiskID]struct{}) (ShardOpInfo, error) {
return i.getMemberRandom(ctx, exclude)
}
func (i *shard) getMemberLeader(ctx context.Context) (ShardOpInfo, error) {
return ShardOpInfo{
DiskID: i.leaderDiskID,
Suid: i.leaderSuid,
RouteVersion: i.version,
}, nil
}
func (i *shard) getMemberRandom(ctx context.Context, exclude map[proto.DiskID]struct{}) (ShardOpInfo, error) {
span := trace.SpanFromContextSafe(ctx)
n := len(i.units)
initIdx := rand.Intn(n)
idx := initIdx
for {
disk, err := i.punishCtrl.GetShardnodeHost(ctx, i.units[idx].DiskID)
if err != nil {
return ShardOpInfo{}, err
}
if _, exist := exclude[i.units[idx].DiskID]; !exist && !disk.Punished && !i.units[idx].Learner {
return i.getShardOpInfo(idx), nil
}
span.Warnf("skip invalid unit, punished or exclude: %+v, diskID:%d, suid:%d, learner:%t", disk, i.units[idx].DiskID, i.units[idx].Suid, i.units[idx].Learner)
idx = (idx + 1) % n
if idx == initIdx {
break
}
}
span.Warnf("can not find expect disk, exclude disk=%d, shard:%+v", exclude, *i)
return ShardOpInfo{}, fmt.Errorf("can not find expect shardnode disk")
}
func (i *shard) getShardOpInfo(idx int) ShardOpInfo {
return ShardOpInfo{
DiskID: i.units[idx].DiskID,
Suid: i.units[idx].Suid,
RouteVersion: i.version,
}
}
func (i *shard) belong(ci *sharding.CompareItem) bool {
return i.rangeExt.Belong(ci)
}
func (i *shard) contain(rg *sharding.Range) bool {
return i.rangeExt.Contain(rg)
}
type compareItem struct {
ci sharding.CompareItem
}
func (i *compareItem) Less(item btree.Item) bool {
than := item.(*shard)
return i.ci.GetBoundary().Less(than.rangeExt.MaxBoundary())
}
func (i *compareItem) String() string {
return i.ci.String()
}
func convertShardUnitInfo(units []clustermgr.ShardUnitInfo) []clustermgr.ShardUnit {
ret := make([]clustermgr.ShardUnit, len(units))
for i, unit := range units {
ret[i] = clustermgr.ShardUnit{
Suid: unit.Suid,
DiskID: unit.DiskID,
Learner: unit.Learner, // most time, $learner is false
}
}
return ret
}
func isInvalidShardStat(sd shardnode.ShardStats) bool {
if sd.Suid == 0 || sd.RouteVersion == 0 || sd.LeaderDiskID == 0 || sd.LeaderSuid == 0 {
return true
}
return false
}
func checkCatalogShardUpdate(val clustermgr.CatalogChangeShardUpdate) error {
if val.RouteVersion == 0 || val.ShardID == 0 || val.Unit.Suid == 0 || val.Unit.DiskID == 0 {
return errCatalogInvalid
}
if val.Unit.LeaderDiskID == 0 {
return errCatalogNoLeader
}
return nil
}
func findAndCheckCatalogShardAdd(val clustermgr.CatalogChangeShardAdd) (int, error) {
leaderIdx := -1
if val.ShardID == 0 || val.RouteVersion == 0 {
return leaderIdx, errCatalogInvalid
}
for i, unit := range val.Units {
if unit.Suid == 0 || unit.DiskID == 0 {
return leaderIdx, errCatalogInvalid
}
// leader disk may be not in the units
if unit.LeaderDiskID == unit.DiskID {
leaderIdx = i
break
}
}
if leaderIdx == -1 {
return 0, errCatalogNoLeader
}
return leaderIdx, nil
}

View File

@ -1,856 +0,0 @@
package controller
import (
"context"
"errors"
"testing"
"time"
"github.com/gogo/protobuf/types"
"github.com/golang/mock/gomock"
"github.com/google/btree"
"github.com/stretchr/testify/require"
acapi "github.com/cubefs/cubefs/blobstore/api/access"
"github.com/cubefs/cubefs/blobstore/api/clustermgr"
"github.com/cubefs/cubefs/blobstore/api/shardnode"
errcode "github.com/cubefs/cubefs/blobstore/common/errors"
"github.com/cubefs/cubefs/blobstore/common/proto"
"github.com/cubefs/cubefs/blobstore/common/sharding"
"github.com/cubefs/cubefs/blobstore/testing/mocks"
)
var (
gAny = gomock.Any()
errMock = errors.New("fake error")
)
func TestShardController(t *testing.T) {
ctx := context.Background()
stopCh := make(chan struct{})
ctr := gomock.NewController(t)
cmCli := mocks.NewMockClientAPI(ctr)
cmCli.EXPECT().AuthSpace(gAny, gAny).Return(nil)
cmCli.EXPECT().GetSpaceByName(gAny, gAny).Return(&clustermgr.Space{
SpaceID: 1,
Name: "spaceTest",
}, nil)
retCatlog := &clustermgr.GetCatalogChangesRet{}
retCatlog.RouteVersion = 1
cmCli.EXPECT().GetCatalogChanges(gAny, gAny).Return(retCatlog, nil)
cmCli.EXPECT().GetService(gAny, gAny).Return(clustermgr.ServiceInfo{
Nodes: []clustermgr.ServiceNode{
{ClusterID: 1, Name: proto.ServiceNameProxy, Host: "proxy-1", Idc: "test-idc"},
{ClusterID: 1, Name: proto.ServiceNameProxy, Host: "proxy-2", Idc: "test-idc"},
},
}, nil)
svrCtrl, err := NewServiceController(ServiceConfig{IDC: "test-idc"}, cmCli, nil, nil)
require.NoError(t, err)
require.NoError(t, err)
s, err := NewShardController(shardCtrlConf{}, cmCli, svrCtrl, stopCh)
require.NotNil(t, err)
require.NotEqual(t, errMock, err)
require.ErrorIs(t, err, errcode.ErrAccessNotFoundShard)
require.Nil(t, s)
blobName := "blob1"
shardKeys := []string{blobName}
// empty tree
// _, err = s.GetShard(ctx, shardKeys)
// require.NotNil(t, err)
sh := &shard{
shardID: 1,
version: 1,
}
// rangePtr := sharding.New(sharding.RangeType_RangeTypeHash, 2) // keys len=1; subs len=2. will panic
rangePtr := sharding.New(sharding.RangeType_RangeTypeHash, 1)
for i := range rangePtr.Subs {
rangePtr.Subs[i].Min = uint64(i * 1)
rangePtr.Subs[i].Max = uint64(i+1) * 1
}
sh.rangeExt = *rangePtr
svr := &shardControllerImpl{
shards: make(map[proto.ShardID]*shard),
ranges: btree.New(defaultBTreeDegree),
subRangeCnt: 1,
}
// add one, not found blob
svr.addShardNoLock(sh)
require.Equal(t, 1, len(svr.shards))
_, err = svr.GetShard(ctx, shardKeys)
require.NotNil(t, err)
svr.delShardNoLock(sh)
require.Equal(t, 0, len(svr.shards))
svr.subRangeCnt = 2
ranges := sharding.InitShardingRange(sharding.RangeType_RangeTypeHash, 1, 8)
{
// add 8 shard
shards := make([]*shard, 8)
for i := 0; i < 8; i++ {
sd := &shard{
shardID: proto.ShardID(i + 1),
leaderDiskID: 1,
version: proto.RouteVersion(i + 1),
units: []clustermgr.ShardUnit{
{
Suid: proto.EncodeSuid(proto.ShardID(i+1), 0, 0),
DiskID: 1,
// Host: "testHost1",
},
{
Suid: proto.EncodeSuid(proto.ShardID(i+1), 1, 0),
DiskID: 2,
},
{
Suid: proto.EncodeSuid(proto.ShardID(i+1), 2, 0),
DiskID: 3,
},
},
punishCtrl: svrCtrl,
}
sd.rangeExt = *ranges[i]
shards[i] = sd
}
for i := 0; i < 8; i++ {
svr.addShardNoLock(shards[i])
}
require.Equal(t, 8, len(svr.shards))
svr.punishCtrl = svrCtrl
svr.version = 8
// ret, err := svr.GetShard(ctx, []byte("blob1__xxx")) // expect 2 keys
ret, err := svr.GetShard(ctx, []string{"blob1__xxx"}) // expect 2 keys
sk := []string{"blob1__xxx"}
bd := sharding.NewCompareItem(sharding.RangeType_RangeTypeHash, sk).GetBoundary()
t.Logf("shard key 1, key boundary=%d, shardBounary=%d, range=%s, treeLen=%d", bd, ret.(*shard).rangeExt.MaxBoundary(), ret.(*shard).String(), svr.ranges.Len())
// for i := range shards {
// sBd := shards[i].rangeExt.MaxBoundary()
// t.Logf("shard=%d, boundary=%d, isLess=%v", shards[i].shardID, sBd, bd.Less(sBd))
// }
require.Nil(t, err)
require.Equal(t, proto.ShardID(2), ret.(*shard).shardID)
// ret, err = svr.GetShard(ctx, []byte("{blob2__yy}{11}"))
ret, err = svr.GetShard(ctx, []string{"blob2__yy", "11"})
bd = sharding.NewCompareItem(sharding.RangeType_RangeTypeHash, []string{"blob2__yy", "11"}).GetBoundary()
t.Logf("shard key 2, get boundary=%d", bd)
require.Nil(t, err)
require.Equal(t, proto.ShardID(7), ret.(*shard).shardID)
}
{
// get
si, ok := svr.getShardByID(1)
require.True(t, ok)
require.Equal(t, proto.ShardID(1), si.shardID)
svr.spaceID = 1
spID := svr.GetSpaceID()
require.Equal(t, proto.SpaceID(1), spID)
// update shard, switch leader 3 -> 2 ; (1, 2 leader, 4)
newShard := *si
// newShard.version++ // shard node switch leader don't increment version
newShard.leaderDiskID = 2
newShard.units[2].Learner = true
newShard.units = append(newShard.units, clustermgr.ShardUnit{
Suid: proto.EncodeSuid(newShard.shardID, 2, 1),
DiskID: 4,
})
err = svr.UpdateShard(ctx, shardnode.ShardStats{
Suid: proto.EncodeSuid(newShard.shardID, 1, 1),
LeaderDiskID: newShard.leaderDiskID,
LeaderSuid: proto.EncodeSuid(newShard.shardID, 1, 1),
RouteVersion: newShard.version,
Range: newShard.rangeExt,
Units: newShard.units,
})
require.NoError(t, err)
require.Equal(t, newShard.leaderDiskID, si.leaderDiskID)
si, ok = svr.getShardByID(1)
require.True(t, ok)
require.Equal(t, clustermgr.ShardUnit{Suid: proto.EncodeSuid(1, 0, 0), DiskID: 1}, si.units[0])
require.Equal(t, clustermgr.ShardUnit{Suid: proto.EncodeSuid(1, 1, 0), DiskID: 2}, si.units[1])
// require.Equal(t, clustermgr.ShardUnit{Suid: proto.EncodeSuid(1, 2, 1), DiskID: 4}, si.units[2])
opInfo, err := si.GetMember(ctx, acapi.GetShardModeLeader, nil)
require.NoError(t, err)
require.Equal(t, ShardOpInfo{
DiskID: 2,
Suid: proto.EncodeSuid(1, 1, 1),
RouteVersion: 1,
}, opInfo)
// update route, disk:3 -> disk:4
si.units[2] = clustermgr.ShardUnit{Suid: proto.EncodeSuid(1, 2, 1), DiskID: 4}
// switch leader 2 -> 4; (1, 2, 4 leader)
newShard.units[2] = newShard.units[3]
newShard.units = newShard.units[:3]
newShard.leaderDiskID = 4
newShard.leaderSuid = proto.EncodeSuid(newShard.shardID, 2, 2)
newShard.version = si.version
err = svr.UpdateShard(ctx, shardnode.ShardStats{
Suid: newShard.units[2].Suid,
LeaderDiskID: newShard.leaderDiskID,
LeaderSuid: newShard.leaderSuid,
RouteVersion: newShard.version,
Range: newShard.rangeExt,
Units: newShard.units,
})
require.NoError(t, err)
require.Equal(t, newShard.leaderDiskID, si.leaderDiskID)
si, ok = svr.getShardByID(1)
require.True(t, ok)
require.Equal(t, newShard, *si)
}
{
// switch leader: oldLeader 4 -> leader 2, units[1,2,4]
si, ok := svr.getShardByID(1)
require.True(t, ok)
require.Equal(t, proto.ShardID(1), si.shardID)
newShard := *si
newShard.leaderDiskID = 2
newShard.leaderSuid = si.units[1].Suid // disk 2 old suid proto.EncodeSuid(newShard.shardID, 1, 0)
err = svr.UpdateShard(ctx, shardnode.ShardStats{
Suid: newShard.units[2].Suid,
LeaderDiskID: newShard.leaderDiskID,
LeaderSuid: newShard.leaderSuid,
RouteVersion: newShard.version,
})
require.NoError(t, err)
require.Equal(t, newShard.leaderDiskID, si.leaderDiskID)
require.Equal(t, newShard.leaderSuid, si.leaderSuid)
si, ok = svr.getShardByID(1)
require.True(t, ok)
require.Equal(t, newShard, *si)
}
{
err = svr.UpdateShard(ctx, shardnode.ShardStats{
Suid: 1,
LeaderDiskID: 2,
LeaderSuid: 0,
RouteVersion: 1,
})
require.ErrorIs(t, err, errShardInvalid)
}
}
func TestShardUpdate(t *testing.T) {
ctx := context.Background()
ctr := gomock.NewController(t)
cmCli := mocks.NewMockClientAPI(ctr)
svr := &shardControllerImpl{
shards: make(map[proto.ShardID]*shard),
ranges: btree.New(defaultBTreeDegree),
}
// concurrence
{
// update
cmCli.EXPECT().GetCatalogChanges(gAny, gAny).DoAndReturn(
func(ctx context.Context, args *clustermgr.GetCatalogChangesArgs) (ret *clustermgr.GetCatalogChangesRet, err error) {
time.Sleep(time.Millisecond * 10)
return &clustermgr.GetCatalogChangesRet{}, errMock
}).Times(1)
svr.cmCli = cmCli
resultCh := make(chan error, 3)
for i := 0; i < 3; i++ {
go func() {
err := svr.UpdateRoute(ctx)
resultCh <- err
}()
}
for i := 0; i < 3; i++ {
err := <-resultCh
require.ErrorIs(t, err, errMock)
}
}
ranges := sharding.InitShardingRange(sharding.RangeType_RangeTypeHash, 1, 10)
{
// error
val := clustermgr.CatalogChangeShardAdd{
ShardID: 0,
}
data, err := val.Marshal()
require.NoError(t, err)
retCatlog := &clustermgr.GetCatalogChangesRet{
RouteVersion: 1,
Items: []clustermgr.CatalogChangeItem{
{
Type: proto.CatalogChangeItemAddShard,
RouteVersion: 1,
Item: &types.Any{
TypeUrl: "",
Value: data,
},
},
},
}
cmCli.EXPECT().GetCatalogChanges(gAny, gAny).Return(retCatlog, nil)
err = svr.UpdateRoute(ctx)
require.ErrorIs(t, err, errCatalogInvalid)
}
{
// update, leader disk is 0, 2 success and 2 wrong
val := clustermgr.CatalogChangeShardAdd{
ShardID: 1,
RouteVersion: 1,
Units: []clustermgr.ShardUnitInfo{
{
Suid: proto.EncodeSuid(1, 0, 0),
DiskID: 1,
AppliedIndex: 0,
LeaderDiskID: 1,
Range: *ranges[0],
RouteVersion: 1,
Host: "testHost1",
Learner: false,
},
},
}
data1, err := val.Marshal()
require.NoError(t, err)
val = clustermgr.CatalogChangeShardAdd{
ShardID: 2,
RouteVersion: 2,
Units: []clustermgr.ShardUnitInfo{
{
Suid: proto.EncodeSuid(2, 0, 0),
DiskID: 2,
LeaderDiskID: 0, // wrong
Range: *ranges[1],
RouteVersion: 2,
},
},
}
data2, err := val.Marshal()
require.NoError(t, err)
val3 := clustermgr.CatalogChangeShardUpdate{
ShardID: 1,
RouteVersion: 3,
Unit: clustermgr.ShardUnitInfo{
Suid: proto.EncodeSuid(1, 0, 1),
DiskID: 3,
LeaderDiskID: 0, // wrong
RouteVersion: 3,
Range: *ranges[0],
},
}
data3, err := val3.Marshal()
require.NoError(t, err)
val4 := clustermgr.CatalogChangeShardUpdate{
ShardID: 1,
RouteVersion: 4,
Unit: clustermgr.ShardUnitInfo{
Suid: proto.EncodeSuid(1, 0, 2),
DiskID: 3,
LeaderDiskID: 3,
RouteVersion: 3,
Range: *ranges[0],
},
}
data4, err := val4.Marshal()
require.NoError(t, err)
retCatlog := &clustermgr.GetCatalogChangesRet{
RouteVersion: val4.RouteVersion,
Items: []clustermgr.CatalogChangeItem{
{
Type: proto.CatalogChangeItemAddShard,
RouteVersion: 1,
Item: &types.Any{TypeUrl: "", Value: data1}, // success
},
{
Type: proto.CatalogChangeItemAddShard,
RouteVersion: 2,
Item: &types.Any{Value: data2}, // wrong
},
{
Type: proto.CatalogChangeItemUpdateShard,
RouteVersion: 3,
Item: &types.Any{Value: data3}, // wrong
},
{
Type: proto.CatalogChangeItemUpdateShard,
RouteVersion: 4,
Item: &types.Any{Value: data4}, // success
},
},
}
cmCli.EXPECT().GetCatalogChanges(gAny, gAny).Return(retCatlog, nil)
err = svr.UpdateRoute(ctx)
require.Equal(t, errCatalogNoLeader, err)
require.Equal(t, proto.RouteVersion(4), svr.version)
require.Equal(t, 2, len(svr.shards))
}
{
// add shard id=9
const shardID = 9
const version = 5
val := clustermgr.CatalogChangeShardAdd{
ShardID: shardID,
RouteVersion: version,
Units: []clustermgr.ShardUnitInfo{
{
Suid: proto.EncodeSuid(shardID, 0, 0),
DiskID: 1,
AppliedIndex: 0,
LeaderDiskID: 2,
Range: *ranges[8],
RouteVersion: version,
Host: "testHost1",
Learner: false,
},
{
Suid: proto.EncodeSuid(shardID, 1, 0),
DiskID: 2,
AppliedIndex: 0,
LeaderDiskID: 2,
Range: *ranges[8],
RouteVersion: version,
Host: "testHost2",
Learner: false,
},
{
Suid: proto.EncodeSuid(shardID, 2, 0),
DiskID: 3,
AppliedIndex: 0,
LeaderDiskID: 2,
Range: *ranges[8],
RouteVersion: version,
Host: "testHost3",
Learner: false,
},
},
}
data, err := val.Marshal()
require.NoError(t, err)
retCatlog := &clustermgr.GetCatalogChangesRet{
RouteVersion: version,
Items: []clustermgr.CatalogChangeItem{
{
RouteVersion: version,
Type: proto.CatalogChangeItemAddShard,
Item: &types.Any{
TypeUrl: "",
Value: data,
},
},
},
}
cmCli.EXPECT().GetCatalogChanges(gAny, gAny).Return(retCatlog, nil)
svr.cmCli = cmCli
err = svr.UpdateRoute(ctx)
require.NoError(t, err)
require.Equal(t, proto.RouteVersion(version), svr.version)
si, ok := svr.getShardByID(shardID)
require.True(t, ok)
require.Equal(t, proto.ShardID(shardID), si.shardID)
require.Equal(t, 3, len(svr.shards))
expect := shard{
shardID: shardID,
leaderDiskID: val.Units[0].LeaderDiskID,
leaderSuid: val.Units[1].Suid,
version: version,
rangeExt: val.Units[1].Range,
units: convertShardUnitInfo(val.Units),
punishCtrl: svr.punishCtrl,
}
require.Equal(t, expect, *si)
}
{
// add shard id=10
const shardID = 10
const oldVersion = 4
const version = 5
sd := &shard{
shardID: proto.ShardID(shardID),
version: oldVersion,
units: make([]clustermgr.ShardUnit, 1),
}
sd.rangeExt = *ranges[9]
svr.addShardNoLock(sd)
require.Equal(t, 4, len(svr.shards))
val := clustermgr.CatalogChangeShardUpdate{
ShardID: shardID,
RouteVersion: version,
Unit: clustermgr.ShardUnitInfo{
Suid: proto.EncodeSuid(shardID, 0, 0),
DiskID: 2,
AppliedIndex: 0,
LeaderDiskID: 2,
Range: *ranges[9],
RouteVersion: version,
Host: "testHost2",
Learner: false,
},
}
data, err := val.Marshal()
require.NoError(t, err)
retCatlog := &clustermgr.GetCatalogChangesRet{
RouteVersion: version,
Items: []clustermgr.CatalogChangeItem{
{
Type: proto.CatalogChangeItemUpdateShard,
RouteVersion: version,
Item: &types.Any{
TypeUrl: "",
Value: data,
},
},
},
}
cmCli.EXPECT().GetCatalogChanges(gAny, gAny).Return(retCatlog, nil)
svr.cmCli = cmCli
err = svr.UpdateRoute(ctx)
require.NoError(t, err)
require.Equal(t, proto.RouteVersion(version), svr.version)
si, ok := svr.getShardByID(shardID)
require.True(t, ok)
require.Equal(t, proto.ShardID(shardID), si.shardID)
require.Equal(t, 4, len(svr.shards))
}
{
// update version, leader disk, suid
rv := svr.version + 1
shardID := proto.ShardID(9)
oldShard, exist := svr.getShardNoLock(shardID)
require.True(t, exist)
val := clustermgr.CatalogChangeShardUpdate{
ShardID: shardID,
RouteVersion: rv,
Unit: clustermgr.ShardUnitInfo{
Suid: proto.EncodeSuid(shardID, 2, 1),
DiskID: 3,
LeaderDiskID: 2,
Range: *ranges[8],
RouteVersion: rv,
Host: "testHost3",
Learner: false,
},
}
data, err := val.Marshal()
require.NoError(t, err)
item := clustermgr.CatalogChangeItem{
RouteVersion: svr.version + 1,
Type: proto.CatalogChangeItemUpdateShard,
Item: &types.Any{
TypeUrl: "",
Value: data,
},
}
err = svr.handleShardUpdate(ctx, item)
require.NoError(t, err)
sd, exist := svr.getShardNoLock(shardID)
require.True(t, exist)
expect := shard{
shardID: oldShard.shardID,
leaderDiskID: 2,
leaderSuid: proto.EncodeSuid(shardID, 1, 0),
version: rv,
rangeExt: oldShard.rangeExt,
units: oldShard.units,
punishCtrl: oldShard.punishCtrl,
}
idx := val.Unit.Suid.Index()
expect.units[idx] = clustermgr.ShardUnit{
Suid: val.Unit.Suid,
DiskID: val.Unit.DiskID,
Learner: val.Unit.Learner,
}
require.Equal(t, expect, *sd)
}
{
// update version, leader disk not in units(old leader)
rv := svr.version + 1
shardID := proto.ShardID(9)
oldShard, exist := svr.getShardNoLock(shardID)
require.True(t, exist)
val := clustermgr.CatalogChangeShardUpdate{
ShardID: shardID,
RouteVersion: rv,
Unit: clustermgr.ShardUnitInfo{
Suid: proto.EncodeSuid(shardID, 2, 2),
DiskID: 5,
LeaderDiskID: 3,
Range: *ranges[8],
RouteVersion: rv,
Host: "testHost3",
Learner: false,
},
}
data, err := val.Marshal()
require.NoError(t, err)
item := clustermgr.CatalogChangeItem{
RouteVersion: svr.version + 1,
Type: proto.CatalogChangeItemUpdateShard,
Item: &types.Any{
TypeUrl: "",
Value: data,
},
}
err = svr.handleShardUpdate(ctx, item)
require.NoError(t, err)
sd, exist := svr.getShardNoLock(shardID)
require.True(t, exist)
opHeader, err := sd.GetMember(ctx, acapi.GetShardModeLeader, nil)
require.NoError(t, err)
expect := ShardOpInfo{
DiskID: 1,
Suid: proto.EncodeSuid(shardID, 0, 0),
RouteVersion: rv,
}
require.Equal(t, expect, opHeader)
require.Equal(t, oldShard.leaderDiskID, expect.DiskID)
}
{
// leader disk is 0
rv := svr.version + 1
shardID := proto.ShardID(9)
oldShard, exist := svr.getShardNoLock(shardID)
require.True(t, exist)
val := clustermgr.CatalogChangeShardUpdate{
ShardID: shardID,
RouteVersion: rv,
Unit: clustermgr.ShardUnitInfo{
Suid: proto.EncodeSuid(shardID, 1, 1),
DiskID: 4,
LeaderDiskID: 0,
Range: *ranges[8],
RouteVersion: rv,
},
}
data, err := val.Marshal()
require.NoError(t, err)
item := clustermgr.CatalogChangeItem{
RouteVersion: rv,
Type: proto.CatalogChangeItemUpdateShard,
Item: &types.Any{
TypeUrl: "",
Value: data,
},
}
err = svr.handleShardUpdate(ctx, item)
require.ErrorIs(t, err, errCatalogNoLeader)
sd, exist := svr.getShardNoLock(shardID)
require.True(t, exist)
expect := shard{
shardID: oldShard.shardID,
leaderDiskID: 4,
leaderSuid: proto.EncodeSuid(shardID, 1, 1),
version: rv,
rangeExt: oldShard.rangeExt,
units: oldShard.units,
punishCtrl: oldShard.punishCtrl,
}
idx := val.Unit.Suid.Index()
expect.units[idx] = clustermgr.ShardUnit{
Suid: val.Unit.Suid,
DiskID: val.Unit.DiskID,
Learner: val.Unit.Learner,
}
require.Equal(t, expect, *sd)
// error
val.RouteVersion = 1
data, err = val.Marshal()
require.NoError(t, err)
item.RouteVersion = val.RouteVersion
item.Item.Value = data
err = svr.handleShardUpdate(ctx, item)
require.ErrorIs(t, err, errCatalogInvalid)
}
}
func TestShardGetShard(t *testing.T) {
ctx := context.Background()
svr := &shardControllerImpl{
shards: make(map[proto.ShardID]*shard),
ranges: btree.New(defaultBTreeDegree),
subRangeCnt: 2,
}
ctr := gomock.NewController(t)
cmCli := mocks.NewMockClientAPI(ctr)
cmCli.EXPECT().GetService(gAny, gAny).Return(clustermgr.ServiceInfo{
Nodes: []clustermgr.ServiceNode{
{ClusterID: 1, Name: proto.ServiceNameProxy, Host: "proxy-1", Idc: "test-idc"},
},
}, nil)
cmCli.EXPECT().ShardNodeDiskInfo(gAny, proto.DiskID(1)).Return(&clustermgr.ShardNodeDiskInfo{
DiskInfo: clustermgr.DiskInfo{Host: "testHost1", Idc: "test-idc"},
ShardNodeDiskHeartbeatInfo: clustermgr.ShardNodeDiskHeartbeatInfo{DiskID: 1},
}, nil)
cmCli.EXPECT().ShardNodeDiskInfo(gAny, proto.DiskID(2)).Return(&clustermgr.ShardNodeDiskInfo{
DiskInfo: clustermgr.DiskInfo{Host: "testHost2", Idc: "test-idc"},
ShardNodeDiskHeartbeatInfo: clustermgr.ShardNodeDiskHeartbeatInfo{DiskID: 2},
}, nil)
cmCli.EXPECT().ShardNodeDiskInfo(gAny, proto.DiskID(3)).Return(&clustermgr.ShardNodeDiskInfo{
DiskInfo: clustermgr.DiskInfo{Host: "testHost3", Idc: "test-idc"},
ShardNodeDiskHeartbeatInfo: clustermgr.ShardNodeDiskHeartbeatInfo{DiskID: 3},
}, nil)
svrCtrl, err := NewServiceController(ServiceConfig{IDC: "test-idc"}, cmCli, nil, nil)
require.NoError(t, err)
svrCtrl.GetShardnodeHost(ctx, 1)
svrCtrl.GetShardnodeHost(ctx, 2)
svrCtrl.GetShardnodeHost(ctx, 3)
// add 8 shard
shards := make([]*shard, 8)
ranges := sharding.InitShardingRange(sharding.RangeType_RangeTypeHash, 2, 8)
for i := 0; i < 8; i++ {
sd := &shard{
shardID: proto.ShardID(i + 1),
version: 1,
leaderDiskID: 1,
leaderSuid: proto.EncodeSuid(proto.ShardID(i+1), 0, 0),
units: []clustermgr.ShardUnit{
{
Suid: proto.EncodeSuid(proto.ShardID(i+1), 0, 0),
DiskID: 1,
Host: "testHost1",
},
{
Suid: proto.EncodeSuid(proto.ShardID(i+1), 1, 0),
DiskID: 2,
Host: "testHost2",
},
{
Suid: proto.EncodeSuid(proto.ShardID(i+1), 2, 0),
DiskID: 3,
Host: "testHost3",
},
},
punishCtrl: svrCtrl,
}
sd.rangeExt = *ranges[i]
shards[i] = sd
}
// shards[7].rangeExt.Subs[0].Max = math.MaxUint64
for i := 0; i < 8; i++ {
svr.addShardNoLock(shards[i])
}
require.Equal(t, 8, len(svr.shards))
{
// sd, err := svr.GetShard(ctx, []byte("{blob1}{1}"))
sd, err := svr.GetShard(ctx, []string{"blob1", "1"})
require.NoError(t, err)
require.Equal(t, proto.ShardID(2), sd.GetShardID())
// sd, err = svr.GetShard(ctx, []byte("{blob1}{}"))
sd, err = svr.GetShard(ctx, []string{"blob1", ""})
require.NoError(t, err)
require.Equal(t, proto.ShardID(2), sd.GetShardID())
// shard others
sd, err = svr.GetShardByID(ctx, proto.ShardID(1))
require.NoError(t, err)
shardInfo, err := sd.GetMember(ctx, acapi.GetShardModeLeader, nil)
require.NoError(t, err)
require.Equal(t, shards[0].shardID, shardInfo.Suid.ShardID())
shardInfo, err = sd.GetMember(ctx, acapi.GetShardModeRandom, nil)
require.NoError(t, err)
require.Equal(t, shards[0].shardID, shardInfo.Suid.ShardID())
shardID := sd.GetShardID()
require.Equal(t, shards[0].shardID, shardID)
}
// get first shard
{
sd, err := svr.GetFisrtShard(ctx)
require.NoError(t, err)
require.Equal(t, proto.ShardID(1), sd.GetShardID())
newDisk, err := sd.GetMember(ctx, acapi.GetShardModeRandom, map[proto.DiskID]struct{}{2: {}, 1: {}})
require.NoError(t, err)
require.NotEqual(t, proto.DiskID(2), newDisk.DiskID)
require.NotEqual(t, proto.DiskID(1), newDisk.DiskID)
require.Contains(t, []proto.DiskID{3}, newDisk.DiskID)
}
// get shard by range
{
sd, err := svr.GetShardByID(ctx, proto.ShardID(2))
require.NoError(t, err)
shardRange := sd.GetRange()
sd, err = svr.GetShardByRange(ctx, shardRange)
require.NoError(t, err)
require.Equal(t, proto.ShardID(2), sd.GetShardID())
require.Equal(t, shardRange, sd.GetRange())
sd, err = svr.GetShardByRange(ctx, shards[0].rangeExt)
require.NoError(t, err)
require.Equal(t, proto.ShardID(1), sd.GetShardID())
sd, err = svr.GetShardByRange(ctx, shards[7].rangeExt)
require.NoError(t, err)
require.Equal(t, proto.ShardID(8), sd.GetShardID())
sd, err = svr.GetNextShard(ctx, shardRange)
require.NoError(t, err)
require.Equal(t, proto.ShardID(3), sd.GetShardID())
require.NotEqual(t, shardRange, sd.GetRange())
// err == nil && shard == nil, means last shard, reach end
sd, err = svr.GetNextShard(ctx, shards[7].rangeExt)
require.NoError(t, err)
require.Nil(t, sd)
}
}

View File

@ -17,7 +17,6 @@ package controller
import (
"context"
"fmt"
"sync"
"time"
"golang.org/x/sync/singleflight"
@ -30,11 +29,15 @@ import (
"github.com/cubefs/cubefs/blobstore/common/proto"
"github.com/cubefs/cubefs/blobstore/common/rpc"
"github.com/cubefs/cubefs/blobstore/common/trace"
"github.com/cubefs/cubefs/blobstore/util/defaulter"
"github.com/cubefs/cubefs/blobstore/util/errors"
"github.com/cubefs/cubefs/blobstore/util/retry"
)
const (
_defaultCacheSize = 1 << 20
_defaultCacheExpiration = int64(2 * time.Minute)
)
// Unit alias of clustermgr.Unit
type Unit = clustermgr.Unit
@ -48,23 +51,23 @@ type VolumePhy struct {
Vid proto.Vid
CodeMode codemode.CodeMode
IsPunish bool
Version uint64
Version uint32
Timestamp int64
Units []Unit
}
// VolumeGetter getter of volume physical location
//
// ctx: context with trace or something
// isCache: is false means reading from proxy cluster then updating memcache
// otherwise reading from memcache -> proxy -> cluster
// ctx: context with trace or something
//
// isCache: is false means reading from proxy cluster then updating memcache
//
// otherwise reading from memcache -> proxy -> cluster
type VolumeGetter interface {
// Get returns volume physical location of vid
Get(ctx context.Context, vid proto.Vid, isCache bool) *VolumePhy
// Punish punish vid with interval seconds
Punish(ctx context.Context, vid proto.Vid, punishIntervalS int)
// Update try to flush volume of proxy
Update(ctx context.Context, vid proto.Vid)
}
type cvid uint64
@ -103,6 +106,9 @@ func (vc *volumeMemCache) Set(key cvid, value *VolumePhy) {
}
type volumeGetterImpl struct {
ctx context.Context
cid proto.ClusterID
volumeMemCache volumePhyCacher
memExpiration int64
punishCache *memcache.MemCache
@ -110,71 +116,40 @@ type volumeGetterImpl struct {
service ServiceController
proxy proxy.Cacher
singleRun *singleflight.Group
unusualLock sync.Mutex
unusualVolume map[proto.Vid]int
config VolumeConfig
}
// VolumeConfig controller of volume's config
type VolumeConfig struct {
ClusterID proto.ClusterID `json:"-"`
VolumeMemcacheSize int `json:"volume_memcache_size"`
VolumeMemcachePunishSize int `json:"volume_memcache_punish_size"`
VolumeMemcacheExpirationMs int64 `json:"volume_memcache_expiration_ms"` // -1 means no expiration
VolumePunishThreshold int `json:"volume_punish_threshold"`
VolumePunishIntervalS int `json:"volume_punish_interval_s"`
}
// NewVolumeGetter new a volume getter
func NewVolumeGetter(cfg VolumeConfig, service ServiceController,
proxy proxy.Cacher, stop <-chan struct{},
) (VolumeGetter, error) {
expiration := cfg.VolumeMemcacheExpirationMs * int64(time.Millisecond)
defaulter.IntegerEqual(&expiration, int64(2*time.Minute))
defaulter.IntegerLess(&expiration, 0)
//
// memExpiration expiration of memcache, 0 means no expiration
func NewVolumeGetter(clusterID proto.ClusterID, service ServiceController,
proxy proxy.Cacher, memExpiration time.Duration) (VolumeGetter, error) {
_, ctx := trace.StartSpanFromContext(context.Background(), "")
defaulter.IntegerLessOrEqual(&cfg.VolumeMemcacheSize, 1<<20)
defaulter.IntegerLessOrEqual(&cfg.VolumeMemcachePunishSize, 1<<10)
defaulter.IntegerLessOrEqual(&cfg.VolumePunishThreshold, 10)
defaulter.IntegerLessOrEqual(&cfg.VolumePunishIntervalS, 600)
expiration := int64(memExpiration)
if expiration < 0 {
expiration = _defaultCacheExpiration
}
mc, err := memcache.NewMemCache(cfg.VolumeMemcacheSize)
mc, err := memcache.NewMemCache(_defaultCacheSize)
if err != nil {
return nil, err
}
punishCache, err := memcache.NewMemCache(cfg.VolumeMemcachePunishSize)
punishCache, err := memcache.NewMemCache(1024)
if err != nil {
return nil, err
}
getter := &volumeGetterImpl{
ctx: ctx,
cid: clusterID,
volumeMemCache: &volumeMemCache{cache: mc},
memExpiration: expiration,
punishCache: punishCache,
service: service,
proxy: proxy,
singleRun: new(singleflight.Group),
unusualVolume: make(map[proto.Vid]int),
config: cfg,
}
go func() {
ticker := time.NewTicker(time.Duration(cfg.VolumePunishIntervalS) * time.Second)
defer ticker.Stop()
for {
getter.tickerUpdate()
select {
case <-ticker.C:
case <-stop:
getter.tickerUpdate()
return
}
}
}()
return getter, nil
}
@ -184,8 +159,8 @@ func NewVolumeGetter(cfg VolumeConfig, service ServiceController,
// 2.second level cache from proxy cluster
func (v *volumeGetterImpl) Get(ctx context.Context, vid proto.Vid, isCache bool) (phy *VolumePhy) {
span := trace.SpanFromContextSafe(ctx)
cid := v.config.ClusterID.ToString()
id := addCVid(v.config.ClusterID, vid)
cid := v.cid.ToString()
id := addCVid(v.cid, vid)
// check if volume punish
defer func() {
@ -240,7 +215,7 @@ func (v *volumeGetterImpl) Get(ctx context.Context, vid proto.Vid, isCache bool)
}
singleID := fmt.Sprintf("get-volume-%d", id)
ver := uint64(0)
ver := uint32(0)
if phy != nil {
ver = phy.Version
}
@ -250,7 +225,6 @@ func (v *volumeGetterImpl) Get(ctx context.Context, vid proto.Vid, isCache bool)
if err != nil {
cacheMetric.WithLabelValues(cid, "proxy", "miss").Inc()
span.Error("get volume location from proxy failed", errors.Detail(err))
phy = nil
return
}
cacheMetric.WithLabelValues(cid, "proxy", "hit").Inc()
@ -264,41 +238,34 @@ func (v *volumeGetterImpl) Get(ctx context.Context, vid proto.Vid, isCache bool)
return
}
func (v *volumeGetterImpl) Punish(_ context.Context, vid proto.Vid, punishIntervalS int) {
v.punishCache.Set(addCVid(v.config.ClusterID, vid), time.Now().Add(time.Duration(punishIntervalS)*time.Second).Unix())
func (v *volumeGetterImpl) Punish(ctx context.Context, vid proto.Vid, punishIntervalS int) {
v.punishCache.Set(addCVid(v.cid, vid), time.Now().Add(time.Duration(punishIntervalS)*time.Second).Unix())
}
func (v *volumeGetterImpl) Update(_ context.Context, vid proto.Vid) {
v.unusualLock.Lock()
v.unusualVolume[vid] += 1
v.unusualLock.Unlock()
}
func (v *volumeGetterImpl) setToLocalCache(_ context.Context, id cvid, phy *VolumePhy) {
func (v *volumeGetterImpl) setToLocalCache(ctx context.Context, id cvid, phy *VolumePhy) {
v.volumeMemCache.Set(id, phy)
}
func (v *volumeGetterImpl) getFromLocalCache(_ context.Context, id cvid) *VolumePhy {
func (v *volumeGetterImpl) getFromLocalCache(ctx context.Context, id cvid) *VolumePhy {
return v.volumeMemCache.Get(id)
}
func (v *volumeGetterImpl) getFromProxy(ctx context.Context, vid proto.Vid, flush bool, ver uint64) (*VolumePhy, error) {
func (v *volumeGetterImpl) getFromProxy(ctx context.Context, vid proto.Vid, flush bool, ver uint32) (*VolumePhy, error) {
span := trace.SpanFromContextSafe(ctx)
hosts, err := v.service.GetServiceHosts(ctx, proto.ServiceNameProxy)
if err != nil {
return nil, err
}
var volume *clustermgr.VolumeInfo
cid := v.config.ClusterID
id := addCVid(cid, vid)
var volume *proxy.VersionVolume
id := addCVid(v.cid, vid)
triedHosts := make(map[string]struct{})
if err = retry.ExponentialBackoff(3, 30).RuptOn(func() (bool, error) {
for _, host := range hosts {
triedHosts[host] = struct{}{}
if volume, err = v.proxy.GetCacheVolume(ctx, host,
&proxy.CacheVolumeArgs{Vid: vid, Flush: flush, Version: ver}); err != nil {
if err == context.Canceled || rpc.DetectStatusCode(err) == errcode.CodeVolumeNotExist {
if rpc.DetectStatusCode(err) == errcode.CodeVolumeNotExist {
return true, err
}
span.Warnf("get from proxy(%s) volume(%d) error(%s)", host, vid, err.Error())
@ -314,26 +281,22 @@ func (v *volumeGetterImpl) getFromProxy(ctx context.Context, vid proto.Vid, flus
Vid: vid,
Timestamp: -time.Now().UnixNano(),
}
span.Infof("to update memcache on not exist volume(%d-%d) %+v", cid, vid, phy)
span.Infof("to update memcache on not exist volume(%d-%d) %+v", v.cid, vid, phy)
v.setToLocalCache(ctx, id, phy)
} else if flush {
span.Warnf("to flush force on all proxy of volume(%d-%d)", cid, vid)
v.setToLocalCache(ctx, id, nil)
v.flush(ctx, vid, 0, hosts, map[string]struct{}{})
}
return nil, errors.Base(err, "get volume from proxy", cid, vid)
return nil, errors.Base(err, "get volume from proxy", v.cid, vid)
}
phy := &VolumePhy{
Vid: volume.Vid,
CodeMode: volume.CodeMode,
Version: uint64(volume.RouteVersion),
Version: volume.Version,
Timestamp: time.Now().UnixNano(),
Units: make([]Unit, len(volume.Units)),
}
copy(phy.Units, volume.Units[:])
span.Debugf("to update memcache on volume(%d-%d) %+v", cid, vid, phy)
span.Debugf("to update memcache on volume(%d-%d) %+v", v.cid, vid, phy)
v.setToLocalCache(ctx, id, phy)
if flush {
@ -344,7 +307,7 @@ func (v *volumeGetterImpl) getFromProxy(ctx context.Context, vid proto.Vid, flus
}
// flush update all proxy cache of this idc
func (v *volumeGetterImpl) flush(ctx context.Context, vid proto.Vid, ver uint64, hosts []string, except map[string]struct{}) {
func (v *volumeGetterImpl) flush(ctx context.Context, vid proto.Vid, ver uint32, hosts []string, except map[string]struct{}) {
span := trace.SpanFromContextSafe(ctx)
span.Infof("to flush volume cache %d on proxy:%v version:%d except:%v", vid, hosts, ver, except)
@ -353,16 +316,15 @@ func (v *volumeGetterImpl) flush(ctx context.Context, vid proto.Vid, ver uint64,
continue
}
bgSpan, bgCtx := trace.StartSpanFromContextWithTraceID(context.Background(), "flush_proxy_volume", span.TraceID())
go func(host string) {
retry.ExponentialBackoff(2, 10).RuptOn(func() (bool, error) {
if _, err := v.proxy.GetCacheVolume(bgCtx, host,
if _, err := v.proxy.GetCacheVolume(ctx, host,
&proxy.CacheVolumeArgs{Vid: vid, Flush: true, Version: ver}); err != nil {
if rpc.DetectStatusCode(err) == errcode.CodeVolumeNotExist {
bgSpan.Info("not found volume", vid)
span.Info("not found volume", vid)
return true, err
}
bgSpan.Warnf("flush volume:%d error:%s", vid, err.Error())
span.Warnf("flush volume:%d error:%s", vid, err.Error())
return false, err
}
return true, nil
@ -370,23 +332,3 @@ func (v *volumeGetterImpl) flush(ctx context.Context, vid proto.Vid, ver uint64,
}(host)
}
}
func (v *volumeGetterImpl) tickerUpdate() {
var vids []proto.Vid
v.unusualLock.Lock()
for vid, n := range v.unusualVolume {
if n > v.config.VolumePunishThreshold {
vids = append(vids, vid)
if len(vids) >= 10 {
break
}
delete(v.unusualVolume, vid)
}
}
v.unusualLock.Unlock()
for _, vid := range vids {
_, ctx := trace.StartSpanFromContext(context.Background(), "update-unusual")
v.getFromProxy(ctx, vid, true, 0)
}
}

View File

@ -26,12 +26,6 @@ import (
"github.com/cubefs/cubefs/blobstore/common/trace"
)
func closedCh() <-chan struct{} {
c := make(chan struct{})
close(c)
return c
}
func proxyService() controller.ServiceController {
service, _ := controller.NewServiceController(controller.ServiceConfig{IDC: idc}, cmcli, proxycli, nil)
return service
@ -40,8 +34,7 @@ func proxyService() controller.ServiceController {
func TestAccessVolumeGetterNew(t *testing.T) {
_, ctx := trace.StartSpanFromContext(context.Background(), "TestAccessVolumeGetterNew")
cfg := controller.VolumeConfig{ClusterID: 1, VolumeMemcacheExpirationMs: 200}
getter, err := controller.NewVolumeGetter(cfg, proxyService(), proxycli, closedCh())
getter, err := controller.NewVolumeGetter(1, proxyService(), proxycli, time.Millisecond*200)
require.Nil(t, err)
require.Nil(t, getter.Get(ctx, proto.Vid(0), true))
@ -69,8 +62,7 @@ func TestAccessVolumeGetterNew(t *testing.T) {
func TestAccessVolumeGetterNotExistVolume(t *testing.T) {
_, ctx := trace.StartSpanFromContext(context.Background(), "TestAccessVolumeGetterNotExistVolume")
cfg := controller.VolumeConfig{ClusterID: 0xfe, VolumeMemcacheExpirationMs: 200}
getter, err := controller.NewVolumeGetter(cfg, proxyService(), proxycli, closedCh())
getter, err := controller.NewVolumeGetter(0xfe, proxyService(), proxycli, time.Millisecond*200)
require.NoError(t, err)
id := vid404
@ -86,8 +78,7 @@ func TestAccessVolumeGetterNotExistVolume(t *testing.T) {
getter.Get(ctx, id, true)
require.Equal(t, 2, dataCalled[id])
cfg = controller.VolumeConfig{ClusterID: 0xee, VolumeMemcacheExpirationMs: -1}
getter, err = controller.NewVolumeGetter(cfg, proxyService(), proxycli, closedCh())
getter, err = controller.NewVolumeGetter(0xee, proxyService(), proxycli, 0)
require.NoError(t, err)
id = vid404
dataCalled[id] = 0
@ -103,34 +94,10 @@ func TestAccessVolumeGetterNotExistVolume(t *testing.T) {
require.Equal(t, 6, dataCalled[id])
}
func TestAccessVolumeGetterNotExistFlush(t *testing.T) {
_, ctx := trace.StartSpanFromContext(context.Background(), "TestAccessVolumeGetterNotExistVolumeFlush")
cfg := controller.VolumeConfig{ClusterID: 0xfe, VolumeMemcacheExpirationMs: 200}
getter, err := controller.NewVolumeGetter(cfg, proxyService(), proxycli, closedCh())
require.NoError(t, err)
id := vid404
require.Nil(t, getter.Get(ctx, id, true))
for range [10]struct{}{} {
require.Nil(t, getter.Get(ctx, id, false))
}
time.Sleep(time.Millisecond * 210)
cfg = controller.VolumeConfig{ClusterID: 0xee, VolumeMemcacheExpirationMs: -1}
getter, err = controller.NewVolumeGetter(cfg, proxyService(), proxycli, closedCh())
require.NoError(t, err)
for range [10]struct{}{} {
require.Nil(t, getter.Get(ctx, id, false))
}
}
func TestAccessVolumeGetterExpiration(t *testing.T) {
_, ctx := trace.StartSpanFromContext(context.Background(), "TestAccessVolumeGetterExpiration")
cfg := controller.VolumeConfig{ClusterID: 1, VolumeMemcacheExpirationMs: 200}
getter, err := controller.NewVolumeGetter(cfg, proxyService(), proxycli, closedCh())
getter, err := controller.NewVolumeGetter(1, proxyService(), proxycli, time.Millisecond*200)
require.Nil(t, err)
id := proto.Vid(1)
@ -151,8 +118,7 @@ func TestAccessVolumeGetterExpiration(t *testing.T) {
func TestAccessVolumePunish(t *testing.T) {
_, ctx := trace.StartSpanFromContext(context.Background(), "TestAccessVolumePunish")
cfg := controller.VolumeConfig{ClusterID: 1, VolumeMemcacheExpirationMs: -1}
getter, err := controller.NewVolumeGetter(cfg, proxyService(), proxycli, closedCh())
getter, err := controller.NewVolumeGetter(1, proxyService(), proxycli, 0)
require.Nil(t, err)
require.Nil(t, getter.Get(ctx, proto.Vid(0), true))
@ -175,50 +141,3 @@ func TestAccessVolumePunish(t *testing.T) {
require.NotNil(t, info)
require.False(t, info.IsPunish)
}
func TestAccessVolumeUpdate(t *testing.T) {
_, ctx := trace.StartSpanFromContext(context.Background(), "TestAccessVolumeUpdate")
ch := make(chan struct{})
go func() {
time.Sleep(time.Second)
close(ch)
}()
cfg := controller.VolumeConfig{ClusterID: 1, VolumeMemcacheExpirationMs: -1}
getter, err := controller.NewVolumeGetter(cfg, proxyService(), proxycli, ch)
require.NoError(t, err)
getter.Update(ctx, 1)
for range [11]struct{}{} {
getter.Update(ctx, vid404)
}
for idx := range [11]struct{}{} {
for range [11]struct{}{} {
getter.Update(ctx, proto.Vid(idx+1))
}
}
getter.Update(ctx, 123)
getter.Update(ctx, 11)
<-ch
}
func TestAccessVolumeUpdateForce(t *testing.T) {
_, ctx := trace.StartSpanFromContext(context.Background(), "TestAccessVolumeUpdateForce")
ctxDone, cancel := context.WithCancel(ctx)
cancel()
ch := make(chan struct{})
go func() {
time.Sleep(time.Second)
close(ch)
}()
cfg := controller.VolumeConfig{ClusterID: 1, VolumeMemcacheExpirationMs: -1}
getter, err := controller.NewVolumeGetter(cfg, proxyService(), proxycli, ch)
require.NoError(t, err)
id := proto.Vid(1)
require.Nil(t, getter.Get(ctxDone, id, false))
require.NotNil(t, getter.Get(ctx, id, true))
<-ch
}

View File

@ -0,0 +1,305 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/cubefs/cubefs/blobstore/access/controller (interfaces: ClusterController,ServiceController,VolumeGetter)
// Package access is a generated GoMock package.
package access
import (
context "context"
reflect "reflect"
controller "github.com/cubefs/cubefs/blobstore/access/controller"
clustermgr "github.com/cubefs/cubefs/blobstore/api/clustermgr"
proto "github.com/cubefs/cubefs/blobstore/common/proto"
gomock "github.com/golang/mock/gomock"
)
// MockClusterController is a mock of ClusterController interface.
type MockClusterController struct {
ctrl *gomock.Controller
recorder *MockClusterControllerMockRecorder
}
// MockClusterControllerMockRecorder is the mock recorder for MockClusterController.
type MockClusterControllerMockRecorder struct {
mock *MockClusterController
}
// NewMockClusterController creates a new mock instance.
func NewMockClusterController(ctrl *gomock.Controller) *MockClusterController {
mock := &MockClusterController{ctrl: ctrl}
mock.recorder = &MockClusterControllerMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockClusterController) EXPECT() *MockClusterControllerMockRecorder {
return m.recorder
}
// All mocks base method.
func (m *MockClusterController) All() []*clustermgr.ClusterInfo {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "All")
ret0, _ := ret[0].([]*clustermgr.ClusterInfo)
return ret0
}
// All indicates an expected call of All.
func (mr *MockClusterControllerMockRecorder) All() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "All", reflect.TypeOf((*MockClusterController)(nil).All))
}
// ChangeChooseAlg mocks base method.
func (m *MockClusterController) ChangeChooseAlg(arg0 controller.AlgChoose) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ChangeChooseAlg", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// ChangeChooseAlg indicates an expected call of ChangeChooseAlg.
func (mr *MockClusterControllerMockRecorder) ChangeChooseAlg(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChangeChooseAlg", reflect.TypeOf((*MockClusterController)(nil).ChangeChooseAlg), arg0)
}
// ChooseOne mocks base method.
func (m *MockClusterController) ChooseOne() (*clustermgr.ClusterInfo, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ChooseOne")
ret0, _ := ret[0].(*clustermgr.ClusterInfo)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// ChooseOne indicates an expected call of ChooseOne.
func (mr *MockClusterControllerMockRecorder) ChooseOne() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChooseOne", reflect.TypeOf((*MockClusterController)(nil).ChooseOne))
}
// GetConfig mocks base method.
func (m *MockClusterController) GetConfig(arg0 context.Context, arg1 string) (string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetConfig", arg0, arg1)
ret0, _ := ret[0].(string)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetConfig indicates an expected call of GetConfig.
func (mr *MockClusterControllerMockRecorder) GetConfig(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetConfig", reflect.TypeOf((*MockClusterController)(nil).GetConfig), arg0, arg1)
}
// GetServiceController mocks base method.
func (m *MockClusterController) GetServiceController(arg0 proto.ClusterID) (controller.ServiceController, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetServiceController", arg0)
ret0, _ := ret[0].(controller.ServiceController)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetServiceController indicates an expected call of GetServiceController.
func (mr *MockClusterControllerMockRecorder) GetServiceController(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetServiceController", reflect.TypeOf((*MockClusterController)(nil).GetServiceController), arg0)
}
// GetVolumeGetter mocks base method.
func (m *MockClusterController) GetVolumeGetter(arg0 proto.ClusterID) (controller.VolumeGetter, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetVolumeGetter", arg0)
ret0, _ := ret[0].(controller.VolumeGetter)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetVolumeGetter indicates an expected call of GetVolumeGetter.
func (mr *MockClusterControllerMockRecorder) GetVolumeGetter(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumeGetter", reflect.TypeOf((*MockClusterController)(nil).GetVolumeGetter), arg0)
}
// Region mocks base method.
func (m *MockClusterController) Region() string {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Region")
ret0, _ := ret[0].(string)
return ret0
}
// Region indicates an expected call of Region.
func (mr *MockClusterControllerMockRecorder) Region() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Region", reflect.TypeOf((*MockClusterController)(nil).Region))
}
// MockServiceController is a mock of ServiceController interface.
type MockServiceController struct {
ctrl *gomock.Controller
recorder *MockServiceControllerMockRecorder
}
// MockServiceControllerMockRecorder is the mock recorder for MockServiceController.
type MockServiceControllerMockRecorder struct {
mock *MockServiceController
}
// NewMockServiceController creates a new mock instance.
func NewMockServiceController(ctrl *gomock.Controller) *MockServiceController {
mock := &MockServiceController{ctrl: ctrl}
mock.recorder = &MockServiceControllerMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockServiceController) EXPECT() *MockServiceControllerMockRecorder {
return m.recorder
}
// GetDiskHost mocks base method.
func (m *MockServiceController) GetDiskHost(arg0 context.Context, arg1 proto.DiskID) (*controller.HostIDC, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetDiskHost", arg0, arg1)
ret0, _ := ret[0].(*controller.HostIDC)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetDiskHost indicates an expected call of GetDiskHost.
func (mr *MockServiceControllerMockRecorder) GetDiskHost(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDiskHost", reflect.TypeOf((*MockServiceController)(nil).GetDiskHost), arg0, arg1)
}
// GetServiceHost mocks base method.
func (m *MockServiceController) GetServiceHost(arg0 context.Context, arg1 string) (string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetServiceHost", arg0, arg1)
ret0, _ := ret[0].(string)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetServiceHost indicates an expected call of GetServiceHost.
func (mr *MockServiceControllerMockRecorder) GetServiceHost(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetServiceHost", reflect.TypeOf((*MockServiceController)(nil).GetServiceHost), arg0, arg1)
}
// GetServiceHosts mocks base method.
func (m *MockServiceController) GetServiceHosts(arg0 context.Context, arg1 string) ([]string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetServiceHosts", arg0, arg1)
ret0, _ := ret[0].([]string)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetServiceHosts indicates an expected call of GetServiceHosts.
func (mr *MockServiceControllerMockRecorder) GetServiceHosts(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetServiceHosts", reflect.TypeOf((*MockServiceController)(nil).GetServiceHosts), arg0, arg1)
}
// PunishDisk mocks base method.
func (m *MockServiceController) PunishDisk(arg0 context.Context, arg1 proto.DiskID, arg2 int) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "PunishDisk", arg0, arg1, arg2)
}
// PunishDisk indicates an expected call of PunishDisk.
func (mr *MockServiceControllerMockRecorder) PunishDisk(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PunishDisk", reflect.TypeOf((*MockServiceController)(nil).PunishDisk), arg0, arg1, arg2)
}
// PunishDiskWithThreshold mocks base method.
func (m *MockServiceController) PunishDiskWithThreshold(arg0 context.Context, arg1 proto.DiskID, arg2 int) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "PunishDiskWithThreshold", arg0, arg1, arg2)
}
// PunishDiskWithThreshold indicates an expected call of PunishDiskWithThreshold.
func (mr *MockServiceControllerMockRecorder) PunishDiskWithThreshold(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PunishDiskWithThreshold", reflect.TypeOf((*MockServiceController)(nil).PunishDiskWithThreshold), arg0, arg1, arg2)
}
// PunishService mocks base method.
func (m *MockServiceController) PunishService(arg0 context.Context, arg1, arg2 string, arg3 int) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "PunishService", arg0, arg1, arg2, arg3)
}
// PunishService indicates an expected call of PunishService.
func (mr *MockServiceControllerMockRecorder) PunishService(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PunishService", reflect.TypeOf((*MockServiceController)(nil).PunishService), arg0, arg1, arg2, arg3)
}
// PunishServiceWithThreshold mocks base method.
func (m *MockServiceController) PunishServiceWithThreshold(arg0 context.Context, arg1, arg2 string, arg3 int) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "PunishServiceWithThreshold", arg0, arg1, arg2, arg3)
}
// PunishServiceWithThreshold indicates an expected call of PunishServiceWithThreshold.
func (mr *MockServiceControllerMockRecorder) PunishServiceWithThreshold(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PunishServiceWithThreshold", reflect.TypeOf((*MockServiceController)(nil).PunishServiceWithThreshold), arg0, arg1, arg2, arg3)
}
// MockVolumeGetter is a mock of VolumeGetter interface.
type MockVolumeGetter struct {
ctrl *gomock.Controller
recorder *MockVolumeGetterMockRecorder
}
// MockVolumeGetterMockRecorder is the mock recorder for MockVolumeGetter.
type MockVolumeGetterMockRecorder struct {
mock *MockVolumeGetter
}
// NewMockVolumeGetter creates a new mock instance.
func NewMockVolumeGetter(ctrl *gomock.Controller) *MockVolumeGetter {
mock := &MockVolumeGetter{ctrl: ctrl}
mock.recorder = &MockVolumeGetterMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockVolumeGetter) EXPECT() *MockVolumeGetterMockRecorder {
return m.recorder
}
// Get mocks base method.
func (m *MockVolumeGetter) Get(arg0 context.Context, arg1 proto.Vid, arg2 bool) *controller.VolumePhy {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Get", arg0, arg1, arg2)
ret0, _ := ret[0].(*controller.VolumePhy)
return ret0
}
// Get indicates an expected call of Get.
func (mr *MockVolumeGetterMockRecorder) Get(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockVolumeGetter)(nil).Get), arg0, arg1, arg2)
}
// Punish mocks base method.
func (m *MockVolumeGetter) Punish(arg0 context.Context, arg1 proto.Vid, arg2 int) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "Punish", arg0, arg1, arg2)
}
// Punish indicates an expected call of Punish.
func (mr *MockVolumeGetterMockRecorder) Punish(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Punish", reflect.TypeOf((*MockVolumeGetter)(nil).Punish), arg0, arg1, arg2)
}

View File

@ -12,7 +12,7 @@
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package stream
package access
import (
"context"
@ -118,20 +118,20 @@ type Writer struct {
var _ io.Writer = &Writer{}
func (w *Writer) Write(p []byte) (n int, err error) {
n, err = w.underlying.Write(p)
now := time.Now()
reserve := w.rate.ReserveN(now, len(p))
reserve := w.rate.ReserveN(now, n)
// Wait if necessary
delay := reserve.DelayFrom(now)
if delay == 0 {
n, err = w.underlying.Write(p)
return
}
span := trace.SpanFromContextSafe(w.ctx)
if !reserve.OK() {
span.Warnf("writer exceeds limiter n:%d, burst:%d", len(p), w.rate.Burst())
n, err = w.underlying.Write(p)
span.Warnf("writer exceeds limiter n:%d, burst:%d", n, w.rate.Burst())
return
}
t := time.NewTimer(delay)
@ -143,7 +143,6 @@ func (w *Writer) Write(p []byte) (n int, err error) {
select {
case <-t.C:
// We can proceed.
n, err = w.underlying.Write(p)
return
case <-w.ctx.Done():
// Context was canceled before we could proceed. Cancel the

View File

@ -12,7 +12,7 @@
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package stream
package access
import (
"context"

View File

@ -12,15 +12,12 @@
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package stream
package access
import (
"os"
"github.com/prometheus/client_golang/prometheus"
"github.com/cubefs/cubefs/blobstore/common/proto"
"github.com/cubefs/cubefs/blobstore/common/rpc/auditlog"
)
var unhealthMetric = prometheus.NewCounterVec(
@ -43,27 +40,9 @@ var downloadMetric = prometheus.NewCounterVec(
[]string{"cluster", "way", "reason"},
)
var readwriteMetric *prometheus.HistogramVec
var SteamReportDownload = reportDownload
func init() {
prometheus.MustRegister(unhealthMetric)
prometheus.MustRegister(downloadMetric)
hostname, _ := os.Hostname()
readwriteMetric = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Namespace: "blobstore",
Subsystem: "access",
Name: "read_write_duration_ms",
Help: "read write duration ms",
Buckets: auditlog.Buckets,
ConstLabels: map[string]string{"host": hostname},
},
[]string{"cluster", "idc", "api"},
)
prometheus.MustRegister(readwriteMetric)
}
func reportUnhealth(cid proto.ClusterID, action, module, host, reason string) {
@ -73,8 +52,3 @@ func reportUnhealth(cid proto.ClusterID, action, module, host, reason string) {
func reportDownload(cid proto.ClusterID, way, reason string) {
downloadMetric.WithLabelValues(cid.ToString(), way, reason).Inc()
}
// upload_read, upload_write, download_read, download_write
func reportReadwrite(cid, idc, api string, ms int64) {
readwriteMetric.WithLabelValues(cid, idc, api).Observe(float64(ms))
}

View File

@ -15,13 +15,14 @@
package access
import (
"crypto/sha1"
"fmt"
"net/http"
"strconv"
"sync"
"time"
"github.com/cubefs/cubefs/blobstore/access/controller"
"github.com/cubefs/cubefs/blobstore/access/stream"
"github.com/cubefs/cubefs/blobstore/api/access"
"github.com/cubefs/cubefs/blobstore/api/clustermgr"
"github.com/cubefs/cubefs/blobstore/cmd"
@ -31,8 +32,8 @@ import (
"github.com/cubefs/cubefs/blobstore/common/proto"
"github.com/cubefs/cubefs/blobstore/common/resourcepool"
"github.com/cubefs/cubefs/blobstore/common/rpc"
"github.com/cubefs/cubefs/blobstore/common/security"
"github.com/cubefs/cubefs/blobstore/common/trace"
"github.com/cubefs/cubefs/blobstore/common/uptoken"
"github.com/cubefs/cubefs/blobstore/util/closer"
"github.com/cubefs/cubefs/blobstore/util/errors"
"github.com/cubefs/cubefs/blobstore/util/log"
@ -47,11 +48,46 @@ const (
limitNameSign = "sign"
)
const (
_tokenExpiration = time.Hour * 12
)
var (
// tokenSecretKeys alloc token with the first secret key always,
// so that you can change the secret key.
//
// parse-1: insert a new key at the first index,
// parse-2: delete the old key at the last index after _tokenExpiration duration.
tokenSecretKeys = [...][20]byte{
{0x5f, 0x00, 0x88, 0x96, 0x00, 0xa1, 0xfe, 0x1b},
{0xff, 0x1f, 0x2f, 0x4f, 0x7f, 0xaf, 0xef, 0xff},
}
_initTokenSecret sync.Once
)
func initTokenSecret(b []byte) {
_initTokenSecret.Do(func() {
for idx := range tokenSecretKeys {
copy(tokenSecretKeys[idx][7:], b)
}
})
}
func initWithRegionMagic(regionMagic string) {
if regionMagic == "" {
log.Warn("no region magic setting, using default secret keys for checksum")
return
}
b := sha1.Sum([]byte(regionMagic))
initTokenSecret(b[:8])
initLocationSecret(b[:8])
}
type accessStatus struct {
Limit stream.Status `json:"limit"`
Limit Status `json:"limit"`
Pool resourcepool.Status `json:"pool"`
Config stream.StreamConfig `json:"config"`
Config StreamConfig `json:"config"`
Clusters []*clustermgr.ClusterInfo `json:"clusters"`
Services map[proto.ClusterID]map[string][]string `json:"services"`
}
@ -60,34 +96,29 @@ type accessStatus struct {
type Config struct {
cmd.Config
ServiceRegister consul.Config `json:"service_register"`
Stream stream.StreamConfig `json:"stream"`
Limit stream.LimitConfig `json:"limit"`
ServiceRegister consul.Config `json:"service_register"`
Stream StreamConfig `json:"stream"`
Limit LimitConfig `json:"limit"`
}
// Service rpc service
type Service struct {
config Config
streamHandler stream.StreamHandler
limiter stream.Limiter
streamHandler StreamHandler
limiter Limiter
closer closer.Closer
}
// New returns an access service
func New(cfg Config) *Service {
// add region magic checksum to the secret keys
security.InitWithRegionMagic(cfg.Stream.ClusterConfig.RegionMagic)
initWithRegionMagic(cfg.Stream.ClusterConfig.RegionMagic)
cl := closer.New()
h, err := stream.NewStreamHandler(&cfg.Stream, cl.Done())
if err != nil {
log.Fatalf("new stream handler failed, err: %+v", err)
}
return &Service{
config: cfg,
streamHandler: h,
limiter: stream.NewLimiter(cfg.Limit),
streamHandler: NewStreamHandler(&cfg.Stream, cl.Done()),
limiter: NewLimiter(cfg.Limit),
closer: cl,
}
}
@ -111,9 +142,9 @@ func (s *Service) RegisterService() {
// RegisterAdminHandler register admin handler to profile
func (s *Service) RegisterAdminHandler() {
profile.HandleFunc(http.MethodGet, "/access/status", func(c *rpc.Context) {
var admin *stream.StreamAdmin
var admin *streamAdmin
if sa := s.streamHandler.Admin(); sa != nil {
if ad, ok := sa.(*stream.StreamAdmin); ok {
if ad, ok := sa.(*streamAdmin); ok {
admin = ad
}
}
@ -127,13 +158,13 @@ func (s *Service) RegisterAdminHandler() {
status := new(accessStatus)
status.Limit = s.limiter.Status()
status.Pool = admin.MemPool.Status()
status.Config = admin.Config
status.Clusters = admin.Controller.All()
status.Pool = admin.memPool.Status()
status.Config = admin.config
status.Clusters = admin.controller.All()
status.Services = make(map[proto.ClusterID]map[string][]string, len(status.Clusters))
for _, cluster := range status.Clusters {
service, err := admin.Controller.GetServiceController(cluster.ClusterID)
service, err := admin.controller.GetServiceController(cluster.ClusterID)
if err != nil {
span.Warn(err.Error())
continue
@ -160,8 +191,8 @@ func (s *Service) RegisterAdminHandler() {
alg := controller.AlgChoose(algInt)
if sa := s.streamHandler.Admin(); sa != nil {
if admin, ok := sa.(*stream.StreamAdmin); ok {
if err := admin.Controller.ChangeChooseAlg(alg); err != nil {
if admin, ok := sa.(*streamAdmin); ok {
if err := admin.controller.ChangeChooseAlg(alg); err != nil {
c.RespondWith(http.StatusForbidden, "", []byte(err.Error()))
return
}
@ -234,7 +265,7 @@ func (s *Service) Put(c *rpc.Context) {
}
rc := s.limiter.Reader(ctx, c.Request.Body)
loc, err := s.streamHandler.Put(ctx, rc, args.Size, hasherMap, args.AssignClusterID, args.CodeMode)
loc, err := s.streamHandler.Put(ctx, rc, args.Size, hasherMap)
if err != nil {
span.Error("stream put failed", errors.Detail(err))
c.RespondError(httpError(err))
@ -246,7 +277,7 @@ func (s *Service) Put(c *rpc.Context) {
hashSumMap[alg] = hasher.Sum(nil)
}
if err := security.LocationCrcFill(loc); err != nil {
if err := fillCrc(loc); err != nil {
span.Error("stream put fill location crc", err)
c.RespondError(httpError(err))
return
@ -277,8 +308,8 @@ func (s *Service) PutAt(c *rpc.Context) {
}
valid := false
for _, secretKey := range security.TokenSecretKeys() {
token := security.DecodeToken(args.Token)
for _, secretKey := range tokenSecretKeys {
token := uptoken.DecodeToken(args.Token)
if token.IsValid(args.ClusterID, args.Vid, args.BlobID, uint32(args.Size), secretKey[:]) {
valid = true
break
@ -338,7 +369,7 @@ func (s *Service) Alloc(c *rpc.Context) {
return
}
if err := security.LocationCrcFill(location); err != nil {
if err := fillCrc(location); err != nil {
span.Error("stream alloc fill location crc", err)
c.RespondError(httpError(err))
return
@ -346,7 +377,7 @@ func (s *Service) Alloc(c *rpc.Context) {
resp := access.AllocResp{
Location: *location,
Tokens: security.StreamGenTokens(location),
Tokens: genTokens(location),
}
c.RespondJSON(resp)
span.Infof("done /alloc request resp:%+v", resp)
@ -364,7 +395,7 @@ func (s *Service) Get(c *rpc.Context) {
span := trace.SpanFromContextSafe(ctx)
span.Debugf("accept /get request args:%+v", args)
if !args.IsValid() || !security.LocationCrcVerify(&args.Location) {
if !args.IsValid() || !verifyCrc(&args.Location) {
c.RespondError(errcode.ErrIllegalArguments)
return
}
@ -380,9 +411,9 @@ func (s *Service) Get(c *rpc.Context) {
w.Header().Set(rpc.HeaderContentType, rpc.MIMEStream)
w.Header().Set(rpc.HeaderContentLength, strconv.FormatInt(int64(args.ReadSize), 10))
if args.ReadSize > 0 && args.ReadSize != args.Location.Size_ {
if args.ReadSize > 0 && args.ReadSize != args.Location.Size {
w.Header().Set(rpc.HeaderContentRange, fmt.Sprintf("bytes %d-%d/%d",
args.Offset, args.Offset+args.ReadSize-1, args.Location.Size_))
args.Offset, args.Offset+args.ReadSize-1, args.Location.Size))
c.RespondStatus(http.StatusPartialContent)
} else {
c.RespondStatus(http.StatusOK)
@ -393,7 +424,7 @@ func (s *Service) Get(c *rpc.Context) {
err = transfer()
if err != nil {
stream.SteamReportDownload(args.Location.ClusterID, "StatusOKError", "-")
reportDownload(args.Location.ClusterID, "StatusOKError", "-")
span.Error("stream get transfer failed", errors.Detail(err))
return
}
@ -440,19 +471,19 @@ func (s *Service) Delete(c *rpc.Context) {
clusterBlobsN := make(map[proto.ClusterID]int, 4)
for _, loc := range args.Locations {
if !security.LocationCrcVerify(&loc) {
if !verifyCrc(&loc) {
span.Infof("invalid crc %+v", loc)
err = errcode.ErrIllegalArguments
return
}
clusterBlobsN[loc.ClusterID] += len(loc.Slices)
clusterBlobsN[loc.ClusterID] += len(loc.Blobs)
}
if len(args.Locations) == 1 {
loc := args.Locations[0]
if err := s.streamHandler.Delete(ctx, &loc); err != nil {
span.Error("stream delete failed", errors.Detail(err))
resp.FailedLocations = []proto.Location{loc}
resp.FailedLocations = []access.Location{loc}
}
return
}
@ -464,12 +495,12 @@ func (s *Service) Delete(c *rpc.Context) {
// max delete locations is 1024, one location is max to 5G,
// merged message max size about 40MB.
merged := make(map[proto.ClusterID][]proto.Slice, len(clusterBlobsN))
merged := make(map[proto.ClusterID][]access.SliceInfo, len(clusterBlobsN))
for id, n := range clusterBlobsN {
merged[id] = make([]proto.Slice, 0, n)
merged[id] = make([]access.SliceInfo, 0, n)
}
for _, loc := range args.Locations {
merged[loc.ClusterID] = append(merged[loc.ClusterID], loc.Slices...)
merged[loc.ClusterID] = append(merged[loc.ClusterID], loc.Blobs...)
}
var wg sync.WaitGroup
@ -478,7 +509,7 @@ func (s *Service) Delete(c *rpc.Context) {
go func() {
for id := range failedCh {
if resp.FailedLocations == nil {
resp.FailedLocations = make([]proto.Location, 0, len(args.Locations))
resp.FailedLocations = make([]access.Location, 0, len(args.Locations))
}
for _, loc := range args.Locations {
if loc.ClusterID == id {
@ -492,10 +523,10 @@ func (s *Service) Delete(c *rpc.Context) {
wg.Add(len(merged))
for id := range merged {
go func(id proto.ClusterID) {
if err := s.streamHandler.Delete(ctx, &proto.Location{
if err := s.streamHandler.Delete(ctx, &access.Location{
ClusterID: id,
SliceSize: 1,
Slices: merged[id],
BlobSize: 1,
Blobs: merged[id],
}); err != nil {
span.Error("stream delete failed", id, errors.Detail(err))
failedCh <- id
@ -527,8 +558,8 @@ func (s *Service) DeleteBlob(c *rpc.Context) {
}
valid := false
for _, secretKey := range security.TokenSecretKeys() {
token := security.DecodeToken(args.Token)
for _, secretKey := range tokenSecretKeys {
token := uptoken.DecodeToken(args.Token)
if token.IsValid(args.ClusterID, args.Vid, args.BlobID, uint32(args.Size), secretKey[:]) {
valid = true
break
@ -540,13 +571,13 @@ func (s *Service) DeleteBlob(c *rpc.Context) {
return
}
if err := s.streamHandler.Delete(ctx, &proto.Location{
if err := s.streamHandler.Delete(ctx, &access.Location{
ClusterID: args.ClusterID,
SliceSize: 1,
Slices: []proto.Slice{{
MinSliceID: args.BlobID,
Vid: args.Vid,
Count: 1,
BlobSize: 1,
Blobs: []access.SliceInfo{{
MinBid: args.BlobID,
Vid: args.Vid,
Count: 1,
}},
}); err != nil {
span.Error("stream delete blob failed", errors.Detail(err))
@ -577,7 +608,7 @@ func (s *Service) Sign(c *rpc.Context) {
loc := args.Location
crcOld := loc.Crc
if err := security.LocationCrcSign(&loc, args.Locations); err != nil {
if err := signCrc(&loc, args.Locations); err != nil {
span.Error("stream sign failed", errors.Detail(err))
c.RespondError(errcode.ErrIllegalArguments)
return
@ -596,3 +627,39 @@ func httpError(err error) error {
}
return errcode.ErrUnexpected
}
// genTokens generate tokens
// 1. Returns 0 token if has no blobs.
// 2. Returns 1 token if file size less than blobsize.
// 3. Returns len(blobs) tokens if size divided by blobsize.
// 4. Otherwise returns len(blobs)+1 tokens, the last token
// will be used by the last blob, even if the last slice blobs' size
// less than blobsize.
// 5. Each segment blob has its specified token include the last blob.
func genTokens(location *access.Location) []string {
tokens := make([]string, 0, len(location.Blobs)+1)
hasMultiBlobs := location.Size >= uint64(location.BlobSize)
lastSize := uint32(location.Size % uint64(location.BlobSize))
for idx, blob := range location.Blobs {
// returns one token if size < blobsize
if hasMultiBlobs {
count := blob.Count
if idx == len(location.Blobs)-1 && lastSize > 0 {
count--
}
tokens = append(tokens, uptoken.EncodeToken(uptoken.NewUploadToken(location.ClusterID,
blob.Vid, blob.MinBid, count,
location.BlobSize, _tokenExpiration, tokenSecretKeys[0][:])))
}
// token of the last blob
if idx == len(location.Blobs)-1 && lastSize > 0 {
tokens = append(tokens, uptoken.EncodeToken(uptoken.NewUploadToken(location.ClusterID,
blob.Vid, blob.MinBid+proto.BlobID(blob.Count)-1, 1,
lastSize, _tokenExpiration, tokenSecretKeys[0][:])))
}
}
return tokens
}

View File

@ -0,0 +1,127 @@
// Copyright 2022 The CubeFS Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package access
import (
"fmt"
"hash/crc32"
"sync"
"github.com/cubefs/cubefs/blobstore/api/access"
"github.com/cubefs/cubefs/blobstore/common/proto"
"github.com/cubefs/cubefs/blobstore/util/bytespool"
)
const (
// DO NOT CHANGE IT.
_crcPoly = uint32(0x59c8943c)
)
var (
// DO NOT CHANGE IT.
_crcTable = crc32.MakeTable(_crcPoly)
_crcMagicKey = [20]byte{
0x52, 0xe, 0x53, 0x53, 0x81,
0x1f, 0x51, 0xb7, 0xa4, 0x72,
0x10, 0x33, 0x64, 0xa7, 0x3a,
0x10, 0x19, 0xbc, 0x60, 0x7,
}
_initLocationSecret sync.Once
)
func initLocationSecret(b []byte) {
_initLocationSecret.Do(func() {
copy(_crcMagicKey[7:], b)
})
}
func calcCrc(loc *access.Location) (uint32, error) {
crcWriter := crc32.New(_crcTable)
buf := bytespool.Alloc(1024)
defer bytespool.Free(buf)
n := loc.Encode2(buf)
if n < 4 {
return 0, fmt.Errorf("no enough bytes(%d) fill into buf", n)
}
if _, err := crcWriter.Write(_crcMagicKey[:]); err != nil {
return 0, fmt.Errorf("fill crc %s", err.Error())
}
if _, err := crcWriter.Write(buf[4:n]); err != nil {
return 0, fmt.Errorf("fill crc %s", err.Error())
}
return crcWriter.Sum32(), nil
}
func fillCrc(loc *access.Location) error {
crc, err := calcCrc(loc)
if err != nil {
return err
}
loc.Crc = crc
return nil
}
func verifyCrc(loc *access.Location) bool {
crc, err := calcCrc(loc)
if err != nil {
return false
}
return loc.Crc == crc
}
func signCrc(loc *access.Location, locs []access.Location) error {
first := locs[0]
bids := make(map[proto.BlobID]struct{}, 64)
if loc.ClusterID != first.ClusterID ||
loc.CodeMode != first.CodeMode ||
loc.BlobSize != first.BlobSize {
return fmt.Errorf("not equal in constant field")
}
for _, l := range locs {
if !verifyCrc(&l) {
return fmt.Errorf("not equal in crc %d", l.Crc)
}
// assert
if l.ClusterID != first.ClusterID ||
l.CodeMode != first.CodeMode ||
l.BlobSize != first.BlobSize {
return fmt.Errorf("not equal in constant field")
}
for _, blob := range l.Blobs {
for c := 0; c < int(blob.Count); c++ {
bids[blob.MinBid+proto.BlobID(c)] = struct{}{}
}
}
}
for _, blob := range loc.Blobs {
for c := 0; c < int(blob.Count); c++ {
bid := blob.MinBid + proto.BlobID(c)
if _, ok := bids[bid]; !ok {
return fmt.Errorf("not equal in blob_id(%d)", bid)
}
}
}
return fillCrc(loc)
}

View File

@ -1,4 +1,4 @@
// Copyright 2024 The CubeFS Authors.
// Copyright 2022 The CubeFS Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -12,7 +12,7 @@
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package security
package access
import (
"fmt"
@ -22,27 +22,28 @@ import (
"github.com/stretchr/testify/require"
"github.com/cubefs/cubefs/blobstore/api/access"
"github.com/cubefs/cubefs/blobstore/common/codemode"
"github.com/cubefs/cubefs/blobstore/common/proto"
"github.com/cubefs/cubefs/blobstore/util/bytespool"
)
var (
testMaxBlob = proto.Slice{
MinSliceID: proto.BlobID(math.MaxUint64),
Vid: proto.Vid(math.MaxInt32),
Count: math.MaxUint32,
testMaxBlob = access.SliceInfo{
MinBid: proto.BlobID(math.MaxUint64),
Vid: proto.Vid(math.MaxInt32),
Count: math.MaxUint32,
}
testMaxLoc = proto.Location{
testMaxLoc = access.Location{
ClusterID: proto.ClusterID(math.MaxUint32),
CodeMode: codemode.CodeMode(math.MaxInt8),
Size_: math.MaxUint64,
SliceSize: math.MaxUint32,
Size: math.MaxUint64,
BlobSize: math.MaxUint32,
Crc: math.MaxUint32,
}
testMinBlob = proto.Slice{}
testMinLoc = proto.Location{}
testMinBlob = access.SliceInfo{}
testMinLoc = access.Location{}
)
func TestAccessServiceLocationCrc(t *testing.T) {
@ -62,7 +63,7 @@ func TestAccessServiceLocationCrc(t *testing.T) {
}
{
loc := testMinLoc.Copy()
loc.Size_ = 1 << 30
loc.Size = 1 << 30
err := fillCrc(&loc)
require.NoError(t, err)
@ -70,7 +71,7 @@ func TestAccessServiceLocationCrc(t *testing.T) {
}
{
loc := testMinLoc.Copy()
loc.Size_ = 1 << 30
loc.Size = 1 << 30
require.False(t, verifyCrc(&loc))
loc.Crc = 0x9e17bc9e
@ -100,45 +101,17 @@ func TestAccessServiceLocationSecret(t *testing.T) {
}
}
func TestAccessServiceTokenSecret(t *testing.T) {
keys := tokenSecretKeys
defer func() {
tokenSecretKeys = keys
}()
b := [...]byte{1: 2, 7: 8}
loc := &proto.Location{
ClusterID: 1,
CodeMode: 1,
Size_: 1023,
SliceSize: 1024,
Slices: []proto.Slice{{
MinSliceID: 11,
Vid: 199,
Count: 1,
}},
}
for idx := range tokenSecretKeys {
copy(tokenSecretKeys[idx][7:], b[:])
}
tokens := genTokens(loc)
require.Equal(t, 1, len(tokens))
token := DecodeToken(tokens[0])
require.True(t, token.IsValid(1, 199, 11, 1023, TokenSecretKeys()[0][:]))
}
func TestAccessServiceLocationSignCrc(t *testing.T) {
loc := &proto.Location{
loc := &access.Location{
ClusterID: 1,
CodeMode: 1,
Size_: 1023,
SliceSize: 6,
Size: 1023,
BlobSize: 6,
Crc: 0,
Slices: []proto.Slice{{
MinSliceID: 11,
Vid: 199,
Count: 10,
Blobs: []access.SliceInfo{{
MinBid: 11,
Vid: 199,
Count: 10,
}},
}
fillCrc(loc)
@ -146,87 +119,42 @@ func TestAccessServiceLocationSignCrc(t *testing.T) {
{
loc1, loc2 := loc.Copy(), loc.Copy()
require.NoError(t, signCrc(loc, []proto.Location{loc1, loc2}))
require.NoError(t, signCrc(loc, []access.Location{loc1, loc2}))
}
{
loc1, loc2 := loc.Copy(), loc.Copy()
loc1.SliceSize = 100
loc1.BlobSize = 100
fillCrc(&loc1)
require.Error(t, signCrc(loc, []proto.Location{loc1, loc2}))
require.Error(t, signCrc(loc, []access.Location{loc1, loc2}))
}
{
loc1, loc2 := loc.Copy(), loc.Copy()
loc2.Crc = 0
require.Error(t, signCrc(loc, []proto.Location{loc1, loc2}))
require.Error(t, signCrc(loc, []access.Location{loc1, loc2}))
}
{
loc1, loc2 := loc.Copy(), loc.Copy()
loc2.ClusterID = 2
fillCrc(&loc2)
require.Error(t, signCrc(loc, []proto.Location{loc1, loc2}))
require.Error(t, signCrc(loc, []access.Location{loc1, loc2}))
}
{
loc1, loc2 := loc.Copy(), loc.Copy()
loc2.CodeMode = 100
fillCrc(&loc2)
require.Error(t, signCrc(loc, []proto.Location{loc1, loc2}))
require.Error(t, signCrc(loc, []access.Location{loc1, loc2}))
}
{
loc1, loc2 := loc.Copy(), loc.Copy()
loc1.Slices = nil
loc2.Slices[0].Count = 5
loc1.Blobs = nil
loc2.Blobs[0].Count = 5
fillCrc(&loc1)
fillCrc(&loc2)
require.Error(t, signCrc(loc, []proto.Location{loc1, loc2}))
require.Error(t, signCrc(loc, []access.Location{loc1, loc2}))
}
}
func TestAccessServiceTokenLast(t *testing.T) {
{
loc := &proto.Location{
ClusterID: 1,
Size_: 4097,
SliceSize: 1024,
Slices: []proto.Slice{{
MinSliceID: 11,
Vid: 100,
Count: 4,
}, {
MinSliceID: 22,
Vid: 200,
Count: 1,
}},
}
tokens := genTokens(loc)
require.Equal(t, 2, len(tokens))
token := DecodeToken(tokens[0])
require.True(t, token.IsValid(1, 100, 11, 1024, TokenSecretKeys()[0][:]))
token = DecodeToken(tokens[1])
require.True(t, token.IsValid(1, 200, 22, 1, TokenSecretKeys()[0][:]))
}
{
loc := &proto.Location{
ClusterID: 1,
Size_: 4096,
SliceSize: 1024,
Slices: []proto.Slice{{
MinSliceID: 11,
Vid: 100,
Count: 4,
}},
}
tokens := genTokens(loc)
require.Equal(t, 1, len(tokens))
token := DecodeToken(tokens[0])
require.True(t, token.IsValid(1, 100, 11, 1024, TokenSecretKeys()[0][:]))
}
}
func calcCrcWithoutMagic(loc *proto.Location) (uint32, error) {
func calcCrcWithoutMagic(loc *access.Location) (uint32, error) {
crcWriter := crc32.New(_crcTable)
buf := bytespool.Alloc(1024)
@ -239,17 +167,16 @@ func calcCrcWithoutMagic(loc *proto.Location) (uint32, error) {
}
func benchmarkCrc(b *testing.B, key string,
location proto.Location, blob proto.Slice,
run func(loc *proto.Location) (uint32, error),
) {
location access.Location, blob access.SliceInfo,
run func(loc *access.Location) (uint32, error)) {
cases := []int{0, 2, 4, 8, 16, 32}
for _, l := range cases {
b.ResetTimer()
b.Run(fmt.Sprintf(key+"-%d", l), func(b *testing.B) {
loc := location.Copy()
loc.Slices = make([]proto.Slice, l)
for idx := range loc.Slices {
loc.Slices[idx] = blob
loc.Blobs = make([]access.SliceInfo, l)
for idx := range loc.Blobs {
loc.Blobs[idx] = blob
}
b.ResetTimer()
for ii := 0; ii <= b.N; ii++ {

View File

@ -30,65 +30,26 @@ import (
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/require"
"github.com/cubefs/cubefs/blobstore/access/stream"
"github.com/cubefs/cubefs/blobstore/api/access"
"github.com/cubefs/cubefs/blobstore/common/codemode"
errcode "github.com/cubefs/cubefs/blobstore/common/errors"
"github.com/cubefs/cubefs/blobstore/common/proto"
"github.com/cubefs/cubefs/blobstore/common/rpc"
"github.com/cubefs/cubefs/blobstore/common/security"
mocks "github.com/cubefs/cubefs/blobstore/testing/mocks"
_ "github.com/cubefs/cubefs/blobstore/testing/nolog"
"github.com/cubefs/cubefs/blobstore/common/uptoken"
)
var (
ctx = context.Background()
_blobSize uint32 = 1 << 20
location = &proto.Location{
location = &access.Location{
ClusterID: 1,
CodeMode: codemode.EC15P12,
SliceSize: _blobSize,
CodeMode: 1,
BlobSize: _blobSize,
Crc: 0,
Slices: []proto.Slice{{
MinSliceID: 111,
Vid: 1111,
Count: 11,
}},
}
locationForClusterID = &proto.Location{
ClusterID: 11,
CodeMode: codemode.EC3P3,
SliceSize: _blobSize,
Crc: 0,
Slices: []proto.Slice{{
MinSliceID: 111,
Vid: 1111,
Count: 1,
}},
}
locationForCodeMode = &proto.Location{
ClusterID: 22,
CodeMode: codemode.EC6P6,
SliceSize: _blobSize,
Crc: 0,
Slices: []proto.Slice{{
MinSliceID: 111,
Vid: 1111,
Count: 1,
}},
}
locationForClusterIDAndCodeMode = &proto.Location{
ClusterID: 99,
CodeMode: codemode.EC12P4,
SliceSize: _blobSize,
Crc: 0,
Slices: []proto.Slice{{
MinSliceID: 111,
Vid: 1111,
Count: 1,
Blobs: []access.SliceInfo{{
MinBid: 111,
Vid: 1111,
Count: 11,
}},
}
@ -105,66 +66,54 @@ func runMockService(s *Service) string {
func newService() *Service {
ctr := gomock.NewController(&testing.T{})
s := mocks.NewMockStreamHandler(ctr)
s := NewMockStreamHandler(ctr)
s.EXPECT().Alloc(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().DoAndReturn(
func(ctx context.Context, size uint64, blobSize uint32,
assignClusterID proto.ClusterID, codeMode codemode.CodeMode,
) (*proto.Location, error) {
assignClusterID proto.ClusterID, codeMode codemode.CodeMode) (*access.Location, error) {
if size < 1024 {
return nil, errors.New("fake alloc location")
}
loc := location.Copy()
loc.Size_ = uint64(size)
security.LocationCrcFill(&loc)
loc.Size = uint64(size)
fillCrc(&loc)
return &loc, nil
})
s.EXPECT().PutAt(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(),
gomock.Any()).AnyTimes().DoAndReturn(
func(ctx context.Context, rc io.Reader,
clusterID proto.ClusterID, vid proto.Vid, bid proto.BlobID, size int64, hasherMap access.HasherMap,
) error {
clusterID proto.ClusterID, vid proto.Vid, bid proto.BlobID, size int64,
hasherMap access.HasherMap) error {
if size < 1024 {
return errcode.ErrAccessLimited
}
return nil
})
s.EXPECT().Put(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().DoAndReturn(
func(ctx context.Context, rc io.Reader, size int64, hasherMap access.HasherMap,
assignClusterID proto.ClusterID, codeMode codemode.CodeMode,
) (*proto.Location, error) {
s.EXPECT().Put(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().DoAndReturn(
func(ctx context.Context, rc io.Reader, size int64, hasherMap access.HasherMap) (*access.Location, error) {
if size < 1024 {
return nil, errors.New("fake put nil body")
}
var loc proto.Location
if assignClusterID == 0 && codeMode == codemode.CodeModeNone {
loc = location.Copy()
} else if assignClusterID == 11 && codeMode == codemode.CodeModeNone {
loc = locationForClusterID.Copy()
} else if assignClusterID == 0 && codeMode == codemode.EC6P6 {
loc = locationForCodeMode.Copy()
} else if assignClusterID == 99 && codeMode == codemode.EC12P4 {
loc = locationForClusterIDAndCodeMode.Copy()
}
loc.Size_ = uint64(size)
security.LocationCrcFill(&loc)
loc := location.Copy()
loc.Size = uint64(size)
fillCrc(&loc)
return &loc, nil
})
s.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().DoAndReturn(
func(ctx context.Context, w io.Writer, location proto.Location, readSize, offset uint64) (func() error, error) {
func(ctx context.Context, w io.Writer, location access.Location, readSize, offset uint64) (func() error, error) {
if readSize < 1024 {
return nil, errors.New("fake get nil body")
}
return func() error { return nil }, nil
})
s.EXPECT().Delete(gomock.Any(), gomock.Any()).AnyTimes().DoAndReturn(
func(ctx context.Context, location *proto.Location) error {
func(ctx context.Context, location *access.Location) error {
if location.ClusterID >= 10 {
return errors.New("fake delete error with cluster")
} else if location.ClusterID == 1 && location.Crc > 0 && location.Size_ < 1024 {
} else if location.ClusterID == 1 && location.Crc > 0 && location.Size < 1024 {
return errors.New("fake delete error")
}
return nil
@ -172,7 +121,7 @@ func newService() *Service {
return &Service{
streamHandler: s,
limiter: stream.NewLimiter(stream.LimitConfig{
limiter: NewLimiter(LimitConfig{
NameRps: map[string]int{
limitNameAlloc: 2,
},
@ -219,21 +168,21 @@ func TestAccessServiceAlloc(t *testing.T) {
resp := &access.AllocResp{}
err := cli.PostWith(ctx, url(), resp, args)
require.NoError(t, err)
require.Equal(t, uint64(1024), resp.Location.Size_)
require.Equal(t, uint64(1024), resp.Location.Size)
}
{
args.Size = uint64(_blobSize)
resp := &access.AllocResp{}
err := cli.PostWith(ctx, url(), resp, args)
require.NoError(t, err)
require.Equal(t, uint64(_blobSize), resp.Location.Size_)
require.Equal(t, uint64(_blobSize), resp.Location.Size)
}
{
args.Size = uint64(_blobSize) + 1
resp := &access.AllocResp{}
err := cli.PostWith(ctx, url(), resp, args)
require.NoError(t, err)
require.Equal(t, uint64(_blobSize)+1, resp.Location.Size_)
require.Equal(t, uint64(_blobSize)+1, resp.Location.Size)
}
}
@ -271,7 +220,7 @@ func TestAccessServicePutAt(t *testing.T) {
resp := &access.PutAtResp{}
req, _ := http.NewRequest(method, url(args.Size, "c1fdcecaacbfafd86f0b00"), bytes.NewReader(buf))
err := cli.DoWith(ctx, req, resp, rpc.WithCrcEncode())
assertErrorCode(t, errcode.CodeAccessLimited, err)
assertErrorCode(t, 552, err)
}
{
args.Size = 1024
@ -296,21 +245,6 @@ func TestAccessServicePut(t *testing.T) {
return fmt.Sprintf("%s/put?size=%d&hashes=%d", host, size, hashes)
}
urlForClusterID := func(size int64, hashes access.HashAlgorithm, assignClusterID proto.ClusterID) string {
return fmt.Sprintf("%s/put?size=%d&hashes=%d&assign_cluster_id=%d", host, size, hashes, assignClusterID)
}
urlForCodeMode := func(size int64, hashes access.HashAlgorithm, codeMode codemode.CodeMode) string {
return fmt.Sprintf("%s/put?size=%d&hashes=%d&code_mode=%d", host, size, hashes, codeMode)
}
urlForClusterIDAndCodeMode := func(size int64, hashes access.HashAlgorithm,
assignClusterID proto.ClusterID, codeMode codemode.CodeMode,
) string {
return fmt.Sprintf("%s/put?size=%d&hashes=%d&assign_cluster_id=%d&code_mode=%d",
host, size, hashes, assignClusterID, codeMode)
}
for _, method := range []string{http.MethodPut, http.MethodPost} {
args := access.PutArgs{
Size: 0,
@ -342,38 +276,7 @@ func TestAccessServicePut(t *testing.T) {
resp := &access.PutResp{}
err := cli.DoWith(ctx, req, resp, rpc.WithCrcEncode())
require.NoError(t, err)
require.Equal(t, uint64(1024), resp.Location.Size_)
require.Equal(t, proto.ClusterID(1), resp.Location.ClusterID)
require.Equal(t, codemode.EC15P12, resp.Location.CodeMode)
}
{
args.Body = bytes.NewReader(make([]byte, 1024))
req, _ := http.NewRequest(method,
urlForClusterIDAndCodeMode(1024, args.Hashes, proto.ClusterID(99), codemode.EC12P4), args.Body)
resp := &access.PutResp{}
err := cli.DoWith(ctx, req, resp, rpc.WithCrcEncode())
require.NoError(t, err)
require.Equal(t, uint64(1024), resp.Location.Size_)
require.Equal(t, proto.ClusterID(99), resp.Location.ClusterID)
require.Equal(t, codemode.EC12P4, resp.Location.CodeMode)
}
{
args.Body = bytes.NewReader(make([]byte, 1024))
req, _ := http.NewRequest(method, urlForClusterID(1024, args.Hashes, proto.ClusterID(11)), args.Body)
resp := &access.PutResp{}
err := cli.DoWith(ctx, req, resp, rpc.WithCrcEncode())
require.NoError(t, err)
require.Equal(t, uint64(1024), resp.Location.Size_)
require.Equal(t, proto.ClusterID(11), resp.Location.ClusterID)
}
{
args.Body = bytes.NewReader(make([]byte, 1024))
req, _ := http.NewRequest(method, urlForCodeMode(1024, args.Hashes, codemode.EC6P6), args.Body)
resp := &access.PutResp{}
err := cli.DoWith(ctx, req, resp, rpc.WithCrcEncode())
require.NoError(t, err)
require.Equal(t, uint64(1024), resp.Location.Size_)
require.Equal(t, codemode.EC6P6, resp.Location.CodeMode)
require.Equal(t, uint64(1024), resp.Location.Size)
}
}
}
@ -398,28 +301,28 @@ func TestAccessServiceGet(t *testing.T) {
require.Equal(t, 400, resp.StatusCode, resp.Status)
}
{
args.Location.Size_ = 1023
args.Location.Size = 1023
args.ReadSize = 1023
security.LocationCrcFill(&args.Location)
fillCrc(&args.Location)
resp, err := cli.Post(ctx, url(), args)
require.NoError(t, err)
resp.Body.Close()
require.Equal(t, 500, resp.StatusCode, resp.Status)
}
{
args.Location.Size_ = 1024
args.Location.Size = 1024
args.ReadSize = 1024
security.LocationCrcFill(&args.Location)
fillCrc(&args.Location)
resp, err := cli.Post(ctx, url(), args)
require.NoError(t, err)
resp.Body.Close()
require.Equal(t, 200, resp.StatusCode, resp.Status)
}
{
args.Location.Size_ = 10240
args.Location.Size = 10240
args.Offset = 1000
args.ReadSize = 1024
security.LocationCrcFill(&args.Location)
fillCrc(&args.Location)
resp, err := cli.Post(ctx, url(), args)
require.NoError(t, err)
resp.Body.Close()
@ -459,7 +362,7 @@ func TestAccessServiceDelete(t *testing.T) {
}
args := access.DeleteArgs{
Locations: []proto.Location{location.Copy()},
Locations: []access.Location{location.Copy()},
}
{
code, _, err := deleteRequest(access.DeleteArgs{})
@ -472,7 +375,7 @@ func TestAccessServiceDelete(t *testing.T) {
require.Equal(t, 400, code)
}
{
security.LocationCrcFill(&args.Locations[0])
fillCrc(&args.Locations[0])
code, resp, err := deleteRequest(args)
require.NoError(t, err)
require.Equal(t, 226, code)
@ -480,17 +383,17 @@ func TestAccessServiceDelete(t *testing.T) {
}
{
loc := &args.Locations[0]
loc.Size_ = 1024
security.LocationCrcFill(loc)
loc.Size = 1024
fillCrc(loc)
code, _, err := deleteRequest(args)
require.NoError(t, err)
require.Equal(t, 200, code)
}
{
loc := location.Copy()
loc.Size_ = 1024
security.LocationCrcFill(&loc)
locs := make([]proto.Location, access.MaxDeleteLocations)
loc.Size = 1024
fillCrc(&loc)
locs := make([]access.Location, access.MaxDeleteLocations)
for idx := range locs {
locs[idx] = loc
}
@ -501,9 +404,9 @@ func TestAccessServiceDelete(t *testing.T) {
}
{
loc := location.Copy()
loc.Size_ = 1024
security.LocationCrcFill(&loc)
locs := make([]proto.Location, access.MaxDeleteLocations+1)
loc.Size = 1024
fillCrc(&loc)
locs := make([]access.Location, access.MaxDeleteLocations+1)
for idx := range locs {
locs[idx] = loc
}
@ -513,22 +416,22 @@ func TestAccessServiceDelete(t *testing.T) {
}
{
loc := location.Copy()
loc.Size_ = 1024
loc.Size = 1024
loc.ClusterID = proto.ClusterID(11)
security.LocationCrcFill(&loc)
code, resp, err := deleteRequest(access.DeleteArgs{Locations: []proto.Location{loc}})
fillCrc(&loc)
code, resp, err := deleteRequest(access.DeleteArgs{Locations: []access.Location{loc}})
require.NoError(t, err)
require.Equal(t, 226, code)
require.Equal(t, 1, len(resp.FailedLocations))
require.Equal(t, proto.ClusterID(11), resp.FailedLocations[0].ClusterID)
}
{
locs := make([]proto.Location, access.MaxDeleteLocations)
locs := make([]access.Location, access.MaxDeleteLocations)
for idx := range locs {
loc := location.Copy()
loc.Size_ = 1024
loc.Size = 1024
loc.ClusterID = proto.ClusterID(idx % 11)
security.LocationCrcFill(&loc)
fillCrc(&loc)
locs[idx] = loc
}
code, resp, err := deleteRequest(access.DeleteArgs{Locations: locs})
@ -599,7 +502,7 @@ func TestAccessServiceSign(t *testing.T) {
return fmt.Sprintf("%s/sign", host)
}
args := access.SignArgs{
Locations: []proto.Location{location.Copy()},
Locations: []access.Location{location.Copy()},
Location: location.Copy(),
}
{
@ -613,7 +516,7 @@ func TestAccessServiceSign(t *testing.T) {
assertErrorCode(t, 400, err)
}
{
security.LocationCrcFill(&args.Locations[0])
fillCrc(&args.Locations[0])
resp := &access.SignResp{}
err := cli.PostWith(ctx, url(), resp, args)
require.NoError(t, err)
@ -627,162 +530,153 @@ func assertErrorCode(t *testing.T, code int, err error) {
}
func TestAccessServiceTokens(t *testing.T) {
skey := security.TokenSecretKeys()[0][:]
checker := func(loc *proto.Location, tokens []string) {
if loc.Size_ == 0 {
skey := tokenSecretKeys[0][:]
checker := func(loc *access.Location, tokens []string) {
if loc.Size == 0 {
require.Equal(t, 0, len(tokens))
return
}
hasMultiBlobs := loc.Size_ >= uint64(loc.SliceSize)
lastSize := uint32(loc.Size_ % uint64(loc.SliceSize))
hasMultiBlobs := loc.Size >= uint64(loc.BlobSize)
lastSize := uint32(loc.Size % uint64(loc.BlobSize))
if !hasMultiBlobs {
require.Equal(t, 1, len(tokens))
token := security.DecodeToken(tokens[0])
blob := loc.Slices[0]
for bid := blob.MinSliceID - 100; bid < blob.MinSliceID+100; bid++ {
require.False(t, token.IsValid(loc.ClusterID, blob.Vid, bid, loc.SliceSize, skey))
token := uptoken.DecodeToken(tokens[0])
blob := loc.Blobs[0]
for bid := blob.MinBid - 100; bid < blob.MinBid+100; bid++ {
require.False(t, token.IsValid(loc.ClusterID, blob.Vid, bid, loc.BlobSize, skey))
}
require.True(t, token.IsValid(loc.ClusterID, blob.Vid, blob.MinSliceID, lastSize, skey))
require.True(t, token.IsValid(loc.ClusterID, blob.Vid, blob.MinBid, lastSize, skey))
return
}
if lastSize == 0 {
require.Equal(t, len(loc.Slices), len(tokens))
for idx, blob := range loc.Slices {
token := security.DecodeToken(tokens[idx])
require.Equal(t, len(loc.Blobs), len(tokens))
for idx, blob := range loc.Blobs {
token := uptoken.DecodeToken(tokens[idx])
for ii := uint32(0); ii < 100; ii++ {
bid := blob.MinSliceID - proto.BlobID(ii) - 1
require.False(t, token.IsValid(loc.ClusterID, blob.Vid, bid, loc.SliceSize, skey))
bid = blob.MinSliceID + proto.BlobID(blob.Count+ii)
require.False(t, token.IsValid(loc.ClusterID, blob.Vid, bid, loc.SliceSize, skey))
bid := blob.MinBid - proto.BlobID(ii) - 1
require.False(t, token.IsValid(loc.ClusterID, blob.Vid, bid, loc.BlobSize, skey))
bid = blob.MinBid + proto.BlobID(blob.Count+ii)
require.False(t, token.IsValid(loc.ClusterID, blob.Vid, bid, loc.BlobSize, skey))
}
for ii := uint32(0); ii < blob.Count; ii++ {
bid := blob.MinSliceID + proto.BlobID(ii)
require.True(t, token.IsValid(loc.ClusterID, blob.Vid, bid, loc.SliceSize, skey))
bid := blob.MinBid + proto.BlobID(ii)
require.True(t, token.IsValid(loc.ClusterID, blob.Vid, bid, loc.BlobSize, skey))
}
}
return
}
if loc.Slices[len(loc.Slices)-1].Count == 1 {
require.Equal(t, len(loc.Slices), len(tokens))
idx := len(loc.Slices) - 1
blob := loc.Slices[idx]
token := security.DecodeToken(tokens[idx])
require.True(t, token.IsValid(loc.ClusterID, blob.Vid, blob.MinSliceID, lastSize, skey))
return
}
require.Equal(t, len(loc.Slices)+1, len(tokens))
for ii := 0; ii < len(loc.Slices)-1; ii++ {
token := security.DecodeToken(tokens[ii])
blob := loc.Slices[ii]
require.Equal(t, len(loc.Blobs)+1, len(tokens))
for ii := 0; ii < len(loc.Blobs)-1; ii++ {
token := uptoken.DecodeToken(tokens[ii])
blob := loc.Blobs[ii]
for ii := uint32(0); ii < blob.Count; ii++ {
bid := blob.MinSliceID + proto.BlobID(ii)
require.True(t, token.IsValid(loc.ClusterID, blob.Vid, bid, loc.SliceSize, skey))
bid := blob.MinBid + proto.BlobID(ii)
require.True(t, token.IsValid(loc.ClusterID, blob.Vid, bid, loc.BlobSize, skey))
}
}
token := security.DecodeToken(tokens[len(loc.Slices)-1])
blob := loc.Slices[len(loc.Slices)-1]
token := uptoken.DecodeToken(tokens[len(loc.Blobs)-1])
blob := loc.Blobs[len(loc.Blobs)-1]
for ii := uint32(0); ii < 100; ii++ {
bid := blob.MinSliceID - proto.BlobID(ii) - 1
require.False(t, token.IsValid(loc.ClusterID, blob.Vid, bid, loc.SliceSize, skey))
bid = blob.MinSliceID + proto.BlobID(blob.Count+ii) - 1
require.False(t, token.IsValid(loc.ClusterID, blob.Vid, bid, loc.SliceSize, skey))
bid := blob.MinBid - proto.BlobID(ii) - 1
require.False(t, token.IsValid(loc.ClusterID, blob.Vid, bid, loc.BlobSize, skey))
bid = blob.MinBid + proto.BlobID(blob.Count+ii) - 1
require.False(t, token.IsValid(loc.ClusterID, blob.Vid, bid, loc.BlobSize, skey))
}
for ii := uint32(0); ii < blob.Count-1; ii++ {
bid := blob.MinSliceID + proto.BlobID(ii)
require.True(t, token.IsValid(loc.ClusterID, blob.Vid, bid, loc.SliceSize, skey))
bid := blob.MinBid + proto.BlobID(ii)
require.True(t, token.IsValid(loc.ClusterID, blob.Vid, bid, loc.BlobSize, skey))
}
token = security.DecodeToken(tokens[len(loc.Slices)])
lastbid := blob.MinSliceID + proto.BlobID(blob.Count) - 1
token = uptoken.DecodeToken(tokens[len(loc.Blobs)])
lastbid := blob.MinBid + proto.BlobID(blob.Count) - 1
require.True(t, token.IsValid(loc.ClusterID, blob.Vid, lastbid, lastSize, skey))
}
{
loc := &proto.Location{
Size_: 0,
SliceSize: 333,
Slices: []proto.Slice{},
loc := &access.Location{
Size: 0,
BlobSize: 333,
Blobs: []access.SliceInfo{},
}
checker(loc, security.StreamGenTokens(loc))
checker(loc, genTokens(loc))
}
{
loc := &proto.Location{
Size_: 1,
SliceSize: 1024,
Slices: []proto.Slice{
{MinSliceID: 100, Vid: 1000, Count: 1},
loc := &access.Location{
Size: 1,
BlobSize: 1024,
Blobs: []access.SliceInfo{
{MinBid: 100, Vid: 1000, Count: 1},
},
}
checker(loc, security.StreamGenTokens(loc))
checker(loc, genTokens(loc))
}
{
loc := &proto.Location{
Size_: 1024,
SliceSize: 1024,
Slices: []proto.Slice{
{MinSliceID: 100, Vid: 1000, Count: 1},
loc := &access.Location{
Size: 1024,
BlobSize: 1024,
Blobs: []access.SliceInfo{
{MinBid: 100, Vid: 1000, Count: 1},
},
}
checker(loc, security.StreamGenTokens(loc))
checker(loc, genTokens(loc))
}
{
loc := &proto.Location{
Size_: 1025,
SliceSize: 1024,
Slices: []proto.Slice{
{MinSliceID: 100, Vid: 1000, Count: 2},
loc := &access.Location{
Size: 1025,
BlobSize: 1024,
Blobs: []access.SliceInfo{
{MinBid: 100, Vid: 1000, Count: 2},
},
}
checker(loc, security.StreamGenTokens(loc))
checker(loc, genTokens(loc))
}
{
loc := &proto.Location{
Size_: 2048,
SliceSize: 1024,
Slices: []proto.Slice{
{MinSliceID: 100, Vid: 1000, Count: 2},
loc := &access.Location{
Size: 2048,
BlobSize: 1024,
Blobs: []access.SliceInfo{
{MinBid: 100, Vid: 1000, Count: 2},
},
}
checker(loc, security.StreamGenTokens(loc))
checker(loc, genTokens(loc))
}
{
loc := &proto.Location{
Size_: 10240,
SliceSize: 1024,
Slices: []proto.Slice{
{MinSliceID: 100, Vid: 1000, Count: 4},
{MinSliceID: 200, Vid: 1000, Count: 6},
loc := &access.Location{
Size: 10240,
BlobSize: 1024,
Blobs: []access.SliceInfo{
{MinBid: 100, Vid: 1000, Count: 4},
{MinBid: 200, Vid: 1000, Count: 6},
},
}
checker(loc, security.StreamGenTokens(loc))
checker(loc, genTokens(loc))
}
{
loc := &proto.Location{
Size_: 1025,
SliceSize: 1024,
Slices: []proto.Slice{
{MinSliceID: 100, Vid: 1000, Count: 1},
{MinSliceID: 200, Vid: 1000, Count: 1},
loc := &access.Location{
Size: 1025,
BlobSize: 1024,
Blobs: []access.SliceInfo{
{MinBid: 100, Vid: 1000, Count: 1},
{MinBid: 200, Vid: 1000, Count: 1},
},
}
checker(loc, security.StreamGenTokens(loc))
checker(loc, genTokens(loc))
}
{
loc := &proto.Location{
Size_: 10242,
SliceSize: 1024,
Slices: []proto.Slice{
{MinSliceID: 100, Vid: 1000, Count: 5},
{MinSliceID: 200, Vid: 1000, Count: 6},
loc := &access.Location{
Size: 10242,
BlobSize: 1024,
Blobs: []access.SliceInfo{
{MinBid: 100, Vid: 1000, Count: 5},
{MinBid: 200, Vid: 1000, Count: 6},
},
}
checker(loc, security.StreamGenTokens(loc))
checker(loc, genTokens(loc))
}
}
@ -804,7 +698,7 @@ func TestAccessServiceLimited(t *testing.T) {
if err != nil {
assertErrorCode(t, errcode.CodeAccessLimited, err)
} else {
require.Equal(t, uint64(1024), resp.Location.Size_)
require.Equal(t, uint64(1024), resp.Location.Size)
}
}()
}

View File

@ -12,7 +12,7 @@
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package stream
package access
import (
"context"
@ -20,17 +20,12 @@ import (
"fmt"
"io"
"strings"
"sync/atomic"
"github.com/afex/hystrix-go/hystrix"
"golang.org/x/sync/singleflight"
"github.com/cubefs/cubefs/blobstore/access/controller"
"github.com/cubefs/cubefs/blobstore/api/access"
"github.com/cubefs/cubefs/blobstore/api/blobnode"
"github.com/cubefs/cubefs/blobstore/api/clustermgr"
"github.com/cubefs/cubefs/blobstore/api/proxy"
"github.com/cubefs/cubefs/blobstore/api/shardnode"
"github.com/cubefs/cubefs/blobstore/common/codemode"
"github.com/cubefs/cubefs/blobstore/common/ec"
"github.com/cubefs/cubefs/blobstore/common/proto"
@ -38,6 +33,7 @@ import (
"github.com/cubefs/cubefs/blobstore/common/trace"
"github.com/cubefs/cubefs/blobstore/util/defaulter"
"github.com/cubefs/cubefs/blobstore/util/errors"
"github.com/cubefs/cubefs/blobstore/util/log"
"github.com/cubefs/cubefs/blobstore/util/retry"
)
@ -49,7 +45,6 @@ const (
rwCommand = "rw"
serviceProxy = proto.ServiceNameProxy
serviceShard = proto.ServiceNameShardNode
)
// StreamHandler stream http handler
@ -61,7 +56,7 @@ type StreamHandler interface {
// codeMode > 0, alloc in this codemode
// return: a location of file
Alloc(ctx context.Context, size uint64, blobSize uint32,
assignClusterID proto.ClusterID, codeMode codemode.CodeMode) (*proto.Location, error)
assignClusterID proto.ClusterID, codeMode codemode.CodeMode) (*access.Location, error)
// PutAt access interface /putat, put one blob
// required: rc file reader
@ -74,9 +69,7 @@ type StreamHandler interface {
// Put put one object
// required: size, file size
// optional: hasher map to calculate hash.Hash
// optional: code to specify codemode and not choose codemode by size
Put(ctx context.Context, rc io.Reader, size int64, hasherMap access.HasherMap,
assignClusterID proto.ClusterID, codeMode codemode.CodeMode) (*proto.Location, error)
Put(ctx context.Context, rc io.Reader, size int64, hasherMap access.HasherMap) (*access.Location, error)
// Get read file
// required: location, readSize
@ -100,35 +93,19 @@ type StreamHandler interface {
//...
//read-9 [d4 p5]
//failed
Get(ctx context.Context, w io.Writer, location proto.Location, readSize, offset uint64) (func() error, error)
Get(ctx context.Context, w io.Writer, location access.Location, readSize, offset uint64) (func() error, error)
// Delete delete all blobs in this location
Delete(ctx context.Context, location *proto.Location) error
Delete(ctx context.Context, location *access.Location) error
// Admin returns internal admin interface.
Admin() any
// GetBlob returns location
GetBlob(ctx context.Context, args *access.GetBlobArgs) (*proto.Location, error)
// DeleteBlob returns error
DeleteBlob(ctx context.Context, args *access.DelBlobArgs) error
// SealBlob returns error
SealBlob(ctx context.Context, args *access.SealBlobArgs) error
// CreateBlob returns location
CreateBlob(ctx context.Context, args *access.CreateBlobArgs) (*proto.Location, error)
// ListBlob returns blobs
ListBlob(ctx context.Context, args *access.ListBlobArgs) (shardnode.ListBlobRet, error)
// AllocSlice returns alloc blob
AllocSlice(ctx context.Context, args *access.AllocSliceArgs) (shardnode.AllocSliceRet, error)
Admin() interface{}
}
type StreamAdmin struct {
Config StreamConfig
MemPool *resourcepool.MemPool
Controller controller.ClusterController
}
type ShardnodeConfig struct {
shardnode.Config
type streamAdmin struct {
config StreamConfig
memPool *resourcepool.MemPool
controller controller.ClusterController
}
// StreamConfig access stream handler config
@ -136,52 +113,25 @@ type StreamConfig struct {
IDC string `json:"idc"`
MaxBlobSize uint32 `json:"max_blob_size"`
VolumePunishIntervalS int `json:"volume_punish_interval_s"`
DiskPunishIntervalS int `json:"disk_punish_interval_s"`
DiskTimeoutPunishIntervalS int `json:"disk_timeout_punish_interval_s"`
ServicePunishIntervalS int `json:"service_punish_interval_s"` // just service of proxy
ShardnodePunishIntervalS int `json:"shardnode_punish_interval_s"`
ServicePunishIntervalS int `json:"service_punish_interval_s"`
AllocRetryTimes int `json:"alloc_retry_times"`
AllocRetryIntervalMS int `json:"alloc_retry_interval_ms"`
EncoderEnableVerify bool `json:"encoder_enableverify"`
EncoderConcurrency int `json:"encoder_concurrency"`
MinReadShardsX int `json:"min_read_shards_x"`
ReadDataOnlyTimeoutMS int `json:"read_data_only_timeout_ms"`
ShardCrcWriteDisable bool `json:"shard_crc_write_disable"`
ShardCrcReadEnable bool `json:"shard_crc_read_enable"`
ShardnodeRetryTimes int `json:"shardnode_retry_times"`
ShardnodeRetryIntervalMS int `json:"shardnode_retry_interval_ms"`
LogSlowBaseTimeMS int `json:"log_slow_base_time_ms"`
LogSlowBaseSpeedKB int `json:"log_slow_base_speed_kb"`
LogSlowTimeFator float32 `json:"log_slow_time_fator"`
// DeleteIntoShardnodePercentage roundrobin percentage [1-100]
DeleteIntoShardnodePercentage int64 `json:"delete_into_shardnode_percentage"`
deleteRoundrobin int64 `json:"-"`
// RepairIntoShardnodePercentage roundrobin percentage [1-100]
RepairIntoShardnodePercentage int64 `json:"repair_into_shardnode_percentage"`
repairRoundrobin int64 `json:"-"`
ShardCrcDisabled bool `json:"shard_crc_disabled"`
MemPoolSizeClasses map[int]int `json:"mem_pool_size_classes"`
// CodeModesPutQuorums
// just for one AZ is down, cant write quorum in all AZs
CodeModesPutQuorums map[codemode.CodeMode]int `json:"code_mode_put_quorums"`
// CodeModesGetOrdered shards with volume unit order to
// reduce reedsolom's inverted matrix cache
//
// EC24P8 in 1AZ, C(32, 8) = 10518300 matrix
// Inverted matrix memory: (24 + 24*24 + 24*24*8) * 10518300 ~= 51 GB
CodeModesGetOrdered map[codemode.CodeMode]bool `json:"code_mode_get_ordered"`
// CodeModesGetIgnoreIDC no distance when getting cross idc
CodeModesGetIgnoreIDC map[codemode.CodeMode]bool `json:"code_mode_get_ignore_idc"`
ClusterConfig controller.ClusterConfig `json:"cluster_config"`
BlobnodeConfig blobnode.Config `json:"blobnode_config"`
ProxyConfig proxy.Config `json:"proxy_config"`
ShardnodeConfig *ShardnodeConfig `json:"shardnode_config"`
ClusterConfig controller.ClusterConfig `json:"cluster_config"`
BlobnodeConfig blobnode.Config `json:"blobnode_config"`
ProxyConfig proxy.Config `json:"proxy_config"`
// hystrix command config
AllocCommandConfig hystrix.CommandConfig `json:"alloc_command_config"`
@ -210,11 +160,9 @@ type Handler struct {
memPool *resourcepool.MemPool
encoder map[codemode.CodeMode]ec.Encoder
clusterController controller.ClusterController
groupRun singleflight.Group
blobnodeClient blobnode.StorageAPI
proxyClient proxy.Client
shardnodeClient shardnode.AccessAPI
blobnodeClient blobnode.StorageAPI
proxyClient proxy.Client
allCodeModes CodeModePairs
maxObjectSize int64
@ -225,12 +173,12 @@ type Handler struct {
StreamConfig
}
func confCheck(cfg *StreamConfig) error {
func confCheck(cfg *StreamConfig) {
if cfg.IDC == "" {
return errors.New("idc config can not be null")
log.Fatal("idc config can not be null")
}
if cfg.ClusterConfig.ConsulAgentAddr == "" && len(cfg.ClusterConfig.Clusters) == 0 {
return errors.New("consul or clusters can not all be empty")
log.Panic("consul or clusters can not all be empty")
}
cfg.ClusterConfig.IDC = cfg.IDC
@ -241,37 +189,20 @@ func confCheck(cfg *StreamConfig) error {
for mode, quorum := range cfg.CodeModesPutQuorums {
tactic := mode.Tactic()
if quorum < tactic.N+tactic.L+1 || quorum > mode.GetShardNum() {
return errors.Newf("invalid put quorum(%d) in codemode(%d): %+v", quorum, mode, tactic)
log.Fatalf("invalid put quorum(%d) in codemode(%d): %+v", quorum, mode, tactic)
}
}
defaulter.Equal(&cfg.MaxBlobSize, defaultMaxBlobSize)
defaulter.IntegerLessOrEqual(&cfg.VolumePunishIntervalS, 60)
defaulter.LessOrEqual(&cfg.DiskPunishIntervalS, defaultDiskPunishIntervalS)
defaulter.LessOrEqual(&cfg.DiskTimeoutPunishIntervalS, defaultDiskPunishIntervalS/10)
defaulter.LessOrEqual(&cfg.ServicePunishIntervalS, defaultServicePunishIntervalS)
defaulter.IntegerLessOrEqual(&cfg.ShardnodePunishIntervalS, 60)
defaulter.LessOrEqual(&cfg.AllocRetryTimes, defaultAllocRetryTimes)
defaulter.LessOrEqual(&cfg.AllocRetryIntervalMS, defaultAllocRetryIntervalMS)
defaulter.LessOrEqual(&cfg.ShardnodeRetryTimes, defaultShardnodeRetryTimes)
if cfg.ShardnodeRetryIntervalMS <= defaultShardnodeRetryIntervalMS {
cfg.ShardnodeRetryIntervalMS = defaultShardnodeRetryIntervalMS
if cfg.AllocRetryIntervalMS <= 100 {
cfg.AllocRetryIntervalMS = defaultAllocRetryIntervalMS
}
defaulter.LessOrEqual(&cfg.EncoderConcurrency, defaultEncoderConcurrency)
defaulter.LessOrEqual(&cfg.MinReadShardsX, defaultMinReadShardsX)
defaulter.LessOrEqual(&cfg.ReadDataOnlyTimeoutMS, 3*1000)
defaulter.LessOrEqual(&cfg.LogSlowBaseTimeMS, 500)
defaulter.Equal(&cfg.LogSlowBaseSpeedKB, 1<<10)
defaulter.LessOrEqual(&cfg.LogSlowTimeFator, float32(2.0))
defaulter.IntegerLess(&cfg.DeleteIntoShardnodePercentage, 0)
if cfg.DeleteIntoShardnodePercentage > 100 {
cfg.DeleteIntoShardnodePercentage = 100
}
defaulter.IntegerLess(&cfg.RepairIntoShardnodePercentage, 0)
if cfg.RepairIntoShardnodePercentage > 100 {
cfg.RepairIntoShardnodePercentage = 100
}
defaulter.LessOrEqual(&cfg.ClusterConfig.CMClientConfig.Config.ClientTimeoutMs, defaultTimeoutClusterMgr)
defaulter.LessOrEqual(&cfg.BlobnodeConfig.ClientTimeoutMs, defaultTimeoutBlobnode)
@ -292,60 +223,39 @@ func confCheck(cfg *StreamConfig) error {
defaulter.LessOrEqual(&hc.SleepWindow, defaultBlobnodeSleepWindow)
defaulter.LessOrEqual(&hc.ErrorPercentThreshold, defaultBlobnodeErrorPercentThreshold)
cfg.RWCommandConfig = hc
return nil
}
// NewStreamHandler returns a stream handler
func NewStreamHandler(cfg *StreamConfig, stopCh <-chan struct{}) (h StreamHandler, e error) {
if e = confCheck(cfg); e != nil {
return nil, e
}
func NewStreamHandler(cfg *StreamConfig, stopCh <-chan struct{}) StreamHandler {
confCheck(cfg)
proxyClient := proxy.New(&cfg.ProxyConfig)
clusterController, err := controller.NewClusterController(&cfg.ClusterConfig, proxyClient, stopCh)
if err != nil {
e = errors.Newf("new cluster controller failed, err: %v", err)
return
log.Fatalf("new cluster controller failed, err: %v", err)
}
handler := &Handler{
memPool: resourcepool.NewMemPool(cfg.MemPoolSizeClasses),
clusterController: clusterController,
blobnodeClient: blobnode.New(&cfg.BlobnodeConfig),
proxyClient: proxyClient,
shardnodeClient: shardnode.NewNonsupportShardnode(),
blobnodeClient: blobnode.New(&cfg.BlobnodeConfig),
proxyClient: proxyClient,
maxObjectSize: defaultMaxObjectSize,
StreamConfig: *cfg,
}
if cfg.ShardnodeConfig != nil { // enable shard node
// Do not use rpc retry, because the stream blob handles retries itself
defaulter.LessOrEqual(&cfg.ShardnodeConfig.Config.Retry, int(1))
handler.shardnodeClient = shardnode.New(cfg.ShardnodeConfig.Config)
} else { // disable write delete/repair msg to shardnode
handler.StreamConfig.DeleteIntoShardnodePercentage = 0
handler.StreamConfig.RepairIntoShardnodePercentage = 0
}
if err = clustermgr.LoadExtendCodemode(context.Background(), handler.clusterController); err != nil {
e = errors.Newf("load extend codemode failed, err: %+v", err)
return
}
rawCodeModePolicies, err := handler.clusterController.GetConfig(context.Background(), proto.CodeModeConfigKey)
if err != nil {
e = errors.Newf("get codemode policy from cluster manager failed, err: %+v", err)
return
log.Fatal("get codemode policy from cluster manager failed, err: ", err)
}
codeModePolicies := make([]codemode.Policy, 0)
err = json.Unmarshal([]byte(rawCodeModePolicies), &codeModePolicies)
if err != nil {
e = errors.Newf("json decode codemode policy failed, err: %+v", err)
return
log.Fatal("json decode codemode policy failed, err: ", err)
}
if len(codeModePolicies) <= 0 {
e = errors.Newf("invalid codemode policy raw: %s", rawCodeModePolicies)
return
log.Fatal("invalid codemode policy raw: ", rawCodeModePolicies)
}
allCodeModes := make(CodeModePairs)
@ -367,8 +277,7 @@ func NewStreamHandler(cfg *StreamConfig, stopCh <-chan struct{}) (h StreamHandle
Concurrency: cfg.EncoderConcurrency,
})
if err != nil {
e = errors.Newf("new encoder failed, err: %v", err)
return
log.Fatalf("new encoder failed, err: %v", err)
}
encoders[codeMode] = encoder
}
@ -384,29 +293,22 @@ func NewStreamHandler(cfg *StreamConfig, stopCh <-chan struct{}) (h StreamHandle
handler.discardVidChan = make(chan discardVid, 8)
handler.stopCh = stopCh
handler.loopDiscardVids()
return handler, nil
return handler
}
// Delete delete all blobs in this location
func (h *Handler) Delete(ctx context.Context, location *proto.Location) error {
func (h *Handler) Delete(ctx context.Context, location *access.Location) error {
span := trace.SpanFromContextSafe(ctx)
if h.DeleteIntoShardnodePercentage > 0 {
percentage := (atomic.AddInt64(&h.deleteRoundrobin, 1) % 100) + 1
if percentage <= h.DeleteIntoShardnodePercentage {
span.Debugf("to delete into shardnode %+v", location)
return h.clearGarbageIntoShardnode(ctx, location)
}
}
span.Debugf("to delete into proxy %+v", location)
span.Debugf("to delete %+v", location)
return h.clearGarbage(ctx, location)
}
// Admin returns internal admin interface.
func (h *Handler) Admin() any {
return &StreamAdmin{
Config: h.StreamConfig,
MemPool: h.memPool,
Controller: h.clusterController,
func (h *Handler) Admin() interface{} {
return &streamAdmin{
config: h.StreamConfig,
memPool: h.memPool,
controller: h.clusterController,
}
}
@ -420,15 +322,6 @@ func (h *Handler) sendRepairMsg(ctx context.Context, blob blobIdent, badIdxes []
span := trace.SpanFromContextSafe(ctx)
span.Infof("to repair %s indexes(%+v)", blob.String(), badIdxes)
if h.RepairIntoShardnodePercentage > 0 {
percentage := (atomic.AddInt64(&h.repairRoundrobin, 1) % 100) + 1
if percentage <= h.RepairIntoShardnodePercentage {
span.Debugf("to repair into shardnode %s", blob.String())
h.sendRepairMsgIntoShardnode(ctx, blob, badIdxes)
return
}
}
clusterID := blob.cid
serviceController, err := h.clusterController.GetServiceController(clusterID)
if err != nil {
@ -445,30 +338,24 @@ func (h *Handler) sendRepairMsg(ctx context.Context, blob blobIdent, badIdxes []
Reason: "access-repair",
}
hosts, err := serviceController.GetServiceHosts(ctx, serviceProxy)
if err != nil {
span.Error(errors.Detail(err))
return
}
for len(hosts) < 3 {
hosts = append(hosts, hosts...)
}
if err := retry.Timed(3, 200).On(func() error {
host := hosts[0]
hosts = hosts[1:]
host, err := serviceController.GetServiceHost(ctx, serviceProxy)
if err != nil {
span.Warn(err)
return err
}
err = h.proxyClient.SendShardRepairMsg(ctx, host, repairArgs)
if err != nil {
if errorTimeout(err) || errorConnectionRefused(err) {
serviceController.PunishServiceWithThreshold(ctx, serviceProxy, host, h.ServicePunishIntervalS)
reportUnhealth(clusterID, "punish", serviceProxy, host, "failed")
} else {
reportUnhealth(clusterID, "repair.msg", serviceProxy, host, "failed")
}
span.Warnf("send to %s repair message(%+v) %s", host, repairArgs, err.Error())
reportUnhealth(clusterID, "punish", serviceProxy, host, "failed")
err = errors.Base(err, host)
}
return err
}); err != nil {
reportUnhealth(clusterID, "repair.msg", serviceProxy, "-", "failed")
span.Errorf("send repair message(%+v) failed %s", repairArgs, errors.Detail(err))
return
}
@ -476,7 +363,7 @@ func (h *Handler) sendRepairMsg(ctx context.Context, blob blobIdent, badIdxes []
span.Infof("send repair message(%+v)", repairArgs)
}
func (h *Handler) clearGarbage(ctx context.Context, location *proto.Location) error {
func (h *Handler) clearGarbage(ctx context.Context, location *access.Location) error {
span := trace.SpanFromContextSafe(ctx)
serviceController, err := h.clusterController.GetServiceController(location.ClusterID)
if err != nil {
@ -497,34 +384,28 @@ func (h *Handler) clearGarbage(ctx context.Context, location *proto.Location) er
})
}
var logMsg any = location
var logMsg interface{} = location
if len(deleteArgs.Blobs) <= 20 {
logMsg = deleteArgs
}
hosts, err := serviceController.GetServiceHosts(ctx, serviceProxy)
if err != nil {
span.Error(err)
return err
}
for len(hosts) < 3 {
hosts = append(hosts, hosts...)
}
if err := retry.Timed(3, 200).On(func() error {
host := hosts[0]
hosts = hosts[1:]
host, err := serviceController.GetServiceHost(ctx, serviceProxy)
if err != nil {
span.Warn(err)
return err
}
err = h.proxyClient.SendDeleteMsg(ctx, host, deleteArgs)
if err != nil {
if errorTimeout(err) || errorConnectionRefused(err) {
serviceController.PunishServiceWithThreshold(ctx, serviceProxy, host, h.ServicePunishIntervalS)
reportUnhealth(location.ClusterID, "punish", serviceProxy, host, "failed")
} else {
reportUnhealth(location.ClusterID, "delete.msg", serviceProxy, host, "failed")
}
span.Warnf("send to %s delete message(%+v) %s", host, logMsg, err.Error())
reportUnhealth(location.ClusterID, "punish", serviceProxy, host, "failed")
err = errors.Base(err, host)
}
return err
}); err != nil {
reportUnhealth(location.ClusterID, "delete.msg", serviceProxy, "-", "failed")
span.Errorf("send delete message(%+v) failed %s", logMsg, errors.Detail(err))
return errors.Base(err, "send delete message:", logMsg)
}
@ -539,25 +420,19 @@ func (h *Handler) getVolume(ctx context.Context, clusterID proto.ClusterID, vid
if err != nil {
return nil, err
}
volume := volumeGetter.Get(ctx, vid, isCache)
if volume == nil {
return nil, errors.Newf("not found volume of (%d %d)", clusterID, vid)
}
return volume, nil
}
func (h *Handler) updateVolume(ctx context.Context, clusterID proto.ClusterID, vid proto.Vid) {
volumeGetter, err := h.clusterController.GetVolumeGetter(clusterID)
if err != nil {
return
}
volumeGetter.Update(ctx, vid)
return volume, nil
}
func (h *Handler) punishVolume(ctx context.Context, clusterID proto.ClusterID, vid proto.Vid, host, reason string) {
reportUnhealth(clusterID, "punish", "volume", host, reason)
if volumeGetter, err := h.clusterController.GetVolumeGetter(clusterID); err == nil {
volumeGetter.Punish(ctx, vid, h.VolumePunishIntervalS)
volumeGetter.Punish(ctx, vid, h.DiskPunishIntervalS)
}
}
@ -575,18 +450,16 @@ func (h *Handler) punishDiskWith(ctx context.Context, clusterID proto.ClusterID,
}
}
func (h *Handler) punishShardnodeDisk(ctx context.Context, clusterID proto.ClusterID, diskID proto.DiskID, host, reason string) {
reportUnhealth(clusterID, "punish", "shardnode", host, reason)
if serviceController, err := h.clusterController.GetServiceController(clusterID); err == nil {
serviceController.PunishShardnode(ctx, diskID, h.ShardnodePunishIntervalS)
}
// blobCount blobSize > 0 is certain
func blobCount(size uint64, blobSize uint32) uint64 {
return (size + uint64(blobSize) - 1) / uint64(blobSize)
}
func (h *Handler) punishShardnodeDiskWith(ctx context.Context, clusterID proto.ClusterID, diskID proto.DiskID, host, reason string) {
reportUnhealth(clusterID, "punish", "shardnode", host, reason)
if serviceController, err := h.clusterController.GetServiceController(clusterID); err == nil {
serviceController.PunishShardnodeDiskWithThreshold(ctx, diskID, h.ShardnodePunishIntervalS)
func minU64(a, b uint64) uint64 {
if a <= b {
return a
}
return b
}
func errorTimeout(err error) bool {

View File

@ -1,608 +0,0 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/cubefs/cubefs/blobstore/access/controller (interfaces: ClusterController,ServiceController,VolumeGetter,IShardController,Shard)
// Package stream is a generated GoMock package.
package stream
import (
context "context"
reflect "reflect"
controller "github.com/cubefs/cubefs/blobstore/access/controller"
access "github.com/cubefs/cubefs/blobstore/api/access"
clustermgr "github.com/cubefs/cubefs/blobstore/api/clustermgr"
shardnode "github.com/cubefs/cubefs/blobstore/api/shardnode"
proto "github.com/cubefs/cubefs/blobstore/common/proto"
sharding "github.com/cubefs/cubefs/blobstore/common/sharding"
gomock "github.com/golang/mock/gomock"
)
// MockClusterController is a mock of ClusterController interface.
type MockClusterController struct {
ctrl *gomock.Controller
recorder *MockClusterControllerMockRecorder
}
// MockClusterControllerMockRecorder is the mock recorder for MockClusterController.
type MockClusterControllerMockRecorder struct {
mock *MockClusterController
}
// NewMockClusterController creates a new mock instance.
func NewMockClusterController(ctrl *gomock.Controller) *MockClusterController {
mock := &MockClusterController{ctrl: ctrl}
mock.recorder = &MockClusterControllerMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockClusterController) EXPECT() *MockClusterControllerMockRecorder {
return m.recorder
}
// All mocks base method.
func (m *MockClusterController) All() []*clustermgr.ClusterInfo {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "All")
ret0, _ := ret[0].([]*clustermgr.ClusterInfo)
return ret0
}
// All indicates an expected call of All.
func (mr *MockClusterControllerMockRecorder) All() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "All", reflect.TypeOf((*MockClusterController)(nil).All))
}
// ChangeChooseAlg mocks base method.
func (m *MockClusterController) ChangeChooseAlg(arg0 controller.AlgChoose) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ChangeChooseAlg", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// ChangeChooseAlg indicates an expected call of ChangeChooseAlg.
func (mr *MockClusterControllerMockRecorder) ChangeChooseAlg(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChangeChooseAlg", reflect.TypeOf((*MockClusterController)(nil).ChangeChooseAlg), arg0)
}
// ChooseOne mocks base method.
func (m *MockClusterController) ChooseOne() (*clustermgr.ClusterInfo, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ChooseOne")
ret0, _ := ret[0].(*clustermgr.ClusterInfo)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// ChooseOne indicates an expected call of ChooseOne.
func (mr *MockClusterControllerMockRecorder) ChooseOne() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChooseOne", reflect.TypeOf((*MockClusterController)(nil).ChooseOne))
}
// GetConfig mocks base method.
func (m *MockClusterController) GetConfig(arg0 context.Context, arg1 string) (string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetConfig", arg0, arg1)
ret0, _ := ret[0].(string)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetConfig indicates an expected call of GetConfig.
func (mr *MockClusterControllerMockRecorder) GetConfig(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetConfig", reflect.TypeOf((*MockClusterController)(nil).GetConfig), arg0, arg1)
}
// GetServiceController mocks base method.
func (m *MockClusterController) GetServiceController(arg0 proto.ClusterID) (controller.ServiceController, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetServiceController", arg0)
ret0, _ := ret[0].(controller.ServiceController)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetServiceController indicates an expected call of GetServiceController.
func (mr *MockClusterControllerMockRecorder) GetServiceController(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetServiceController", reflect.TypeOf((*MockClusterController)(nil).GetServiceController), arg0)
}
// GetShardController mocks base method.
func (m *MockClusterController) GetShardController(arg0 proto.ClusterID) (controller.IShardController, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetShardController", arg0)
ret0, _ := ret[0].(controller.IShardController)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetShardController indicates an expected call of GetShardController.
func (mr *MockClusterControllerMockRecorder) GetShardController(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetShardController", reflect.TypeOf((*MockClusterController)(nil).GetShardController), arg0)
}
// GetVolumeGetter mocks base method.
func (m *MockClusterController) GetVolumeGetter(arg0 proto.ClusterID) (controller.VolumeGetter, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetVolumeGetter", arg0)
ret0, _ := ret[0].(controller.VolumeGetter)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetVolumeGetter indicates an expected call of GetVolumeGetter.
func (mr *MockClusterControllerMockRecorder) GetVolumeGetter(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumeGetter", reflect.TypeOf((*MockClusterController)(nil).GetVolumeGetter), arg0)
}
// Region mocks base method.
func (m *MockClusterController) Region() string {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Region")
ret0, _ := ret[0].(string)
return ret0
}
// Region indicates an expected call of Region.
func (mr *MockClusterControllerMockRecorder) Region() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Region", reflect.TypeOf((*MockClusterController)(nil).Region))
}
// MockServiceController is a mock of ServiceController interface.
type MockServiceController struct {
ctrl *gomock.Controller
recorder *MockServiceControllerMockRecorder
}
// MockServiceControllerMockRecorder is the mock recorder for MockServiceController.
type MockServiceControllerMockRecorder struct {
mock *MockServiceController
}
// NewMockServiceController creates a new mock instance.
func NewMockServiceController(ctrl *gomock.Controller) *MockServiceController {
mock := &MockServiceController{ctrl: ctrl}
mock.recorder = &MockServiceControllerMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockServiceController) EXPECT() *MockServiceControllerMockRecorder {
return m.recorder
}
// GetDiskHost mocks base method.
func (m *MockServiceController) GetDiskHost(arg0 context.Context, arg1 proto.DiskID) (*controller.HostIDC, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetDiskHost", arg0, arg1)
ret0, _ := ret[0].(*controller.HostIDC)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetDiskHost indicates an expected call of GetDiskHost.
func (mr *MockServiceControllerMockRecorder) GetDiskHost(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDiskHost", reflect.TypeOf((*MockServiceController)(nil).GetDiskHost), arg0, arg1)
}
// GetServiceHost mocks base method.
func (m *MockServiceController) GetServiceHost(arg0 context.Context, arg1 string) (string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetServiceHost", arg0, arg1)
ret0, _ := ret[0].(string)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetServiceHost indicates an expected call of GetServiceHost.
func (mr *MockServiceControllerMockRecorder) GetServiceHost(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetServiceHost", reflect.TypeOf((*MockServiceController)(nil).GetServiceHost), arg0, arg1)
}
// GetServiceHosts mocks base method.
func (m *MockServiceController) GetServiceHosts(arg0 context.Context, arg1 string) ([]string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetServiceHosts", arg0, arg1)
ret0, _ := ret[0].([]string)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetServiceHosts indicates an expected call of GetServiceHosts.
func (mr *MockServiceControllerMockRecorder) GetServiceHosts(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetServiceHosts", reflect.TypeOf((*MockServiceController)(nil).GetServiceHosts), arg0, arg1)
}
// GetShardnodeHost mocks base method.
func (m *MockServiceController) GetShardnodeHost(arg0 context.Context, arg1 proto.DiskID) (*controller.HostIDC, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetShardnodeHost", arg0, arg1)
ret0, _ := ret[0].(*controller.HostIDC)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetShardnodeHost indicates an expected call of GetShardnodeHost.
func (mr *MockServiceControllerMockRecorder) GetShardnodeHost(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetShardnodeHost", reflect.TypeOf((*MockServiceController)(nil).GetShardnodeHost), arg0, arg1)
}
// IsPunishShardnode mocks base method.
func (m *MockServiceController) IsPunishShardnode(arg0 proto.DiskID) bool {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "IsPunishShardnode", arg0)
ret0, _ := ret[0].(bool)
return ret0
}
// IsPunishShardnode indicates an expected call of IsPunishShardnode.
func (mr *MockServiceControllerMockRecorder) IsPunishShardnode(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsPunishShardnode", reflect.TypeOf((*MockServiceController)(nil).IsPunishShardnode), arg0)
}
// PunishDisk mocks base method.
func (m *MockServiceController) PunishDisk(arg0 context.Context, arg1 proto.DiskID, arg2 int) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "PunishDisk", arg0, arg1, arg2)
}
// PunishDisk indicates an expected call of PunishDisk.
func (mr *MockServiceControllerMockRecorder) PunishDisk(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PunishDisk", reflect.TypeOf((*MockServiceController)(nil).PunishDisk), arg0, arg1, arg2)
}
// PunishDiskWithThreshold mocks base method.
func (m *MockServiceController) PunishDiskWithThreshold(arg0 context.Context, arg1 proto.DiskID, arg2 int) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "PunishDiskWithThreshold", arg0, arg1, arg2)
}
// PunishDiskWithThreshold indicates an expected call of PunishDiskWithThreshold.
func (mr *MockServiceControllerMockRecorder) PunishDiskWithThreshold(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PunishDiskWithThreshold", reflect.TypeOf((*MockServiceController)(nil).PunishDiskWithThreshold), arg0, arg1, arg2)
}
// PunishService mocks base method.
func (m *MockServiceController) PunishService(arg0 context.Context, arg1, arg2 string, arg3 int) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "PunishService", arg0, arg1, arg2, arg3)
}
// PunishService indicates an expected call of PunishService.
func (mr *MockServiceControllerMockRecorder) PunishService(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PunishService", reflect.TypeOf((*MockServiceController)(nil).PunishService), arg0, arg1, arg2, arg3)
}
// PunishServiceWithThreshold mocks base method.
func (m *MockServiceController) PunishServiceWithThreshold(arg0 context.Context, arg1, arg2 string, arg3 int) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "PunishServiceWithThreshold", arg0, arg1, arg2, arg3)
}
// PunishServiceWithThreshold indicates an expected call of PunishServiceWithThreshold.
func (mr *MockServiceControllerMockRecorder) PunishServiceWithThreshold(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PunishServiceWithThreshold", reflect.TypeOf((*MockServiceController)(nil).PunishServiceWithThreshold), arg0, arg1, arg2, arg3)
}
// PunishShardnode mocks base method.
func (m *MockServiceController) PunishShardnode(arg0 context.Context, arg1 proto.DiskID, arg2 int) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "PunishShardnode", arg0, arg1, arg2)
}
// PunishShardnode indicates an expected call of PunishShardnode.
func (mr *MockServiceControllerMockRecorder) PunishShardnode(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PunishShardnode", reflect.TypeOf((*MockServiceController)(nil).PunishShardnode), arg0, arg1, arg2)
}
// PunishShardnodeDiskWithThreshold mocks base method.
func (m *MockServiceController) PunishShardnodeDiskWithThreshold(arg0 context.Context, arg1 proto.DiskID, arg2 int) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "PunishShardnodeDiskWithThreshold", arg0, arg1, arg2)
}
// PunishShardnodeDiskWithThreshold indicates an expected call of PunishShardnodeDiskWithThreshold.
func (mr *MockServiceControllerMockRecorder) PunishShardnodeDiskWithThreshold(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PunishShardnodeDiskWithThreshold", reflect.TypeOf((*MockServiceController)(nil).PunishShardnodeDiskWithThreshold), arg0, arg1, arg2)
}
// MockVolumeGetter is a mock of VolumeGetter interface.
type MockVolumeGetter struct {
ctrl *gomock.Controller
recorder *MockVolumeGetterMockRecorder
}
// MockVolumeGetterMockRecorder is the mock recorder for MockVolumeGetter.
type MockVolumeGetterMockRecorder struct {
mock *MockVolumeGetter
}
// NewMockVolumeGetter creates a new mock instance.
func NewMockVolumeGetter(ctrl *gomock.Controller) *MockVolumeGetter {
mock := &MockVolumeGetter{ctrl: ctrl}
mock.recorder = &MockVolumeGetterMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockVolumeGetter) EXPECT() *MockVolumeGetterMockRecorder {
return m.recorder
}
// Get mocks base method.
func (m *MockVolumeGetter) Get(arg0 context.Context, arg1 proto.Vid, arg2 bool) *controller.VolumePhy {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Get", arg0, arg1, arg2)
ret0, _ := ret[0].(*controller.VolumePhy)
return ret0
}
// Get indicates an expected call of Get.
func (mr *MockVolumeGetterMockRecorder) Get(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockVolumeGetter)(nil).Get), arg0, arg1, arg2)
}
// Punish mocks base method.
func (m *MockVolumeGetter) Punish(arg0 context.Context, arg1 proto.Vid, arg2 int) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "Punish", arg0, arg1, arg2)
}
// Punish indicates an expected call of Punish.
func (mr *MockVolumeGetterMockRecorder) Punish(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Punish", reflect.TypeOf((*MockVolumeGetter)(nil).Punish), arg0, arg1, arg2)
}
// Update mocks base method.
func (m *MockVolumeGetter) Update(arg0 context.Context, arg1 proto.Vid) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "Update", arg0, arg1)
}
// Update indicates an expected call of Update.
func (mr *MockVolumeGetterMockRecorder) Update(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockVolumeGetter)(nil).Update), arg0, arg1)
}
// MockShardController is a mock of IShardController interface.
type MockShardController struct {
ctrl *gomock.Controller
recorder *MockShardControllerMockRecorder
}
// MockShardControllerMockRecorder is the mock recorder for MockShardController.
type MockShardControllerMockRecorder struct {
mock *MockShardController
}
// NewMockShardController creates a new mock instance.
func NewMockShardController(ctrl *gomock.Controller) *MockShardController {
mock := &MockShardController{ctrl: ctrl}
mock.recorder = &MockShardControllerMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockShardController) EXPECT() *MockShardControllerMockRecorder {
return m.recorder
}
// GetFisrtShard mocks base method.
func (m *MockShardController) GetFisrtShard(arg0 context.Context) (controller.Shard, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetFisrtShard", arg0)
ret0, _ := ret[0].(controller.Shard)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetFisrtShard indicates an expected call of GetFisrtShard.
func (mr *MockShardControllerMockRecorder) GetFisrtShard(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFisrtShard", reflect.TypeOf((*MockShardController)(nil).GetFisrtShard), arg0)
}
// GetNextShard mocks base method.
func (m *MockShardController) GetNextShard(arg0 context.Context, arg1 sharding.Range) (controller.Shard, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetNextShard", arg0, arg1)
ret0, _ := ret[0].(controller.Shard)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetNextShard indicates an expected call of GetNextShard.
func (mr *MockShardControllerMockRecorder) GetNextShard(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNextShard", reflect.TypeOf((*MockShardController)(nil).GetNextShard), arg0, arg1)
}
// GetShard mocks base method.
func (m *MockShardController) GetShard(arg0 context.Context, arg1 []string) (controller.Shard, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetShard", arg0, arg1)
ret0, _ := ret[0].(controller.Shard)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetShard indicates an expected call of GetShard.
func (mr *MockShardControllerMockRecorder) GetShard(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetShard", reflect.TypeOf((*MockShardController)(nil).GetShard), arg0, arg1)
}
// GetShardByID mocks base method.
func (m *MockShardController) GetShardByID(arg0 context.Context, arg1 proto.ShardID) (controller.Shard, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetShardByID", arg0, arg1)
ret0, _ := ret[0].(controller.Shard)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetShardByID indicates an expected call of GetShardByID.
func (mr *MockShardControllerMockRecorder) GetShardByID(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetShardByID", reflect.TypeOf((*MockShardController)(nil).GetShardByID), arg0, arg1)
}
// GetShardByRange mocks base method.
func (m *MockShardController) GetShardByRange(arg0 context.Context, arg1 sharding.Range) (controller.Shard, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetShardByRange", arg0, arg1)
ret0, _ := ret[0].(controller.Shard)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetShardByRange indicates an expected call of GetShardByRange.
func (mr *MockShardControllerMockRecorder) GetShardByRange(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetShardByRange", reflect.TypeOf((*MockShardController)(nil).GetShardByRange), arg0, arg1)
}
// GetShardSubRangeCount mocks base method.
func (m *MockShardController) GetShardSubRangeCount(arg0 context.Context) int {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetShardSubRangeCount", arg0)
ret0, _ := ret[0].(int)
return ret0
}
// GetShardSubRangeCount indicates an expected call of GetShardSubRangeCount.
func (mr *MockShardControllerMockRecorder) GetShardSubRangeCount(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetShardSubRangeCount", reflect.TypeOf((*MockShardController)(nil).GetShardSubRangeCount), arg0)
}
// GetSpaceID mocks base method.
func (m *MockShardController) GetSpaceID() proto.SpaceID {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetSpaceID")
ret0, _ := ret[0].(proto.SpaceID)
return ret0
}
// GetSpaceID indicates an expected call of GetSpaceID.
func (mr *MockShardControllerMockRecorder) GetSpaceID() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSpaceID", reflect.TypeOf((*MockShardController)(nil).GetSpaceID))
}
// UpdateRoute mocks base method.
func (m *MockShardController) UpdateRoute(arg0 context.Context) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpdateRoute", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// UpdateRoute indicates an expected call of UpdateRoute.
func (mr *MockShardControllerMockRecorder) UpdateRoute(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateRoute", reflect.TypeOf((*MockShardController)(nil).UpdateRoute), arg0)
}
// UpdateShard mocks base method.
func (m *MockShardController) UpdateShard(arg0 context.Context, arg1 shardnode.ShardStats) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpdateShard", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// UpdateShard indicates an expected call of UpdateShard.
func (mr *MockShardControllerMockRecorder) UpdateShard(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateShard", reflect.TypeOf((*MockShardController)(nil).UpdateShard), arg0, arg1)
}
// MockShard is a mock of Shard interface.
type MockShard struct {
ctrl *gomock.Controller
recorder *MockShardMockRecorder
}
// MockShardMockRecorder is the mock recorder for MockShard.
type MockShardMockRecorder struct {
mock *MockShard
}
// NewMockShard creates a new mock instance.
func NewMockShard(ctrl *gomock.Controller) *MockShard {
mock := &MockShard{ctrl: ctrl}
mock.recorder = &MockShardMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockShard) EXPECT() *MockShardMockRecorder {
return m.recorder
}
// GetMember mocks base method.
func (m *MockShard) GetMember(arg0 context.Context, arg1 access.GetShardMode, arg2 map[proto.DiskID]struct{}) (controller.ShardOpInfo, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetMember", arg0, arg1, arg2)
ret0, _ := ret[0].(controller.ShardOpInfo)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetMember indicates an expected call of GetMember.
func (mr *MockShardMockRecorder) GetMember(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMember", reflect.TypeOf((*MockShard)(nil).GetMember), arg0, arg1, arg2)
}
// GetRange mocks base method.
func (m *MockShard) GetRange() sharding.Range {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetRange")
ret0, _ := ret[0].(sharding.Range)
return ret0
}
// GetRange indicates an expected call of GetRange.
func (mr *MockShardMockRecorder) GetRange() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRange", reflect.TypeOf((*MockShard)(nil).GetRange))
}
// GetShardID mocks base method.
func (m *MockShard) GetShardID() proto.ShardID {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetShardID")
ret0, _ := ret[0].(proto.ShardID)
return ret0
}
// GetShardID indicates an expected call of GetShardID.
func (mr *MockShardMockRecorder) GetShardID() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetShardID", reflect.TypeOf((*MockShard)(nil).GetShardID))
}

View File

@ -1,784 +0,0 @@
package stream
import (
"context"
"fmt"
"sync/atomic"
"time"
"github.com/cubefs/cubefs/blobstore/access/controller"
acapi "github.com/cubefs/cubefs/blobstore/api/access"
"github.com/cubefs/cubefs/blobstore/api/shardnode"
errcode "github.com/cubefs/cubefs/blobstore/common/errors"
"github.com/cubefs/cubefs/blobstore/common/proto"
"github.com/cubefs/cubefs/blobstore/common/rpc"
"github.com/cubefs/cubefs/blobstore/common/sharding"
"github.com/cubefs/cubefs/blobstore/common/trace"
"github.com/cubefs/cubefs/blobstore/util/errors"
"github.com/cubefs/cubefs/blobstore/util/retry"
)
func (h *Handler) GetBlob(ctx context.Context, args *acapi.GetBlobArgs) (*proto.Location, error) {
span := trace.SpanFromContextSafe(ctx)
span.Debugf("get blob args:%+v", *args)
var blob shardnode.GetBlobRet
rerr := retry.ExponentialBackoff(3, 200).RuptOn(func() (bool, error) {
header, err := h.getShardOpHeader(ctx, acapi.GetShardCommonArgs{
ClusterID: args.ClusterID,
BlobName: args.BlobName,
Mode: args.Mode,
})
if err != nil {
return true, err // not retry
}
host, err := h.getShardHost(ctx, args.ClusterID, header.DiskID)
if err != nil {
return true, err
}
blob, err = h.shardnodeClient.GetBlob(ctx, host, shardnode.GetBlobArgs{
Header: header,
Name: args.BlobName,
})
if err != nil {
return h.punishAndUpdate(ctx, &punishArgs{
ShardOpHeader: header,
clusterID: args.ClusterID,
host: host,
mode: args.Mode,
err: err,
})
}
return true, nil
})
if rerr != nil {
span.Errorf("get blob failed, args:%+v, err:%+v", *args, rerr)
}
return &blob.Blob.Location, rerr
}
func (h *Handler) CreateBlob(ctx context.Context, args *acapi.CreateBlobArgs) (*proto.Location, error) {
span := trace.SpanFromContextSafe(ctx)
span.Debugf("create blob args:%+v", *args)
err := h.fixCreateBlobArgs(ctx, args)
if err != nil {
return nil, err
}
var blob shardnode.CreateBlobRet
rerr := retry.ExponentialBackoff(3, 200).RuptOn(func() (bool, error) {
header, err := h.getShardOpHeader(ctx, acapi.GetShardCommonArgs{
ClusterID: args.ClusterID,
BlobName: args.BlobName,
Mode: acapi.GetShardModeLeader,
})
if err != nil {
return true, err
}
host, err := h.getShardHost(ctx, args.ClusterID, header.DiskID)
if err != nil {
return true, err
}
blob, err = h.shardnodeClient.CreateBlob(ctx, host, shardnode.CreateBlobArgs{
Header: header,
Name: args.BlobName,
CodeMode: args.CodeMode,
Size_: args.Size,
SliceSize: args.SliceSize,
})
if err != nil {
return h.punishAndUpdate(ctx, &punishArgs{
ShardOpHeader: header,
clusterID: args.ClusterID,
host: host,
mode: acapi.GetShardModeLeader,
err: err,
})
}
return true, nil
})
if rerr != nil {
span.Errorf("create blob failed, args:%+v, err:%+v", *args, rerr)
}
return &blob.Blob.Location, rerr
}
func (h *Handler) DeleteBlob(ctx context.Context, args *acapi.DelBlobArgs) error {
span := trace.SpanFromContextSafe(ctx)
span.Debugf("delete blob args:%+v", *args)
rerr := retry.ExponentialBackoff(3, 200).RuptOn(func() (bool, error) {
header, err := h.getShardOpHeader(ctx, acapi.GetShardCommonArgs{
ClusterID: args.ClusterID,
BlobName: args.BlobName,
Mode: acapi.GetShardModeLeader,
})
if err != nil {
return true, err
}
host, err := h.getShardHost(ctx, args.ClusterID, header.DiskID)
if err != nil {
return true, err
}
if err = h.shardnodeClient.DeleteBlob(ctx, host, shardnode.DeleteBlobArgs{
Header: header,
Name: args.BlobName,
}); err != nil {
return h.punishAndUpdate(ctx, &punishArgs{
ShardOpHeader: header,
clusterID: args.ClusterID,
host: host,
mode: acapi.GetShardModeLeader,
err: err,
})
}
return true, nil
})
if rerr != nil {
span.Errorf("delete blob failed, args:%+v, err:%+v", *args, rerr)
}
return rerr
}
func (h *Handler) SealBlob(ctx context.Context, args *acapi.SealBlobArgs) error {
span := trace.SpanFromContextSafe(ctx)
span.Debugf("seal blob args:%+v", *args)
rerr := retry.ExponentialBackoff(3, 200).RuptOn(func() (bool, error) {
header, err := h.getShardOpHeader(ctx, acapi.GetShardCommonArgs{
ClusterID: args.ClusterID,
BlobName: args.BlobName,
Mode: acapi.GetShardModeLeader,
})
if err != nil {
return true, err
}
host, err := h.getShardHost(ctx, args.ClusterID, header.DiskID)
if err != nil {
return true, err
}
err = h.shardnodeClient.SealBlob(ctx, host, shardnode.SealBlobArgs{
Header: header,
Name: args.BlobName,
Size_: args.Size,
Slices: args.Slices,
})
if err != nil {
return h.punishAndUpdate(ctx, &punishArgs{
ShardOpHeader: header,
clusterID: args.ClusterID,
host: host,
mode: acapi.GetShardModeLeader,
err: err,
})
}
return true, nil
})
if rerr != nil {
span.Errorf("seal blob failed, args:%+v, err:%+v", *args, rerr)
}
return rerr
}
func (h *Handler) ListBlob(ctx context.Context, args *acapi.ListBlobArgs) (ret shardnode.ListBlobRet, err error) {
span := trace.SpanFromContextSafe(ctx)
span.Debugf("list blob args:%+v", *args)
defer func() {
if err != nil {
span.Errorf("list blob failed, args:%+v, err:%+v", *args, err)
}
}()
if args.ShardID != 0 {
return h.listSpecificShard(ctx, args)
}
return h.listManyShards(ctx, args)
}
func (h *Handler) AllocSlice(ctx context.Context, args *acapi.AllocSliceArgs) (shardnode.AllocSliceRet, error) {
span := trace.SpanFromContextSafe(ctx)
span.Debugf("alloc blob args:%+v", *args)
var slices shardnode.AllocSliceRet
rerr := retry.ExponentialBackoff(3, 200).RuptOn(func() (bool, error) {
header, err := h.getShardOpHeader(ctx, acapi.GetShardCommonArgs{
ClusterID: args.ClusterID,
BlobName: args.BlobName,
Mode: acapi.GetShardModeLeader,
})
if err != nil {
return true, err
}
host, err := h.getShardHost(ctx, args.ClusterID, header.DiskID)
if err != nil {
return true, err
}
slices, err = h.shardnodeClient.AllocSlice(ctx, host, shardnode.AllocSliceArgs{
Header: header,
Name: args.BlobName,
CodeMode: args.CodeMode,
Size_: args.Size,
FailedSlice: args.FailSlice,
})
if err != nil {
return h.punishAndUpdate(ctx, &punishArgs{
ShardOpHeader: header,
clusterID: args.ClusterID,
host: host,
mode: acapi.GetShardModeLeader,
err: err,
})
}
return true, nil
})
if rerr != nil {
span.Errorf("alloc slice failed, args:%+v, err:%+v", *args, rerr)
}
return slices, rerr
}
func (h *Handler) listSpecificShard(ctx context.Context, args *acapi.ListBlobArgs) (shardnode.ListBlobRet, error) {
span := trace.SpanFromContextSafe(ctx)
var ret shardnode.ListBlobRet
rerr := retry.ExponentialBackoff(3, 200).RuptOn(func() (bool, error) {
header, err := h.getOpHeaderByID(ctx, args.ClusterID, args.ShardID, args.Mode)
if err != nil {
return true, err
}
interrupt := false
ret, interrupt, err = h.listSingleShardEnough(ctx, args, header)
span.Debugf("list blob, shardID=%d, interrupt:%t, length:%d, err:%+v", args.ShardID, interrupt, len(ret.Blobs), err)
if err != nil {
return interrupt, err
}
return true, nil
})
return ret, rerr
}
func (h *Handler) listManyShards(ctx context.Context, args *acapi.ListBlobArgs) (shardnode.ListBlobRet, error) {
span := trace.SpanFromContextSafe(ctx)
shardMgr, err := h.clusterController.GetShardController(args.ClusterID)
if err != nil {
return shardnode.ListBlobRet{}, err
}
var (
shard controller.Shard
allBlob shardnode.ListBlobRet
)
if len(args.Marker) == 0 {
shard, err = shardMgr.GetFisrtShard(ctx)
if err != nil {
return shardnode.ListBlobRet{}, err
}
} else {
unionMarker := acapi.ListBlobEncodeMarker{}
if err = unionMarker.UnmarshalFromString(args.Marker); err != nil {
return shardnode.ListBlobRet{}, fmt.Errorf("fail to unmarshal marker, err: %+v", err)
}
allBlob.NextMarker = unionMarker.Marker
shard, err = shardMgr.GetShardByRange(ctx, unionMarker.Range)
if err != nil {
return shardnode.ListBlobRet{}, err
}
span.Debugf("list blob at multi shards, prefix=%s, range=%s, marker=%s", args.Prefix, unionMarker.Range.String(), unionMarker.Marker)
}
lastRange := shard.GetRange()
count := int64(args.Count)
for count > 0 {
var ret shardnode.ListBlobRet
interrupt := false
rerr := retry.ExponentialBackoff(3, 200).RuptOn(func() (bool, error) {
header, err := h.getOpHeaderByShard(ctx, shardMgr, shard, args.Mode)
if err != nil {
return interrupt, err
}
args.Marker = allBlob.NextMarker
args.Count = uint64(count)
ret, interrupt, err = h.listSingleShardEnough(ctx, args, header)
span.Debugf("list blob, shardID=%d, interrupt:%t, length:%d, err:%+v", args.ShardID, interrupt, len(ret.Blobs), err)
if err != nil {
return interrupt, err
}
return true, nil
})
if rerr != nil {
return shardnode.ListBlobRet{}, rerr
}
allBlob.Blobs = append(allBlob.Blobs, ret.Blobs...)
allBlob.NextMarker = ret.NextMarker
count -= int64(len(ret.Blobs))
if ret.NextMarker == "" {
shard, err = shardMgr.GetNextShard(ctx, lastRange)
if err != nil {
return shardnode.ListBlobRet{}, err
}
// err == nil && shard == nil, means last shard, reach end
if shard == nil {
lastRange = sharding.Range{}
break // reach end
}
lastRange = shard.GetRange()
}
}
// reach end, don't need marshal
if len(allBlob.NextMarker) == 0 && lastRange.Type == 0 {
return allBlob, nil
}
markers := acapi.ListBlobEncodeMarker{
Range: lastRange, // empty, means reach the end; else, means next expect shard
Marker: allBlob.NextMarker, // empty, means current shard list end; else, means expect begin blob name
}
unionMarker, err := markers.MarshalToString()
allBlob.NextMarker = unionMarker
return allBlob, err
}
func (h *Handler) listSingleShardEnough(ctx context.Context, args *acapi.ListBlobArgs, header shardnode.ShardOpHeader) (shardnode.ListBlobRet, bool, error) {
host, err := h.getShardHost(ctx, args.ClusterID, header.DiskID)
if err != nil {
return shardnode.ListBlobRet{}, true, err
}
ret, err := h.shardnodeClient.ListBlob(ctx, host, shardnode.ListBlobArgs{
Header: header,
Prefix: args.Prefix,
Marker: args.Marker,
Count: args.Count,
})
if err != nil {
interrupt, err1 := h.punishAndUpdate(ctx, &punishArgs{
ShardOpHeader: header,
clusterID: args.ClusterID,
host: host,
mode: args.Mode,
err: err,
})
return shardnode.ListBlobRet{}, interrupt, err1
}
return ret, true, nil
}
func (h *Handler) getShardOpHeader(ctx context.Context, args acapi.GetShardCommonArgs) (shardnode.ShardOpHeader, error) {
shardMgr, err := h.clusterController.GetShardController(args.ClusterID)
if err != nil {
return shardnode.ShardOpHeader{}, err
}
shardKeys := shardnode.DecodeShardKeys(args.BlobName, shardMgr.GetShardSubRangeCount(ctx))
shard, err := shardMgr.GetShard(ctx, shardKeys)
if err != nil {
return shardnode.ShardOpHeader{}, err
}
oh, err := h.getOpHeaderByShard(ctx, shardMgr, shard, args.Mode)
return oh, err
}
func (h *Handler) getOpHeaderByID(ctx context.Context, clusterID proto.ClusterID, shardID proto.ShardID, mode acapi.GetShardMode) (shardnode.ShardOpHeader, error) {
shardMgr, err := h.clusterController.GetShardController(clusterID)
if err != nil {
return shardnode.ShardOpHeader{}, err
}
shard, err := shardMgr.GetShardByID(ctx, shardID)
if err != nil {
return shardnode.ShardOpHeader{}, err
}
return h.getOpHeaderByShard(ctx, shardMgr, shard, mode)
}
func (h *Handler) getOpHeaderByShard(ctx context.Context, shardMgr controller.IShardController, shard controller.Shard,
mode acapi.GetShardMode,
) (shardnode.ShardOpHeader, error) {
span := trace.SpanFromContextSafe(ctx)
spaceID := shardMgr.GetSpaceID()
info, err := shard.GetMember(ctx, mode, nil)
if err != nil {
return shardnode.ShardOpHeader{}, err
}
oh := shardnode.ShardOpHeader{
SpaceID: spaceID,
DiskID: info.DiskID,
Suid: info.Suid,
RouteVersion: info.RouteVersion,
}
span.Debugf("shard op header: %+v", oh)
return oh, nil
}
func (h *Handler) getShardHost(ctx context.Context, clusterID proto.ClusterID, diskID proto.DiskID) (string, error) {
span := trace.SpanFromContextSafe(ctx)
s, err := h.clusterController.GetServiceController(clusterID)
if err != nil {
return "", err
}
hostInfo, err := s.GetShardnodeHost(ctx, diskID)
if err != nil {
return "", err
}
span.Debugf("get shard host:%+v", *hostInfo)
return hostInfo.Host, nil
}
type punishArgs struct {
shardnode.ShardOpHeader
clusterID proto.ClusterID
host string
mode acapi.GetShardMode
exclude map[proto.DiskID]struct{}
err error
}
func (h *Handler) punishAndUpdate(ctx context.Context, args *punishArgs) (bool, error) {
span := trace.SpanFromContextSafe(ctx)
// This error is coming from the shardnode interface, and we want to make sure that the error can be parsed into an error code
code := rpc.DetectStatusCode(args.err)
// leader disk status: normal->EIO->broken->repairing->repaired. it greater than broken will mark punished
// if bad disk(punished), we select another disk as leader; old leader is not in disk units, after update route(replace suid index unit)
// and then call sn return NoLeader, and we fetch and update new leader shard
// 1. old leader: repaired(eio, or status >= broken), sn will remove disk and return DiskNotFound, update route
// 2. old leader: broken(eio, broken, repairing), sn return DiskBroken
// cm catalog units is always correct, but its leaderDiskID may be wrong
switch code {
case errcode.CodeDiskBroken: // read shard at bad disk, but shard/disk is reparing
// if follow node broken disk, it will not election, just try again, change other shard;
// if leader node broken disk, it cant get shard stats, wait new leader
h.punishShardnodeDisk(ctx, args.clusterID, args.DiskID, args.host, "Broken")
if args.mode == acapi.GetShardModeLeader {
err1 := h.updateLeaderFromNewHost(ctx, args)
if err1 != nil {
span.Warnf("fail to change other shard node, cluster:%d, err:%+v", args.clusterID, err1)
}
}
return false, args.err
// update route and punish
case errcode.CodeShardNodeDiskNotFound: // read shard at bad disk, but old broken disk is repaired, all shard repaired
h.punishShardnodeDisk(ctx, args.clusterID, args.DiskID, args.host, "NotFound")
if err1 := h.updateShardRoute(ctx, args.clusterID); err1 != nil {
span.Warnf("fail to update shard route, cluster:%d, err:%+v", args.clusterID, err1)
}
return false, args.err
// update route
case errcode.CodeShardDoesNotExist, // intermediate state disk, not a final state; shard is removed, disk is repairing/repaired ; suid not match disk id
errcode.CodeShardRouteVersionNeedUpdate: // header op version less than shardnode version
if err1 := h.updateShardRoute(ctx, args.clusterID); err1 != nil {
span.Warnf("fail to update shard route, cluster:%d, err:%+v", args.clusterID, err1)
}
return false, args.err
// select master
case errcode.CodeShardNodeNotLeader: // leader disk id error when create/delete/seal
if err1 := h.updateLeaderFromNewHost(ctx, args); err1 != nil {
span.Warnf("fail to update leader and shard info, cluster:%d, err:%+v", args.clusterID, err1)
}
return false, args.err
default:
}
// err:dial tcp 127.0.0.1:9100: connect: connection refused code:500
if errorConnectionRefused(args.err) {
span.Warnf("shardnode connection refused/timeout, args:%+v, err:%+v", *args, args.err)
h.groupRun.Do("shardnode-leader-"+args.DiskID.ToString(), func() (interface{}, error) {
// must wait have master leader, block wait
h.punishShardnodeDisk(ctx, args.clusterID, args.DiskID, args.host, "Refused")
err1 := h.updateLeaderFromNewHost(ctx, args)
if err1 != nil {
span.Warnf("fail to change other shard node, cluster:%d, err:%+v", args.clusterID, err1)
}
return nil, err1
})
return false, errcode.ErrConnectionRefused
}
if errorTimeout(args.err) {
h.punishShardnodeDiskWith(ctx, args.clusterID, args.DiskID, args.host, "Timeout")
return false, args.err
}
// eio or other error; if shardNode restarts quickly so wait for it to start, and try again
return false, args.err
}
func (h *Handler) updateShardRoute(ctx context.Context, clusterID proto.ClusterID) error {
shardMgr, err := h.clusterController.GetShardController(clusterID)
if err != nil {
return err
}
return shardMgr.UpdateRoute(ctx)
}
// updateLeaderFromCurrentHost from old current shard host/disk, get leader and update shard
func (h *Handler) updateLeaderFromCurrentHost(ctx context.Context, args *punishArgs) error {
shardMgr, err := h.clusterController.GetShardController(args.clusterID)
if err != nil {
return err
}
shardStat, err := h.getLeaderShardInfo(ctx, args.clusterID, args.host, args.DiskID, args.Suid, 0)
if err != nil {
return err
}
return shardMgr.UpdateShard(ctx, shardStat)
}
// updateLeaderFromNewHost from other shard host/disk, get leader and update shard
func (h *Handler) updateLeaderFromNewHost(ctx context.Context, args *punishArgs) error {
shardMgr, err := h.clusterController.GetShardController(args.clusterID)
if err != nil {
return err
}
shard, err := shardMgr.GetShardByID(ctx, args.Suid.ShardID())
if err != nil {
return err
}
if args.exclude == nil {
args.exclude = make(map[proto.DiskID]struct{})
args.exclude[args.DiskID] = struct{}{}
}
// we get new disk, exclude bad diskID
newDisk, err := shard.GetMember(ctx, acapi.GetShardModeRandom, args.exclude)
if err != nil {
return err
}
newHost, err := h.getShardHost(ctx, args.clusterID, newDisk.DiskID)
if err != nil {
return err
}
// span := trace.SpanFromContextSafe(ctx)
// span.Debugf("get newDisk:%+v, old host:%s, old disk:%d", newDisk, args.host, args.DiskID)
shardStat, err := h.getLeaderShardInfo(ctx, args.clusterID, newHost, newDisk.DiskID, newDisk.Suid, args.DiskID)
if err != nil {
args.exclude[newDisk.DiskID] = struct{}{}
return err
}
return shardMgr.UpdateShard(ctx, shardStat)
}
func (h *Handler) getLeaderShardInfo(ctx context.Context, clusterID proto.ClusterID, host string, diskID proto.DiskID, suid proto.Suid, badDisk proto.DiskID) (shardnode.ShardStats, error) {
span := trace.SpanFromContextSafe(ctx)
for i := 0; i < h.ShardnodeRetryTimes; i++ {
// 1. get leader info
leader, err := h.shardnodeClient.GetShardStats(ctx, host, shardnode.GetShardArgs{
DiskID: diskID,
Suid: suid,
})
if err != nil {
if code := rpc.DetectStatusCode(err); code == errcode.CodeShardNoLeader {
span.Warnf("shard node is in the election, host:%s, disk:%d, suid:%d, badDisk:%d", host, diskID, suid, badDisk)
time.Sleep(time.Millisecond * time.Duration(h.ShardnodeRetryIntervalMS))
continue
}
return shardnode.ShardStats{}, err
}
// skip bad host. LeaderDiskID is 0 means in the election. bad disk is last leader, not start election yet
if leader.LeaderDiskID == 0 || leader.LeaderDiskID == badDisk {
span.Warnf("shard node is in the election, host:%s, disk:%d, suid:%d, badDisk:%d", host, diskID, suid, badDisk)
time.Sleep(time.Millisecond * time.Duration(h.ShardnodeRetryIntervalMS))
continue
}
return leader, nil
}
return shardnode.ShardStats{}, errcode.ErrShardNoLeader
}
func (h *Handler) fixCreateBlobArgs(ctx context.Context, args *acapi.CreateBlobArgs) error {
span := trace.SpanFromContextSafe(ctx)
if int64(args.Size) > h.maxObjectSize {
span.Info("exceed max object size", h.maxObjectSize)
return errcode.ErrAccessExceedSize
}
if args.SliceSize == 0 {
args.SliceSize = atomic.LoadUint32(&h.MaxBlobSize)
span.Debugf("fill slice size:%d", args.SliceSize)
}
if args.CodeMode == 0 {
args.CodeMode = h.allCodeModes.SelectCodeMode(int64(args.Size))
span.Debugf("select codemode:%d", args.CodeMode)
}
if !args.CodeMode.IsValid() {
span.Infof("invalid codemode:%d", args.CodeMode)
return errcode.ErrIllegalArguments
}
if args.ClusterID == 0 {
cluster, err := h.clusterController.ChooseOne()
if err != nil {
return err
}
args.ClusterID = cluster.ClusterID
span.Debugf("choose cluster[%+v]", cluster)
}
return nil
}
func (h *Handler) getRepairMessageShardnode(ctx context.Context,
shardController controller.IShardController, clusterID proto.ClusterID, blob blobIdent, badIdxes []uint8,
) (args shardnode.RepairSliceArgs, host string, err error) {
args.Vid = blob.vid
args.Bid = blob.bid
args.Reason = "access-repair"
for _, idx := range badIdxes {
args.BadIdx = append(args.BadIdx, uint32(idx))
}
tagNum := shardController.GetShardSubRangeCount(ctx)
var shard controller.Shard
shard, err = shardController.GetShard(ctx, args.GetShardKeys(tagNum))
if err != nil {
return
}
args.Header, err = h.getOpHeaderByShard(ctx, shardController, shard, acapi.GetShardModeLeader)
if err != nil {
return
}
host, err = h.getShardHost(ctx, clusterID, args.Header.DiskID)
return
}
func (h *Handler) sendRepairMsgIntoShardnode(ctx context.Context, blob blobIdent, badIdxes []uint8) {
span := trace.SpanFromContextSafe(ctx)
clusterID := blob.cid
shardController, err := h.clusterController.GetShardController(clusterID)
if err != nil {
span.Error(errors.Detail(err))
return
}
if err := retry.Timed(3, 100).On(func() error {
args, host, err := h.getRepairMessageShardnode(ctx, shardController, clusterID, blob, badIdxes)
if err != nil {
reportUnhealth(clusterID, "repair.msg", serviceShard, "-", "failed")
span.Warn(err)
return err
}
if err = h.shardnodeClient.RepairSlice(ctx, host, args); err != nil {
span.Warnf("send to shardnode %s repair message(%+v) %s", host, args, err.Error())
reportUnhealth(clusterID, "repair.msg", serviceShard, host, "failed")
_, err = h.punishAndUpdate(ctx, &punishArgs{
ShardOpHeader: args.Header,
clusterID: clusterID,
host: host,
mode: acapi.GetShardModeLeader,
err: err,
})
err = errors.Base(err, host)
}
return err
}); err != nil {
span.Errorf("send shardnode repair message(%+v) failed %s", blob, errors.Detail(err))
return
}
span.Infof("send shardnode repair message(%+v)", blob)
}
func (h *Handler) getDeleteMessageShardnode(ctx context.Context,
shardController controller.IShardController, clusterID proto.ClusterID, slice proto.Slice,
) (args shardnode.DeleteBlobRawArgs, host string, err error) {
args.Slice = slice
tagNum := shardController.GetShardSubRangeCount(ctx)
var shard controller.Shard
shard, err = shardController.GetShard(ctx, args.GetShardKeys(tagNum))
if err != nil {
return
}
args.Header, err = h.getOpHeaderByShard(ctx, shardController, shard, acapi.GetShardModeLeader)
if err != nil {
return
}
host, err = h.getShardHost(ctx, clusterID, args.Header.DiskID)
return
}
func (h *Handler) clearGarbageIntoShardnode(ctx context.Context, location *proto.Location) error {
span := trace.SpanFromContextSafe(ctx)
shardController, err := h.clusterController.GetShardController(location.ClusterID)
if err != nil {
span.Error(errors.Detail(err))
return errors.Base(err, "clear location:", *location)
}
clusterID := location.ClusterID
for _, slice := range location.Slices {
if err := retry.Timed(3, 100).On(func() error {
args, host, err := h.getDeleteMessageShardnode(ctx, shardController, clusterID, slice)
if err != nil {
reportUnhealth(clusterID, "delete.msg", serviceShard, "-", "failed")
span.Warn(err)
return err
}
if err = h.shardnodeClient.DeleteBlobRaw(ctx, host, args); err != nil {
span.Warnf("send to shardnode %s delete message(%+v) %s", host, slice, err.Error())
reportUnhealth(clusterID, "delete.msg", serviceShard, host, "failed")
_, err = h.punishAndUpdate(ctx, &punishArgs{
ShardOpHeader: args.Header,
clusterID: clusterID,
host: host,
mode: acapi.GetShardModeLeader,
err: err,
})
err = errors.Base(err, host)
}
return err
}); err != nil {
span.Errorf("send shardnode delete message(%+v) failed %s", slice, errors.Detail(err))
return errors.Base(err, "send shardnode delete message:", slice)
}
}
span.Infof("send shardnode delete message(%+v)", location)
return nil
}

View File

@ -1,568 +0,0 @@
package stream
import (
"context"
"errors"
"io"
"math"
"strings"
"testing"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/require"
"github.com/cubefs/cubefs/blobstore/access/controller"
acapi "github.com/cubefs/cubefs/blobstore/api/access"
"github.com/cubefs/cubefs/blobstore/api/clustermgr"
"github.com/cubefs/cubefs/blobstore/api/shardnode"
"github.com/cubefs/cubefs/blobstore/common/codemode"
errcode "github.com/cubefs/cubefs/blobstore/common/errors"
"github.com/cubefs/cubefs/blobstore/common/proto"
"github.com/cubefs/cubefs/blobstore/common/sharding"
"github.com/cubefs/cubefs/blobstore/testing/mocks"
)
func newStreamHandlerSuccess(t *testing.T) *Handler {
ctr := gomock.NewController(t)
gAny := gomock.Any()
info := controller.ShardOpInfo{
DiskID: 101,
Suid: proto.EncodeSuid(1, 0, 1),
RouteVersion: 1,
}
shardInfo := NewMockShard(ctr)
shardInfo.EXPECT().GetMember(gAny, gAny, gAny).Return(info, nil).AnyTimes()
shardMgr := NewMockShardController(ctr)
shardMgr.EXPECT().GetShard(gAny, gAny).Return(shardInfo, nil).AnyTimes()
shardMgr.EXPECT().GetSpaceID().Return(proto.SpaceID(1)).AnyTimes()
shardMgr.EXPECT().UpdateRoute(gAny).Return(nil).AnyTimes()
shardMgr.EXPECT().GetShardSubRangeCount(gAny).Return(2).AnyTimes()
svrCtrl := NewMockServiceController(ctr)
svrCtrl.EXPECT().GetShardnodeHost(gAny, gAny).Return(&controller.HostIDC{Host: "host"}, nil).AnyTimes()
clu := NewMockClusterController(ctr)
clu.EXPECT().GetShardController(gAny).Return(shardMgr, nil).AnyTimes()
clu.EXPECT().GetServiceController(gAny).Return(svrCtrl, nil).AnyTimes()
shardCli := mocks.NewMockShardnodeAccess(ctr)
proxyClient := mocks.NewMockProxyClient(ctr)
allCodeModes := CodeModePairs{
codemode.EC3P3: CodeModePair{
Policy: codemode.Policy{
ModeName: codemode.EC3P3.Name(),
MaxSize: math.MaxInt64,
Enable: true,
},
Tactic: codemode.EC3P3.Tactic(),
},
}
handler := &Handler{
clusterController: clu,
shardnodeClient: shardCli,
proxyClient: proxyClient,
maxObjectSize: 100,
allCodeModes: allCodeModes,
}
return handler
}
func TestStreamBlobGet(t *testing.T) {
ctx := context.Background()
ctr := gomock.NewController(t)
gAny := gomock.Any()
info := controller.ShardOpInfo{}
shardInfo := NewMockShard(ctr)
shardInfo.EXPECT().GetMember(gAny, gAny, gAny).Return(info, nil).Times(2)
shardMgr := NewMockShardController(ctr)
shardMgr.EXPECT().GetShard(gAny, gAny).Return(shardInfo, nil).Times(2)
shardMgr.EXPECT().GetSpaceID().Return(proto.SpaceID(1)).Times(2)
shardMgr.EXPECT().UpdateRoute(gAny).Return(nil)
shardMgr.EXPECT().GetShardSubRangeCount(gAny).Return(2).AnyTimes()
svrCtrl := NewMockServiceController(ctr)
svrCtrl.EXPECT().GetShardnodeHost(gAny, gAny).Return(&controller.HostIDC{Host: "host"}, nil).Times(2)
clu := NewMockClusterController(ctr)
clu.EXPECT().GetShardController(gAny).Return(shardMgr, nil).Times(3)
clu.EXPECT().GetServiceController(gAny).Return(svrCtrl, nil).Times(2)
blobName := "blob1"
blob := &proto.Blob{
Name: blobName,
Location: proto.Location{
ClusterID: 1,
CodeMode: codemode.EC3P3,
Size_: 1,
SliceSize: 1,
Crc: 1,
Slices: nil,
},
}
ret := shardnode.GetBlobRet{
Blob: *blob,
}
shardCli := mocks.NewMockShardnodeAccess(ctr)
shardCli.EXPECT().GetBlob(gAny, gAny, gAny).Return(ret, errcode.ErrShardRouteVersionNeedUpdate)
shardCli.EXPECT().GetBlob(gAny, gAny, gAny).Return(ret, nil)
handler := &Handler{
clusterController: clu,
shardnodeClient: shardCli,
}
args := acapi.GetBlobArgs{
BlobName: string(blobName),
Mode: acapi.GetShardModeRandom,
}
loc, err := handler.GetBlob(ctx, &args)
require.NoError(t, err)
require.Equal(t, ret.Blob.Location, *loc)
}
func TestStreamBlobCreate(t *testing.T) {
ctx := context.Background()
gAny := gomock.Any()
h := newStreamHandlerSuccess(t)
args := acapi.CreateBlobArgs{
BlobName: ("blob-create"),
CodeMode: 0,
ClusterID: 0,
Size: 10,
SliceSize: 4,
}
ret := shardnode.CreateBlobRet{
Blob: proto.Blob{
Name: "blob-create",
Location: proto.Location{
ClusterID: 1,
CodeMode: codemode.EC3P3,
Size_: 10,
SliceSize: 4,
Crc: 1,
Slices: nil,
},
},
}
h.shardnodeClient.(*mocks.MockShardnodeAccess).EXPECT().CreateBlob(gAny, gAny, gAny).Return(ret, errcode.ErrShardRouteVersionNeedUpdate)
h.shardnodeClient.(*mocks.MockShardnodeAccess).EXPECT().CreateBlob(gAny, gAny, gAny).Return(ret, nil)
h.clusterController.(*MockClusterController).EXPECT().ChooseOne().Return(&clustermgr.ClusterInfo{
ClusterID: 1,
}, nil)
loc, err := h.CreateBlob(ctx, &args)
require.NoError(t, err)
require.Equal(t, ret.Blob.Location, *loc)
}
func TestStreamBlobDelete(t *testing.T) {
ctx := context.Background()
gAny := gomock.Any()
h := newStreamHandlerSuccess(t)
args := acapi.DelBlobArgs{ClusterID: 1, BlobName: "blob-del"}
h.shardnodeClient.(*mocks.MockShardnodeAccess).EXPECT().DeleteBlob(gAny, gAny, gAny).Return(errcode.ErrShardRouteVersionNeedUpdate)
h.shardnodeClient.(*mocks.MockShardnodeAccess).EXPECT().DeleteBlob(gAny, gAny, gAny).Return(nil)
require.NoError(t, h.DeleteBlob(ctx, &args))
}
func TestStreamBlobDeleteRaw(t *testing.T) {
ctx, gAny := context.Background(), gomock.Any()
h := newStreamHandlerSuccess(t)
h.StreamConfig.DeleteIntoShardnodePercentage = 100
h.shardnodeClient.(*mocks.MockShardnodeAccess).EXPECT().DeleteBlobRaw(gAny, gAny, gAny).Return(errcode.ErrUnexpected).Times(3)
require.Error(t, h.Delete(ctx, &proto.Location{Slices: []proto.Slice{{}, {}}}))
h.shardnodeClient.(*mocks.MockShardnodeAccess).EXPECT().DeleteBlobRaw(gAny, gAny, gAny).Return(nil).Times(2)
require.NoError(t, h.Delete(ctx, &proto.Location{Slices: []proto.Slice{{}, {}}}))
}
func TestStreamBlobSeal(t *testing.T) {
ctx := context.Background()
gAny := gomock.Any()
h := newStreamHandlerSuccess(t)
args := acapi.SealBlobArgs{
BlobName: ("blob-seal"),
ClusterID: 1,
Slices: make([]proto.Slice, 1),
}
h.shardnodeClient.(*mocks.MockShardnodeAccess).EXPECT().SealBlob(gAny, gAny, gAny).Return(errcode.ErrShardRouteVersionNeedUpdate)
h.shardnodeClient.(*mocks.MockShardnodeAccess).EXPECT().SealBlob(gAny, gAny, gAny).Return(nil)
err := h.SealBlob(ctx, &args)
require.NoError(t, err)
}
func TestStreamBlobList(t *testing.T) {
ctx := context.Background()
gAny := gomock.Any()
errMock := errors.New("fake error")
ctr := gomock.NewController(t)
info := controller.ShardOpInfo{
DiskID: 101,
Suid: proto.EncodeSuid(1, 0, 1),
RouteVersion: 1,
}
shardInfo := NewMockShard(ctr)
shardInfo.EXPECT().GetMember(gAny, gAny, gAny).Return(info, nil).Times(3)
shardMgr := NewMockShardController(ctr)
shardMgr.EXPECT().GetShardByID(gAny, gAny).Return(shardInfo, nil).Times(3)
shardMgr.EXPECT().GetSpaceID().Return(proto.SpaceID(1)).Times(3)
shardMgr.EXPECT().UpdateRoute(gAny).Return(nil).Times(3)
shardMgr.EXPECT().GetShardSubRangeCount(gAny).Return(2).AnyTimes()
svrCtrl := NewMockServiceController(ctr)
svrCtrl.EXPECT().GetShardnodeHost(gAny, gAny).Return(&controller.HostIDC{Host: "host"}, nil).Times(3)
clu := NewMockClusterController(ctr)
clu.EXPECT().GetShardController(gAny).Return(shardMgr, nil).Times(3 * 2)
clu.EXPECT().GetServiceController(gAny).Return(svrCtrl, nil).Times(3)
h := &Handler{
clusterController: clu,
shardnodeClient: mocks.NewMockShardnodeAccess(ctr),
}
args := acapi.ListBlobArgs{
ClusterID: 1,
ShardID: 1,
Prefix: ("test-"),
Marker: ("test-blob-1"),
Count: 4,
}
// list one shard
h.shardnodeClient.(*mocks.MockShardnodeAccess).EXPECT().ListBlob(gAny, gAny, gAny).Return(shardnode.ListBlobRet{}, errcode.ErrShardRouteVersionNeedUpdate).Times(3)
ret, err := h.ListBlob(ctx, &args)
require.NotNil(t, err)
require.ErrorIs(t, err, errcode.ErrShardRouteVersionNeedUpdate)
require.Equal(t, 0, len(ret.Blobs))
// list one shard, 3 blob
shardInfo.EXPECT().GetMember(gAny, gAny, gAny).Return(info, nil)
shardMgr.EXPECT().GetShardByID(gAny, gAny).Return(shardInfo, nil)
shardMgr.EXPECT().GetSpaceID().Return(proto.SpaceID(1))
svrCtrl.EXPECT().GetShardnodeHost(gAny, gAny).Return(&controller.HostIDC{Host: "host"}, nil)
clu.EXPECT().GetShardController(gAny).Return(shardMgr, nil)
clu.EXPECT().GetServiceController(gAny).Return(svrCtrl, nil)
listRet := shardnode.ListBlobRet{
Blobs: []proto.Blob{
{Name: "test-blob-1"},
{Name: "test-blob-2"},
{Name: "test-blob-3"},
},
NextMarker: "",
}
h.shardnodeClient.(*mocks.MockShardnodeAccess).EXPECT().ListBlob(gAny, gAny, gAny).Return(listRet, nil)
ret, err = h.ListBlob(ctx, &args)
require.NoError(t, err)
require.Equal(t, "", ret.NextMarker) // require.Equal(t, []byte(nil), ret.NextMarker)
require.Equal(t, 3, len(ret.Blobs))
// list all
shards := make([]controller.Shard, 4)
ranges := sharding.InitShardingRange(sharding.RangeType_RangeTypeHash, 1, 3)
for i := range shards {
shards[i] = NewMockShard(ctr)
shards[i].(*MockShard).EXPECT().GetShardID().Return(proto.ShardID(i + 1)).AnyTimes()
shards[i].(*MockShard).EXPECT().GetRange().Return(*ranges[i]).AnyTimes()
shards[i].(*MockShard).EXPECT().GetMember(gAny, gAny, gAny).Return(info, nil).AnyTimes()
}
shardMgr.EXPECT().GetFisrtShard(gAny).Return(shards[0], nil).Times(1)
shardMgr.EXPECT().GetNextShard(gAny, gAny).Return(shards[1], nil)
shardMgr.EXPECT().GetNextShard(gAny, gAny).Return(shards[2], nil)
shardMgr.EXPECT().GetSpaceID().Return(proto.SpaceID(1)).Times(2)
svrCtrl.EXPECT().GetShardnodeHost(gAny, gAny).Return(&controller.HostIDC{Host: "host"}, nil).Times(2)
h.clusterController.(*MockClusterController).EXPECT().GetShardController(gAny).Return(shardMgr, nil).Times(1)
h.clusterController.(*MockClusterController).EXPECT().GetServiceController(gAny).Return(svrCtrl, nil).Times(2)
listRet = shardnode.ListBlobRet{
Blobs: []proto.Blob{
{Name: "test-blob-1"},
{Name: "test-blob-2"},
},
NextMarker: "",
}
h.shardnodeClient.(*mocks.MockShardnodeAccess).EXPECT().ListBlob(gAny, gAny, gAny).Return(listRet, nil).Times(2)
args.ShardID = 0
args.Marker = ""
args.Count = 4
ret, err = h.ListBlob(ctx, &args)
expectMarker := acapi.ListBlobEncodeMarker{
Range: *ranges[2],
Marker: "",
}
require.NoError(t, err)
require.Equal(t, 4, len(ret.Blobs))
actual := acapi.ListBlobEncodeMarker{}
err = actual.Unmarshal([]byte(ret.NextMarker))
require.NoError(t, err)
require.Equal(t, expectMarker, actual) // string(ret.NextMarker))
// list all, from next shard 3, list twice
args.ShardID = 0
args.Count = 4
args.Marker = ret.NextMarker
shardMgr.EXPECT().GetSpaceID().Return(proto.SpaceID(1)).Times(2)
shardMgr.EXPECT().GetShardByRange(gAny, expectMarker.Range).Return(shards[2], nil)
shardMgr.EXPECT().GetNextShard(gAny, gAny).Return(nil, errMock)
svrCtrl.EXPECT().GetShardnodeHost(gAny, gAny).Return(&controller.HostIDC{Host: "host"}, nil).Times(2)
h.clusterController.(*MockClusterController).EXPECT().GetShardController(gAny).Return(shardMgr, nil).Times(1)
h.clusterController.(*MockClusterController).EXPECT().GetServiceController(gAny).Return(svrCtrl, nil).Times(2)
listRet.NextMarker = ret.NextMarker
h.shardnodeClient.(*mocks.MockShardnodeAccess).EXPECT().ListBlob(gAny, gAny, gAny).Return(listRet, nil)
listRet.NextMarker = ""
h.shardnodeClient.(*mocks.MockShardnodeAccess).EXPECT().ListBlob(gAny, gAny, gAny).Return(listRet, nil)
ret, err = h.ListBlob(ctx, &args)
require.NotNil(t, err)
require.ErrorIs(t, err, errMock)
require.Equal(t, 0, len(ret.Blobs))
// list all, access until last shard not enough count
lastEndMarker := acapi.ListBlobEncodeMarker{
Range: *ranges[2], // total count 4
Marker: args.Marker,
}
lastEnd, err := lastEndMarker.Marshal()
require.NoError(t, err)
args.ShardID = 0
args.Count = 100
args.Marker = string(lastEnd)
h.clusterController.(*MockClusterController).EXPECT().GetShardController(gAny).Return(shardMgr, nil)
shardMgr.EXPECT().GetShardByRange(gAny, lastEndMarker.Range).Return(shards[2], nil)
shardMgr.EXPECT().GetSpaceID().Return(proto.SpaceID(1)).Times(2)
shardMgr.EXPECT().GetNextShard(gAny, gAny).Return(shards[3], nil)
shardMgr.EXPECT().GetNextShard(gAny, gAny).Return(nil, nil)
svrCtrl.EXPECT().GetShardnodeHost(gAny, gAny).Return(&controller.HostIDC{Host: "host"}, nil).Times(2)
h.clusterController.(*MockClusterController).EXPECT().GetServiceController(gAny).Return(svrCtrl, nil).Times(2)
listRet.NextMarker = ""
h.shardnodeClient.(*mocks.MockShardnodeAccess).EXPECT().ListBlob(gAny, gAny, gAny).Return(listRet, nil).Times(2)
ret, err = h.ListBlob(ctx, &args)
require.NoError(t, err)
require.Equal(t, 4, len(ret.Blobs))
// endOneMarker := acapi.ListBlobEncodeMarker{
// Range: sharding.Range{}, // *ranges[3],
// Marker: args.Marker,
// }
// endOne, err := endOneMarker.Marshal()
// require.NoError(t, err)
require.Equal(t, "", ret.NextMarker) // require.Equal(t, []byte(nil), ret.NextMarker)
// list all, error marker
args = acapi.ListBlobArgs{
ClusterID: 1,
Mode: 1,
ShardID: 0,
Prefix: "", //[]byte("test-"),
Marker: ("abcd"),
Count: 100,
}
h.clusterController.(*MockClusterController).EXPECT().GetShardController(gAny).Return(shardMgr, nil)
ret, err = h.ListBlob(ctx, &args)
require.NotNil(t, err)
require.True(t, strings.Contains(err.Error(), "fail to unmarshal marker"))
}
func TestStreamBlobAlloc(t *testing.T) {
ctx := context.Background()
gAny := gomock.Any()
h := newStreamHandlerSuccess(t)
args := acapi.AllocSliceArgs{
BlobName: ("blob-seal"),
ClusterID: 1,
CodeMode: 1,
Size: 1,
FailSlice: proto.Slice{},
}
ret := shardnode.AllocSliceRet{
Slices: make([]proto.Slice, 1),
}
ret.Slices[0].Vid = 1
h.shardnodeClient.(*mocks.MockShardnodeAccess).EXPECT().AllocSlice(gAny, gAny, gAny).Return(ret, errcode.ErrShardRouteVersionNeedUpdate)
h.shardnodeClient.(*mocks.MockShardnodeAccess).EXPECT().AllocSlice(gAny, gAny, gAny).Return(ret, nil)
loc, err := h.AllocSlice(ctx, &args)
require.NoError(t, err)
require.NotNil(t, loc)
require.Equal(t, 1, len(loc.Slices))
require.Equal(t, proto.Vid(1), loc.Slices[0].Vid)
}
func TestStreamBlobOther(t *testing.T) {
ctx := context.Background()
gAny := gomock.Any()
ctr := gomock.NewController(t)
svrCtrl := NewMockServiceController(ctr)
svrCtrl.EXPECT().PunishShardnode(gAny, gAny, gAny).Times(2)
shardMgr := NewMockShardController(ctr)
shardMgr.EXPECT().UpdateRoute(gAny).Return(nil).Times(2)
shardMgr.EXPECT().UpdateShard(gAny, gAny).Return(nil)
clu := NewMockClusterController(ctr)
clu.EXPECT().GetServiceController(gAny).Return(svrCtrl, nil).Times(2)
clu.EXPECT().GetShardController(gAny).Return(shardMgr, nil).Times(3)
h := &Handler{
clusterController: clu,
}
interrupt, err1 := h.punishAndUpdate(ctx, &punishArgs{
err: errcode.ErrShardNodeDiskNotFound,
})
require.Equal(t, false, interrupt)
require.ErrorIs(t, err1, errcode.ErrShardNodeDiskNotFound)
interrupt, err1 = h.punishAndUpdate(ctx, &punishArgs{
err: errcode.ErrShardDoesNotExist,
})
require.Equal(t, false, interrupt)
require.ErrorIs(t, err1, errcode.ErrShardDoesNotExist)
interrupt, err1 = h.punishAndUpdate(ctx, &punishArgs{
err: io.EOF,
})
require.Equal(t, false, interrupt)
require.ErrorIs(t, err1, io.EOF)
// broken disk
interrupt, err1 = h.punishAndUpdate(ctx, &punishArgs{
ShardOpHeader: shardnode.ShardOpHeader{},
clusterID: 0,
host: "",
err: errcode.ErrDiskBroken,
})
require.Equal(t, false, interrupt)
require.ErrorIs(t, err1, errcode.ErrDiskBroken)
shardnodeClient := mocks.NewMockShardnodeAccess(ctr)
shardnodeClient.EXPECT().GetShardStats(gAny, gAny, gAny).Return(shardnode.ShardStats{LeaderDiskID: 11}, nil).Times(1)
h.shardnodeClient = shardnodeClient
h.ShardnodeRetryTimes = defaultShardnodeRetryTimes
err1 = h.updateLeaderFromCurrentHost(ctx, &punishArgs{
err: errcode.ErrShardNodeNotLeader,
})
require.NoError(t, err1)
// wait connect refused
h.ShardnodeRetryTimes = defaultShardnodeRetryTimes
info := controller.ShardOpInfo{
DiskID: 101,
Suid: proto.EncodeSuid(1, 0, 1),
RouteVersion: 1,
}
clu.EXPECT().GetShardController(gAny).Return(shardMgr, nil)
clu.EXPECT().GetServiceController(gAny).Return(svrCtrl, nil).Times(2)
shardInfo := NewMockShard(ctr)
shardInfo.EXPECT().GetMember(gAny, gAny, map[proto.DiskID]struct{}{1: {}}).Return(info, nil)
shardMgr.EXPECT().GetShardByID(gAny, gAny).Return(shardInfo, nil)
shardMgr.EXPECT().UpdateShard(gAny, gAny).Return(nil)
svrCtrl.EXPECT().GetShardnodeHost(gAny, proto.DiskID(101)).Return(&controller.HostIDC{Host: "host101"}, nil)
svrCtrl.EXPECT().PunishShardnode(gAny, gAny, gAny)
shardnodeClient.EXPECT().GetShardStats(gAny, gAny, gAny).Return(shardnode.ShardStats{LeaderDiskID: 1}, nil)
shardnodeClient.EXPECT().GetShardStats(gAny, "host101", shardnode.GetShardArgs{
DiskID: proto.DiskID(101),
Suid: info.Suid,
}).Return(shardnode.ShardStats{LeaderDiskID: 102}, nil)
interrupt, err1 = h.punishAndUpdate(ctx, &punishArgs{
ShardOpHeader: shardnode.ShardOpHeader{
DiskID: 1,
Suid: 2,
},
err: errors.New("dial tcp localhost:9100: connect: connection refused"),
})
require.Equal(t, false, interrupt)
// require.True(t, strings.Contains(err1.Error(), "connection refused"))
require.ErrorIs(t, err1, errcode.ErrConnectionRefused)
// interrupt, err1 = convertError(kvstore.ErrNotFound)
// require.Equal(t, true, interrupt)
// require.ErrorIs(t, err1, errcode.ErrCallShardNodeFail)
}
func TestStreamBlob_NotLeader_RetrySuccess(t *testing.T) {
ctx := context.Background()
ctr := gomock.NewController(t)
gAny := gomock.Any()
// old leader, Not Leader
oldInfo := controller.ShardOpInfo{
DiskID: 101,
Suid: proto.EncodeSuid(1, 0, 1),
RouteVersion: 1,
}
// new leader
newInfo := controller.ShardOpInfo{
DiskID: 102,
Suid: proto.EncodeSuid(1, 0, 2),
RouteVersion: 1,
}
// shard mock
shard := NewMockShard(ctr)
shard.EXPECT().GetMember(gAny, gAny, gAny).Return(oldInfo, nil).AnyTimes() // getShardOpHeader
shard.EXPECT().GetMember(gAny, acapi.GetShardModeRandom, map[proto.DiskID]struct{}{oldInfo.DiskID: {}}).Return(newInfo, nil).AnyTimes() // waitShardnodeNextLeader
shard.EXPECT().GetShardID().Return(proto.ShardID(1)).AnyTimes()
// shard controller mock
shardMgr := NewMockShardController(ctr)
shardMgr.EXPECT().GetShard(gAny, gAny).Return(shard, nil).AnyTimes()
shardMgr.EXPECT().GetShardByID(gAny, proto.ShardID(1)).Return(shard, nil).AnyTimes()
shardMgr.EXPECT().GetSpaceID().Return(proto.SpaceID(1)).AnyTimes()
shardMgr.EXPECT().UpdateRoute(gAny).Return(nil).AnyTimes()
shardMgr.EXPECT().GetShardSubRangeCount(gAny).Return(2).AnyTimes()
shardMgr.EXPECT().UpdateShard(gAny, gAny).Return(nil).Times(1) // waitShardnodeNextLeader finally
// service controller mock
svrCtrl := NewMockServiceController(ctr)
svrCtrl.EXPECT().GetShardnodeHost(gAny, proto.DiskID(oldInfo.DiskID)).Return(&controller.HostIDC{Host: "host-old"}, nil).AnyTimes()
svrCtrl.EXPECT().GetShardnodeHost(gAny, proto.DiskID(newInfo.DiskID)).Return(&controller.HostIDC{Host: "host-new"}, nil).AnyTimes()
// cluster controller mock
clu := NewMockClusterController(ctr)
clu.EXPECT().GetShardController(gAny).Return(shardMgr, nil).AnyTimes()
clu.EXPECT().GetServiceController(gAny).Return(svrCtrl, nil).AnyTimes()
// shardnode client mockfirst NotLeaderand then success
shardCli := mocks.NewMockShardnodeAccess(ctr)
shardCli.EXPECT().DeleteBlob(gAny, gAny, gAny).Return(errcode.ErrShardNodeNotLeader)
shardCli.EXPECT().GetShardStats(gAny, gAny, gAny).Return(shardnode.ShardStats{LeaderDiskID: newInfo.DiskID}, nil)
shardCli.EXPECT().DeleteBlob(gAny, gAny, gAny).Return(nil)
h := &Handler{
clusterController: clu,
shardnodeClient: shardCli,
}
h.ShardnodeRetryTimes = defaultShardnodeRetryTimes
args := acapi.DelBlobArgs{ClusterID: 1, BlobName: "blob-notleader"}
require.NoError(t, h.DeleteBlob(ctx, &args))
}

View File

@ -12,21 +12,19 @@
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package stream
package access
import (
"context"
"sync/atomic"
"github.com/afex/hystrix-go/hystrix"
"github.com/cubefs/cubefs/blobstore/api/access"
"github.com/cubefs/cubefs/blobstore/api/proxy"
"github.com/cubefs/cubefs/blobstore/common/codemode"
errcode "github.com/cubefs/cubefs/blobstore/common/errors"
"github.com/cubefs/cubefs/blobstore/common/proto"
"github.com/cubefs/cubefs/blobstore/common/trace"
"github.com/cubefs/cubefs/blobstore/util"
"github.com/cubefs/cubefs/blobstore/util/errors"
"github.com/cubefs/cubefs/blobstore/util/retry"
)
@ -41,8 +39,7 @@ var errAllocatePunishedVolume = errors.New("allocate punished volume")
// codeMode > 0, alloc in this codemode
// return: a location of file
func (h *Handler) Alloc(ctx context.Context, size uint64, blobSize uint32,
assignClusterID proto.ClusterID, codeMode codemode.CodeMode,
) (*proto.Location, error) {
assignClusterID proto.ClusterID, codeMode codemode.CodeMode) (*access.Location, error) {
span := trace.SpanFromContextSafe(ctx)
span.Debugf("alloc request with size:%d blobsize:%d cluster:%d codemode:%d",
size, blobSize, assignClusterID, codeMode)
@ -57,7 +54,7 @@ func (h *Handler) Alloc(ctx context.Context, size uint64, blobSize uint32,
span.Debugf("fill blobsize:%d", blobSize)
}
if codeMode == codemode.CodeModeNone {
if codeMode == 0 {
codeMode = h.allCodeModes.SelectCodeMode(int64(size))
span.Debugf("select codemode:%d", codeMode)
}
@ -73,20 +70,19 @@ func (h *Handler) Alloc(ctx context.Context, size uint64, blobSize uint32,
}
span.Debugf("allocated from %d %+v", clusterID, blobs)
location := &proto.Location{
location := &access.Location{
ClusterID: clusterID,
CodeMode: codeMode,
Size_: size,
SliceSize: blobSize,
Slices: blobs,
Size: size,
BlobSize: blobSize,
Blobs: blobs,
}
span.Debugf("alloc ok %+v", location)
return location, nil
}
func (h *Handler) allocFromAllocatorWithHystrix(ctx context.Context,
codeMode codemode.CodeMode, size uint64, blobSize uint32, clusterID proto.ClusterID,
) (cid proto.ClusterID, bidRets []proto.Slice, err error) {
func (h *Handler) allocFromAllocatorWithHystrix(ctx context.Context, codeMode codemode.CodeMode, size uint64, blobSize uint32,
clusterID proto.ClusterID) (cid proto.ClusterID, bidRets []access.SliceInfo, err error) {
err = hystrix.Do(allocCommand, func() error {
cid, bidRets, err = h.allocFromAllocator(ctx, codeMode, size, blobSize, clusterID)
return err
@ -94,9 +90,8 @@ func (h *Handler) allocFromAllocatorWithHystrix(ctx context.Context,
return
}
func (h *Handler) allocFromAllocator(ctx context.Context,
codeMode codemode.CodeMode, size uint64, blobSize uint32, clusterID proto.ClusterID,
) (proto.ClusterID, []proto.Slice, error) {
func (h *Handler) allocFromAllocator(ctx context.Context, codeMode codemode.CodeMode, size uint64, blobSize uint32,
clusterID proto.ClusterID) (proto.ClusterID, []access.SliceInfo, error) {
span := trace.SpanFromContextSafe(ctx)
if blobSize == 0 {
@ -113,28 +108,32 @@ func (h *Handler) allocFromAllocator(ctx context.Context,
args := proxy.AllocVolsArgs{
Fsize: size,
CodeMode: codeMode,
BidCount: util.AlignedBlocks(size, uint64(blobSize)),
}
serviceController, err := h.clusterController.GetServiceController(clusterID)
if err != nil {
span.Error(err)
return 0, nil, err
}
hosts, err := serviceController.GetServiceHosts(ctx, serviceProxy)
if err != nil {
span.Error(err)
return 0, nil, err
}
for len(hosts) < h.AllocRetryTimes {
hosts = append(hosts, hosts...)
BidCount: blobCount(size, blobSize),
}
var allocRets []proxy.AllocRet
var allocHost string
if err := retry.ExponentialBackoff(h.AllocRetryTimes, uint32(h.AllocRetryIntervalMS)).RuptOn(func() (bool, error) {
host := hosts[0]
hosts = hosts[1:]
hostsSet := make(map[string]struct{}, 1)
if err := retry.ExponentialBackoff(h.AllocRetryTimes, uint32(h.AllocRetryIntervalMS)).On(func() error {
serviceController, err := h.clusterController.GetServiceController(clusterID)
if err != nil {
span.Warn(err)
return errors.Info(err, "get service controller", clusterID)
}
var host string
for range [10]struct{}{} {
host, err = serviceController.GetServiceHost(ctx, serviceProxy)
if err != nil {
span.Warn(err)
return errors.Info(err, "get proxy host", clusterID)
}
if _, ok := hostsSet[host]; ok {
continue
}
hostsSet[host] = struct{}{}
break
}
allocHost = host
allocRets, err = h.proxyClient.VolumeAlloc(ctx, host, &args)
@ -145,10 +144,7 @@ func (h *Handler) allocFromAllocator(ctx context.Context,
serviceController.PunishServiceWithThreshold(ctx, serviceProxy, host, h.ServicePunishIntervalS)
}
span.Warn(host, err)
if err == context.Canceled {
return true, err
}
return false, errors.Base(err, "alloc from proxy", host)
return errors.Base(err, "alloc from proxy", host)
}
// filter punished volume in allocating progress
@ -156,18 +152,18 @@ func (h *Handler) allocFromAllocator(ctx context.Context,
vInfo, err := h.getVolume(ctx, clusterID, ret.Vid, true)
if err != nil {
span.Warn(err)
return false, err
return err
}
if vInfo.IsPunish {
// return err and retry allocate
err = errAllocatePunishedVolume
args.Excludes = append(args.Excludes, vInfo.Vid)
span.Warn("next retry exclude vid:", vInfo.Vid, err)
return false, err
return err
}
}
return true, nil
return nil
}); err != nil {
if err != errAllocatePunishedVolume {
reportUnhealth(clusterID, "allocate", "-", "-", "failed")
@ -182,20 +178,20 @@ func (h *Handler) allocFromAllocator(ctx context.Context,
setCacheVidHost(clusterID, ret.Vid, allocHost)
}
blobN := util.AlignedBlocks(size, uint64(blobSize))
blobs := make([]proto.Slice, 0, blobN)
blobN := blobCount(size, blobSize)
blobs := make([]access.SliceInfo, 0, blobN)
for _, bidRet := range allocRets {
if blobN <= 0 {
break
}
count := util.Min(blobN, uint64(bidRet.BidEnd)-uint64(bidRet.BidStart)+1)
count := minU64(blobN, uint64(bidRet.BidEnd)-uint64(bidRet.BidStart)+1)
blobN -= count
blobs = append(blobs, proto.Slice{
MinSliceID: bidRet.BidStart,
Vid: bidRet.Vid,
Count: uint32(count),
blobs = append(blobs, access.SliceInfo{
MinBid: bidRet.BidStart,
Vid: bidRet.Vid,
Count: uint32(count),
})
}
if blobN > 0 {

View File

@ -12,10 +12,9 @@
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package stream
package access
import (
"context"
"testing"
"time"
@ -33,26 +32,26 @@ func TestAccessStreamAllocBase(t *testing.T) {
require.NoError(t, err)
require.Equal(t, clusterID, loc.ClusterID)
require.Equal(t, codemode.EC6P6, loc.CodeMode)
require.Equal(t, uint64(1<<30), loc.Size_)
require.Equal(t, uint32(1<<22), loc.SliceSize)
require.Equal(t, 2, len(loc.Slices))
require.Equal(t, uint32(1), loc.Slices[0].Count)
require.Equal(t, uint32((1<<8)-1), loc.Slices[1].Count)
require.Equal(t, uint64(1<<30), loc.Size)
require.Equal(t, uint32(1<<22), loc.BlobSize)
require.Equal(t, 2, len(loc.Blobs))
require.Equal(t, uint32(1), loc.Blobs[0].Count)
require.Equal(t, uint32((1<<8)-1), loc.Blobs[1].Count)
}
{
loc, err := streamer.Alloc(ctx(), (1<<30)+1, 0, 0, 0)
require.NoError(t, err)
require.Equal(t, 2, len(loc.Slices))
require.Equal(t, uint32(1), loc.Slices[0].Count)
require.Equal(t, uint32(1<<8), loc.Slices[1].Count)
require.Equal(t, 2, len(loc.Blobs))
require.Equal(t, uint32(1), loc.Blobs[0].Count)
require.Equal(t, uint32(1<<8), loc.Blobs[1].Count)
}
// 1M blobsize
{
loc, err := streamer.Alloc(ctx(), 1<<30, 1<<20, 0, 0)
require.NoError(t, err)
require.Equal(t, 2, len(loc.Slices))
require.Equal(t, uint32(1), loc.Slices[0].Count)
require.Equal(t, uint32((1<<10)-1), loc.Slices[1].Count)
require.Equal(t, 2, len(loc.Blobs))
require.Equal(t, uint32(1), loc.Blobs[0].Count)
require.Equal(t, uint32((1<<10)-1), loc.Blobs[1].Count)
}
// max size + 1
{
@ -69,11 +68,3 @@ func TestAccessStreamAllocBase(t *testing.T) {
require.Error(t, err)
}
}
func TestAccessStreamAllocCanceled(t *testing.T) {
ctxfunc := ctxWithName("TestAccessStreamAllocCanceled")
ctx, cancel := context.WithCancel(ctxfunc())
cancel()
_, err := streamer.Alloc(ctx, 1, 0, 0, 0)
require.ErrorIs(t, err, context.Canceled)
}

View File

@ -12,12 +12,11 @@
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package stream
package access
import (
"context"
"fmt"
"hash/crc32"
"io"
"math/rand"
"sort"
@ -27,6 +26,7 @@ import (
"github.com/afex/hystrix-go/hystrix"
"github.com/cubefs/cubefs/blobstore/access/controller"
"github.com/cubefs/cubefs/blobstore/api/access"
"github.com/cubefs/cubefs/blobstore/api/blobnode"
"github.com/cubefs/cubefs/blobstore/common/codemode"
"github.com/cubefs/cubefs/blobstore/common/ec"
@ -34,7 +34,6 @@ import (
"github.com/cubefs/cubefs/blobstore/common/proto"
"github.com/cubefs/cubefs/blobstore/common/rpc"
"github.com/cubefs/cubefs/blobstore/common/trace"
"github.com/cubefs/cubefs/blobstore/util"
"github.com/cubefs/cubefs/blobstore/util/errors"
"github.com/cubefs/cubefs/blobstore/util/retry"
)
@ -67,7 +66,6 @@ type shardData struct {
index int
status bool
buffer []byte
time int
}
type sortedVuid struct {
@ -112,7 +110,7 @@ type pipeBuffer struct {
// ...
// read-9 [d4 p5]
// failed
func (h *Handler) Get(ctx context.Context, w io.Writer, location proto.Location, readSize, offset uint64) (func() error, error) {
func (h *Handler) Get(ctx context.Context, w io.Writer, location access.Location, readSize, offset uint64) (func() error, error) {
span := trace.SpanFromContextSafe(ctx)
span.Debugf("get request cluster:%d size:%d offset:%d", location.ClusterID, readSize, offset)
@ -143,7 +141,6 @@ func (h *Handler) Get(ctx context.Context, w io.Writer, location proto.Location,
getTime := new(timeReadWrite)
defer func() {
span.AppendRPCTrackLog([]string{getTime.String()})
getTime.Report(clusterID.ToString(), h.IDC, false)
}()
// try to read data shard only,
@ -169,10 +166,6 @@ func (h *Handler) Get(ctx context.Context, w io.Writer, location proto.Location,
}
}
var spanpipe trace.Span
spanpipe, ctx = trace.StartSpanFromContextWithTraceID(context.Background(), "", span.TraceID())
defer spanpipe.Finish()
// data stream flow:
// client <--copy-- pipeline <--swap-- readBlob <--copy-- blobnode
//
@ -192,21 +185,18 @@ func (h *Handler) Get(ctx context.Context, w io.Writer, location proto.Location,
if blobVolume == nil || blobVolume.Vid != blob.Vid {
blobVolume, err = h.getVolume(ctx, clusterID, blob.Vid, true)
if err != nil {
spanpipe.Error("get volume", err)
span.Error("get volume", err)
ch <- pipeBuffer{err: err}
return
}
// do not use local shards
ordered := h.CodeModesGetOrdered[blobVolume.CodeMode]
ignoreIDC := h.CodeModesGetIgnoreIDC[blobVolume.CodeMode]
sortedVuids = genSortedVuidByIDC(ctx,
serviceController, h.IDC, blobVolume.Units[:tactic.N+tactic.M], ordered, ignoreIDC)
spanpipe.Debugf("to read %s with read-shard-x:%d active-shard-n:%d of data-n:%d party-n:%d",
sortedVuids = genSortedVuidByIDC(ctx, serviceController, h.IDC, blobVolume.Units[:tactic.N+tactic.M])
span.Debugf("to read %s with read-shard-x:%d active-shard-n:%d of data-n:%d party-n:%d",
blob.ID(), h.MinReadShardsX, len(sortedVuids), tactic.N, tactic.M)
if len(sortedVuids) < tactic.N {
err = fmt.Errorf("broken %s", blob.ID())
spanpipe.Error(err)
span.Error(err)
ch <- pipeBuffer{err: err}
return
}
@ -222,7 +212,7 @@ func (h *Handler) Get(ctx context.Context, w io.Writer, location proto.Location,
err = h.readOneBlob(ctx, getTime, serviceController, blob, sortedVuids, shards)
if err != nil {
spanpipe.Error("read one blob", blob.ID(), err)
span.Error("read one blob", blob.ID(), err)
for _, buf := range shards {
h.memPool.Put(buf)
}
@ -265,7 +255,7 @@ func (h *Handler) Get(ctx context.Context, w io.Writer, location proto.Location,
continue
}
toRead := util.Min(toReadSize, l-off)
toRead := minU64(toReadSize, l-off)
if _, e := w.Write(buf[off : off+toRead]); e != nil {
err = errors.Info(e, "write to response")
break
@ -311,8 +301,7 @@ func (h *Handler) Get(ctx context.Context, w io.Writer, location proto.Location,
// 4. Just read essential bytes if the data is a segment of one shard.
func (h *Handler) readOneBlob(ctx context.Context, getTime *timeReadWrite,
serviceController controller.ServiceController,
blob blobGetArgs, sortedVuids []sortedVuid, shards [][]byte,
) error {
blob blobGetArgs, sortedVuids []sortedVuid, shards [][]byte) error {
span := trace.SpanFromContextSafe(ctx)
tactic := blob.CodeMode.Tactic()
@ -380,31 +369,7 @@ func (h *Handler) readOneBlob(ctx context.Context, getTime *timeReadWrite,
startRead := time.Now()
reconstructed := false
got, mostTime := 0, 0
for shard := range shardPipe {
if got++; got == dataN-1 {
mostTime = shard.time
}
var shardSpeed float32
if shard.time > 0 {
shardSpeed = float32(blob.ShardReadSize) / (float32(shard.time) / 1e9) / (1 << 10)
}
// find slow data shard, if it speed greater than index data-1 shard time.
if mostTime > 0 && shard.time/1e6 > h.LogSlowBaseTimeMS &&
shardSpeed < float32(h.LogSlowBaseSpeedKB) &&
float32(shard.time) > h.LogSlowTimeFator*float32(mostTime) {
var logvuid sortedVuid
for idx := range sortedVuids {
if sortedVuids[idx].index == shard.index {
logvuid = sortedVuids[idx]
break
}
}
span.Warnf("slow disk(host:%s diskid:%d) time(most:%dms shard:%dms) speed:%.2fKB/s",
logvuid.host, logvuid.diskID, mostTime/1e6, shard.time/1e6, shardSpeed)
}
// swap shard buffer
if shard.status {
buf := shards[shard.index]
@ -497,8 +462,7 @@ func (h *Handler) readOneBlob(ctx context.Context, getTime *timeReadWrite,
}
func (h *Handler) readOneShard(ctx context.Context, serviceController controller.ServiceController,
blob blobGetArgs, vuid sortedVuid, stopChan <-chan struct{},
) shardData {
blob blobGetArgs, vuid sortedVuid, stopChan <-chan struct{}) shardData {
clusterID, vid := blob.Cid, blob.Vid
shardOffset, shardReadSize := blob.ShardOffset, blob.ShardReadSize
span := trace.SpanFromContextSafe(ctx)
@ -506,14 +470,12 @@ func (h *Handler) readOneShard(ctx context.Context, serviceController controller
index: vuid.index,
status: false,
}
shardStart := time.Now()
args := blobnode.RangeGetShardArgs{
GetShardArgs: blobnode.GetShardArgs{
DiskID: vuid.diskID,
Vuid: vuid.vuid,
Bid: blob.Bid,
Type: blobnode.ReadIO,
},
Offset: int64(shardOffset),
Size: int64(shardReadSize),
@ -522,10 +484,9 @@ func (h *Handler) readOneShard(ctx context.Context, serviceController controller
var (
err error
body io.ReadCloser
crc uint32
)
if hErr := hystrix.Do(rwCommand, func() error {
body, crc, err = h.getOneShardFromHost(ctx, serviceController, vuid.host, vuid.diskID, args,
body, err = h.getOneShardFromHost(ctx, serviceController, vuid.host, vuid.diskID, args,
vuid.index, clusterID, vid, 3, stopChan)
if err != nil && (errorTimeout(err) || rpc.DetectStatusCode(err) == errcode.CodeOverload) {
return err
@ -537,13 +498,10 @@ func (h *Handler) readOneShard(ctx context.Context, serviceController controller
}
if err != nil {
if err == errPunishedDisk {
if err == errPunishedDisk || err == errCanceledReadShard {
span.Warnf("read %s on %s: %s", blob.ID(), vuid.ID(), err.Error())
return shardResult
}
if err == errCanceledReadShard {
return shardResult
}
span.Warnf("rpc read %s on %s: %s", blob.ID(), vuid.ID(), errors.Detail(err))
return shardResult
}
@ -561,26 +519,14 @@ func (h *Handler) readOneShard(ctx context.Context, serviceController controller
span.Warnf("io read %s on %s: %s", blob.ID(), vuid.ID(), err.Error())
return shardResult
}
if h.ShardCrcReadEnable && crc > 0 {
newCrc := crc32.ChecksumIEEE(buf[shardOffset : shardOffset+shardReadSize])
if newCrc != crc {
h.memPool.Put(buf)
reportDownload(clusterID, "Download", "CrcMismatch")
span.Errorf("blob:%+v vuid:%s crc mismatch 0x%x(%d) != 0x%x(%d)",
blob, vuid.ID(), crc, crc, newCrc, newCrc)
return shardResult
}
}
shardResult.status = true
shardResult.buffer = buf
shardResult.time = int(time.Since(shardStart))
return shardResult
}
func (h *Handler) getDataShardOnly(ctx context.Context, getTime *timeReadWrite,
w io.Writer, serviceController controller.ServiceController, blob blobGetArgs,
) error {
w io.Writer, serviceController controller.ServiceController, blob blobGetArgs) error {
span := trace.SpanFromContextSafe(ctx)
if blob.ReadSize == 0 {
return nil
@ -603,10 +549,6 @@ func (h *Handler) getDataShardOnly(ctx context.Context, getTime *timeReadWrite,
firstShardIdx := int(blob.Offset) / shardSize
shardOffset := int(blob.Offset) % shardSize
ctx, cancel := context.WithDeadline(ctx,
time.Now().Add(time.Millisecond*time.Duration(h.ReadDataOnlyTimeoutMS)))
defer cancel()
startRead := time.Now()
remainSize := blob.ReadSize
bufOffset := 0
@ -615,30 +557,22 @@ func (h *Handler) getDataShardOnly(ctx context.Context, getTime *timeReadWrite,
break
}
diskInfo, err := serviceController.GetDiskHost(ctx, shard.DiskID)
if err != nil {
span.Warnf("get disk host failed: %s", err)
return errNeedReconstructRead
}
host := diskInfo.Host
toReadSize := util.Min(remainSize, uint64(shardSize-shardOffset))
toReadSize := minU64(remainSize, uint64(shardSize-shardOffset))
args := blobnode.RangeGetShardArgs{
GetShardArgs: blobnode.GetShardArgs{
DiskID: shard.DiskID,
Vuid: shard.Vuid,
Bid: blob.Bid,
Type: blobnode.ReadIO,
},
Offset: int64(shardOffset),
Size: int64(toReadSize),
}
body, crc, err := h.getOneShardFromHost(ctx, serviceController, host, shard.DiskID, args,
body, err := h.getOneShardFromHost(ctx, serviceController, shard.Host, shard.DiskID, args,
firstShardIdx+i, blob.Cid, blob.Vid, 1, nil)
if err != nil {
span.Warnf("read %s on blobnode(vuid:%d disk:%d host:%s) ecidx(%02d): %s", blob.ID(),
shard.Vuid, shard.DiskID, host, firstShardIdx+i, errors.Detail(err))
shard.Vuid, shard.DiskID, shard.Host, firstShardIdx+i, errors.Detail(err))
return errNeedReconstructRead
}
defer body.Close()
@ -649,14 +583,6 @@ func (h *Handler) getDataShardOnly(ctx context.Context, getTime *timeReadWrite,
span.Warn(err)
return errNeedReconstructRead
}
if h.ShardCrcReadEnable && crc > 0 {
if newCrc := crc32.ChecksumIEEE(buf); newCrc != crc {
reportDownload(blob.Cid, "Download", "CrcMismatch")
span.Errorf("blob:%+v (host:%s diskid:%d vuid:%d) crc mismatch 0x%x(%d) != 0x%x(%d)",
blob, host, shard.DiskID, shard.Vuid, crc, crc, newCrc, newCrc)
return errNeedReconstructRead
}
}
// reset next shard offset
shardOffset = 0
@ -684,16 +610,20 @@ func (h *Handler) getOneShardFromHost(ctx context.Context, serviceController con
host string, diskID proto.DiskID, args blobnode.RangeGetShardArgs, // get shard param with host diskid
index int, clusterID proto.ClusterID, vid proto.Vid, // param to update volume cache
attempts int, cancelChan <-chan struct{}, // do not retry again if cancelChan was closed
) (rbody io.ReadCloser, rcrc uint32, rerr error) {
) (io.ReadCloser, error) {
span := trace.SpanFromContextSafe(ctx)
// skip punished disk
if diskHost, err := serviceController.GetDiskHost(ctx, diskID); err != nil {
return nil, 0, err
return nil, err
} else if diskHost.Punished {
return nil, 0, errPunishedDisk
return nil, errPunishedDisk
}
var (
rbody io.ReadCloser
rerr error
)
rerr = retry.ExponentialBackoff(attempts, 200).RuptOn(func() (bool, error) {
if cancelChan != nil {
select {
@ -703,10 +633,14 @@ func (h *Handler) getOneShardFromHost(ctx context.Context, serviceController con
}
}
body, crc, err := h.blobnodeClient.RangeGetShard(ctx, host, &args)
// new child span to get from blobnode, we should finish it here.
spanChild, ctxChild := trace.StartSpanFromContextWithTraceID(
context.Background(), "GetFromBlobnode", span.TraceID())
defer spanChild.Finish()
body, _, err := h.blobnodeClient.RangeGetShard(ctxChild, host, &args)
if err == nil {
rbody = body
rcrc = crc
return true, nil
}
@ -754,12 +688,10 @@ func (h *Handler) getOneShardFromHost(ctx context.Context, serviceController con
// do not retry on timeout then punish threshold this disk
if errorTimeout(err) {
h.updateVolume(ctx, clusterID, vid)
h.punishDiskWith(ctx, clusterID, diskID, host, "Timeout")
return true, err
}
if errorConnectionRefused(err) {
h.updateVolume(ctx, clusterID, vid)
return true, err
}
span.Debugf("read from disk:%d blobnode/%s", diskID, err.Error())
@ -768,17 +700,17 @@ func (h *Handler) getOneShardFromHost(ctx context.Context, serviceController con
return false, err
})
return
return rbody, rerr
}
func genLocationBlobs(location *proto.Location, readSize uint64, offset uint64) ([]blobGetArgs, error) {
if readSize > location.Size_ || offset > location.Size_ || offset+readSize > location.Size_ {
return nil, fmt.Errorf("FileSize:%d ReadSize:%d Offset:%d", location.Size_, readSize, offset)
func genLocationBlobs(location *access.Location, readSize uint64, offset uint64) ([]blobGetArgs, error) {
if readSize > location.Size || offset > location.Size || offset+readSize > location.Size {
return nil, fmt.Errorf("FileSize:%d ReadSize:%d Offset:%d", location.Size, readSize, offset)
}
blobSize := uint64(location.SliceSize)
blobSize := uint64(location.BlobSize)
if blobSize <= 0 {
return nil, fmt.Errorf("SliceSize:%d", blobSize)
return nil, fmt.Errorf("BlobSize:%d", blobSize)
}
remainSize := readSize
@ -789,8 +721,8 @@ func genLocationBlobs(location *proto.Location, readSize uint64, offset uint64)
idx := uint64(0)
blobs := make([]blobGetArgs, 0, 1+(readSize+blobOffset)/blobSize)
for _, blob := range location.Slices {
currBlobID := blob.MinSliceID
for _, blob := range location.Blobs {
currBlobID := blob.MinBid
for ii := uint32(0); ii < blob.Count; ii++ {
if remainSize <= 0 {
@ -798,10 +730,10 @@ func genLocationBlobs(location *proto.Location, readSize uint64, offset uint64)
}
if idx >= firstBlobIdx {
toReadSize := util.Min(remainSize, blobSize-blobOffset)
toReadSize := minU64(remainSize, blobSize-blobOffset)
if toReadSize > 0 {
// update the last blob size
fixedBlobSize := util.Min(location.Size_-idx*blobSize, blobSize)
fixedBlobSize := minU64(location.Size-idx*blobSize, blobSize)
sizes, _ := ec.GetBufferSizes(int(fixedBlobSize), tactic)
shardSize := sizes.ShardSize
@ -839,10 +771,8 @@ func genLocationBlobs(location *proto.Location, readSize uint64, offset uint64)
return blobs, nil
}
func genSortedVuidByIDC(ctx context.Context,
serviceController controller.ServiceController, idc string, vuidPhys []controller.Unit,
ordered bool, ignoreIDC bool,
) []sortedVuid {
func genSortedVuidByIDC(ctx context.Context, serviceController controller.ServiceController, idc string,
vuidPhys []controller.Unit) []sortedVuid {
span := trace.SpanFromContextSafe(ctx)
vuids := make([]sortedVuid, 0, len(vuidPhys))
@ -862,11 +792,7 @@ func genSortedVuidByIDC(ctx context.Context,
continue
}
destIDC := hostIDC.IDC
if ignoreIDC {
destIDC = idc
}
dis := distance(idc, destIDC, hostIDC.Punished)
dis := distance(idc, hostIDC.IDC, hostIDC.Punished)
if _, ok := sortMap[dis]; !ok {
sortMap[dis] = make([]sortedVuid, 0, 8)
}
@ -874,7 +800,7 @@ func genSortedVuidByIDC(ctx context.Context,
index: idx,
vuid: phy.Vuid,
diskID: phy.DiskID,
host: hostIDC.Host,
host: phy.Host,
})
}
@ -886,11 +812,9 @@ func genSortedVuidByIDC(ctx context.Context,
for _, dis := range keys {
ids := sortMap[dis]
if !ordered {
rand.Shuffle(len(ids), func(i, j int) {
ids[i], ids[j] = ids[j], ids[i]
})
}
rand.Shuffle(len(ids), func(i, j int) {
ids[i], ids[j] = ids[j], ids[i]
})
vuids = append(vuids, ids...)
if dis > 1 {
span.Debugf("distance: %d punished vuids: %+v", dis, ids)
@ -917,7 +841,7 @@ func emptyDataShardIndexes(sizes ec.BufferSizes) map[int]struct{} {
firstEmptyIdx := (sizes.DataSize + sizes.ShardSize - 1) / sizes.ShardSize
n := sizes.ECDataSize / sizes.ShardSize
if firstEmptyIdx >= n {
return nil
return make(map[int]struct{})
}
set := make(map[int]struct{}, n-firstEmptyIdx)

View File

@ -12,7 +12,7 @@
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package stream
package access
import (
"bytes"
@ -24,6 +24,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/cubefs/cubefs/blobstore/api/access"
"github.com/cubefs/cubefs/blobstore/common/codemode"
"github.com/cubefs/cubefs/blobstore/common/proto"
)
@ -34,7 +35,7 @@ func TestAccessStreamGetBase(t *testing.T) {
{
dataShards.clean()
data := []byte("x")
loc, err := streamer.Put(ctx(), bytes.NewReader(data), int64(len(data)), nil, proto.ClusterID(0), codemode.CodeModeNone)
loc, err := streamer.Put(ctx(), bytes.NewReader(data), int64(len(data)), nil)
require.NoError(t, err)
buff := bytes.NewBuffer(nil)
@ -47,7 +48,7 @@ func TestAccessStreamGetBase(t *testing.T) {
{
dataShards.clean()
data := []byte("x")
loc, err := streamer.Put(ctx(), bytes.NewReader(data), int64(len(data)), nil, proto.ClusterID(0), codemode.CodeModeNone)
loc, err := streamer.Put(ctx(), bytes.NewReader(data), int64(len(data)), nil)
require.NoError(t, err)
buff := bytes.NewBuffer(nil)
@ -83,7 +84,7 @@ func TestAccessStreamGetBase(t *testing.T) {
size := cs.size
data := make([]byte, size)
rand.Read(data)
loc, err := streamer.Put(ctx(), bytes.NewReader(data), int64(size), nil, proto.ClusterID(0), codemode.CodeModeNone)
loc, err := streamer.Put(ctx(), bytes.NewReader(data), int64(size), nil)
require.NoError(t, err)
buff := bytes.NewBuffer(nil)
@ -117,7 +118,7 @@ func TestAccessStreamGetBroken(t *testing.T) {
rand.Read(data)
// time wait the punished services
time.Sleep(time.Second * time.Duration(punishServiceS))
loc, err := streamer.Put(ctx(), bytes.NewReader(data), int64(size), nil, proto.ClusterID(0), codemode.CodeModeNone)
loc, err := streamer.Put(ctx(), bytes.NewReader(data), int64(size), nil)
require.NoError(t, err)
cases := []struct {
@ -183,7 +184,7 @@ func TestAccessStreamGetOffset(t *testing.T) {
size := cs.size
data := make([]byte, size)
rand.Read(data)
loc, err := streamer.Put(ctx(), bytes.NewReader(data), size, nil, proto.ClusterID(0), codemode.CodeModeNone)
loc, err := streamer.Put(ctx(), bytes.NewReader(data), size, nil)
require.NoError(t, err)
buff := bytes.NewBuffer(nil)
@ -210,7 +211,7 @@ func TestAccessStreamGetShardTimeout(t *testing.T) {
size := 1 << 22
buff := make([]byte, size)
rand.Read(buff)
loc, err := streamer.Put(ctx(), bytes.NewReader(buff), int64(size), nil, proto.ClusterID(0), codemode.CodeModeNone)
loc, err := streamer.Put(ctx(), bytes.NewReader(buff), int64(size), nil)
require.NoError(t, err)
// no delay when blocking one shard, cos MinReadShardsX = 1
@ -219,9 +220,13 @@ func TestAccessStreamGetShardTimeout(t *testing.T) {
vuidController.Unblock(1001)
}()
{
startTime := time.Now()
transfer, _ := streamer.Get(ctx(), bytes.NewBuffer(nil), *loc, uint64(size), 0)
err := transfer()
require.NoError(t, err)
duration := time.Since(startTime)
require.GreaterOrEqual(t, vuidController.duration, duration, "greater duration: ", duration)
}
// delay one duration when blocking two shard, cos MinReadShardsX = 1
@ -244,59 +249,6 @@ func TestAccessStreamGetShardTimeout(t *testing.T) {
}
}
func TestAccessStreamGetShardSlow(t *testing.T) {
ctx := ctxWithName("TestAccessStreamGetShardSlow")
dataShards.clean()
vuidController.Unbreak(1005)
streamer.MinReadShardsX = 0
defer func() {
vuidController.SetSlowdown(1001, -1)
vuidController.Break(1005)
streamer.MinReadShardsX = minReadShardsX
dataShards.clean()
}()
size := 1 << 20
buff := make([]byte, size)
rand.Read(buff)
loc, err := streamer.Put(ctx(), bytes.NewReader(buff), int64(size), nil, proto.ClusterID(0), codemode.CodeModeNone)
require.NoError(t, err)
vuidController.SetSlowdown(1001, 500*time.Millisecond)
transfer, err := streamer.Get(ctx(), bytes.NewBuffer(nil), *loc, uint64(size), 0)
require.NoError(t, err)
err = transfer()
require.NoError(t, err)
}
func TestAccessStreamGetShardCrcMismatch(t *testing.T) {
ctx := ctxWithName("TestAccessStreamGetShardCrcMismatch")
vuidController.Unbreak(1005)
streamer.MinReadShardsX = 0
defer func() {
vuidController.SetCrcMismatch(1001, false)
vuidController.SetCrcMismatch(1002, false)
vuidController.Break(1005)
streamer.MinReadShardsX = minReadShardsX
dataShards.clean()
}()
vuidController.SetCrcMismatch(1001, true)
vuidController.SetCrcMismatch(1002, true)
for _, size := range []int{1, 1023, 2048, 1 << 20} {
dataShards.clean()
buff := make([]byte, size)
rand.Read(buff)
loc, err := streamer.Put(ctx(), bytes.NewReader(buff), int64(size), nil, proto.ClusterID(0), codemode.CodeModeNone)
require.NoError(t, err)
transfer, err := streamer.Get(ctx(), bytes.NewBuffer(nil), *loc, uint64(size), 0)
require.NoError(t, err)
err = transfer()
require.NoError(t, err)
}
}
func TestAccessStreamGetShardBroken(t *testing.T) {
ctx := ctxWithName("TestAccessStreamGetShardBroken")
dataShards.clean()
@ -311,7 +263,7 @@ func TestAccessStreamGetShardBroken(t *testing.T) {
size := 1 << 22
buff := make([]byte, size)
rand.Read(buff)
loc, err := streamer.Put(ctx(), bytes.NewReader(buff), int64(size), nil, proto.ClusterID(0), codemode.CodeModeNone)
loc, err := streamer.Put(ctx(), bytes.NewReader(buff), int64(size), nil)
require.NoError(t, err)
// no delay when blocking one shard, cos MinReadShardsX = 1
@ -339,39 +291,6 @@ func TestAccessStreamGetShardBroken(t *testing.T) {
}
}
func TestAccessStreamGetShardOnlyTimeout(t *testing.T) {
ctx := ctxWithName("TestAccessStreamGetShardOnlyTimeout")
dataShards.clean()
oldMs := streamer.ReadDataOnlyTimeoutMS
streamer.ReadDataOnlyTimeoutMS = 100
defer func() {
streamer.ReadDataOnlyTimeoutMS = oldMs
dataShards.clean()
}()
size := 1
buff := make([]byte, size)
rand.Read(buff)
loc, err := streamer.Put(ctx(), bytes.NewReader(buff), int64(size), nil, proto.ClusterID(0), codemode.CodeModeNone)
require.NoError(t, err)
// blocking the data shard, force to waiting ReadDataOnlyTimeoutMS
vuidController.Block(1001)
defer func() {
vuidController.Unblock(1001)
}()
{
startTime := time.Now()
transfer, err := streamer.Get(ctx(), bytes.NewBuffer(nil), *loc, uint64(size), 0)
require.NoError(t, err)
err = transfer()
require.NoError(t, err)
duration := time.Since(startTime)
require.GreaterOrEqual(t, duration, 100*time.Millisecond, "greater duration:", duration)
}
}
func TestAccessStreamGetLocalIDC(t *testing.T) {
ctx := ctxWithName("TestAccessStreamGetLocalIDC")
dataShards.clean()
@ -384,7 +303,7 @@ func TestAccessStreamGetLocalIDC(t *testing.T) {
size := 1 << 22
buff := make([]byte, size)
rand.Read(buff)
loc, err := streamer.Put(ctx(), bytes.NewReader(buff), int64(size), nil, proto.ClusterID(0), codemode.CodeModeNone)
loc, err := streamer.Put(ctx(), bytes.NewReader(buff), int64(size), nil)
require.NoError(t, err)
// no delay when blocking other idc all shards
@ -495,7 +414,7 @@ func TestAccessStreamGetAligned(t *testing.T) {
data := make([]byte, cs.size)
rand.Read(data)
loc, err := streamer.Put(ctx(), bytes.NewReader(data), int64(cs.size), nil, proto.ClusterID(0), codemode.CodeModeNone)
loc, err := streamer.Put(ctx(), bytes.NewReader(data), int64(cs.size), nil)
require.NoError(t, err)
// cos put shards asynchronously, should wait all shard written
@ -525,21 +444,21 @@ func TestAccessStreamGenLocationBlobs(t *testing.T) {
firstSliceStart := proto.BlobID(100)
secondSliceStart := proto.BlobID(200)
loc := proto.Location{
loc := access.Location{
ClusterID: 0,
CodeMode: codemode.EC6P6,
Size_: 1024*4 + 37 + 1024*2, // 5 fine blobs and 2 missing blobs
SliceSize: 1024,
Slices: []proto.Slice{
Size: 1024*4 + 37 + 1024*2, // 5 fine blobs and 2 missing blobs
BlobSize: 1024,
Blobs: []access.SliceInfo{
{
MinSliceID: firstSliceStart,
Vid: proto.Vid(1001),
Count: 3,
MinBid: firstSliceStart,
Vid: proto.Vid(1001),
Count: 3,
},
{
MinSliceID: secondSliceStart,
Vid: proto.Vid(2001),
Count: 2,
MinBid: secondSliceStart,
Vid: proto.Vid(2001),
Count: 2,
},
},
}
@ -675,7 +594,7 @@ func BenchmarkAccessStreamGet(b *testing.B) {
for _, cs := range cases {
b.ResetTimer()
b.Run(cs.name, func(b *testing.B) {
loc, err := streamer.Put(ctx, newReader(cs.size), int64(cs.size), nil, proto.ClusterID(0), codemode.CodeModeNone)
loc, err := streamer.Put(ctx, newReader(cs.size), int64(cs.size), nil)
require.NoError(b, err)
b.ResetTimer()

View File

@ -12,7 +12,7 @@
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package stream
package access
import (
"context"

View File

@ -12,7 +12,11 @@
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package stream
package access
// github.com/cubefs/cubefs/blobstore/access/... module access interfaces
//go:generate mockgen -destination=./controller_mock_test.go -package=access -mock_names ClusterController=MockClusterController,ServiceController=MockServiceController,VolumeGetter=MockVolumeGetter github.com/cubefs/cubefs/blobstore/access/controller ClusterController,ServiceController,VolumeGetter
//go:generate mockgen -destination=./access_mock_test.go -package=access -mock_names StreamHandler=MockStreamHandler,Limiter=MockLimiter github.com/cubefs/cubefs/blobstore/access StreamHandler,Limiter
import (
"bytes"
@ -21,6 +25,7 @@ import (
"fmt"
"hash/crc32"
"io"
"io/ioutil"
"math"
"math/rand"
"strconv"
@ -43,7 +48,6 @@ import (
"github.com/cubefs/cubefs/blobstore/common/trace"
"github.com/cubefs/cubefs/blobstore/testing/mocks"
_ "github.com/cubefs/cubefs/blobstore/testing/nolog"
"github.com/cubefs/cubefs/blobstore/util/bytespool"
)
var (
@ -78,10 +82,10 @@ var (
cc controller.ClusterController
clusterInfo *clustermgr.ClusterInfo
dataVolume *clustermgr.VolumeInfo
dataVolume *proxy.VersionVolume
dataAllocs []proxy.AllocRet
dataNodes map[string]clustermgr.ServiceInfo
dataDisks map[proto.DiskID]clustermgr.BlobNodeDiskInfo
dataDisks map[proto.DiskID]blobnode.DiskInfo
dataShards *shardsData
vuidController *vuidControl
@ -143,8 +147,6 @@ type vuidControl struct {
mutex sync.Mutex
broken map[proto.Vuid]bool
blocked map[proto.Vuid]bool
slowdown map[proto.Vuid]time.Duration
crc map[proto.Vuid]bool
block func()
duration time.Duration
@ -189,36 +191,6 @@ func (c *vuidControl) Isblocked(id proto.Vuid) bool {
return ok && v
}
func (c *vuidControl) SetSlowdown(id proto.Vuid, t time.Duration) {
c.mutex.Lock()
if t < 0 {
delete(c.slowdown, id)
} else {
c.slowdown[id] = t
}
c.mutex.Unlock()
}
func (c *vuidControl) GetSlowdown(id proto.Vuid) time.Duration {
c.mutex.Lock()
v := c.slowdown[id]
c.mutex.Unlock()
return v
}
func (c *vuidControl) SetCrcMismatch(id proto.Vuid, crc bool) {
c.mutex.Lock()
c.crc[id] = crc
c.mutex.Unlock()
}
func (c *vuidControl) GetCrcMismatch(id proto.Vuid) bool {
c.mutex.Lock()
v := c.crc[id]
c.mutex.Unlock()
return v
}
func (c *vuidControl) SetBNRealError(b bool) {
c.mutex.Lock()
c.isBNRealError = b
@ -237,8 +209,7 @@ func randBlobnodeRealError(errors []errcode.Error) error {
}
var storageAPIRangeGetShard = func(ctx context.Context, host string, args *blobnode.RangeGetShardArgs) (
body io.ReadCloser, shardCrc uint32, err error,
) {
body io.ReadCloser, shardCrc uint32, err error) {
if vuidController.Isbroken(args.Vuid) {
err = errors.New("get shard fake error")
if vuidController.IsBNRealError() {
@ -255,9 +226,6 @@ var storageAPIRangeGetShard = func(ctx context.Context, host string, args *blobn
}
return
}
if slow := vuidController.GetSlowdown(args.Vuid); slow > 0 {
time.Sleep(slow)
}
buff := dataShards.get(args.Vuid, args.Bid)
if len(buff) == 0 {
@ -267,21 +235,15 @@ var storageAPIRangeGetShard = func(ctx context.Context, host string, args *blobn
err = errors.New("get shard concurrently")
return
}
if len(buff) == int(args.Size) {
shardCrc = crc32.ChecksumIEEE(buff)
if vuidController.GetCrcMismatch(args.Vuid) {
shardCrc++
}
}
buff = buff[int(args.Offset):int(args.Offset+args.Size)]
body = io.NopCloser(bytes.NewReader(buff))
shardCrc = crc32.ChecksumIEEE(buff)
body = ioutil.NopCloser(bytes.NewReader(buff))
return
}
var storageAPIPutShard = func(ctx context.Context, host string, args *blobnode.PutShardArgs) (
crc uint32, err error,
) {
crc uint32, err error) {
if vuidController.Isbroken(args.Vuid) {
err = errors.New("put shard fake error")
if vuidController.IsBNRealError() {
@ -294,21 +256,14 @@ var storageAPIPutShard = func(ctx context.Context, host string, args *blobnode.P
err = errors.New("put shard timeout")
return
}
if slow := vuidController.GetSlowdown(args.Vuid); slow > 0 {
time.Sleep(slow)
}
buffer, _ := memPool.Alloc(int(args.Size))
defer memPool.Put(buffer)
buffer = buffer[:int(args.Size)]
if args.NopData {
bytespool.Zero(buffer)
} else {
_, err = io.ReadFull(args.Body, buffer)
if err != nil {
return
}
_, err = io.ReadFull(args.Body, buffer)
if err != nil {
return
}
crc = crc32.ChecksumIEEE(buffer)
@ -329,7 +284,7 @@ func initMockData() {
Vid: volumeID,
}
dataVolume = &clustermgr.VolumeInfo{
dataVolume = &proxy.VersionVolume{VolumeInfo: clustermgr.VolumeInfo{
VolumeInfoBase: clustermgr.VolumeInfoBase{
Vid: volumeID,
CodeMode: codemode.EC6P6,
@ -339,11 +294,12 @@ func initMockData() {
units = append(units, clustermgr.Unit{
Vuid: proto.Vuid(id),
DiskID: proto.DiskID(id),
Host: strconv.Itoa(id),
})
}
return
}(),
}
}}
proxyNodes := make([]clustermgr.ServiceNode, 32)
for idx := range proxyNodes {
@ -360,17 +316,17 @@ func initMockData() {
Nodes: proxyNodes,
}
dataDisks = make(map[proto.DiskID]clustermgr.BlobNodeDiskInfo)
dataDisks = make(map[proto.DiskID]blobnode.DiskInfo)
for _, id := range idcID {
dataDisks[proto.DiskID(id)] = clustermgr.BlobNodeDiskInfo{
DiskInfo: clustermgr.DiskInfo{ClusterID: clusterID, Idc: idc, Host: strconv.Itoa(id)},
DiskHeartBeatInfo: clustermgr.DiskHeartBeatInfo{DiskID: proto.DiskID(id)},
dataDisks[proto.DiskID(id)] = blobnode.DiskInfo{
ClusterID: clusterID, Idc: idc, Host: strconv.Itoa(id),
DiskHeartBeatInfo: blobnode.DiskHeartBeatInfo{DiskID: proto.DiskID(id)},
}
}
for _, id := range idcOtherID {
dataDisks[proto.DiskID(id)] = clustermgr.BlobNodeDiskInfo{
DiskInfo: clustermgr.DiskInfo{ClusterID: clusterID, Idc: idcOther, Host: strconv.Itoa(id)},
DiskHeartBeatInfo: clustermgr.DiskHeartBeatInfo{DiskID: proto.DiskID(id)},
dataDisks[proto.DiskID(id)] = blobnode.DiskInfo{
ClusterID: clusterID, Idc: idcOther, Host: strconv.Itoa(id),
DiskHeartBeatInfo: blobnode.DiskHeartBeatInfo{DiskID: proto.DiskID(id)},
}
}
@ -401,7 +357,7 @@ func initMockData() {
proxycli.EXPECT().GetCacheVolume(gomock.Any(), gomock.Any(), gomock.Any()).
AnyTimes().Return(dataVolume, nil)
proxycli.EXPECT().GetCacheDisk(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().DoAndReturn(
func(_ context.Context, _ string, args *proxy.CacheDiskArgs) (*clustermgr.BlobNodeDiskInfo, error) {
func(_ context.Context, _ string, args *proxy.CacheDiskArgs) (*blobnode.DiskInfo, error) {
if val, ok := dataDisks[args.DiskID]; ok {
return &val, nil
}
@ -410,26 +366,17 @@ func initMockData() {
serviceController, _ = controller.NewServiceController(
controller.ServiceConfig{
ClusterID: clusterID,
IDC: idc,
ServiceReloadSecs: 1000,
ClusterID: clusterID,
IDC: idc,
ReloadSec: 1000,
}, cmcli, proxycli, nil)
volumeGetter, _ = controller.NewVolumeGetter(controller.VolumeConfig{
ClusterID: clusterID,
VolumeMemcacheExpirationMs: -1,
}, serviceController, proxycli, nil)
volumeGetter, _ = controller.NewVolumeGetter(clusterID, serviceController, proxycli, 0)
ctr = gomock.NewController(&testing.T{})
c := NewMockClusterController(ctr)
c.EXPECT().Region().AnyTimes().Return("test-region")
c.EXPECT().ChooseOne().AnyTimes().Return(clusterInfo, nil)
c.EXPECT().GetServiceController(gomock.Any()).AnyTimes().DoAndReturn(
func(needClusterID proto.ClusterID) (controller.ServiceController, error) {
if needClusterID != clusterID {
return nil, fmt.Errorf("no service controller of %d", needClusterID)
}
return serviceController, nil
})
c.EXPECT().GetServiceController(gomock.Any()).AnyTimes().Return(serviceController, nil)
c.EXPECT().GetVolumeGetter(gomock.Any()).AnyTimes().Return(volumeGetter, nil)
c.EXPECT().ChangeChooseAlg(gomock.Any()).AnyTimes().DoAndReturn(
func(alg controller.AlgChoose) error {
@ -446,11 +393,6 @@ func initMockData() {
allocCli.EXPECT().SendShardRepairMsg(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(nil)
allocCli.EXPECT().VolumeAlloc(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().DoAndReturn(
func(ctx context.Context, host string, args *proxy.AllocVolsArgs) ([]proxy.AllocRet, error) {
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
}
if args.Fsize > allocTimeoutSize {
return nil, errAllocTimeout
}
@ -524,10 +466,8 @@ func initEC() {
func initController() {
vuidController = &vuidControl{
broken: make(map[proto.Vuid]bool),
blocked: make(map[proto.Vuid]bool),
slowdown: make(map[proto.Vuid]time.Duration),
crc: make(map[proto.Vuid]bool),
broken: make(map[proto.Vuid]bool),
blocked: make(map[proto.Vuid]bool),
block: func() {
time.Sleep(200 * time.Millisecond)
},
@ -563,8 +503,6 @@ func init() {
initMockData()
initController()
hystrix.ConfigureCommand(allocCommand, hystrix.CommandConfig{Timeout: defaultAllocatorTimeout})
streamer = &Handler{
memPool: memPool,
encoder: encoder,
@ -583,11 +521,6 @@ func init() {
AllocRetryTimes: 3,
AllocRetryIntervalMS: 3000,
MinReadShardsX: minReadShardsX,
ReadDataOnlyTimeoutMS: 10000,
ShardCrcReadEnable: true,
LogSlowBaseTimeMS: 10,
LogSlowBaseSpeedKB: 1 << 10,
LogSlowTimeFator: 1.3,
},
discardVidChan: make(chan discardVid, 8),
stopCh: make(chan struct{}),

View File

@ -12,7 +12,7 @@
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package stream
package access
import (
"bytes"
@ -28,24 +28,23 @@ import (
"github.com/cubefs/cubefs/blobstore/api/access"
"github.com/cubefs/cubefs/blobstore/api/blobnode"
"github.com/cubefs/cubefs/blobstore/common/codemode"
"github.com/cubefs/cubefs/blobstore/common/ec"
errcode "github.com/cubefs/cubefs/blobstore/common/errors"
"github.com/cubefs/cubefs/blobstore/common/proto"
"github.com/cubefs/cubefs/blobstore/common/rpc"
"github.com/cubefs/cubefs/blobstore/common/trace"
"github.com/cubefs/cubefs/blobstore/util/errors"
"github.com/cubefs/cubefs/blobstore/util/retry"
)
// TODO: To Be Continue
// put empty shard to blobnode if file has been aligned.
// Put put one object
//
// required: size, file size
// optional: hasher map to calculate hash.Hash
func (h *Handler) Put(ctx context.Context,
rc io.Reader, size int64, hasherMap access.HasherMap,
assignClusterID proto.ClusterID, codeMode codemode.CodeMode,
) (*proto.Location, error) {
func (h *Handler) Put(ctx context.Context, rc io.Reader, size int64,
hasherMap access.HasherMap) (*access.Location, error) {
span := trace.SpanFromContextSafe(ctx)
span.Debugf("put request size:%d hashes:b(%b)", size, hasherMap.ToHashAlgorithm())
@ -63,20 +62,11 @@ func (h *Handler) Put(ctx context.Context,
}
// 2.choose cluster and alloc volume from allocator
selectedCodeMode := codeMode
if selectedCodeMode == codemode.CodeModeNone {
selectedCodeMode = h.allCodeModes.SelectCodeMode(size)
} else {
valid := h.allCodeModes.VerifySelectCodeMode(selectedCodeMode)
if !valid {
span.Errorf("specify codemode %d not found in codemode policy", selectedCodeMode)
return nil, errcode.ErrIllegalArguments
}
}
span.Debugf("select codemode %d, specify codemode %d", selectedCodeMode, codeMode)
selectedCodeMode := h.allCodeModes.SelectCodeMode(size)
span.Debugf("select codemode %d", selectedCodeMode)
blobSize := atomic.LoadUint32(&h.MaxBlobSize)
clusterID, blobs, err := h.allocFromAllocatorWithHystrix(ctx, selectedCodeMode, uint64(size), blobSize, assignClusterID)
clusterID, blobs, err := h.allocFromAllocatorWithHystrix(ctx, selectedCodeMode, uint64(size), blobSize, 0)
if err != nil {
span.Error("alloc failed", errors.Detail(err))
return nil, err
@ -85,20 +75,19 @@ func (h *Handler) Put(ctx context.Context,
// 3.read body and split, alloc from mem pool;ec encode and put into data node
limitReader := io.LimitReader(rc, int64(size))
location := &proto.Location{
location := &access.Location{
ClusterID: clusterID,
CodeMode: selectedCodeMode,
Size_: uint64(size),
SliceSize: blobSize,
Slices: blobs,
Size: uint64(size),
BlobSize: blobSize,
Blobs: blobs,
}
uploadSucc := false
defer func() {
if !uploadSucc {
span.Infof("put failed clean location %+v", location)
_, newCtx := trace.StartSpanFromContextWithTraceID(context.Background(), "", span.TraceID())
if err := h.clearGarbage(newCtx, location); err != nil {
if err := h.clearGarbage(ctx, location); err != nil {
span.Warn(errors.Detail(err))
}
}
@ -110,7 +99,6 @@ func (h *Handler) Put(ctx context.Context,
// release ec buffer which have not takeover
buffer.Release()
span.AppendRPCTrackLog([]string{putTime.String()})
putTime.Report(clusterID.ToString(), h.IDC, true)
}()
// concurrent buffer in per request
@ -133,7 +121,6 @@ func (h *Handler) Put(ctx context.Context,
if err != nil {
return nil, err
}
empties := emptyDataShardIndexes(buffer.BufferSizes)
readBuff := buffer.DataBuf[:bsize]
shards, err := encoder.Split(buffer.ECDataBuf)
@ -166,7 +153,7 @@ func (h *Handler) Put(ctx context.Context,
buffer = nil
<-ready
startWrite := time.Now()
err = h.writeToBlobnodesWithHystrix(ctx, blobident, shards, empties, func() {
err = h.writeToBlobnodesWithHystrix(ctx, blobident, shards, func() {
takeoverBuffer.Release()
ready <- struct{}{}
})
@ -181,12 +168,11 @@ func (h *Handler) Put(ctx context.Context,
}
func (h *Handler) writeToBlobnodesWithHystrix(ctx context.Context,
blob blobIdent, shards [][]byte, empties map[int]struct{}, callback func(),
) error {
blob blobIdent, shards [][]byte, callback func()) error {
safe := make(chan struct{}, 1)
err := hystrix.Do(rwCommand, func() error {
safe <- struct{}{}
return h.writeToBlobnodes(ctx, blob, shards, empties, callback)
return h.writeToBlobnodes(ctx, blob, shards, callback)
}, nil)
select {
@ -206,8 +192,8 @@ type shardPutStatus struct {
// takeover ec buffer release by callback.
// return if had quorum successful shards, then wait all shards in background.
func (h *Handler) writeToBlobnodes(ctx context.Context,
blob blobIdent, shards [][]byte, empties map[int]struct{}, callback func(),
) (err error) {
blob blobIdent, shards [][]byte, callback func()) (err error) {
span := trace.SpanFromContextSafe(ctx)
clusterID, vid, bid := blob.cid, blob.vid, blob.bid
wg := &sync.WaitGroup{}
@ -235,14 +221,6 @@ func (h *Handler) writeToBlobnodes(ctx context.Context,
putQuorum = uint32(num)
}
span := trace.SpanFromContextSafe(ctx)
// new context span to write blobnode in background
span, ctx = trace.StartSpanFromContextWithTraceID(context.Background(), "", span.TraceID())
defer span.Finish()
writeStart := time.Now()
writeTime := int32(0)
// writtenNum ONLY apply on data and partiy shards
// TODO: count N and M in each AZ,
// decision ec data is recoverable or not.
@ -260,27 +238,28 @@ func (h *Handler) writeToBlobnodes(ctx context.Context,
wg.Done()
}()
_, empty := empties[index]
diskID := unit.DiskID
args := &blobnode.PutShardArgs{
DiskID: diskID,
Vuid: unit.Vuid,
Bid: bid,
Size: int64(len(shards[index])),
Type: blobnode.WriteIO,
NopData: empty,
Type: blobnode.NormalIO,
}
crcDisable := h.ShardCrcWriteDisable
crcDisabled := h.ShardCrcDisabled
var crcOrigin uint32
if !crcDisable {
if !crcDisabled {
crcOrigin = crc32.ChecksumIEEE(shards[index])
}
// new child span to write to blobnode, we should finish it here.
spanChild, ctxChild := trace.StartSpanFromContextWithTraceID(
context.Background(), "WriteToBlobnode", span.TraceID())
defer spanChild.Finish()
RETRY:
hostInfo, err := serviceController.GetDiskHost(ctx, diskID)
hostInfo, err := serviceController.GetDiskHost(ctxChild, diskID)
if err != nil {
span.Error("get disk host failed", errors.Detail(err))
return
@ -299,28 +278,14 @@ func (h *Handler) writeToBlobnodes(ctx context.Context,
crc uint32
)
writeErr = retry.ExponentialBackoff(3, 200).RuptOn(func() (bool, error) {
if !args.NopData {
args.Body = bytes.NewReader(shards[index])
}
args.Body = bytes.NewReader(shards[index])
crc, err = h.blobnodeClient.PutShard(ctx, host, args)
crc, err = h.blobnodeClient.PutShard(ctxChild, host, args)
if err == nil {
if !crcDisable && crc != crcOrigin {
if !crcDisabled && crc != crcOrigin {
return false, fmt.Errorf("crc mismatch 0x%x != 0x%x", crc, crcOrigin)
}
// slow disk if speed lower and time greater than most shards, also retry.
if mostTime := atomic.LoadInt32(&writeTime); mostTime > 0 {
shardTime := time.Since(writeStart)
shardSpeed := float32(args.Size) / (float32(shardTime) / 1e9) / (1 << 10)
if int(shardTime/1e6) > h.LogSlowBaseTimeMS &&
shardSpeed < float32(h.LogSlowBaseSpeedKB) &&
float32(shardTime) > h.LogSlowTimeFator*float32(mostTime) {
span.Warnf("slow disk(host:%s diskid:%d) time(most:%dms shard:%dms) speed:%.2fKB/s",
host, diskID, mostTime/1e6, shardTime/1e6, shardSpeed)
}
}
needRetry = false
return true, nil
}
@ -339,7 +304,7 @@ func (h *Handler) writeToBlobnodes(ctx context.Context,
switch code {
// EIO and Readonly error, then we need to punish disk in local and no necessary to retry
case errcode.CodeVUIDReadonly:
case errcode.CodeDiskBroken, errcode.CodeVUIDReadonly:
h.punishVolume(ctx, clusterID, vid, host, "BrokenOrRO")
h.punishDisk(ctx, clusterID, diskID, host, "BrokenOrRO")
span.Warnf("punish disk:%d volume:%d cos:blobnode/%d", diskID, vid, code)
@ -351,10 +316,9 @@ func (h *Handler) writeToBlobnodes(ctx context.Context,
span.Warnf("punish volume:%d cos:blobnode/%d", vid, code)
return true, err
// disk broken may be some chunks have repaired
// vuid not found means the reflection between vuid and diskID has change, we should refresh the volume
// disk not found means disk has been repaired or offline
case errcode.CodeDiskBroken, errcode.CodeDiskNotFound, errcode.CodeVuidNotFound:
case errcode.CodeDiskNotFound, errcode.CodeVuidNotFound:
latestVolume, e := h.getVolume(ctx, clusterID, vid, false)
if e != nil {
return true, errors.Base(err, "get volume with no cache failed").Detail(e)
@ -371,12 +335,8 @@ func (h *Handler) writeToBlobnodes(ctx context.Context,
return true, err
}
reason := "NotFound"
if code == errcode.CodeDiskBroken {
reason = "Broken"
}
h.punishVolume(ctx, clusterID, vid, host, reason)
h.punishDisk(ctx, clusterID, diskID, host, reason)
h.punishVolume(ctx, clusterID, vid, host, "NotFound")
h.punishDisk(ctx, clusterID, diskID, host, "NotFound")
span.Warnf("punish disk:%d volume:%d cos:blobnode/%d", diskID, vid, code)
return true, err
default:
@ -384,9 +344,8 @@ func (h *Handler) writeToBlobnodes(ctx context.Context,
// in timeout case and writtenNum is not satisfied with putQuorum, then should retry
if errorTimeout(err) && atomic.LoadUint32(&writtenNum) < putQuorum {
h.updateVolume(ctx, clusterID, vid)
h.punishDiskWith(ctx, clusterID, diskID, host, "Timeout")
span.Warn("timeout need to punish threshold disk", diskID, host)
span.Warn("connect timeout, need to punish threshold disk", diskID, host)
return false, err
}
@ -414,11 +373,6 @@ func (h *Handler) writeToBlobnodes(ctx context.Context,
for len(received) < len(volume.Units) && atomic.LoadUint32(&writtenNum) < putQuorum {
st := <-statusCh
received[st.index] = st
// trace slow disk after written 3/4 shards
if atomic.LoadInt32(&writeTime) == 0 && len(received) > len(volume.Units)*3/4 {
atomic.StoreInt32(&writeTime, int32(time.Since(writeStart)))
}
}
writeDone := make(chan struct{}, 1)

View File

@ -12,7 +12,7 @@
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package stream
package access
import (
"bytes"
@ -28,7 +28,6 @@ import (
"github.com/stretchr/testify/require"
"github.com/cubefs/cubefs/blobstore/api/access"
"github.com/cubefs/cubefs/blobstore/common/codemode"
errcode "github.com/cubefs/cubefs/blobstore/common/errors"
"github.com/cubefs/cubefs/blobstore/common/proto"
)
@ -43,41 +42,41 @@ func TestAccessStreamPutBase(t *testing.T) {
// 0
{
size := 0
_, err := streamer.Put(ctx(), newReader(size), int64(size), nil, proto.ClusterID(0), codemode.CodeModeNone)
_, err := streamer.Put(ctx(), newReader(size), int64(size), nil)
require.Error(t, err)
}
// 1 byte
{
size := 1
loc, err := streamer.Put(ctx(), newReader(size), int64(size), nil, proto.ClusterID(0), codemode.CodeModeNone)
loc, err := streamer.Put(ctx(), newReader(size), int64(size), nil)
require.NoError(t, err)
require.Equal(t, 1, len(loc.Slices))
require.Equal(t, uint32(1), loc.Slices[0].Count)
require.Equal(t, 1, len(loc.Blobs))
require.Equal(t, uint32(1), loc.Blobs[0].Count)
// time wait the punished services
time.Sleep(time.Second * time.Duration(punishServiceS))
}
// <4M
{
size := 1 << 18
loc, err := streamer.Put(ctx(), newReader(size), int64(size), nil, proto.ClusterID(0), codemode.CodeModeNone)
loc, err := streamer.Put(ctx(), newReader(size), int64(size), nil)
require.NoError(t, err)
require.Equal(t, 1, len(loc.Slices))
require.Equal(t, uint32(1), loc.Slices[0].Count)
require.Equal(t, 1, len(loc.Blobs))
require.Equal(t, uint32(1), loc.Blobs[0].Count)
time.Sleep(time.Second * time.Duration(punishServiceS))
}
// 8M + 1k
{
size := (1 << 23) + 1024
loc, err := streamer.Put(ctx(), newReader(size), int64(size), nil, proto.ClusterID(0), codemode.CodeModeNone)
loc, err := streamer.Put(ctx(), newReader(size), int64(size), nil)
require.NoError(t, err)
require.Equal(t, 2, len(loc.Slices))
require.Equal(t, uint32(2), loc.Slices[1].Count)
require.Equal(t, 2, len(loc.Blobs))
require.Equal(t, uint32(2), loc.Blobs[1].Count)
time.Sleep(time.Second * time.Duration(punishServiceS))
}
// max size + 1
{
size := defaultMaxObjectSize + 1
_, err := streamer.Put(ctx(), nil, int64(size), nil, proto.ClusterID(0), codemode.CodeModeNone)
_, err := streamer.Put(ctx(), nil, int64(size), nil)
require.EqualError(t, errcode.ErrAccessExceedSize, err.Error())
}
@ -98,7 +97,7 @@ func TestAccessStreamPutSum(t *testing.T) {
}
hashSumMap := make(access.HashSumMap, len(hasherMap))
_, err := streamer.Put(ctx(), bytes.NewReader(data), int64(len(data)), hasherMap, proto.ClusterID(0), codemode.CodeModeNone)
_, err := streamer.Put(ctx(), bytes.NewReader(data), int64(len(data)), hasherMap)
require.NoError(t, err)
for alg, hasher := range hasherMap {
hashSumMap[alg] = hasher.Sum(nil)
@ -232,7 +231,7 @@ func TestAccessStreamPutShardTimeout(t *testing.T) {
buff := make([]byte, size)
rand.Read(buff)
startTime := time.Now()
loc, err := streamer.Put(ctx(), bytes.NewReader(buff), int64(size), nil, proto.ClusterID(0), codemode.CodeModeNone)
loc, err := streamer.Put(ctx(), bytes.NewReader(buff), int64(size), nil)
require.NoError(t, err)
// response immediately if had quorum shards
@ -248,7 +247,7 @@ func TestAccessStreamPutShardTimeout(t *testing.T) {
vuidController.Block(1002)
{
startTime := time.Now()
_, err := streamer.Put(ctx(), bytes.NewReader(buff), int64(size), nil, proto.ClusterID(0), codemode.CodeModeNone)
_, err := streamer.Put(ctx(), bytes.NewReader(buff), int64(size), nil)
require.Error(t, err)
duration := time.Since(startTime)
@ -260,25 +259,6 @@ func TestAccessStreamPutShardTimeout(t *testing.T) {
}
}
func TestAccessStreamPutShardSlow(t *testing.T) {
ctx := ctxWithName("TestAccessStreamPutShardSlow")
dataShards.clean()
vuidController.SetSlowdown(1001, time.Second)
vuidController.Unbreak(1005)
defer func() {
vuidController.SetSlowdown(1001, -1)
vuidController.Break(1005)
dataShards.clean()
}()
size := 3
buff := make([]byte, size)
rand.Read(buff)
_, err := streamer.Put(ctx(), bytes.NewReader(buff), int64(size), nil, proto.ClusterID(0), codemode.CodeModeNone)
require.NoError(t, err)
time.Sleep(time.Second)
}
func TestAccessStreamPutQuorum(t *testing.T) {
ctx := ctxWithName("TestAccessStreamPutQuorum")
defer func() {
@ -319,7 +299,7 @@ func TestAccessStreamPutQuorum(t *testing.T) {
vuidController.Break(id)
}
_, err := streamer.Put(ctx(), bytes.NewReader(buff), int64(size), nil, proto.ClusterID(0), codemode.CodeModeNone)
_, err := streamer.Put(ctx(), bytes.NewReader(buff), int64(size), nil)
if cs.hasError {
require.NotNil(t, err)
} else {
@ -333,49 +313,6 @@ func TestAccessStreamPutQuorum(t *testing.T) {
}
}
func TestAccessStreamPutWithClusterIDAndCodeMode(t *testing.T) {
ctx := ctxWithName("TestAccessStreamPutWithClusterIDAndCodeMode")
{
size := 1 << 22
_, err := streamer.Put(ctx(), newReader(size), int64(size), nil, proto.ClusterID(0), codemode.EC16P20L2)
require.Error(t, err)
}
{
size := 1 << 22
_, err := streamer.Put(ctx(), newReader(size), int64(size), nil, proto.ClusterID(2), codemode.CodeModeNone)
require.Error(t, err)
}
{
size := 1 << 22
_, err := streamer.Put(ctx(), newReader(size), int64(size), nil, proto.ClusterID(2), codemode.EC15P12)
require.Error(t, err)
}
{
size := 1 << 20
loc, err := streamer.Put(ctx(), newReader(size), int64(size), nil, proto.ClusterID(0), codemode.EC6P6)
require.NoError(t, err)
require.Equal(t, codemode.EC6P6, loc.CodeMode)
require.Equal(t, 1, len(loc.Slices))
require.Equal(t, uint32(1), loc.Slices[0].Count)
}
{
size := 1 << 22
loc, err := streamer.Put(ctx(), newReader(size), int64(size), nil, proto.ClusterID(1), codemode.EC6P6)
require.NoError(t, err)
require.Equal(t, codemode.EC6P6, loc.CodeMode)
require.Equal(t, 1, len(loc.Slices))
require.Equal(t, uint32(1), loc.Slices[0].Count)
}
{
size := 1 << 22
loc, err := streamer.Put(ctx(), newReader(size), int64(size), nil, proto.ClusterID(1), codemode.CodeModeNone)
require.NoError(t, err)
require.Equal(t, codemode.EC6P6, loc.CodeMode)
require.Equal(t, 1, len(loc.Slices))
require.Equal(t, uint32(1), loc.Slices[0].Count)
}
}
func BenchmarkAccessStreamPut(b *testing.B) {
ctx := ctxWithName("BenchmarkAccessStreamPut")()
vuidController.Unbreak(1005)
@ -401,7 +338,7 @@ func BenchmarkAccessStreamPut(b *testing.B) {
b.ResetTimer()
b.Run(cs.name, func(b *testing.B) {
for ii := 0; ii <= b.N; ii++ {
streamer.Put(ctx, bytes.NewReader(buff[:cs.size]), int64(cs.size), nil, proto.ClusterID(0), codemode.CodeModeNone)
streamer.Put(ctx, bytes.NewReader(buff[:cs.size]), int64(cs.size), nil)
}
})
}

View File

@ -12,7 +12,7 @@
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package stream
package access
import (
"context"
@ -33,9 +33,8 @@ import (
// required: size, one blob size
// optional: hasherMap, computing hash
func (h *Handler) PutAt(ctx context.Context, rc io.Reader,
clusterID proto.ClusterID, vid proto.Vid, bid proto.BlobID,
size int64, hasherMap access.HasherMap,
) error {
clusterID proto.ClusterID, vid proto.Vid, bid proto.BlobID, size int64,
hasherMap access.HasherMap) error {
span := trace.SpanFromContextSafe(ctx)
span.Debugf("putat request cluster:%d vid:%d bid:%d size:%d hashes:b(%b)",
clusterID, vid, bid, size, hasherMap.ToHashAlgorithm())
@ -56,14 +55,12 @@ func (h *Handler) PutAt(ctx context.Context, rc io.Reader,
if err != nil {
return err
}
empties := emptyDataShardIndexes(buffer.BufferSizes)
putTime := new(timeReadWrite)
putTime.IncA(time.Since(st))
defer func() {
buffer.Release()
span.AppendRPCTrackLog([]string{putTime.String()})
putTime.Report(clusterID.ToString(), h.IDC, true)
}()
shards, err := encoder.Split(buffer.ECDataBuf)
@ -93,7 +90,7 @@ func (h *Handler) PutAt(ctx context.Context, rc io.Reader,
takeoverBuffer := buffer
buffer = nil
startWrite := time.Now()
err = h.writeToBlobnodesWithHystrix(ctx, blobident, shards, empties, func() {
err = h.writeToBlobnodesWithHystrix(ctx, blobident, shards, func() {
takeoverBuffer.Release()
})
putTime.IncW(time.Since(startWrite))

View File

@ -12,7 +12,7 @@
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package stream
package access
import (
"bytes"

View File

@ -12,7 +12,7 @@
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package stream
package access
import (
"bytes"
@ -24,7 +24,6 @@ import (
"github.com/cubefs/cubefs/blobstore/access/controller"
"github.com/cubefs/cubefs/blobstore/common/codemode"
"github.com/cubefs/cubefs/blobstore/common/proto"
)
func newReader(size int) io.Reader {
@ -45,42 +44,25 @@ func TestAccessStreamConfig(t *testing.T) {
ConsulAgentAddr: "http://127.0.0.1:8500",
},
}
err := confCheck(&cfg)
confCheck(&cfg)
require.NoError(t, err)
require.Equal(t, idcOther, cfg.IDC)
require.Equal(t, map[int]int{1024: 1}, cfg.MemPoolSizeClasses)
require.Equal(t, defaultDiskPunishIntervalS, cfg.DiskPunishIntervalS)
cfg = StreamConfig{
IDC: idcOther,
MemPoolSizeClasses: map[int]int{1024: 1},
CodeModesPutQuorums: map[codemode.CodeMode]int{
codemode.EC15P12: 16,
codemode.EC6P10L2: 18,
},
ClusterConfig: controller.ClusterConfig{
Clusters: []controller.Cluster{
{ClusterID: 1, Hosts: []string{"host1"}},
{ClusterID: 2, Hosts: []string{"host2"}},
},
},
}
err = confCheck(&cfg)
require.NoError(t, err)
}
func TestAccessStreamNew(t *testing.T) {
require.Equal(t, idc, streamer.IDC)
_, err := NewStreamHandler(&StreamConfig{IDC: "idc"}, nil)
require.NotNil(t, err)
require.Panics(t, func() {
NewStreamHandler(&StreamConfig{IDC: "idc"}, nil)
})
}
func TestAccessStreamDelete(t *testing.T) {
ctx := ctxWithName("TestAccessStreamDelete")
size := 1 << 18
loc, err := streamer.Put(ctx(), newReader(size), int64(size), nil, proto.ClusterID(0), codemode.CodeModeNone)
loc, err := streamer.Put(ctx(), newReader(size), int64(size), nil)
require.NoError(t, err)
err = streamer.Delete(ctx(), loc)
@ -95,19 +77,19 @@ func TestAccessStreamAdmin(t *testing.T) {
sa := handler.Admin()
require.NotNil(t, sa)
admin := sa.(*StreamAdmin)
require.Nil(t, admin.MemPool)
require.Nil(t, admin.Controller)
admin := sa.(*streamAdmin)
require.Nil(t, admin.memPool)
require.Nil(t, admin.controller)
}
{
sa := streamer.Admin()
require.NotNil(t, sa)
admin := sa.(*StreamAdmin)
require.NotNil(t, admin.MemPool)
t.Log("mempool status:", admin.MemPool.Status())
admin := sa.(*streamAdmin)
require.NotNil(t, admin.memPool)
t.Log("mempool status:", admin.memPool.Status())
ctr := admin.Controller
ctr := admin.controller
require.NotNil(t, ctr)
t.Log("region:", ctr.Region())
require.Error(t, ctr.ChangeChooseAlg(controller.AlgChoose(100)))

View File

@ -12,7 +12,7 @@
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package stream
package access
import (
"fmt"
@ -38,16 +38,6 @@ func (t *timeReadWrite) IncW(dur time.Duration) {
atomic.AddInt64(&t.w, int64(dur))
}
func (t *timeReadWrite) Report(cid, idc string, upload bool) {
if upload {
reportReadwrite(cid, idc, "upload_read", atomic.LoadInt64(&t.r)/1e6)
reportReadwrite(cid, idc, "upload_write", atomic.LoadInt64(&t.w)/1e6)
} else {
reportReadwrite(cid, idc, "download_read", atomic.LoadInt64(&t.r)/1e6)
reportReadwrite(cid, idc, "download_write", atomic.LoadInt64(&t.w)/1e6)
}
}
// String within milliseconds
func (t *timeReadWrite) String() string {
a := atomic.LoadInt64(&t.a) / 1e6

View File

@ -22,13 +22,12 @@ import (
"net/http"
"runtime"
"sort"
"sync"
"sync/atomic"
"time"
"github.com/hashicorp/consul/api"
"gopkg.in/natefinch/lumberjack.v2"
"github.com/cubefs/cubefs/blobstore/api/shardnode"
errcode "github.com/cubefs/cubefs/blobstore/common/errors"
"github.com/cubefs/cubefs/blobstore/common/proto"
"github.com/cubefs/cubefs/blobstore/common/resourcepool"
@ -43,7 +42,9 @@ import (
const (
defaultMaxSizePutOnce int64 = 1 << 28 // 256MB
defaultMaxPartRetry int = 3
defaultMaxHostRetry int = 3
defaultPartConcurrence int = 4
defaultServiceInterval int = 3600 // one hour.
defaultServiceName = "access"
)
@ -51,7 +52,9 @@ const (
type RPCConnectMode uint8
// timeout: [short - - - - - - - - -> long]
// ----- quick --> general --> default --> slow --> nolimit
//
// quick --> general --> default --> slow --> nolimit
//
// speed: 40MB --> 20MB --> 10MB --> 4MB --> nolimit
const (
DefaultConnMode RPCConnectMode = iota
@ -88,7 +91,9 @@ func (mode RPCConnectMode) getConfig(speed float64, timeout, baseTimeout int64)
BodyBaseTimeoutMs: getBaseTimeout(30 * 1000),
Tc: rpc.TransportConfig{
// dial timeout
DialTimeoutMs: 200,
DialTimeoutMs: 5 * 1000,
// response header timeout after send the request
ResponseHeaderTimeoutMs: 5 * 1000,
// IdleConnTimeout is the maximum amount of time an idle
// (keep-alive) connection will remain idle before closing
// itself.Zero means no limit.
@ -106,21 +111,29 @@ func (mode RPCConnectMode) getConfig(speed float64, timeout, baseTimeout int64)
config.ClientTimeoutMs = getTimeout(getSpeed(40))
config.BodyBandwidthMBPs = getSpeed(40)
config.BodyBaseTimeoutMs = getBaseTimeout(3 * 1000)
config.Tc.DialTimeoutMs = 2 * 1000
config.Tc.ResponseHeaderTimeoutMs = 2 * 1000
config.Tc.IdleConnTimeoutMs = 10 * 1000
case GeneralConnMode:
config.ClientTimeoutMs = getTimeout(getSpeed(20))
config.BodyBandwidthMBPs = getSpeed(20)
config.BodyBaseTimeoutMs = getBaseTimeout(10 * 1000)
config.Tc.DialTimeoutMs = 3 * 1000
config.Tc.ResponseHeaderTimeoutMs = 3 * 1000
config.Tc.IdleConnTimeoutMs = 30 * 1000
case SlowConnMode:
config.ClientTimeoutMs = getTimeout(getSpeed(4))
config.BodyBandwidthMBPs = getSpeed(4)
config.BodyBaseTimeoutMs = getBaseTimeout(120 * 1000)
config.Tc.DialTimeoutMs = 10 * 1000
config.Tc.ResponseHeaderTimeoutMs = 10 * 1000
config.Tc.IdleConnTimeoutMs = 60 * 1000
case NoLimitConnMode:
config.ClientTimeoutMs = 0
config.BodyBandwidthMBPs = getSpeed(0)
config.BodyBaseTimeoutMs = getBaseTimeout(0)
config.Tc.DialTimeoutMs = 0
config.Tc.ResponseHeaderTimeoutMs = 0
config.Tc.IdleConnTimeoutMs = 600 * 1000
default:
}
@ -131,62 +144,61 @@ func (mode RPCConnectMode) getConfig(speed float64, timeout, baseTimeout int64)
// Config access client config
type Config struct {
// ConnMode rpc connection timeout setting
ConnMode RPCConnectMode `json:"connection_mode"`
ConnMode RPCConnectMode
// ClientTimeoutMs the whole request and response timeout
ClientTimeoutMs int64 `json:"client_timeout_ms"`
ClientTimeoutMs int64
// BodyBandwidthMBPs reading body timeout, request or response
// timeout = ContentLength/BodyBandwidthMBPs + BodyBaseTimeoutMs
BodyBandwidthMBPs float64 `json:"body_bandwidth_mbps"`
BodyBandwidthMBPs float64
// BodyBaseTimeoutMs base timeout for read body
BodyBaseTimeoutMs int64 `json:"body_base_timeout_ms"`
BodyBaseTimeoutMs int64
// Consul is consul config for discovering service
Consul ConsulConfig `json:"consul"`
// ServiceIntervalS is interval seconds for discovering service hosts,
// at least 5 seconds and default is 5 minutes.
ServiceIntervalS int `json:"service_interval_s"`
Consul ConsulConfig
// ServiceIntervalS is interval seconds for discovering service
ServiceIntervalS int
// PriorityAddrs priority addrs of access service when retry
PriorityAddrs []string `json:"priority_addrs"`
// MaxSizePutOnce max size using once-put object interface, default is 256MB.
MaxSizePutOnce int64 `json:"max_size_put_once"`
PriorityAddrs []string
// MaxSizePutOnce max size using once-put object interface
MaxSizePutOnce int64
// MaxPartRetry max retry times when putting one part, 0 means forever
MaxPartRetry int `json:"max_part_retry"`
// MaxHostRetry max retry hosts of access, default all hosts.
MaxHostRetry int `json:"max_host_retry"`
MaxPartRetry int
// MaxHostRetry max retry hosts of access service
MaxHostRetry int
// PartConcurrence concurrence of put parts
PartConcurrence int `json:"part_concurrence"`
PartConcurrence int
// rpc selector config
// Failure retry interval, default value is 300s,
// if FailRetryIntervalS < 0, remove failed hosts will not work.
FailRetryIntervalS int `json:"fail_retry_interval_s"`
// Failure retry interval, default value is -1, if FailRetryIntervalS < 0,
// remove failed hosts will not work.
FailRetryIntervalS int
// Within MaxFailsPeriodS, if the number of failures is greater than or equal to MaxFails,
// the host is considered disconnected.
MaxFailsPeriodS int `json:"max_fails_period_s"`
MaxFailsPeriodS int
// HostTryTimes Number of host failure retries
HostTryTimes int `json:"host_try_times"`
HostTryTimes int
// RPCConfig user-defined rpc config
// All connections will use the config if it's not nil
// ConnMode will be ignored if rpc config is setting
RPCConfig *rpc.Config `json:"rpc_config"`
RPCConfig *rpc.Config
// LogLevel client output logging level.
LogLevel log.Level `json:"log_level"`
LogLevel log.Level
// Logger trace all logging to the logger if setting.
// It is an io.WriteCloser that writes to the specified filename.
// YOU should CLOSE it after you do not use the client anymore.
Logger *Logger `json:"logger"`
Logger *Logger
}
// ConsulConfig alias of consul api.Config
// Fixup: client and sdk using the same config type
type ConsulConfig = api.Config
// Logger alias of AsyncLogger
// Logger alias of lumberjack Logger
// See more at: https://github.com/natefinch/lumberjack
type Logger = log.AsyncLogger
type Logger = lumberjack.Logger
// client access rpc client
type client struct {
@ -203,24 +215,12 @@ type API interface {
//
// If PutArgs' body is of type *bytes.Buffer, *bytes.Reader, or *strings.Reader,
// GetBody is populated, then the Put once request has retry ability.
Put(ctx context.Context, args *PutArgs) (location proto.Location, hashSumMap HashSumMap, err error)
Put(ctx context.Context, args *PutArgs) (location Location, hashSumMap HashSumMap, err error)
// Get object, range is supported.
Get(ctx context.Context, args *GetArgs) (body io.ReadCloser, err error)
// Delete all blobs in these locations.
//
// Returns:
// - (nil, nil): all blobs deleted successfully.
// - (nil, ErrIllegalArguments): when args is invalid.
// - (failedLocations, err): returns the list of locations that have not yet been deleted.
Delete(ctx context.Context, args *DeleteArgs) (failedLocations []proto.Location, err error)
}
type Client interface {
API
ListBlob(ctx context.Context, args *ListBlobArgs) (shardnode.ListBlobRet, error)
GetBlob(ctx context.Context, args *GetBlobArgs) (io.ReadCloser, error)
DeleteBlob(ctx context.Context, args *DelBlobArgs) error
PutBlob(ctx context.Context, args *PutBlobArgs) (proto.ClusterID, HashSumMap, error)
// return failed locations which have yet been deleted if error is not nil.
Delete(ctx context.Context, args *DeleteArgs) (failedLocations []Location, err error)
}
var _ API = (*client)(nil)
@ -232,42 +232,28 @@ var _ io.ReadCloser = (*noopBody)(nil)
func (rc noopBody) Read(p []byte) (n int, err error) { return 0, io.EOF }
func (rc noopBody) Close() error { return nil }
var (
memPool *resourcepool.MemPool
poolOnce sync.Once
)
var memPool *resourcepool.MemPool
func lazyInitSingletonMemPool() {
poolOnce.Do(func() {
if memPool == nil {
memPool = resourcepool.NewMemPool(map[int]int{
1 << 12: -1,
1 << 14: -1,
1 << 18: -1,
1 << 20: -1,
1 << 22: -1,
1 << 23: -1,
1 << 24: -1,
})
}
func init() {
memPool = resourcepool.NewMemPool(map[int]int{
1 << 12: -1,
1 << 14: -1,
1 << 18: -1,
1 << 20: -1,
1 << 22: -1,
1 << 23: -1,
1 << 24: -1,
})
}
// ResetMemoryPool is thread unsafe, call it on init.
func ResetMemoryPool(sizeClasses map[int]int) {
memPool = resourcepool.NewMemPool(sizeClasses)
}
// New returns an access API
func New(cfg Config) (API, error) {
defaulter.LessOrEqual(&cfg.MaxSizePutOnce, defaultMaxSizePutOnce)
defaulter.Less(&cfg.MaxPartRetry, defaultMaxPartRetry)
defaulter.LessOrEqual(&cfg.MaxHostRetry, defaultMaxHostRetry)
defaulter.LessOrEqual(&cfg.PartConcurrence, defaultPartConcurrence)
defaulter.Equal(&cfg.FailRetryIntervalS, 300)
defaulter.LessOrEqual(&cfg.MaxFailsPeriodS, 10)
defaulter.Equal(&cfg.ServiceIntervalS, 300) // 5 minutes
if cfg.ServiceIntervalS < 5 {
cfg.ServiceIntervalS = 5
if cfg.ServiceIntervalS < 300 { // at least 5 minutes
cfg.ServiceIntervalS = defaultServiceInterval
}
log.SetOutputLevel(cfg.LogLevel)
@ -275,8 +261,6 @@ func New(cfg Config) (API, error) {
log.SetOutput(cfg.Logger)
}
lazyInitSingletonMemPool()
c := &client{
config: cfg,
stop: make(chan struct{}),
@ -338,11 +322,10 @@ func New(cfg Config) (API, error) {
}
c.rpcClient.Store(getClient(&cfg, hosts))
ticker := time.NewTicker(time.Duration(cfg.ServiceIntervalS) * time.Second)
go func() {
ticker := time.NewTicker(time.Duration(cfg.ServiceIntervalS) * time.Second)
defer ticker.Stop()
for {
old := hosts[:]
old := hosts
select {
case <-ticker.C:
hosts, err = hostGetter()
@ -355,10 +338,10 @@ func New(cfg Config) (API, error) {
if ok && oldClient != nil {
oldClient.Close()
}
log.Warnf("update hosts of client (%v) -> (%v)", old, hosts)
c.rpcClient.Store(getClient(&cfg, hosts))
}
case <-c.stop:
ticker.Stop()
return
}
}
@ -404,13 +387,13 @@ func getClient(cfg *Config, hosts []string) rpc.Client {
return rpc.NewLbClient(lbConfig, nil)
}
func (c *client) Put(ctx context.Context, args *PutArgs) (location proto.Location, hashSumMap HashSumMap, err error) {
func (c *client) Put(ctx context.Context, args *PutArgs) (location Location, hashSumMap HashSumMap, err error) {
if args.Size == 0 {
hashSumMap := args.Hashes.ToHashSumMap()
for alg := range hashSumMap {
hashSumMap[alg] = alg.ToHasher().Sum(nil)
}
return proto.Location{Slices: make([]proto.Slice, 0)}, hashSumMap, nil
return Location{Blobs: make([]SliceInfo, 0)}, hashSumMap, nil
}
ctx = withReqidContext(ctx)
@ -420,19 +403,14 @@ func (c *client) Put(ctx context.Context, args *PutArgs) (location proto.Locatio
return c.putParts(ctx, args)
}
func (c *client) putObject(ctx context.Context, args *PutArgs) (location proto.Location, hashSumMap HashSumMap, err error) {
func (c *client) putObject(ctx context.Context, args *PutArgs) (location Location, hashSumMap HashSumMap, err error) {
rpcClient := c.rpcClient.Load().(rpc.Client)
urlStr := fmt.Sprintf("/put?size=%d&hashes=%d&assign_cluster_id=%d&code_mode=%d",
args.Size, args.Hashes, args.AssignClusterID, args.CodeMode)
urlStr := fmt.Sprintf("/put?size=%d&hashes=%d", args.Size, args.Hashes)
req, err := http.NewRequest(http.MethodPut, urlStr, args.Body)
if err != nil {
return
}
if args.GetBody != nil {
req.GetBody = args.GetBody
}
resp := &PutResp{}
if err = rpcClient.DoWith(ctx, req, resp, rpc.WithCrcEncode()); err == nil {
@ -469,8 +447,7 @@ func (c *client) putPartsBatch(ctx context.Context, parts []blobPart) error {
})
}
newCtx := trace.NewContextFromContext(ctx)
if err := task.Run(ctx, tasks...); err != nil {
if err := task.Run(context.Background(), tasks...); err != nil {
for _, part := range parts {
part := part
// asynchronously delete blob
@ -481,7 +458,7 @@ func (c *client) putPartsBatch(ctx context.Context, parts []blobPart) error {
if err != nil {
return
}
rpcClient.DoWith(newCtx, req, nil)
rpcClient.DoWith(ctx, req, nil)
}()
}
return err
@ -490,8 +467,7 @@ func (c *client) putPartsBatch(ctx context.Context, parts []blobPart) error {
}
func (c *client) readerPipeline(span trace.Span, reqBody io.Reader,
closeCh <-chan struct{}, size, blobSize int,
) <-chan []byte {
closeCh <-chan struct{}, size, blobSize int) <-chan []byte {
ch := make(chan []byte, c.config.PartConcurrence-1)
go func() {
for size > 0 {
@ -525,7 +501,7 @@ func (c *client) readerPipeline(span trace.Span, reqBody io.Reader,
return ch
}
func (c *client) putParts(ctx context.Context, args *PutArgs) (proto.Location, HashSumMap, error) {
func (c *client) putParts(ctx context.Context, args *PutArgs) (Location, HashSumMap, error) {
span := trace.SpanFromContextSafe(ctx)
rpcClient := c.rpcClient.Load().(rpc.Client)
@ -541,7 +517,7 @@ func (c *client) putParts(ctx context.Context, args *PutArgs) (proto.Location, H
}
var (
loc proto.Location
loc Location
tokens []string
)
@ -552,18 +528,16 @@ func (c *client) putParts(ctx context.Context, args *PutArgs) (proto.Location, H
return
}
// force to clean up, even canceled context
newCtx := trace.NewContextFromSpan(span)
locations := signArgs.Locations[:]
if len(locations) > 1 {
signArgs.Location = loc.Copy()
signResp := &SignResp{}
if err := rpcClient.PostWith(newCtx, "/sign", signResp, signArgs); err == nil {
locations = []proto.Location{signResp.Location.Copy()}
if err := rpcClient.PostWith(ctx, "/sign", signResp, signArgs); err == nil {
locations = []Location{signResp.Location.Copy()}
}
}
if len(locations) > 0 {
if _, err := c.Delete(newCtx, &DeleteArgs{Locations: locations}); err != nil {
if _, err := c.Delete(ctx, &DeleteArgs{Locations: locations}); err != nil {
span.Warnf("clean location '%+v' failed %s", locations, err.Error())
}
}
@ -571,12 +545,7 @@ func (c *client) putParts(ctx context.Context, args *PutArgs) (proto.Location, H
// alloc
allocResp := &AllocResp{}
allocArgs := &AllocArgs{
Size: uint64(args.Size),
AssignClusterID: args.AssignClusterID,
CodeMode: args.CodeMode,
}
if err := rpcClient.PostWith(ctx, "/alloc", allocResp, allocArgs); err != nil {
if err := rpcClient.PostWith(ctx, "/alloc", allocResp, AllocArgs{Size: uint64(args.Size)}); err != nil {
return allocResp.Location, nil, err
}
loc = allocResp.Location
@ -585,7 +554,7 @@ func (c *client) putParts(ctx context.Context, args *PutArgs) (proto.Location, H
// buffer pipeline
closeCh := make(chan struct{})
bufferPipe := c.readerPipeline(span, reqBody, closeCh, int(loc.Size_), int(loc.SliceSize))
bufferPipe := c.readerPipeline(span, reqBody, closeCh, int(loc.Size), int(loc.BlobSize))
defer func() {
close(closeCh)
// waiting pipeline close if has error
@ -604,17 +573,17 @@ func (c *client) putParts(ctx context.Context, args *PutArgs) (proto.Location, H
currBlobIdx := 0
currBlobCount := uint32(0)
remainSize := loc.Size_
remainSize := loc.Size
restPartsLoc := loc
readSize := 0
for readSize < int(loc.Size_) {
for readSize < int(loc.Size) {
parts := make([]blobPart, 0, c.config.PartConcurrence)
// waiting at least one blob
buf, ok := <-bufferPipe
if !ok && readSize < int(loc.Size_) {
return proto.Location{}, nil, errcode.ErrAccessReadRequestBody
if !ok && readSize < int(loc.Size) {
return Location{}, nil, errcode.ErrAccessReadRequestBody
}
readSize += len(buf)
parts = append(parts, blobPart{size: len(buf), buf: buf})
@ -624,9 +593,9 @@ func (c *client) putParts(ctx context.Context, args *PutArgs) (proto.Location, H
select {
case buf, ok := <-bufferPipe:
if !ok {
if readSize < int(loc.Size_) {
if readSize < int(loc.Size) {
releaseBuffer(parts)
return proto.Location{}, nil, errcode.ErrAccessReadRequestBody
return Location{}, nil, errcode.ErrAccessReadRequestBody
}
more = false
} else {
@ -640,9 +609,9 @@ func (c *client) putParts(ctx context.Context, args *PutArgs) (proto.Location, H
tryTimes := c.config.MaxPartRetry
for {
if len(loc.Slices) > MaxLocationBlobs {
if len(loc.Blobs) > MaxLocationBlobs {
releaseBuffer(parts)
return proto.Location{}, nil, errcode.ErrUnexpected
return Location{}, nil, errcode.ErrUnexpected
}
// feed new params
@ -650,16 +619,16 @@ func (c *client) putParts(ctx context.Context, args *PutArgs) (proto.Location, H
currCount := currBlobCount
for i := range parts {
token := tokens[currIdx]
if restPartsLoc.Size_ > uint64(loc.SliceSize) && parts[i].size < int(loc.SliceSize) {
token = tokens[len(tokens)-1]
if restPartsLoc.Size > uint64(loc.BlobSize) && parts[i].size < int(loc.BlobSize) {
token = tokens[currIdx+1]
}
parts[i].token = token
parts[i].cid = loc.ClusterID
parts[i].vid = loc.Slices[currIdx].Vid
parts[i].bid = loc.Slices[currIdx].MinSliceID + proto.BlobID(currCount)
parts[i].vid = loc.Blobs[currIdx].Vid
parts[i].bid = loc.Blobs[currIdx].MinBid + proto.BlobID(currCount)
currCount++
if loc.Slices[currIdx].Count == currCount {
if loc.Blobs[currIdx].Count == currCount {
currIdx++
currCount = 0
}
@ -671,7 +640,7 @@ func (c *client) putParts(ctx context.Context, args *PutArgs) (proto.Location, H
remainSize -= uint64(part.size)
currBlobCount++
// next blobs
if loc.Slices[currBlobIdx].Count == currBlobCount {
if loc.Blobs[currBlobIdx].Count == currBlobCount {
currBlobIdx++
currBlobCount = 0
}
@ -685,7 +654,7 @@ func (c *client) putParts(ctx context.Context, args *PutArgs) (proto.Location, H
if tryTimes == 1 {
releaseBuffer(parts)
span.Error("exceed the max retry limit", c.config.MaxPartRetry)
return proto.Location{}, nil, errcode.ErrUnexpected
return Location{}, nil, errcode.ErrUnexpected
}
tryTimes--
}
@ -696,14 +665,14 @@ func (c *client) putParts(ctx context.Context, args *PutArgs) (proto.Location, H
resp := &AllocResp{}
if err := rpcClient.PostWith(ctx, "/alloc", resp, AllocArgs{
Size: remainSize,
BlobSize: loc.SliceSize,
BlobSize: loc.BlobSize,
CodeMode: loc.CodeMode,
AssignClusterID: loc.ClusterID,
}); err != nil {
return true, err
}
if len(resp.Location.Slices) > 0 {
if newVid := resp.Location.Slices[0].Vid; newVid == loc.Slices[currBlobIdx].Vid {
if len(resp.Location.Blobs) > 0 {
if newVid := resp.Location.Blobs[0].Vid; newVid == loc.Blobs[currBlobIdx].Vid {
return false, fmt.Errorf("alloc the same vid %d", newVid)
}
}
@ -713,17 +682,17 @@ func (c *client) putParts(ctx context.Context, args *PutArgs) (proto.Location, H
if err != nil {
releaseBuffer(parts)
span.Error("alloc another parts to put", err)
return proto.Location{}, nil, err
return Location{}, nil, errcode.ErrUnexpected
}
restPartsLoc = restPartsResp.Location
signArgs.Locations = append(signArgs.Locations, restPartsLoc.Copy())
if currBlobCount > 0 {
loc.Slices[currBlobIdx].Count = currBlobCount
loc.Blobs[currBlobIdx].Count = currBlobCount
currBlobIdx++
}
loc.Slices = append(loc.Slices[:currBlobIdx], restPartsLoc.Slices...)
loc.Blobs = append(loc.Blobs[:currBlobIdx], restPartsLoc.Blobs...)
tokens = append(tokens[:currBlobIdx], restPartsResp.Tokens...)
currBlobCount = 0
@ -738,7 +707,7 @@ func (c *client) putParts(ctx context.Context, args *PutArgs) (proto.Location, H
signResp := &SignResp{}
if err := rpcClient.PostWith(ctx, "/sign", signResp, signArgs); err != nil {
span.Error("sign location with crc", err)
return proto.Location{}, nil, err
return Location{}, nil, errcode.ErrUnexpected
}
loc = signResp.Location
}
@ -757,7 +726,7 @@ func (c *client) Get(ctx context.Context, args *GetArgs) (body io.ReadCloser, er
rpcClient := c.rpcClient.Load().(rpc.Client)
ctx = withReqidContext(ctx)
if args.Location.Size_ == 0 || args.ReadSize == 0 {
if args.Location.Size == 0 || args.ReadSize == 0 {
return noopBody{}, nil
}
@ -772,7 +741,7 @@ func (c *client) Get(ctx context.Context, args *GetArgs) (body io.ReadCloser, er
return resp.Body, nil
}
func (c *client) Delete(ctx context.Context, args *DeleteArgs) ([]proto.Location, error) {
func (c *client) Delete(ctx context.Context, args *DeleteArgs) ([]Location, error) {
if !args.IsValid() {
if args == nil {
return nil, errcode.ErrIllegalArguments
@ -782,9 +751,9 @@ func (c *client) Delete(ctx context.Context, args *DeleteArgs) ([]proto.Location
rpcClient := c.rpcClient.Load().(rpc.Client)
ctx = withReqidContext(ctx)
locations := make([]proto.Location, 0, len(args.Locations))
locations := make([]Location, 0, len(args.Locations))
for _, loc := range args.Locations {
if loc.Size_ > 0 {
if loc.Size > 0 {
locations = append(locations, loc.Copy())
}
}

View File

@ -31,8 +31,6 @@ const (
reqidKey
)
var ClientWithReqidContext = withReqidContext
// WithRequestID trace request id in full life of the request
// The second parameter rid could be the one of type below:
//

View File

@ -21,6 +21,7 @@ import (
"fmt"
"hash/crc32"
"io"
"io/ioutil"
mrand "math/rand"
"net/http"
"net/http/httptest"
@ -38,8 +39,8 @@ import (
errcode "github.com/cubefs/cubefs/blobstore/common/errors"
"github.com/cubefs/cubefs/blobstore/common/proto"
"github.com/cubefs/cubefs/blobstore/common/rpc"
"github.com/cubefs/cubefs/blobstore/common/security"
"github.com/cubefs/cubefs/blobstore/common/trace"
"github.com/cubefs/cubefs/blobstore/common/uptoken"
_ "github.com/cubefs/cubefs/blobstore/testing/nolog"
"github.com/cubefs/cubefs/blobstore/util/bytespool"
"github.com/cubefs/cubefs/blobstore/util/log"
@ -157,60 +158,60 @@ func handleAlloc(c *rpc.Context) {
return
}
loc := proto.Location{
loc := access.Location{
ClusterID: 1,
Size_: args.Size,
SliceSize: blobSize,
Slices: []proto.Slice{
Size: args.Size,
BlobSize: blobSize,
Blobs: []access.SliceInfo{
{
MinSliceID: proto.BlobID(mrand.Int()),
Vid: proto.Vid(mrand.Int()),
Count: uint32((args.Size + blobSize - 1) / blobSize),
MinBid: proto.BlobID(mrand.Int()),
Vid: proto.Vid(mrand.Int()),
Count: uint32((args.Size + blobSize - 1) / blobSize),
},
},
}
// split to two blobs if large enough
if loc.Slices[0].Count > 2 {
loc.Slices[0].Count = 2
loc.Slices = append(loc.Slices, []proto.Slice{
if loc.Blobs[0].Count > 2 {
loc.Blobs[0].Count = 2
loc.Blobs = append(loc.Blobs, []access.SliceInfo{
{
MinSliceID: proto.BlobID(mrand.Int()),
Vid: proto.Vid(mrand.Int()),
Count: uint32((args.Size - 2*blobSize + blobSize - 1) / blobSize),
MinBid: proto.BlobID(mrand.Int()),
Vid: proto.Vid(mrand.Int()),
Count: uint32((args.Size - 2*blobSize + blobSize - 1) / blobSize),
},
}...)
}
// alloc the rest parts
if args.AssignClusterID > 0 {
loc.Slices = []proto.Slice{
loc.Blobs = []access.SliceInfo{
{
MinSliceID: proto.BlobID(mrand.Int()),
Vid: proto.Vid(mrand.Int()),
Count: uint32((args.Size + blobSize - 1) / blobSize),
MinBid: proto.BlobID(mrand.Int()),
Vid: proto.Vid(mrand.Int()),
Count: uint32((args.Size + blobSize - 1) / blobSize),
},
}
}
tokens := make([]string, 0, len(loc.Slices)+1)
tokens := make([]string, 0, len(loc.Blobs)+1)
hasMultiBlobs := loc.Size_ >= uint64(loc.SliceSize)
lastSize := uint32(loc.Size_ % uint64(loc.SliceSize))
for idx, blob := range loc.Slices {
hasMultiBlobs := loc.Size >= uint64(loc.BlobSize)
lastSize := uint32(loc.Size % uint64(loc.BlobSize))
for idx, blob := range loc.Blobs {
// returns one token if size < blobsize
if hasMultiBlobs {
count := blob.Count
if idx == len(loc.Slices)-1 && lastSize > 0 {
if idx == len(loc.Blobs)-1 && lastSize > 0 {
count--
}
tokens = append(tokens, security.EncodeToken(security.NewUploadToken(loc.ClusterID,
blob.Vid, blob.MinSliceID, count,
loc.SliceSize, 0, tokenAlloc[:])))
tokens = append(tokens, uptoken.EncodeToken(uptoken.NewUploadToken(loc.ClusterID,
blob.Vid, blob.MinBid, count,
loc.BlobSize, 0, tokenAlloc[:])))
}
// token of the last blob
if idx == len(loc.Slices)-1 && lastSize > 0 {
tokens = append(tokens, security.EncodeToken(security.NewUploadToken(loc.ClusterID,
blob.Vid, blob.MinSliceID+proto.BlobID(blob.Count)-1, 1,
if idx == len(loc.Blobs)-1 && lastSize > 0 {
tokens = append(tokens, uptoken.EncodeToken(uptoken.NewUploadToken(loc.ClusterID,
blob.Vid, blob.MinBid+proto.BlobID(blob.Count)-1, 1,
lastSize, 0, tokenAlloc[:])))
}
}
@ -251,7 +252,7 @@ func handlePut(c *rpc.Context) {
hashSumMap[alg] = hasher.Sum(nil)
}
loc := proto.Location{Size_: uint64(args.Size)}
loc := access.Location{Size: uint64(args.Size)}
fillCrc(&loc)
c.RespondJSON(access.PutResp{
Location: loc,
@ -271,7 +272,7 @@ func handlePutAt(c *rpc.Context) {
return
}
token := security.DecodeToken(args.Token)
token := uptoken.DecodeToken(args.Token)
if !token.IsValid(args.ClusterID, args.Vid, args.BlobID, uint32(args.Size), tokenPutat[:]) {
c.RespondStatus(http.StatusForbidden)
return
@ -298,7 +299,7 @@ func handleGet(c *rpc.Context) {
return
}
if args.Location.Size_ == 100 {
if args.Location.Size == 100 {
c.RespondStatus(http.StatusBadRequest)
return
}
@ -359,7 +360,7 @@ func handleSign(c *rpc.Context) {
c.RespondJSON(access.SignResp{Location: args.Location})
}
func calcCrc(loc *proto.Location) (uint32, error) {
func calcCrc(loc *access.Location) (uint32, error) {
crcWriter := crc32.New(crc32.IEEETable)
buf := bytespool.Alloc(1024)
@ -377,7 +378,7 @@ func calcCrc(loc *proto.Location) (uint32, error) {
return crcWriter.Sum32(), nil
}
func fillCrc(loc *proto.Location) error {
func fillCrc(loc *access.Location) error {
crc, err := calcCrc(loc)
if err != nil {
return err
@ -386,7 +387,7 @@ func fillCrc(loc *proto.Location) error {
return nil
}
func verifyCrc(loc *proto.Location) bool {
func verifyCrc(loc *access.Location) bool {
crc, err := calcCrc(loc)
if err != nil {
return false
@ -394,13 +395,13 @@ func verifyCrc(loc *proto.Location) bool {
return loc.Crc == crc
}
func signCrc(loc *proto.Location, locs []proto.Location) error {
func signCrc(loc *access.Location, locs []access.Location) error {
first := locs[0]
bids := make(map[proto.BlobID]struct{}, 64)
if loc.ClusterID != first.ClusterID ||
loc.CodeMode != first.CodeMode ||
loc.SliceSize != first.SliceSize {
loc.BlobSize != first.BlobSize {
return fmt.Errorf("not equal in constant field")
}
@ -412,20 +413,20 @@ func signCrc(loc *proto.Location, locs []proto.Location) error {
// assert
if l.ClusterID != first.ClusterID ||
l.CodeMode != first.CodeMode ||
l.SliceSize != first.SliceSize {
l.BlobSize != first.BlobSize {
return fmt.Errorf("not equal in constant field")
}
for _, blob := range l.Slices {
for _, blob := range l.Blobs {
for c := 0; c < int(blob.Count); c++ {
bids[blob.MinSliceID+proto.BlobID(c)] = struct{}{}
bids[blob.MinBid+proto.BlobID(c)] = struct{}{}
}
}
}
for _, blob := range loc.Slices {
for _, blob := range loc.Blobs {
for c := 0; c < int(blob.Count); c++ {
bid := blob.MinSliceID + proto.BlobID(c)
bid := blob.MinBid + proto.BlobID(c)
if _, ok := bids[bid]; !ok {
return fmt.Errorf("not equal in blob_id(%d)", bid)
}
@ -500,10 +501,10 @@ func TestAccessClientConnectionMode(t *testing.T) {
if cs.size <= 0 {
continue
}
loc := proto.Location{Size_: uint64(mrand.Int63n(cs.size))}
loc := access.Location{Size: uint64(mrand.Int63n(cs.size))}
fillCrc(&loc)
_, err = cli.Delete(randCtx(), &access.DeleteArgs{
Locations: []proto.Location{loc},
Locations: []access.Location{loc},
})
require.NoError(t, err)
}
@ -545,7 +546,7 @@ func TestAccessClientPutGet(t *testing.T) {
}
// test code 400
_, err := client.Get(randCtx(), &access.GetArgs{Location: proto.Location{Size_: 100}, ReadSize: uint64(100)})
_, err := client.Get(randCtx(), &access.GetArgs{Location: access.Location{Size: 100}, ReadSize: uint64(100)})
require.Error(t, err)
}
@ -690,7 +691,7 @@ func TestAccessClientPutMaxBlobsLength(t *testing.T) {
}
_, _, err := client.Put(randCtx(), &args)
require.ErrorIs(t, err, cs.err)
require.ErrorIs(t, cs.err, err)
}
}
@ -723,16 +724,17 @@ func TestAccessClientPutTimeout(t *testing.T) {
maxMs time.Duration
}
cases := []caseT{
// QuickConnMode 3s + size / 40
// QuickConnMode 3s + size / 40, dial and response 2s
{access.QuickConnMode, mb * -119, ms * 0, ms * 600},
{access.QuickConnMode, mb * -100, ms * 500, ms * 1000},
{access.QuickConnMode, mb * -80, ms * 1000, ms * 1500},
{access.QuickConnMode, mb * -1, ms * 2500, ms * 3500},
{access.QuickConnMode, mb * -1, ms * 2000, ms * 2500},
// DefaultConnMode 30s + size / 10
// DefaultConnMode 30s + size / 10, dial and response 5s
{access.DefaultConnMode, mb * -299, ms * 0, ms * 600},
{access.DefaultConnMode, mb * -280, ms * 2000, ms * 2500},
{access.DefaultConnMode, mb * -270, ms * 3000, ms * 3500},
{access.DefaultConnMode, mb * -1, ms * 5000, ms * 5500},
}
var wg sync.WaitGroup
@ -776,40 +778,40 @@ func TestAccessClientDelete(t *testing.T) {
{
locs, err := client.Delete(randCtx(), nil)
require.Nil(t, locs)
require.ErrorIs(t, err, errcode.ErrIllegalArguments)
require.ErrorIs(t, errcode.ErrIllegalArguments, err)
}
{
locs, err := client.Delete(randCtx(), &access.DeleteArgs{})
require.Nil(t, locs)
require.ErrorIs(t, err, errcode.ErrIllegalArguments)
require.ErrorIs(t, errcode.ErrIllegalArguments, err)
}
{
locs, err := client.Delete(randCtx(), &access.DeleteArgs{
Locations: make([]proto.Location, 1),
Locations: make([]access.Location, 1),
})
require.Nil(t, locs)
require.NoError(t, err)
}
{
args := &access.DeleteArgs{
Locations: make([]proto.Location, 1000),
Locations: make([]access.Location, 1000),
}
_, err := client.Delete(randCtx(), args)
require.NoError(t, err)
}
{
args := &access.DeleteArgs{
Locations: make([]proto.Location, access.MaxDeleteLocations+1),
Locations: make([]access.Location, access.MaxDeleteLocations+1),
}
locs, err := client.Delete(randCtx(), args)
require.Equal(t, args.Locations, locs)
require.ErrorIs(t, err, errcode.ErrIllegalArguments)
require.ErrorIs(t, errcode.ErrIllegalArguments, err)
}
{
loc := proto.Location{Size_: 100, Slices: make([]proto.Slice, 0)}
loc := access.Location{Size: 100, Blobs: make([]access.SliceInfo, 0)}
fillCrc(&loc)
args := &access.DeleteArgs{
Locations: make([]proto.Location, 0, access.MaxDeleteLocations),
Locations: make([]access.Location, 0, access.MaxDeleteLocations),
}
for i := 1; i < access.MaxDeleteLocations/10; i++ {
args.Locations = append(args.Locations, loc)
@ -892,7 +894,7 @@ func TestAccessClientPutAtToken(t *testing.T) {
Body: bytes.NewBuffer(buff),
}
_, _, err := client.Put(randCtx(), &args)
require.ErrorIs(t, err, errcode.ErrUnexpected)
require.ErrorIs(t, errcode.ErrUnexpected, err)
}
}
@ -926,7 +928,7 @@ func TestAccessClientRPCConfig(t *testing.T) {
}
func TestAccessClientLogger(t *testing.T) {
file, err := os.CreateTemp(os.TempDir(), "TestAccessClientLogger")
file, err := ioutil.TempFile(os.TempDir(), "TestAccessClientLogger")
require.NoError(t, err)
require.NoError(t, file.Close())
defer func() {
@ -938,7 +940,7 @@ func TestAccessClientLogger(t *testing.T) {
Logger: &access.Logger{
Filename: file.Name(),
},
PriorityAddrs: []string{"127.0.0.1:37173"},
PriorityAddrs: []string{"127.0.0.1:9500"},
}
client, err := access.New(cfg)
require.NoError(t, err)
@ -954,35 +956,3 @@ func TestAccessClientLogger(t *testing.T) {
_, _, err = client.Put(randCtx(), &args)
require.Error(t, err)
}
func TestAccessClientPutRetryGetBody(t *testing.T) {
refusedAddr := "http://127.0.0.1:37173"
cfg := access.Config{}
cfg.RPCConfig = &rpc.Config{}
cfg.LogLevel = log.Lfatal
cfg.PriorityAddrs = []string{refusedAddr, refusedAddr, mockServer.URL, refusedAddr}
client, err := access.New(cfg)
require.NoError(t, err)
for range [1000]struct{}{} {
retried := false
args := access.PutArgs{
Size: int64(1),
Body: bytes.NewBuffer([]byte{'a'}),
GetBody: func() (io.ReadCloser, error) {
retried = true
return io.NopCloser(bytes.NewBuffer([]byte{'z'})), nil
},
}
loc, _, err := client.Put(randCtx(), &args)
require.NoError(t, err)
rc, err := client.Get(randCtx(), &access.GetArgs{Location: loc, ReadSize: uint64(1)})
require.NoError(t, err)
buff, err := io.ReadAll(rc)
require.NoError(t, err)
if len(buff) > 0 && buff[0] == 'z' {
require.True(t, retried)
return
}
}
require.Fail(t, "retry with get body failed")
}

View File

@ -18,7 +18,10 @@ import (
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"encoding/base64"
"encoding/binary"
"encoding/hex"
"fmt"
"hash"
"hash/crc32"
"io"
@ -193,27 +196,298 @@ func (h HashSumMap) All() map[string]interface{} {
return m
}
// Location file location, 4 + 1 + 8 + 4 + 4 + len*16 bytes
// | |
// | ClusterID(4) | CodeMode(1) |
// | Size(8) |
// | BlobSize(4) | Crc(4) |
// | len*SliceInfo(16) |
//
// ClusterID which cluster file is in
// CodeMode is ec encode mode, see defined in "common/lib/codemode"
// Size is file size
// BlobSize is every blob's size but the last one which's size=(Size mod BlobSize)
// Crc is the checksum, change anything of the location, crc will mismatch
// Blobs all blob information
type Location struct {
_ [0]byte
ClusterID proto.ClusterID `json:"cluster_id"`
CodeMode codemode.CodeMode `json:"code_mode"`
Size uint64 `json:"size"`
BlobSize uint32 `json:"blob_size"`
Crc uint32 `json:"crc"`
Blobs []SliceInfo `json:"blobs"`
}
// SliceInfo blobs info, 8 + 4 + 4 bytes
//
// MinBid is the first blob id
// Vid is which volume all blobs in
// Count is num of consecutive blob ids, count=1 just has one blob
//
// blob ids = [MinBid, MinBid+count)
type SliceInfo struct {
_ [0]byte
MinBid proto.BlobID `json:"min_bid"`
Vid proto.Vid `json:"vid"`
Count uint32 `json:"count"`
}
// Blob is one piece of data in a location
//
// Bid is the blob id
// Vid is which volume the blob in
// Size is real size of the blob
type Blob struct {
Bid proto.BlobID
Vid proto.Vid
Size uint32
}
// Copy returns a new same Location
func (loc *Location) Copy() Location {
dst := Location{
ClusterID: loc.ClusterID,
CodeMode: loc.CodeMode,
Size: loc.Size,
BlobSize: loc.BlobSize,
Crc: loc.Crc,
Blobs: make([]SliceInfo, len(loc.Blobs)),
}
copy(dst.Blobs, loc.Blobs)
return dst
}
// Encode transfer Location to slice byte
// Returns the buf created by me
//
// (n) means max-n bytes
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// | field | crc | clusterid | codemode | size | blobsize |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// | n-bytes | 4 | uvarint(5) | 1 | uvarint(10) | uvarint(5) |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// 25 + (5){len(blobs)} + len(Blobs) * 20
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// | blobs | minbid | vid | count | ... |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// | n-bytes | (10) | (5) | (5) | (20) | (20) | ... |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
func (loc *Location) Encode() []byte {
if loc == nil {
return nil
}
n := 25 + 5 + len(loc.Blobs)*20
buf := make([]byte, n)
n = loc.Encode2(buf)
return buf[:n]
}
// Encode2 transfer Location to the buf, the buf reuse by yourself
// Returns the number of bytes read
// If the buffer is too small, Encode2 will panic
func (loc *Location) Encode2(buf []byte) int {
if loc == nil {
return 0
}
n := 0
binary.BigEndian.PutUint32(buf[n:], loc.Crc)
n += 4
n += binary.PutUvarint(buf[n:], uint64(loc.ClusterID))
buf[n] = byte(loc.CodeMode)
n++
n += binary.PutUvarint(buf[n:], uint64(loc.Size))
n += binary.PutUvarint(buf[n:], uint64(loc.BlobSize))
n += binary.PutUvarint(buf[n:], uint64(len(loc.Blobs)))
for _, blob := range loc.Blobs {
n += binary.PutUvarint(buf[n:], uint64(blob.MinBid))
n += binary.PutUvarint(buf[n:], uint64(blob.Vid))
n += binary.PutUvarint(buf[n:], uint64(blob.Count))
}
return n
}
// Decode parse location from buf
// Returns the number of bytes read
// Error is not nil when parsing failed
func (loc *Location) Decode(buf []byte) (int, error) {
if loc == nil {
return 0, fmt.Errorf("location receiver is nil")
}
location, n, err := DecodeLocation(buf)
if err != nil {
return n, err
}
*loc = location
return n, nil
}
// ToString transfer location to hex string
func (loc *Location) ToString() string {
return loc.HexString()
}
// HexString transfer location to hex string
func (loc *Location) HexString() string {
return hex.EncodeToString(loc.Encode())
}
// Base64String transfer location to base64 string
func (loc *Location) Base64String() string {
return base64.StdEncoding.EncodeToString(loc.Encode())
}
// Spread location blobs to slice
func (loc *Location) Spread() []Blob {
count := 0
for _, blob := range loc.Blobs {
count += int(blob.Count)
}
blobs := make([]Blob, 0, count)
for _, blob := range loc.Blobs {
for offset := uint32(0); offset < blob.Count; offset++ {
blobs = append(blobs, Blob{
Bid: blob.MinBid + proto.BlobID(offset),
Vid: blob.Vid,
Size: loc.BlobSize,
})
}
}
if len(blobs) > 0 && loc.BlobSize > 0 {
if lastSize := loc.Size % uint64(loc.BlobSize); lastSize > 0 {
blobs[len(blobs)-1].Size = uint32(lastSize)
}
}
return blobs
}
// DecodeLocation parse location from buf
// Returns Location and the number of bytes read
// Error is not nil when parsing failed
func DecodeLocation(buf []byte) (Location, int, error) {
var (
loc Location
n int
val uint64
nn int
)
next := func() (uint64, int) {
val, nn := binary.Uvarint(buf)
if nn <= 0 {
return 0, nn
}
n += nn
buf = buf[nn:]
return val, nn
}
if len(buf) < 4 {
return loc, n, fmt.Errorf("bytes crc %d", len(buf))
}
loc.Crc = binary.BigEndian.Uint32(buf)
n += 4
buf = buf[4:]
if val, nn = next(); nn <= 0 {
return loc, n, fmt.Errorf("bytes cluster_id %d", nn)
}
loc.ClusterID = proto.ClusterID(val)
if len(buf) < 1 {
return loc, n, fmt.Errorf("bytes codemode %d", len(buf))
}
loc.CodeMode = codemode.CodeMode(buf[0])
n++
buf = buf[1:]
if val, nn = next(); nn <= 0 {
return loc, n, fmt.Errorf("bytes size %d", nn)
}
loc.Size = val
if val, nn = next(); nn <= 0 {
return loc, n, fmt.Errorf("bytes blob_size %d", nn)
}
loc.BlobSize = uint32(val)
if val, nn = next(); nn <= 0 {
return loc, n, fmt.Errorf("bytes length blobs %d", nn)
}
length := int(val)
if length > 0 {
loc.Blobs = make([]SliceInfo, 0, length)
}
for index := 0; index < length; index++ {
var blob SliceInfo
if val, nn = next(); nn <= 0 {
return loc, n, fmt.Errorf("bytes %dth-blob min_bid %d", index, nn)
}
blob.MinBid = proto.BlobID(val)
if val, nn = next(); nn <= 0 {
return loc, n, fmt.Errorf("bytes %dth-blob vid %d", index, nn)
}
blob.Vid = proto.Vid(val)
if val, nn = next(); nn <= 0 {
return loc, n, fmt.Errorf("bytes %dth-blob count %d", index, nn)
}
blob.Count = uint32(val)
loc.Blobs = append(loc.Blobs, blob)
}
return loc, n, nil
}
// DecodeLocationFrom decode location from hex string
func DecodeLocationFrom(s string) (Location, error) {
return DecodeLocationFromHex(s)
}
// DecodeLocationFromHex decode location from hex string
func DecodeLocationFromHex(s string) (Location, error) {
var loc Location
src, err := hex.DecodeString(s)
if err != nil {
return loc, err
}
_, err = loc.Decode(src)
if err != nil {
return loc, err
}
return loc, nil
}
// DecodeLocationFromBase64 decode location from base64 string
func DecodeLocationFromBase64(s string) (Location, error) {
var loc Location
src, err := base64.StdEncoding.DecodeString(s)
if err != nil {
return loc, err
}
_, err = loc.Decode(src)
if err != nil {
return loc, err
}
return loc, nil
}
// PutArgs for service /put
// Hashes means how to calculate check sum,
// HashAlgCRC32 | HashAlgMD5 equal 2 + 4 = 6
// AssignClusterID > 0 means that cluster_id is assigned by the API caller
// AssignClusterID = 0 means that cluster_id is assigned by access cluster controller
// CodeMode > 0 means that codemode is assigned by the API caller
// CodeMode = 0 means that codemode is assigned by code_mode_policies
type PutArgs struct {
Size int64 `json:"size"`
Hashes HashAlgorithm `json:"hashes,omitempty"`
Body io.Reader `json:"-"`
AssignClusterID proto.ClusterID `json:"assign_cluster_id,omitempty"`
CodeMode codemode.CodeMode `json:"code_mode,omitempty"`
// GetBody defines an optional func to return a new copy of Body.
// It is used for client requests when a redirect requires reading
// the body more than once. Use of GetBody still requires setting Body.
//
// There force reset request.GetBody if it is setting.
GetBody func() (io.ReadCloser, error) `json:"-"`
}
// IsValid is valid put args
@ -226,8 +500,8 @@ func (args *PutArgs) IsValid() bool {
// PutResp put response result
type PutResp struct {
Location proto.Location `json:"location"`
HashSumMap HashSumMap `json:"hashsum,omitempty"`
Location Location `json:"location"`
HashSumMap HashSumMap `json:"hashsum"`
}
// PutAtArgs for service /putat
@ -281,16 +555,15 @@ func (args *AllocArgs) IsValid() bool {
// if size mod blobsize == 0, length of tokens equal length of location blobs
// otherwise additional token for the last blob uploading
type AllocResp struct {
Location proto.Location `json:"location"`
Tokens []string `json:"tokens"`
Location Location `json:"location"`
Tokens []string `json:"tokens"`
}
// GetArgs for service /get
type GetArgs struct {
Location proto.Location `json:"location"`
Offset uint64 `json:"offset"`
ReadSize uint64 `json:"read_size"`
Writer io.Writer `json:"-"`
Location Location `json:"location"`
Offset uint64 `json:"offset"`
ReadSize uint64 `json:"read_size"`
}
// IsValid is valid get args
@ -298,14 +571,14 @@ func (args *GetArgs) IsValid() bool {
if args == nil {
return false
}
return args.Offset <= args.Location.Size_ &&
args.ReadSize <= args.Location.Size_ &&
args.Offset+args.ReadSize <= args.Location.Size_
return args.Offset <= args.Location.Size &&
args.ReadSize <= args.Location.Size &&
args.Offset+args.ReadSize <= args.Location.Size
}
// DeleteArgs for service /delete
type DeleteArgs struct {
Locations []proto.Location `json:"locations"`
Locations []Location `json:"locations"`
}
// IsValid is valid delete args
@ -318,7 +591,7 @@ func (args *DeleteArgs) IsValid() bool {
// DeleteResp delete response with failed locations
type DeleteResp struct {
FailedLocations []proto.Location `json:"failed_locations,omitempty"`
FailedLocations []Location `json:"failed_locations,omitempty"`
}
// DeleteBlobArgs for service /deleteblob
@ -345,8 +618,8 @@ func (args *DeleteBlobArgs) IsValid() bool {
// Locations are signed location getting from /alloc
// Location is to be signed location which merged by yourself
type SignArgs struct {
Locations []proto.Location `json:"locations"`
Location proto.Location `json:"location"`
Locations []Location `json:"locations"`
Location Location `json:"location"`
}
// IsValid is valid sign args
@ -359,141 +632,5 @@ func (args *SignArgs) IsValid() bool {
// SignResp sign response location with crc
type SignResp struct {
Location proto.Location `json:"location"`
}
// Shardnode Blob
type GetShardMode int
const (
GetShardModeRandom = GetShardMode(iota)
GetShardModeLeader
)
type CreateBlobArgs struct {
ClusterID proto.ClusterID
CodeMode codemode.CodeMode
BlobName string
Size uint64
SliceSize uint32
}
func (args *CreateBlobArgs) IsValid() bool {
if args == nil {
return false
}
return args.Size != 0 && len(args.BlobName) != 0
}
type CreateBlobRet struct {
Location proto.Location
}
type ListBlobArgs struct {
ClusterID proto.ClusterID
ShardID proto.ShardID
Mode GetShardMode
Prefix string
Marker string
Count uint64
}
func (args *ListBlobArgs) IsValid() bool {
if args == nil {
return false
}
return args.ClusterID != 0
}
type SealBlobArgs struct {
ClusterID proto.ClusterID
BlobName string
Size uint64
Slices []proto.Slice
}
func (args *SealBlobArgs) IsValid() bool {
if args == nil {
return false
}
return args.ClusterID != 0 && len(args.BlobName) != 0
}
type GetBlobArgs struct {
ClusterID proto.ClusterID
Mode GetShardMode
BlobName string
Offset uint64
ReadSize uint64
Writer io.Writer
}
// IsValid is valid get args
func (args *GetBlobArgs) IsValid() bool {
if args == nil {
return false
}
return args.ClusterID != 0 && len(args.BlobName) != 0
}
type DelBlobArgs struct {
ClusterID proto.ClusterID
BlobName string
}
func (args *DelBlobArgs) IsValid() bool {
if args == nil {
return false
}
return args.ClusterID != 0 && len(args.BlobName) != 0
}
type AllocSliceArgs struct {
ClusterID proto.ClusterID
CodeMode codemode.CodeMode
BlobName string
Size uint64
FailSlice proto.Slice
}
func (args *AllocSliceArgs) IsValid() bool {
if args == nil {
return false
}
return args.ClusterID != 0 && args.CodeMode.IsValid() && args.Size != 0 && len(args.BlobName) != 0
}
type PutBlobArgs struct {
CodeMode codemode.CodeMode
BlobName string
NeedSeal bool
Size uint64
Hashes HashAlgorithm
Body io.Reader
}
func (args *PutBlobArgs) IsValid() bool {
if args == nil {
return false
}
return args.CodeMode != 0 && args.Size != 0 && len(args.BlobName) != 0
}
type GetShardCommonArgs struct {
ClusterID proto.ClusterID
ShardID proto.ShardID
Mode GetShardMode
BlobName string
}
func (args *ListBlobEncodeMarker) MarshalToString() (string, error) {
raw, err := args.Marshal()
return string(raw), err
}
func (args *ListBlobEncodeMarker) UnmarshalFromString(marker string) error {
return args.Unmarshal([]byte(marker))
Location Location `json:"location"`
}

View File

@ -259,6 +259,192 @@ func TestHashAlgorithm2HashSumMap(t *testing.T) {
}
}
func TestLocationEncodeDecodeNil(t *testing.T) {
var loc *access.Location
require.Nil(t, loc.Encode())
require.Equal(t, 0, loc.Encode2(nil))
require.Equal(t, "", loc.ToString())
require.Equal(t, "", loc.HexString())
require.Equal(t, "", loc.Base64String())
n, err := loc.Decode(nil)
require.Error(t, err)
require.Equal(t, 0, n)
locx, n, err := access.DecodeLocation(nil)
require.Error(t, err)
require.Equal(t, 0, n)
require.Equal(t, access.Location{}, locx)
locx, err = access.DecodeLocationFrom("")
require.Error(t, err)
require.Equal(t, access.Location{}, locx)
locx, err = access.DecodeLocationFrom("xxx")
require.Error(t, err)
require.Equal(t, access.Location{}, locx)
locx, err = access.DecodeLocationFromHex("xxx")
require.Error(t, err)
require.Equal(t, access.Location{}, locx)
locx, err = access.DecodeLocationFromBase64("xxx")
require.Error(t, err)
require.Equal(t, access.Location{}, locx)
}
func TestLocationEncodeDecode(t *testing.T) {
for ii := 0; ii < 100; ii++ {
loc := &access.Location{
ClusterID: proto.ClusterID(mrand.Uint32()),
CodeMode: codemode.CodeMode(mrand.Intn(0xff)),
Size: mrand.Uint64(),
BlobSize: mrand.Uint32(),
Crc: mrand.Uint32(),
}
num := mrand.Intn(5)
for i := 0; i < num; i++ {
loc.Blobs = append(loc.Blobs, access.SliceInfo{
MinBid: proto.BlobID(mrand.Uint64()),
Vid: proto.Vid(mrand.Uint32()),
Count: mrand.Uint32(),
})
}
buf := loc.Encode()
bufx := make([]byte, len(buf))
n := loc.Encode2(bufx)
require.Equal(t, len(buf), n)
require.Equal(t, buf, bufx)
require.Panics(t, func() { loc.Encode2(nil) })
require.Panics(t, func() { loc.Encode2(bufx[:3]) })
require.Panics(t, func() { loc.Encode2(bufx[:n/2]) })
require.Panics(t, func() { loc.Encode2(bufx[:n-1]) })
locx := access.Location{}
locx.Decode(bufx)
require.Equal(t, loc.ToString(), locx.ToString())
require.Equal(t, loc.HexString(), locx.HexString())
require.Equal(t, loc.Base64String(), locx.Base64String())
str := loc.ToString()
locx, err := access.DecodeLocationFrom(str)
require.NoError(t, err)
require.Equal(t, *loc, locx)
str = loc.HexString()
locx, err = access.DecodeLocationFromHex(str)
require.NoError(t, err)
require.Equal(t, *loc, locx)
str = loc.Base64String()
locx, err = access.DecodeLocationFromBase64(str)
require.NoError(t, err)
require.Equal(t, *loc, locx)
}
}
func TestLocationDecodeError(t *testing.T) {
loc := &access.Location{
ClusterID: proto.ClusterID(math.MaxUint32),
CodeMode: codemode.CodeMode(math.MaxInt8),
Size: math.MaxUint64,
BlobSize: math.MaxUint32,
Crc: math.MaxUint32,
}
buf := loc.Encode()
require.Equal(t, 25+1, len(buf))
for _, n := range []int{3, 8, 9, 19, 24} {
_, _, err := access.DecodeLocation(buf[:n])
require.Error(t, err)
t.Log(err)
}
loc.Blobs = append(loc.Blobs, access.SliceInfo{
MinBid: proto.BlobID(math.MaxUint64),
Vid: proto.Vid(math.MaxUint32),
Count: math.MaxUint32,
})
buf = loc.Encode()
require.Equal(t, 25+1+20, len(buf))
for _, n := range []int{25, 35, 40, 45} {
_, _, err := access.DecodeLocation(buf[:n])
require.Error(t, err)
t.Log(err)
}
}
func TestLocationSpread(t *testing.T) {
{
var loc access.Location
blobs := loc.Spread()
require.NotNil(t, blobs)
require.Equal(t, 0, len(blobs))
}
{
loc := &access.Location{
Size: 10,
BlobSize: 1 << 22,
Blobs: []access.SliceInfo{{
MinBid: 100,
Vid: 4,
Count: 1,
}},
}
blobs := loc.Spread()
require.Equal(t, 1, len(blobs))
require.Equal(t, proto.BlobID(100), blobs[0].Bid)
require.Equal(t, proto.Vid(4), blobs[0].Vid)
require.Equal(t, uint32(10), blobs[0].Size)
}
{
loc := &access.Location{
Size: (1 << 22) + 10,
BlobSize: 1 << 22,
Blobs: []access.SliceInfo{{
MinBid: 100,
Vid: 4,
Count: 2,
}},
}
blobs := loc.Spread()
require.Equal(t, 2, len(blobs))
require.Equal(t, proto.BlobID(100), blobs[0].Bid)
require.Equal(t, proto.Vid(4), blobs[0].Vid)
require.Equal(t, uint32(1<<22), blobs[0].Size)
require.Equal(t, proto.BlobID(101), blobs[1].Bid)
require.Equal(t, proto.Vid(4), blobs[1].Vid)
require.Equal(t, uint32(10), blobs[1].Size)
}
{
loc := &access.Location{
Size: 1 << 23,
BlobSize: 1 << 22,
Blobs: []access.SliceInfo{{
MinBid: 100,
Vid: 4,
Count: 1,
}, {
MinBid: 200,
Vid: 4,
Count: 1,
}},
}
blobs := loc.Spread()
require.Equal(t, 2, len(blobs))
require.Equal(t, proto.BlobID(100), blobs[0].Bid)
require.Equal(t, proto.Vid(4), blobs[0].Vid)
require.Equal(t, uint32(1<<22), blobs[0].Size)
require.Equal(t, proto.BlobID(200), blobs[1].Bid)
require.Equal(t, proto.Vid(4), blobs[1].Vid)
require.Equal(t, uint32(1<<22), blobs[1].Size)
}
}
func TestPutArgs(t *testing.T) {
cases := []struct {
size int64
@ -352,7 +538,7 @@ func TestGetArgs(t *testing.T) {
Offset: cs.offset,
ReadSize: cs.readSize,
}
args.Location.Size_ = cs.size
args.Location.Size = cs.size
require.Equal(t, cs.valid, args.IsValid())
}
}
@ -361,11 +547,11 @@ func TestDeleteArgs(t *testing.T) {
args := access.DeleteArgs{}
require.False(t, args.IsValid())
require.False(t, (*access.DeleteArgs)(nil).IsValid())
args.Locations = []proto.Location{{}}
args.Locations = []access.Location{{}}
require.True(t, args.IsValid())
args.Locations = make([]proto.Location, access.MaxDeleteLocations)
args.Locations = make([]access.Location, access.MaxDeleteLocations)
require.True(t, args.IsValid())
args.Locations = make([]proto.Location, access.MaxDeleteLocations+1)
args.Locations = make([]access.Location, access.MaxDeleteLocations+1)
require.False(t, args.IsValid())
}
@ -387,7 +573,7 @@ func TestSignArgs(t *testing.T) {
args := access.SignArgs{}
require.False(t, args.IsValid())
require.False(t, (*access.DeleteArgs)(nil).IsValid())
args.Locations = []proto.Location{{}}
args.Locations = []access.Location{{}}
require.True(t, args.IsValid())
}

View File

@ -1,384 +0,0 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: stream_blob.proto
package access
import (
fmt "fmt"
sharding "github.com/cubefs/cubefs/blobstore/common/sharding"
_ "github.com/gogo/protobuf/gogoproto"
proto "github.com/gogo/protobuf/proto"
io "io"
math "math"
math_bits "math/bits"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
type ListBlobEncodeMarker struct {
Range sharding.Range `protobuf:"bytes,1,opt,name=range,proto3" json:"range"`
Marker string `protobuf:"bytes,2,opt,name=marker,proto3" json:"marker,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ListBlobEncodeMarker) Reset() { *m = ListBlobEncodeMarker{} }
func (m *ListBlobEncodeMarker) String() string { return proto.CompactTextString(m) }
func (*ListBlobEncodeMarker) ProtoMessage() {}
func (*ListBlobEncodeMarker) Descriptor() ([]byte, []int) {
return fileDescriptor_3f58ae6b23694640, []int{0}
}
func (m *ListBlobEncodeMarker) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *ListBlobEncodeMarker) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_ListBlobEncodeMarker.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *ListBlobEncodeMarker) XXX_Merge(src proto.Message) {
xxx_messageInfo_ListBlobEncodeMarker.Merge(m, src)
}
func (m *ListBlobEncodeMarker) XXX_Size() int {
return m.Size()
}
func (m *ListBlobEncodeMarker) XXX_DiscardUnknown() {
xxx_messageInfo_ListBlobEncodeMarker.DiscardUnknown(m)
}
var xxx_messageInfo_ListBlobEncodeMarker proto.InternalMessageInfo
func (m *ListBlobEncodeMarker) GetRange() sharding.Range {
if m != nil {
return m.Range
}
return sharding.Range{}
}
func (m *ListBlobEncodeMarker) GetMarker() string {
if m != nil {
return m.Marker
}
return ""
}
func init() {
proto.RegisterType((*ListBlobEncodeMarker)(nil), "cubefs.blobstore.api.access.ListBlobEncodeMarker")
}
func init() { proto.RegisterFile("stream_blob.proto", fileDescriptor_3f58ae6b23694640) }
var fileDescriptor_3f58ae6b23694640 = []byte{
// 217 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x2c, 0x2e, 0x29, 0x4a,
0x4d, 0xcc, 0x8d, 0x4f, 0xca, 0xc9, 0x4f, 0xd2, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x92, 0x4e,
0x2e, 0x4d, 0x4a, 0x4d, 0x2b, 0xd6, 0x03, 0x09, 0x15, 0x97, 0xe4, 0x17, 0xa5, 0xea, 0x25, 0x16,
0x64, 0xea, 0x25, 0x26, 0x27, 0xa7, 0x16, 0x17, 0x4b, 0x89, 0xa4, 0xe7, 0xa7, 0xe7, 0x83, 0xd5,
0xe9, 0x83, 0x58, 0x10, 0x2d, 0x52, 0x3a, 0x10, 0x2d, 0xfa, 0x70, 0x2d, 0xfa, 0xc9, 0xf9, 0xb9,
0xb9, 0xf9, 0x79, 0xfa, 0xc5, 0x19, 0x89, 0x45, 0x29, 0x99, 0x79, 0xe9, 0xfa, 0x45, 0x89, 0x79,
0xe9, 0xa9, 0x10, 0xd5, 0x4a, 0xc5, 0x5c, 0x22, 0x3e, 0x99, 0xc5, 0x25, 0x4e, 0x39, 0xf9, 0x49,
0xae, 0x79, 0xc9, 0xf9, 0x29, 0xa9, 0xbe, 0x89, 0x45, 0xd9, 0xa9, 0x45, 0x42, 0xce, 0x5c, 0xac,
0x60, 0x65, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0xdc, 0x46, 0xea, 0x7a, 0x18, 0x0e, 0x81, 0x98, 0xaa,
0x07, 0x33, 0x55, 0x2f, 0x08, 0xa4, 0xdc, 0x89, 0xe5, 0xc4, 0x3d, 0x79, 0x86, 0x20, 0x88, 0x5e,
0x21, 0x31, 0x2e, 0xb6, 0x5c, 0xb0, 0x71, 0x12, 0x4c, 0x0a, 0x8c, 0x1a, 0x9c, 0x41, 0x50, 0x9e,
0x93, 0xf8, 0x89, 0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x18, 0xc5,
0xa9, 0xa7, 0x6f, 0x0d, 0xf1, 0x51, 0x12, 0x1b, 0xd8, 0x51, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff,
0xff, 0x17, 0x5f, 0xcb, 0x7b, 0x0a, 0x01, 0x00, 0x00,
}
func (m *ListBlobEncodeMarker) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *ListBlobEncodeMarker) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *ListBlobEncodeMarker) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Marker) > 0 {
i -= len(m.Marker)
copy(dAtA[i:], m.Marker)
i = encodeVarintStreamBlob(dAtA, i, uint64(len(m.Marker)))
i--
dAtA[i] = 0x12
}
{
size, err := m.Range.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintStreamBlob(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
return len(dAtA) - i, nil
}
func encodeVarintStreamBlob(dAtA []byte, offset int, v uint64) int {
offset -= sovStreamBlob(v)
base := offset
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return base
}
func (m *ListBlobEncodeMarker) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = m.Range.Size()
n += 1 + l + sovStreamBlob(uint64(l))
l = len(m.Marker)
if l > 0 {
n += 1 + l + sovStreamBlob(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func sovStreamBlob(x uint64) (n int) {
return (math_bits.Len64(x|1) + 6) / 7
}
func sozStreamBlob(x uint64) (n int) {
return sovStreamBlob(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *ListBlobEncodeMarker) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowStreamBlob
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: ListBlobEncodeMarker: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: ListBlobEncodeMarker: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Range", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowStreamBlob
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthStreamBlob
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthStreamBlob
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if err := m.Range.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Marker", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowStreamBlob
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthStreamBlob
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthStreamBlob
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Marker = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipStreamBlob(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthStreamBlob
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipStreamBlob(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
depth := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowStreamBlob
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
wireType := int(wire & 0x7)
switch wireType {
case 0:
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowStreamBlob
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
iNdEx++
if dAtA[iNdEx-1] < 0x80 {
break
}
}
case 1:
iNdEx += 8
case 2:
var length int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowStreamBlob
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
length |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if length < 0 {
return 0, ErrInvalidLengthStreamBlob
}
iNdEx += length
case 3:
depth++
case 4:
if depth == 0 {
return 0, ErrUnexpectedEndOfGroupStreamBlob
}
depth--
case 5:
iNdEx += 4
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
if iNdEx < 0 {
return 0, ErrInvalidLengthStreamBlob
}
if depth == 0 {
return iNdEx, nil
}
}
return 0, io.ErrUnexpectedEOF
}
var (
ErrInvalidLengthStreamBlob = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowStreamBlob = fmt.Errorf("proto: integer overflow")
ErrUnexpectedEndOfGroupStreamBlob = fmt.Errorf("proto: unexpected end of group")
)

View File

@ -1,31 +0,0 @@
// Copyright 2024 The CubeFS Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
syntax = "proto3";
package cubefs.blobstore.api.access;
option go_package = "./;access";
option (gogoproto.sizer_all) = true;
option (gogoproto.marshaler_all) = true;
option (gogoproto.unmarshaler_all) = true;
import "gogoproto/gogo.proto";
import "cubefs/blobstore/common/sharding/range.proto";
message ListBlobEncodeMarker {
cubefs.blobstore.common.sharding.Range range = 1 [(gogoproto.nullable) = false];
string marker = 2;
}

View File

@ -16,24 +16,157 @@ package blobnode
import (
"context"
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"time"
"github.com/cubefs/cubefs/blobstore/api/clustermgr"
bloberr "github.com/cubefs/cubefs/blobstore/common/errors"
"github.com/cubefs/cubefs/blobstore/common/proto"
"github.com/cubefs/cubefs/blobstore/common/rpc"
)
const (
ChunkStatusDefault ChunkStatus = iota // 0
ChunkStatusNormal // 1
ChunkStatusReadOnly // 2
ChunkStatusRelease // 3
ChunkNumStatus // 4
)
const (
ReleaseForUser = "release for user"
ReleaseForCompact = "release for compact"
)
// Chunk ID
// vuid + timestamp
const (
chunkVuidLen = 8
chunkTimestmapLen = 8
ChunkIdLength = chunkVuidLen + chunkTimestmapLen
)
var InvalidChunkId ChunkId = [ChunkIdLength]byte{}
var (
_vuidHexLen = hex.EncodedLen(chunkVuidLen)
_timestampHexLen = hex.EncodedLen(chunkTimestmapLen)
// ${vuid_hex}-${tiemstamp_hex}
// |-- 8 Bytes --|-- 1 Bytes --|-- 8 Bytes --|
delimiter = []byte("-")
ChunkIdEncodeLen = _vuidHexLen + _timestampHexLen + len(delimiter)
)
type (
ChunkId [ChunkIdLength]byte
ChunkStatus uint8
)
func (c ChunkId) UnixTime() uint64 {
return binary.BigEndian.Uint64(c[chunkVuidLen:ChunkIdLength])
}
func (c ChunkId) VolumeUnitId() proto.Vuid {
return proto.Vuid(binary.BigEndian.Uint64(c[:chunkVuidLen]))
}
func (c *ChunkId) Marshal() ([]byte, error) {
buf := make([]byte, ChunkIdEncodeLen)
var i int
hex.Encode(buf[i:_vuidHexLen], c[:chunkVuidLen])
i += _vuidHexLen
copy(buf[i:i+len(delimiter)], delimiter)
i += len(delimiter)
hex.Encode(buf[i:], c[chunkVuidLen:ChunkIdLength])
return buf, nil
}
func (c *ChunkId) Unmarshal(data []byte) error {
if len(data) != ChunkIdEncodeLen {
panic(errors.New("chunk buf size not match"))
}
var i int
_, err := hex.Decode(c[:chunkVuidLen], data[i:_vuidHexLen])
if err != nil {
return err
}
i += _vuidHexLen
i += len(delimiter)
_, err = hex.Decode(c[chunkVuidLen:], data[i:])
if err != nil {
return err
}
return nil
}
func (c ChunkId) String() string {
buf, _ := c.Marshal()
return string(buf[:])
}
func (c ChunkId) MarshalJSON() ([]byte, error) {
b := make([]byte, ChunkIdEncodeLen+2)
b[0], b[ChunkIdEncodeLen+1] = '"', '"'
buf, _ := c.Marshal()
copy(b[1:], buf)
return b, nil
}
func (c *ChunkId) UnmarshalJSON(data []byte) (err error) {
if len(data) != ChunkIdEncodeLen+2 {
return errors.New("failed unmarshal json")
}
return c.Unmarshal(data[1 : ChunkIdEncodeLen+1])
}
func EncodeChunk(id ChunkId) string {
return id.String()
}
func NewChunkId(vuid proto.Vuid) (chunkId ChunkId) {
binary.BigEndian.PutUint64(chunkId[:chunkVuidLen], uint64(vuid))
binary.BigEndian.PutUint64(chunkId[chunkVuidLen:ChunkIdLength], uint64(time.Now().UnixNano()))
return
}
func IsValidDiskID(id proto.DiskID) bool {
return id != proto.InvalidDiskID
}
func IsValidChunkID(id clustermgr.ChunkID) bool {
return id != clustermgr.InvalidChunkID
func IsValidChunkId(id ChunkId) bool {
return id != InvalidChunkId
}
func IsValidChunkStatus(status clustermgr.ChunkStatus) bool {
return status < clustermgr.ChunkNumStatus
func IsValidChunkStatus(status ChunkStatus) bool {
return status < ChunkNumStatus
}
func DecodeChunk(name string) (id ChunkId, err error) {
buf := []byte(name)
if len(buf) != ChunkIdEncodeLen {
return InvalidChunkId, errors.New("invalid chunk name")
}
if err = id.Unmarshal(buf); err != nil {
return InvalidChunkId, errors.New("chunk unmarshal failed")
}
return
}
type CreateChunkArgs struct {
@ -60,14 +193,14 @@ type StatChunkArgs struct {
Vuid proto.Vuid `json:"vuid"`
}
func (c *client) StatChunk(ctx context.Context, host string, args *StatChunkArgs) (ci *clustermgr.ChunkInfo, err error) {
func (c *client) StatChunk(ctx context.Context, host string, args *StatChunkArgs) (ci *ChunkInfo, err error) {
if !IsValidDiskID(args.DiskID) {
err = bloberr.ErrInvalidDiskId
return
}
urlStr := fmt.Sprintf("%v/chunk/stat/diskid/%v/vuid/%v", host, args.DiskID, args.Vuid)
ci = new(clustermgr.ChunkInfo)
ci = new(ChunkInfo)
err = c.GetWith(ctx, urlStr, ci)
return
}
@ -122,10 +255,10 @@ type ListChunkArgs struct {
}
type ListChunkRet struct {
ChunkInfos []*clustermgr.ChunkInfo `json:"chunk_infos"`
ChunkInfos []*ChunkInfo `json:"chunk_infos"`
}
func (c *client) ListChunks(ctx context.Context, host string, args *ListChunkArgs) (ret []*clustermgr.ChunkInfo, err error) {
func (c *client) ListChunks(ctx context.Context, host string, args *ListChunkArgs) (ret []*ChunkInfo, err error) {
if !IsValidDiskID(args.DiskID) {
err = bloberr.ErrInvalidDiskId
return
@ -155,5 +288,4 @@ type BadShard struct {
DiskID proto.DiskID
Vuid proto.Vuid
Bid proto.BlobID
Err error
}

View File

@ -21,59 +21,58 @@ import (
"github.com/stretchr/testify/require"
"github.com/cubefs/cubefs/blobstore/api/clustermgr"
"github.com/cubefs/cubefs/blobstore/common/proto"
"github.com/cubefs/cubefs/blobstore/util/log"
)
func TestIsValidChunkId(t *testing.T) {
id := clustermgr.InvalidChunkID
require.Equal(t, false, IsValidChunkID(id))
id := InvalidChunkId
require.Equal(t, false, IsValidChunkId(id))
id = clustermgr.ChunkID{0x1}
require.Equal(t, true, IsValidChunkID(id))
id = ChunkId{0x1}
require.Equal(t, true, IsValidChunkId(id))
}
func TestChunkIdNew(t *testing.T) {
chunkid := clustermgr.NewChunkID(101)
require.Equal(t, clustermgr.ChunkIDLength, len(chunkid))
require.NotEqual(t, clustermgr.InvalidChunkID, chunkid)
chunkid := NewChunkId(101)
require.Equal(t, ChunkIdLength, len(chunkid))
require.NotEqual(t, InvalidChunkId, chunkid)
expectedVuid := chunkid.VolumeUnitId()
require.Equal(t, expectedVuid, proto.Vuid(101))
chunkname := chunkid.String()
require.Equal(t, clustermgr.ChunkIDEncodeLen, len(chunkname))
require.Equal(t, ChunkIdEncodeLen, len(chunkname))
arrs := strings.Split(chunkname, "-")
arrs := strings.Split(chunkname, string(delimiter))
require.Equal(t, 2, len(arrs))
require.Equal(t, "0000000000000065", arrs[0])
}
func TestChunkId_Marshal(t *testing.T) {
chunkid := clustermgr.NewChunkID(101)
chunkid := NewChunkId(101)
data, err := chunkid.Marshal()
require.NoError(t, err)
require.Equal(t, clustermgr.ChunkIDEncodeLen, len(data))
require.Equal(t, ChunkIdEncodeLen, len(data))
log.Infof("data:%s", data)
var newchunk clustermgr.ChunkID
var newchunk ChunkId
err = newchunk.Unmarshal(data)
require.NoError(t, err)
require.Equal(t, chunkid, newchunk)
}
func TestChunkId_MarshalJSON(t *testing.T) {
chunkid := clustermgr.NewChunkID(101)
chunkid := NewChunkId(101)
data, err := json.Marshal(chunkid)
require.NoError(t, err)
require.Equal(t, clustermgr.ChunkIDEncodeLen+2, len(data))
require.Equal(t, ChunkIdEncodeLen+2, len(data))
log.Infof("data:%s", data)
var newchunk clustermgr.ChunkID
var newchunk ChunkId
err = json.Unmarshal(data, &newchunk)
require.NoError(t, err)
require.Equal(t, chunkid, newchunk)

View File

@ -19,7 +19,6 @@ import (
"fmt"
"io"
"github.com/cubefs/cubefs/blobstore/api/clustermgr"
"github.com/cubefs/cubefs/blobstore/common/errors"
"github.com/cubefs/cubefs/blobstore/common/proto"
"github.com/cubefs/cubefs/blobstore/common/rpc"
@ -49,9 +48,9 @@ func (c *client) Close(ctx context.Context, host string) (err error) {
return nil
}
func (c *client) Stat(ctx context.Context, host string) (dis []*clustermgr.BlobNodeDiskInfo, err error) {
func (c *client) Stat(ctx context.Context, host string) (dis []*DiskInfo, err error) {
urlStr := fmt.Sprintf("%v/stat", host)
dis = make([]*clustermgr.BlobNodeDiskInfo, 0)
dis = make([]*DiskInfo, 0)
err = c.GetWith(ctx, urlStr, &dis)
return
}
@ -65,46 +64,17 @@ type InspectRateArgs struct {
Rate int `json:"rate"`
}
type InspectCleanMetricArgs struct {
DiskID proto.DiskID `json:"diskid"`
}
// LimiterStat single limiter using counter
type LimiterStat struct {
Running int `json:"running"`
Capacity int `json:"capacity"`
Remaining int `json:"remaining"`
}
// IoLimiterStats single level qos type limiter stats
type IoLimiterStats struct {
Concurrency LimiterStat `json:"concurrency"`
BidConcurrency LimiterStat `json:"bid_concurrency"`
Bandwidth LimiterStat `json:"bandwidth"`
}
type QosStatArgs struct {
DiskID proto.DiskID `json:"diskid"`
}
func (c *client) QosStat(ctx context.Context, host string, args *QosStatArgs) (stat map[proto.DiskID]map[string]IoLimiterStats, err error) {
urlStr := fmt.Sprintf("%s/disk/stat/diskid/%d", host, args.DiskID)
stat = make(map[proto.DiskID]map[string]IoLimiterStats)
err = c.GetWith(ctx, urlStr, stat)
return
}
type DiskStatArgs struct {
DiskID proto.DiskID `json:"diskid"`
}
func (c *client) DiskInfo(ctx context.Context, host string, args *DiskStatArgs) (di *clustermgr.BlobNodeDiskInfo, err error) {
func (c *client) DiskInfo(ctx context.Context, host string, args *DiskStatArgs) (di *DiskInfo, err error) {
if !IsValidDiskID(args.DiskID) {
return nil, errors.ErrInvalidDiskId
}
urlStr := fmt.Sprintf("%v/disk/stat/diskid/%v", host, args.DiskID)
di = new(clustermgr.BlobNodeDiskInfo)
di = new(DiskInfo)
err = c.GetWith(ctx, urlStr, di)
return
}
@ -113,22 +83,20 @@ type StorageAPI interface {
String(ctx context.Context, host string) string
IsOnline(ctx context.Context, host string) bool
Close(ctx context.Context, host string) error
Stat(ctx context.Context, host string) (infos []*clustermgr.BlobNodeDiskInfo, err error)
DiskInfo(ctx context.Context, host string, args *DiskStatArgs) (di *clustermgr.BlobNodeDiskInfo, err error)
QosStat(ctx context.Context, host string, args *QosStatArgs) (stat map[proto.DiskID]map[string]IoLimiterStats, err error)
Stat(ctx context.Context, host string) (infos []*DiskInfo, err error)
DiskInfo(ctx context.Context, host string, args *DiskStatArgs) (di *DiskInfo, err error)
// chunks
CreateChunk(ctx context.Context, host string, args *CreateChunkArgs) (err error)
StatChunk(ctx context.Context, host string, args *StatChunkArgs) (ci *clustermgr.ChunkInfo, err error)
StatChunk(ctx context.Context, host string, args *StatChunkArgs) (ci *ChunkInfo, err error)
ReleaseChunk(ctx context.Context, host string, args *ChangeChunkStatusArgs) (err error)
SetChunkReadonly(ctx context.Context, host string, args *ChangeChunkStatusArgs) (err error)
SetChunkReadwrite(ctx context.Context, host string, args *ChangeChunkStatusArgs) (err error)
ListChunks(ctx context.Context, host string, args *ListChunkArgs) (cis []*clustermgr.ChunkInfo, err error)
ListChunks(ctx context.Context, host string, args *ListChunkArgs) (cis []*ChunkInfo, err error)
// shard
GetShard(ctx context.Context, host string, args *GetShardArgs) (body io.ReadCloser, shardCrc uint32, err error)
RangeGetShard(ctx context.Context, host string, args *RangeGetShardArgs) (body io.ReadCloser, shardCrc uint32, err error)
GetShards(ctx context.Context, host string, args *GetShardsArgs) (getter ShardGetter, err error)
PutShard(ctx context.Context, host string, args *PutShardArgs) (crc uint32, err error)
StatShard(ctx context.Context, host string, args *StatShardArgs) (si *ShardInfo, err error)
MarkDeleteShard(ctx context.Context, host string, args *DeleteShardArgs) (err error)

View File

@ -17,7 +17,7 @@ package blobnode
import (
"bytes"
"context"
"io"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
@ -108,7 +108,7 @@ func TestNewBlobNodeClient(t *testing.T) {
body, _, err := cli.RangeGetShard(ctx, mockServer.URL, getShardArgs)
require.NoError(t, err)
if body != nil {
b, _ := io.ReadAll(body)
b, _ := ioutil.ReadAll(body)
span.Infof("body: %s\n", b)
}

View File

@ -0,0 +1,65 @@
// Copyright 2022 The CubeFS Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package blobnode
import (
"time"
"github.com/cubefs/cubefs/blobstore/common/proto"
)
type DiskHeartBeatInfo struct {
DiskID proto.DiskID `json:"disk_id"`
Used int64 `json:"used"` // disk used space
Free int64 `json:"free"` // remaining free space on the disk
Size int64 `json:"size"` // total physical disk space
MaxChunkCnt int64 `json:"max_chunk_cnt"` // note: maintained by clustermgr
FreeChunkCnt int64 `json:"free_chunk_cnt"` // note: maintained by clustermgr
UsedChunkCnt int64 `json:"used_chunk_cnt"` // current number of chunks on the disk
}
type DiskInfo struct {
ClusterID proto.ClusterID `json:"cluster_id"`
Idc string `json:"idc"`
Rack string `json:"rack"`
Host string `json:"host"`
Path string `json:"path"`
Status proto.DiskStatus `json:"status"` // normal、broken、repairing、repaired、dropped
Readonly bool `json:"readonly"`
CreateAt time.Time `json:"create_time"`
LastUpdateAt time.Time `json:"last_update_time"`
DiskHeartBeatInfo
}
type ChunkInfo struct {
Id ChunkId `json:"id"`
Vuid proto.Vuid `json:"vuid"`
DiskID proto.DiskID `json:"diskid"`
Total uint64 `json:"total"` // ChunkSize
Used uint64 `json:"used"` // user data size
Free uint64 `json:"free"` // ChunkSize - Used
Size uint64 `json:"size"` // Chunk File Size (logic size)
Status ChunkStatus `json:"status"` // normal、readOnly
Compacting bool `json:"compacting"`
}
type ShardInfo struct {
Vuid proto.Vuid `json:"vuid"`
Bid proto.BlobID `json:"bid"`
Size int64 `json:"size"`
Crc uint32 `json:"crc"`
Flag ShardStatus `json:"flag"` // 1:normal,2:markDelete
Inline bool `json:"inline"`
}

View File

@ -28,48 +28,35 @@ const (
type IOType uint64
const (
WriteIO IOType = iota // From: external: user io: write
BackgroundIO // From: external: background io: shard repair; disk repair, compact;balance, drop, manual migrate
ReadIO
DeleteIO
IOTypeMax // 4
NormalIO IOType = iota // From: external: user io: read/write
BackgroundIO // From: external: background io: shard repair;disk repair, delete, compact;balance, drop, manual migrate; internal, inspect
IOTypeMax // 2
IOTypeOldMax = 8 // For compatibility with previous versions
)
var (
ioTypeArray = [...]string{
"write",
"background",
"read",
"delete",
}
revertIOMap = make(map[string]IOType, IOTypeMax)
)
var _ = ioTypeArray[IOTypeMax-1]
func init() {
for id, str := range ioTypeArray {
revertIOMap[str] = IOType(id)
}
var IOtypemap = [...]string{
"normal",
"background",
}
var _ = IOtypemap[IOTypeMax-1]
func (it IOType) IsValid() bool {
return it >= WriteIO && it < IOTypeMax
return it >= NormalIO && it < IOTypeOldMax
}
func (it IOType) String() string {
return ioTypeArray[it]
return IOtypemap[it]
}
func (it IOType) IsHighLevel() bool {
return it == WriteIO || it == ReadIO
return it == NormalIO
}
func GetIoType(ctx context.Context) IOType {
v := ctx.Value(_ioFlowStatKey)
if v == nil {
return IOTypeMax
return NormalIO
}
return v.(IOType)
}
@ -77,15 +64,3 @@ func GetIoType(ctx context.Context) IOType {
func SetIoType(ctx context.Context, iot IOType) context.Context {
return context.WithValue(ctx, _ioFlowStatKey, iot)
}
func StringToIOType(str string) IOType {
tp, exist := revertIOMap[str]
if exist {
return tp
}
return IOTypeMax
}
func GetAllIOType() [IOTypeMax]string {
return ioTypeArray
}

View File

@ -24,104 +24,14 @@ import (
func TestGetIoType(t *testing.T) {
ctx := context.TODO()
// nil ctx, should return IOTypeMax/invalid
iotype := GetIoType(ctx)
require.Equal(t, IOTypeMax, iotype)
require.Equal(t, NormalIO, iotype)
ctx0 := context.WithValue(ctx, _ioFlowStatKey, BackgroundIO)
iotype = GetIoType(ctx0)
require.Equal(t, BackgroundIO, iotype)
ctx1 := SetIoType(ctx, ReadIO)
ctx1 := context.WithValue(ctx0, _ioFlowStatKey, BackgroundIO)
iotype = GetIoType(ctx1)
require.Equal(t, ReadIO, iotype)
ctx = SetIoType(ctx, DeleteIO)
iotype = GetIoType(ctx)
require.Equal(t, DeleteIO, iotype)
}
func TestIOType_IsValid(t *testing.T) {
tests := []struct {
name string
ioType IOType
expected bool
}{
{name: "WriteIO is valid", ioType: WriteIO, expected: true},
{name: "BackgroundIO is valid", ioType: BackgroundIO, expected: true},
{name: "ReadIO is valid", ioType: ReadIO, expected: true},
{name: "DeleteIO is valid", ioType: DeleteIO, expected: true},
{name: "IOTypeMax is invalid", ioType: IOTypeMax, expected: false},
{name: "IOTypeMax+1 is invalid", ioType: IOTypeMax + 1, expected: false},
{name: "IOTypeMax-1 is valid", ioType: IOTypeMax - 1, expected: true},
{name: "Zero value is valid (WriteIO)", ioType: 0, expected: true},
{name: "Large value is invalid", ioType: 100, expected: false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := tt.ioType.IsValid()
require.Equal(t, tt.expected, result)
})
}
}
func TestIOType_String(t *testing.T) {
tests := []struct {
name string
ioType IOType
expected string
}{
{name: "WriteIO string", ioType: WriteIO, expected: "write"},
{name: "BackgroundIO string", ioType: BackgroundIO, expected: "background"},
{name: "ReadIO string", ioType: ReadIO, expected: "read"},
{name: "DeleteIO string", ioType: DeleteIO, expected: "delete"},
{name: "IOTypeMax string (should panic or return empty)", ioType: IOTypeMax, expected: ""},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.ioType >= IOTypeMax {
// Test that it doesn't panic
require.Panics(t, func() {
_ = tt.ioType.String()
})
} else {
result := tt.ioType.String()
require.Equal(t, tt.expected, result)
}
})
}
}
func TestIOtypemap(t *testing.T) {
t.Run("ioTypeArray contains correct values", func(t *testing.T) {
require.Len(t, ioTypeArray, int(IOTypeMax))
require.Equal(t, "write", ioTypeArray[WriteIO])
require.Equal(t, "background", ioTypeArray[BackgroundIO])
require.Equal(t, "read", ioTypeArray[ReadIO])
require.Equal(t, "delete", ioTypeArray[DeleteIO])
})
t.Run("ioTypeArray index validation", func(t *testing.T) {
// Test that all valid IO types have corresponding strings
for i := WriteIO; i < IOTypeMax; i++ {
require.NotEmpty(t, ioTypeArray[i])
}
})
}
func TestRevertIOtypeMap(t *testing.T) {
t.Run("revertIOMap contains correct values", func(t *testing.T) {
require.Len(t, revertIOMap, int(IOTypeMax))
require.Equal(t, revertIOMap["write"], WriteIO)
require.Equal(t, revertIOMap["background"], BackgroundIO)
require.Equal(t, revertIOMap["read"], ReadIO)
require.Equal(t, revertIOMap["delete"], DeleteIO)
require.Equal(t, revertIOMap[WriteIO.String()], WriteIO)
require.Equal(t, revertIOMap[BackgroundIO.String()], BackgroundIO)
require.Equal(t, revertIOMap[ReadIO.String()], ReadIO)
require.Equal(t, revertIOMap[DeleteIO.String()], DeleteIO)
})
require.Equal(t, BackgroundIO, iotype)
}

View File

@ -16,14 +16,12 @@ package blobnode
import (
"context"
"encoding/binary"
"fmt"
"io"
"math"
"net/http"
"strconv"
"github.com/cubefs/cubefs/blobstore/blobnode/base"
bloberr "github.com/cubefs/cubefs/blobstore/common/errors"
"github.com/cubefs/cubefs/blobstore/common/proto"
"github.com/cubefs/cubefs/blobstore/common/rpc"
@ -31,29 +29,9 @@ import (
)
const (
MaxShardSize = math.MaxUint32
GetShardsHeaderSize = 4
MaxShardSize = math.MaxUint32
)
type ShardInfo struct {
Vuid proto.Vuid `json:"vuid"`
Bid proto.BlobID `json:"bid"`
Size int64 `json:"size"`
Crc uint32 `json:"crc"`
Offset int64 `json:"offset"`
Flag ShardStatus `json:"flag"` // 1:normal,2:markDelete
Inline bool `json:"inline"`
NopData bool `json:"nopdata"` // data all zero
}
type BidInfo struct {
Bid proto.BlobID `json:"bid"`
Size int64 `json:"size"`
Offset int64 `json:"offset"`
Crc uint32 `json:"crc"`
}
type ShardStatus uint8
const (
@ -64,11 +42,8 @@ const (
const (
ShardDataInline = 0x80 // 1000 0000
ShardDataNop = 0x40 // 0100 0000
)
var putWithCrcOption = []rpc.Option{rpc.WithCrcEncode()}
type PutShardArgs struct {
DiskID proto.DiskID `json:"diskid"`
Vuid proto.Vuid `json:"vuid"`
@ -76,8 +51,6 @@ type PutShardArgs struct {
Size int64 `json:"size"`
Type IOType `json:"iotype,omitempty"`
Body io.Reader `json:"-"`
NopData bool `json:"nopdata,omitempty"`
}
type PutShardRet struct {
@ -104,20 +77,13 @@ func (c *client) PutShard(ctx context.Context, host string, args *PutShardArgs)
}
urlStr := fmt.Sprintf("%v/shard/put/diskid/%v/vuid/%v/bid/%v/size/%v?iotype=%d",
host, args.DiskID, args.Vuid, args.Bid, args.Size, args.Type)
if args.NopData {
urlStr += "&nopdata=true"
}
req, err := http.NewRequest(http.MethodPost, urlStr, args.Body)
if err != nil {
err = convertEIO(err)
return
}
var opts []rpc.Option
if !args.NopData {
req.ContentLength = args.Size
opts = putWithCrcOption
}
if err = c.DoWith(ctx, req, ret, opts...); err == nil {
req.ContentLength = args.Size
err = c.DoWith(ctx, req, ret, rpc.WithCrcEncode())
if err == nil {
crc = ret.Crc
}
@ -149,7 +115,6 @@ func (c *client) GetShard(ctx context.Context, host string, args *GetShardArgs)
resp, err := c.Get(ctx, urlStr)
if err != nil {
err = convertEIO(err)
return nil, 0, err
}
@ -196,7 +161,6 @@ func (c *client) RangeGetShard(ctx context.Context, host string, args *RangeGetS
req, err := http.NewRequest(http.MethodGet, urlStr, nil)
if err != nil {
err = convertEIO(err)
span.Errorf("Failed new req. urlStr:%s, err:%v", urlStr, err)
return
}
@ -243,8 +207,7 @@ func (c *client) MarkDeleteShard(ctx context.Context, host string, args *DeleteS
urlStr := fmt.Sprintf("%v/shard/markdelete/diskid/%v/vuid/%v/bid/%v", host, args.DiskID, args.Vuid, args.Bid)
err = c.PostWith(ctx, urlStr, nil, rpc.NoneBody)
return convertEIO(err)
return
}
func (c *client) DeleteShard(ctx context.Context, host string, args *DeleteShardArgs) (err error) {
@ -255,15 +218,13 @@ func (c *client) DeleteShard(ctx context.Context, host string, args *DeleteShard
urlStr := fmt.Sprintf("%v/shard/delete/diskid/%v/vuid/%v/bid/%v", host, args.DiskID, args.Vuid, args.Bid)
err = c.PostWith(ctx, urlStr, nil, rpc.NoneBody)
return convertEIO(err)
return
}
type StatShardArgs struct {
DiskID proto.DiskID `json:"diskid"`
Vuid proto.Vuid `json:"vuid"`
Bid proto.BlobID `json:"bid"`
Type IOType `json:"iotype,omitempty"`
}
func (c *client) StatShard(ctx context.Context, host string, args *StatShardArgs) (si *ShardInfo, err error) {
@ -272,12 +233,11 @@ func (c *client) StatShard(ctx context.Context, host string, args *StatShardArgs
return
}
urlStr := fmt.Sprintf("%v/shard/stat/diskid/%v/vuid/%v/bid/%v?iotype=%d",
host, args.DiskID, args.Vuid, args.Bid, args.Type)
urlStr := fmt.Sprintf("%v/shard/stat/diskid/%v/vuid/%v/bid/%v",
host, args.DiskID, args.Vuid, args.Bid)
si = &ShardInfo{}
err = c.GetWith(ctx, urlStr, si)
return si, convertEIO(err)
return
}
type ListShardsArgs struct {
@ -305,129 +265,8 @@ func (c *client) ListShards(ctx context.Context, host string, args *ListShardsAr
listRet := ListShardsRet{}
err = c.GetWith(ctx, urlStr, &listRet)
if err != nil {
err = convertEIO(err)
return nil, proto.InValidBlobID, err
}
return listRet.ShardInfos, listRet.Next, nil
}
func convertEIO(err error) error {
if base.IsEIO(err) {
return bloberr.ErrDiskBroken
}
return err
}
type GetShardsArgs struct {
DiskID proto.DiskID `json:"diskid"`
Vuid proto.Vuid `json:"vuid" `
Bids []BidInfo `json:"bids"`
Type IOType `json:"type"`
}
func (c *client) GetShards(ctx context.Context, host string, args *GetShardsArgs) (getter ShardGetter, err error) {
if !args.Type.IsValid() {
err = bloberr.ErrInvalidParam
return
}
if !IsValidDiskID(args.DiskID) {
err = bloberr.ErrInvalidDiskId
return
}
urlStr := fmt.Sprintf("%v/shards/get", host)
resp, err := c.Post(ctx, urlStr, args)
if err != nil {
err = convertEIO(err)
return
}
if resp.StatusCode/100 != 2 {
defer resp.Body.Close()
err = rpc.ParseResponseErr(resp)
return
}
return &shardGetter{bids: args.Bids, body: resp.Body}, nil
}
type ShardGetter interface {
// NextShard before read next shard must read all data of last shard, if not will get unexpect error
NextShard(ctx context.Context) (body io.ReadCloser, err error, ok bool)
Close() error
}
type shardGetter struct {
body io.ReadCloser
bids []BidInfo
idx int
}
func (b *shardGetter) NextShard(ctx context.Context) (io.ReadCloser, error, bool) {
span := trace.SpanFromContextSafe(ctx)
if b.idx >= len(b.bids) {
return nil, nil, false
}
var header ShardsHeader
_, err := io.ReadFull(b.body, header[:])
if err != nil {
return nil, err, true
}
code := header.Get()
if code != 200 {
span.Errorf("download shard failed, errCode: %s", code)
return nil, bloberr.ErrBidNotMatch, true
}
bid := b.bids[b.idx]
b.idx++
return io.NopCloser(io.LimitReader(b.body, bid.Size)), nil, true
}
func (b *shardGetter) Close() error {
return b.body.Close()
}
type shardWriter struct {
header int
headerWritten bool
shard io.WriterTo
}
func NewShardWriter(header int, shard io.WriterTo) io.WriterTo {
return &shardWriter{header: header, shard: shard}
}
func (s *shardWriter) WriteTo(w io.Writer) (int64, error) {
if s.headerWritten {
return s.shard.WriteTo(w)
}
// write header
var header ShardsHeader
header.Set(s.header)
start := int64(0)
for start < int64(len(header)) {
n, err := w.Write(header[start:])
if err != nil {
return start, err
}
start += int64(n)
}
s.headerWritten = true
if s.header != http.StatusOK {
return int64(len(header)), bloberr.ErrBidNotMatch
}
// write data
n, err := s.shard.WriteTo(w)
return n + start, err
}
type ShardsHeader [GetShardsHeaderSize]byte
func (s *ShardsHeader) Set(code int) {
binary.BigEndian.PutUint32(s[:], uint32(code))
}
func (s *ShardsHeader) Get() int {
return int(binary.BigEndian.Uint32(s[:]))
}

View File

@ -15,26 +15,13 @@
package blobnode
import (
"syscall"
"testing"
"github.com/cubefs/cubefs/util/errors"
"github.com/stretchr/testify/require"
bloberr "github.com/cubefs/cubefs/blobstore/common/errors"
)
func TestShardStatus(t *testing.T) {
require.Equal(t, ShardStatusDefault, ShardStatus(0))
require.Equal(t, ShardStatusNormal, ShardStatus(1))
require.Equal(t, ShardStatusMarkDelete, ShardStatus(2))
var err error
require.Nil(t, convertEIO(err))
err = syscall.EIO
require.ErrorIs(t, convertEIO(err), bloberr.ErrDiskBroken)
err = errors.New("input/output error")
require.ErrorIs(t, convertEIO(err), bloberr.ErrDiskBroken)
}

View File

@ -1,108 +0,0 @@
package clustermgr
import (
"bytes"
"context"
"crypto/md5"
"encoding/base64"
"encoding/binary"
"fmt"
"time"
)
const (
hashBytesLength = 16
authVersionV1 uint8 = 1
)
func (c *Client) CreateSpace(ctx context.Context, args *CreateSpaceArgs) (err error) {
err = c.PostWith(ctx, "/space/create", nil, args)
return
}
func (c *Client) GetSpaceByName(ctx context.Context, args *GetSpaceByNameArgs) (ret *Space, err error) {
ret = &Space{}
err = c.GetWith(ctx, "/space/get?name="+args.Name, ret)
return
}
func (c *Client) GetSpaceByID(ctx context.Context, args *GetSpaceByIDArgs) (ret *Space, err error) {
ret = &Space{}
err = c.GetWith(ctx, "/space/get?space_id="+args.SpaceID.ToString(), ret)
return
}
func (c *Client) AuthSpace(ctx context.Context, args *AuthSpaceArgs) (err error) {
err = c.GetWith(ctx, fmt.Sprintf("/space/auth?name=%s&token=%s", args.Name, args.Token), nil)
return
}
func (c *Client) ListSpace(ctx context.Context, args *ListSpaceArgs) (ret ListSpaceRet, err error) {
err = c.GetWith(ctx, fmt.Sprintf("/space/list?marker=%d&count=%d", args.Marker, args.Count), &ret)
return
}
type AuthInfo struct {
AccessKey string
SecretKey string
}
// EncodeAuthInfo SDK generates token based on ak/sk
func EncodeAuthInfo(auth *AuthInfo) (token string, err error) {
timeStamp := time.Now().Unix()
hashBytes := CalculateHash(auth, timeStamp)
w := bytes.NewBuffer([]byte{})
if err = binary.Write(w, binary.LittleEndian, authVersionV1); err != nil {
return
}
if err = binary.Write(w, binary.LittleEndian, &timeStamp); err != nil {
return
}
if err = binary.Write(w, binary.LittleEndian, &hashBytes); err != nil {
return
}
return base64.URLEncoding.EncodeToString(w.Bytes()), nil
}
// DecodeAuthInfo server parses token
func DecodeAuthInfo(token string) (timestamp int64, hashBytes []byte, err error) {
b, err := base64.URLEncoding.DecodeString(token)
if err != nil {
return
}
var authVersion uint8
hashBytes = make([]byte, hashBytesLength)
r := bytes.NewBuffer(b)
if err = binary.Read(r, binary.LittleEndian, &authVersion); err != nil {
return
}
if authVersion == authVersionV1 {
if err = binary.Read(r, binary.LittleEndian, &timestamp); err != nil {
return
}
if err = binary.Read(r, binary.LittleEndian, &hashBytes); err != nil {
return
}
}
return
}
// CalculateHash server caculates hash based on ak/sk and timeStamp
func CalculateHash(auth *AuthInfo, timeStamp int64) (hashBytes []byte) {
b := make([]byte, 8)
binary.LittleEndian.PutUint64(b, uint64(timeStamp))
hash := md5.New()
hash.Write(b)
hash.Write([]byte(auth.AccessKey))
hash.Write([]byte(auth.SecretKey))
hashBytes = hash.Sum(nil)
return hashBytes
}
func (c *Client) GetCatalogChanges(ctx context.Context, args *GetCatalogChangesArgs) (ret *GetCatalogChangesRet, err error) {
ret = &GetCatalogChangesRet{}
err = c.GetWith(ctx, fmt.Sprintf("/catalogchanges/get?route_version=%d&node_id=%d", args.RouteVersion, args.NodeID), ret)
return
}

File diff suppressed because it is too large Load Diff

View File

@ -1,96 +0,0 @@
syntax = "proto3";
package cubefs.blobstore.api.clustermgr;
option go_package = "./;clustermgr";
option (gogoproto.sizer_all) = true;
option (gogoproto.marshaler_all) = true;
option (gogoproto.unmarshaler_all) = true;
option (gogoproto.goproto_unkeyed_all) = true;
option (gogoproto.goproto_unrecognized_all) = true;
option (gogoproto.goproto_sizecache_all) = true;
option (gogoproto.goproto_stringer_all) = false;
option (gogoproto.stringer_all) = true;
option (gogoproto.gostring_all) = true;
import "gogoproto/gogo.proto";
import "google/protobuf/any.proto";
import "cubefs/blobstore/api/clustermgr/shard.proto";
message Space {
uint32 space_id = 1 [(gogoproto.customname) = "SpaceID", (gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.SpaceID"];
string name = 2;
uint32 status = 3[(gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.SpaceStatus"];
repeated FieldMeta field_metas = 4 [(gogoproto.nullable) = false];
string acc_key = 5;
string sec_key = 6;
}
message FieldMeta {
uint32 id = 1 [(gogoproto.customname) = "ID", (gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.FieldID"];
string name = 2;
uint32 field_type = 3 [(gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.FieldType"];
uint32 index_option = 4 [(gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.IndexOption"];
}
message CreateSpaceArgs {
string name = 1;
repeated FieldMeta field_metas = 2 [(gogoproto.nullable) = false];
}
message GetSpaceByNameArgs {
string name = 1;
}
message GetSpaceByIDArgs {
uint32 space_id = 1 [(gogoproto.customname) = "SpaceID", (gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.SpaceID"];
}
message GetSpaceArgs {
string name = 1;
uint32 space_id = 2 [(gogoproto.customname) = "SpaceID", (gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.SpaceID"];
}
message AuthSpaceArgs {
string name = 1;
string token = 2;
}
message CatalogChangeShardAdd {
uint32 shard_id = 1 [(gogoproto.customname) = "ShardID", (gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.ShardID"];
uint64 route_version = 2 [(gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.RouteVersion"];
repeated ShardUnitInfo units = 3 [(gogoproto.nullable) = false];
}
message CatalogChangeShardUpdate {
uint32 shard_id = 1 [(gogoproto.customname) = "ShardID", (gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.ShardID"];
uint64 route_version = 2 [(gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.RouteVersion"];
ShardUnitInfo unit = 3 [(gogoproto.nullable) = false];
}
message CatalogChangeItem {
uint64 route_version = 1 [(gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.RouteVersion"];
uint32 type = 2 [(gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.CatalogChangeItemType"];
google.protobuf.Any item =3;
}
message GetCatalogChangesArgs {
uint64 route_version = 1 [(gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.RouteVersion"];
uint32 node_id = 2 [(gogoproto.customname) = "NodeID", (gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.NodeID"];
}
message GetCatalogChangesRet {
uint64 route_version = 1 [(gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.RouteVersion"];
repeated CatalogChangeItem items = 2 [(gogoproto.nullable) = false];
}
message ListSpaceArgs {
uint32 marker = 1 [(gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.SpaceID"];
uint32 count = 2;
}
message ListSpaceRet {
repeated Space spaces = 1;
uint32 marker = 2 [(gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.SpaceID"];
}

View File

@ -1,181 +0,0 @@
// Copyright 2024 The CubeFS Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package clustermgr
import (
"encoding/binary"
"encoding/hex"
"errors"
"time"
"github.com/cubefs/cubefs/blobstore/common/proto"
)
type ChunkInfo struct {
Id ChunkID `json:"id"`
Vuid proto.Vuid `json:"vuid"`
DiskID proto.DiskID `json:"diskid"`
Total uint64 `json:"total"` // ChunkSize
Used uint64 `json:"used"` // user data size
Free uint64 `json:"free"` // ChunkSize - Used
Size uint64 `json:"size"` // Chunk File Size (logic size)
Status ChunkStatus `json:"status"` // normal、readOnly
Compacting bool `json:"compacting"`
}
const (
ChunkStatusDefault ChunkStatus = iota // 0
ChunkStatusNormal // 1
ChunkStatusReadOnly // 2
ChunkStatusRelease // 3
ChunkNumStatus // 4
)
const (
ReleaseForUser = "release for user"
ReleaseForCompact = "release for compact"
)
// Chunk ID
// vuid + timestamp
const (
chunkVuidLen = 8
chunkTimestmapLen = 8
ChunkIDLength = chunkVuidLen + chunkTimestmapLen
)
var InvalidChunkID ChunkID = [ChunkIDLength]byte{}
var (
_vuidHexLen = hex.EncodedLen(chunkVuidLen)
_timestampHexLen = hex.EncodedLen(chunkTimestmapLen)
// ${vuid_hex}-${tiemstamp_hex}
// |-- 8 Bytes --|-- 1 Bytes --|-- 8 Bytes --|
delimiter = []byte("-")
ChunkIDEncodeLen = _vuidHexLen + _timestampHexLen + len(delimiter)
)
type (
ChunkID [ChunkIDLength]byte
ChunkStatus uint8
)
func (c ChunkID) UnixTime() uint64 {
return binary.BigEndian.Uint64(c[chunkVuidLen:ChunkIDLength])
}
func (c ChunkID) VolumeUnitId() proto.Vuid {
return proto.Vuid(binary.BigEndian.Uint64(c[:chunkVuidLen]))
}
func (c *ChunkID) Marshal() ([]byte, error) {
buf := make([]byte, ChunkIDEncodeLen)
var i int
hex.Encode(buf[i:_vuidHexLen], c[:chunkVuidLen])
i += _vuidHexLen
copy(buf[i:i+len(delimiter)], delimiter)
i += len(delimiter)
hex.Encode(buf[i:], c[chunkVuidLen:ChunkIDLength])
return buf, nil
}
func (c *ChunkID) Unmarshal(data []byte) error {
if len(data) != ChunkIDEncodeLen {
panic(errors.New("chunk buf size not match"))
}
var i int
_, err := hex.Decode(c[:chunkVuidLen], data[i:_vuidHexLen])
if err != nil {
return err
}
i += _vuidHexLen
i += len(delimiter)
_, err = hex.Decode(c[chunkVuidLen:], data[i:])
if err != nil {
return err
}
return nil
}
func (c ChunkID) String() string {
buf, _ := c.Marshal()
return string(buf[:])
}
func (c ChunkID) MarshalJSON() ([]byte, error) {
b := make([]byte, ChunkIDEncodeLen+2)
b[0], b[ChunkIDEncodeLen+1] = '"', '"'
buf, _ := c.Marshal()
copy(b[1:], buf)
return b, nil
}
func (c *ChunkID) UnmarshalJSON(data []byte) (err error) {
if len(data) != ChunkIDEncodeLen+2 {
return errors.New("failed unmarshal json")
}
return c.Unmarshal(data[1 : ChunkIDEncodeLen+1])
}
func EncodeChunk(id ChunkID) string {
return id.String()
}
func NewChunkID(vuid proto.Vuid) (chunkId ChunkID) {
binary.BigEndian.PutUint64(chunkId[:chunkVuidLen], uint64(vuid))
binary.BigEndian.PutUint64(chunkId[chunkVuidLen:ChunkIDLength], uint64(time.Now().UnixNano()))
return
}
func DecodeChunk(name string) (id ChunkID, err error) {
buf := []byte(name)
if len(buf) != ChunkIDEncodeLen {
return InvalidChunkID, errors.New("invalid chunk name")
}
if err = id.Unmarshal(buf); err != nil {
return InvalidChunkID, errors.New("chunk unmarshal failed")
}
return
}
func (s *ChunkStatus) String() string {
switch *s {
case ChunkStatusDefault:
return "default"
case ChunkStatusNormal:
return "normal"
case ChunkStatusReadOnly:
return "readOnly"
case ChunkStatusRelease:
return "release"
default:
return "unkown"
}
}

View File

@ -22,7 +22,6 @@ import (
"github.com/cubefs/cubefs/blobstore/common/errors"
"github.com/cubefs/cubefs/blobstore/common/proto"
"github.com/cubefs/cubefs/blobstore/common/rpc"
"github.com/cubefs/cubefs/blobstore/util"
)
const (
@ -135,12 +134,3 @@ func (c *Client) Stat(ctx context.Context) (ret *StatInfo, err error) {
func (c *Client) Snapshot(ctx context.Context) (*http.Response, error) {
return c.Get(ctx, "/snapshot/dump")
}
type SetClusterReadonlyArgs struct {
Readonly bool `json:"readonly"`
}
func (c *Client) SetClusterReadonly(ctx context.Context, args *SetClusterReadonlyArgs) (err error) {
err = c.PostWith(ctx, "/cluster/set?readonly="+util.Any2String(args.Readonly), nil, nil)
return
}

View File

@ -16,10 +16,7 @@ package clustermgr
import (
"context"
"encoding/json"
"github.com/cubefs/cubefs/blobstore/common/codemode"
"github.com/cubefs/cubefs/blobstore/common/proto"
"github.com/cubefs/cubefs/blobstore/common/rpc"
)
@ -41,8 +38,8 @@ func (c *Client) GetConfig(ctx context.Context, key string) (ret string, err err
return
}
func (c *Client) SetConfig(ctx context.Context, key, value string) (err error) {
err = c.PostWith(ctx, "/config/set", nil, &ConfigSetArgs{Key: key, Value: value})
func (c *Client) SetConfig(ctx context.Context, args *ConfigSetArgs) (err error) {
err = c.PostWith(ctx, "/config/set", nil, args)
return
}
@ -50,19 +47,3 @@ func (c *Client) DeleteConfig(ctx context.Context, key string) (err error) {
err = c.PostWith(ctx, "/config/delete?key="+key, nil, rpc.NoneBody)
return
}
func LoadExtendCodemode(ctx context.Context, configer interface {
GetConfig(context.Context, string) (string, error)
},
) error {
extend, err := configer.GetConfig(ctx, proto.CodeModeExtendKey)
if err != nil {
return err
}
extends := make([]codemode.ExtendCodeMode, 0)
if err = json.Unmarshal([]byte(extend), &extends); err != nil {
return err
}
codemode.Extend(extends...)
return nil
}

View File

@ -16,69 +16,14 @@ package clustermgr
import (
"context"
"encoding/json"
"errors"
"fmt"
"time"
"github.com/cubefs/cubefs/blobstore/api/blobnode"
"github.com/cubefs/cubefs/blobstore/common/proto"
"github.com/cubefs/cubefs/blobstore/common/rpc"
)
type ShardNodeDiskInfo struct {
DiskInfo
ShardNodeDiskHeartbeatInfo
}
func (s *ShardNodeDiskInfo) Marshal() ([]byte, error) {
return json.Marshal(s)
}
func (s *ShardNodeDiskInfo) Unmarshal(raw []byte) error {
return json.Unmarshal(raw, s)
}
type ShardNodeDiskHeartbeatInfo struct {
DiskID proto.DiskID `json:"disk_id"`
Used int64 `json:"used"` // disk used space
Free int64 `json:"free"` // remaining free space on the disk
Size int64 `json:"size"` // total physical disk space
MaxShardCnt int32 `json:"max_shard_cnt"` // note: maintained by clustermgr
FreeShardCnt int32 `json:"free_shard_cnt"` // note: maintained by clustermgr
UsedShardCnt int32 `json:"used_shard_cnt"` // current number of shards on the disk
}
type BlobNodeDiskInfo struct {
DiskInfo
DiskHeartBeatInfo
}
type DiskHeartBeatInfo struct {
DiskID proto.DiskID `json:"disk_id"`
Used int64 `json:"used"` // disk used space
Free int64 `json:"free"` // remaining free space on the disk
Size int64 `json:"size"` // total physical disk space
MaxChunkCnt int64 `json:"max_chunk_cnt"` // note: maintained by clustermgr
FreeChunkCnt int64 `json:"free_chunk_cnt"` // note: maintained by clustermgr
UsedChunkCnt int64 `json:"used_chunk_cnt"` // current number of chunks on the disk
OversoldFreeChunkCnt int64 `json:"oversold_free_chunk_cnt"` // note: maintained by clustermgr
}
type DiskInfo struct {
ClusterID proto.ClusterID `json:"cluster_id"`
Idc string `json:"idc"`
Rack string `json:"rack"`
Host string `json:"host"`
Path string `json:"path"`
Status proto.DiskStatus `json:"status"` // normal、broken、repairing、repaired、dropped
Readonly bool `json:"readonly"`
CreateAt time.Time `json:"create_time"`
LastUpdateAt time.Time `json:"last_update_time"`
DiskSetID proto.DiskSetID `json:"disk_set_id"`
NodeID proto.NodeID `json:"node_id"`
}
type DiskInfoArgs struct {
DiskID proto.DiskID `json:"disk_id"`
}
@ -104,21 +49,12 @@ type ListOptionArgs struct {
}
type ListDiskRet struct {
Disks []*BlobNodeDiskInfo `json:"disks"`
Marker proto.DiskID `json:"marker"`
}
type ListShardNodeDiskRet struct {
Disks []*ShardNodeDiskInfo `json:"disks"`
Disks []*blobnode.DiskInfo `json:"disks"`
Marker proto.DiskID `json:"marker"`
}
type DisksHeartbeatArgs struct {
Disks []*DiskHeartBeatInfo `json:"disks"`
}
type ShardNodeDisksHeartbeatArgs struct {
Disks []ShardNodeDiskHeartbeatInfo `json:"disks"`
Disks []*blobnode.DiskHeartBeatInfo `json:"disks"`
}
type DisksHeartbeatRet struct {
@ -132,32 +68,26 @@ type DiskHeartbeatRet struct {
}
type DiskStatInfo struct {
IDC string `json:"idc"`
Total int `json:"total"`
TotalChunk int64 `json:"total_chunk,omitempty"`
TotalFreeChunk int64 `json:"total_free_chunk,omitempty"`
TotalOversoldFreeChunk int64 `json:"total_oversold_free_chunk,omitempty"`
TotalShard int64 `json:"total_shard,omitempty"`
TotalFreeShard int64 `json:"total_free_shard,omitempty"`
Available int `json:"available"`
Readonly int `json:"readonly"`
Expired int `json:"expired"`
Broken int `json:"broken"`
Repairing int `json:"repairing"`
Repaired int `json:"repaired"`
Dropping int `json:"dropping"`
Dropped int `json:"dropped"`
IDC string `json:"idc"`
Total int `json:"total"`
TotalChunk int64 `json:"total_chunk"`
TotalFreeChunk int64 `json:"total_free_chunk"`
Available int `json:"available"`
Readonly int `json:"readonly"`
Expired int `json:"expired"`
Broken int `json:"broken"`
Repairing int `json:"repairing"`
Repaired int `json:"repaired"`
Dropping int `json:"dropping"`
Dropped int `json:"dropped"`
}
type SpaceStatInfo struct {
TotalSpace int64 `json:"total_space"` // total physical space
FreeSpace int64 `json:"free_space"` // free physical space which is writable
ReadOnlySpace int64 `json:"readonly_space"` // free physical space which is readonly
UsedSpace int64 `json:"used_space"` // used physical space
ReservedSpace int64 `json:"reserved_space"` // reserved logical space
WritableSpace int64 `json:"writable_space"` // writable logical space
TotalBlobNode int64 `json:"total_blob_node,omitempty"`
TotalShardNode int64 `json:"total_shard_node,omitempty"`
TotalSpace int64 `json:"total_space"`
FreeSpace int64 `json:"free_space"`
UsedSpace int64 `json:"used_space"`
WritableSpace int64 `json:"writable_space"`
TotalBlobNode int64 `json:"total_blob_node"`
TotalDisk int64 `json:"total_disk"`
DisksStatInfos []DiskStatInfo `json:"disk_stat_infos"`
}
@ -178,14 +108,14 @@ func (c *Client) AllocDiskID(ctx context.Context) (proto.DiskID, error) {
}
// DiskInfo get disk info from cluster manager
func (c *Client) DiskInfo(ctx context.Context, id proto.DiskID) (ret *BlobNodeDiskInfo, err error) {
ret = &BlobNodeDiskInfo{}
func (c *Client) DiskInfo(ctx context.Context, id proto.DiskID) (ret *blobnode.DiskInfo, err error) {
ret = &blobnode.DiskInfo{}
err = c.GetWith(ctx, "/disk/info?disk_id="+id.ToString(), ret)
return
}
// AddDisk add/register a new disk into cluster manager
func (c *Client) AddDisk(ctx context.Context, info *BlobNodeDiskInfo) (err error) {
func (c *Client) AddDisk(ctx context.Context, info *blobnode.DiskInfo) (err error) {
err = c.PostWith(ctx, "/disk/add", nil, info)
return
}
@ -199,7 +129,7 @@ func (c *Client) SetDisk(ctx context.Context, id proto.DiskID, status proto.Disk
}
// ListHostDisk list specified host disk info from cluster manager
func (c *Client) ListHostDisk(ctx context.Context, host string) (ret []*BlobNodeDiskInfo, err error) {
func (c *Client) ListHostDisk(ctx context.Context, host string) (ret []*blobnode.DiskInfo, err error) {
listRet := ListDiskRet{}
opt := &ListOptionArgs{Host: host, Count: 200}
for {
@ -228,7 +158,7 @@ func (c *Client) ListDisk(ctx context.Context, options *ListOptionArgs) (ret Lis
}
// HeartbeatDisk report blobnode disk latest capacity info to cluster manager
func (c *Client) HeartbeatDisk(ctx context.Context, infos []*DiskHeartBeatInfo) (ret []*DiskHeartbeatRet, err error) {
func (c *Client) HeartbeatDisk(ctx context.Context, infos []*blobnode.DiskHeartBeatInfo) (ret []*DiskHeartbeatRet, err error) {
result := &DisksHeartbeatRet{}
args := &DisksHeartbeatArgs{Disks: infos}
err = c.PostWith(ctx, "/disk/heartbeat", result, args)
@ -246,7 +176,7 @@ func (c *Client) DroppedDisk(ctx context.Context, id proto.DiskID) (err error) {
return
}
func (c *Client) ListDroppingDisk(ctx context.Context) (ret []*BlobNodeDiskInfo, err error) {
func (c *Client) ListDroppingDisk(ctx context.Context) (ret []*blobnode.DiskInfo, err error) {
result := &ListDiskRet{}
err = c.GetWith(ctx, "/disk/droppinglist", result)
ret = result.Disks
@ -257,56 +187,3 @@ func (c *Client) SetReadonlyDisk(ctx context.Context, id proto.DiskID, readonly
err = c.PostWith(ctx, "/disk/access", nil, &DiskAccessArgs{DiskID: id, Readonly: readonly})
return
}
// AddShardNodeDisk add/register a new disk of shardnode into cluster manager
func (c *Client) AddShardNodeDisk(ctx context.Context, info *ShardNodeDiskInfo) (err error) {
err = c.PostWith(ctx, "/shardnode/disk/add", nil, info)
return
}
// HeartbeatShardNodeDisk report shardnode disk latest capacity info to cluster manager
func (c *Client) HeartbeatShardNodeDisk(ctx context.Context, infos []ShardNodeDiskHeartbeatInfo) (err error) {
args := &ShardNodeDisksHeartbeatArgs{Disks: infos}
err = c.PostWith(ctx, "/shardnode/disk/heartbeat", nil, args)
return
}
// AllocShardNodeDiskID alloc shardnode diskID from cluster manager
func (c *Client) AllocShardNodeDiskID(ctx context.Context) (proto.DiskID, error) {
ret := &DiskIDAllocRet{}
err := c.PostWith(ctx, "/shardnode/diskid/alloc", ret, rpc.NoneBody)
if err != nil {
return 0, err
}
return ret.DiskID, nil
}
// ListShardNodeDisk list disk info from cluster manager
// when ListOptionArgs is default value, defalut return 10 diskInfos
func (c *Client) ListShardNodeDisk(ctx context.Context, options *ListOptionArgs) (ret ListShardNodeDiskRet, err error) {
err = c.GetWith(ctx, fmt.Sprintf(
"/shardnode/disk/list?idc=%s&rack=%s&host=%s&status=%d&marker=%d&count=%d",
options.Idc,
options.Rack,
options.Host,
options.Status,
options.Marker,
options.Count,
), &ret)
return
}
// ShardNodeDiskInfo get shardnode disk info from cluster manager
func (c *Client) ShardNodeDiskInfo(ctx context.Context, id proto.DiskID) (ret *ShardNodeDiskInfo, err error) {
ret = &ShardNodeDiskInfo{}
err = c.GetWith(ctx, "/shardnode/disk/info?disk_id="+id.ToString(), ret)
return
}
// SetShardNodeDisk set shardnode disk status
func (c *Client) SetShardNodeDisk(ctx context.Context, id proto.DiskID, status proto.DiskStatus) (err error) {
if !status.IsValid() {
return errors.New("invalid status")
}
return c.PostWith(ctx, "/shardnode/disk/set", nil, &DiskSetArgs{DiskID: id, Status: status})
}

View File

@ -1,121 +0,0 @@
// Copyright 2024 The CubeFS Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package clustermgr
import (
"context"
"github.com/cubefs/cubefs/blobstore/common/proto"
)
type BlobNodeInfo struct {
NodeInfo
}
type ShardNodeInfo struct {
NodeInfo
ShardNodeExtraInfo
}
type ShardNodeExtraInfo struct {
RaftHost string `json:"raft_host"`
}
type NodeInfo struct {
NodeID proto.NodeID `json:"node_id"`
NodeSetID proto.NodeSetID `json:"node_set_id"`
ClusterID proto.ClusterID `json:"cluster_id"`
DiskType proto.DiskType `json:"disk_type"` // one node only manages one diskType disk
Idc string `json:"idc"`
Rack string `json:"rack"`
Host string `json:"host"`
Role proto.NodeRole `json:"role"`
Status proto.NodeStatus `json:"status"`
}
type NodeInfoArgs struct {
NodeID proto.NodeID `json:"node_id"`
}
type NodeIDAllocRet struct {
NodeID proto.NodeID `json:"node_id"`
}
type NodeSetInfo struct {
ID proto.NodeSetID `json:"id"`
Number int `json:"number"`
Nodes []proto.NodeID `json:"nodes"`
DiskSets map[proto.DiskSetID][]proto.DiskID `json:"disk_sets"`
}
type TopoInfo struct {
CurNodeSetID proto.NodeSetID `json:"cur_node_set_id"`
CurDiskSetID proto.DiskSetID `json:"cur_disk_set_id"`
AllNodeSets map[string]map[proto.NodeSetID]*NodeSetInfo `json:"all_node_sets"`
}
// AddNode add a new node into cluster manager and return allocated nodeID
func (c *Client) AddNode(ctx context.Context, info *BlobNodeInfo) (proto.NodeID, error) {
ret := &NodeIDAllocRet{}
err := c.PostWith(ctx, "/node/add", ret, info)
if err != nil {
return 0, err
}
return ret.NodeID, nil
}
// DropNode drop a node from cluster manager
func (c *Client) DropNode(ctx context.Context, id proto.NodeID) (err error) {
err = c.PostWith(ctx, "/node/drop", nil, &NodeInfoArgs{NodeID: id})
return
}
// NodeInfo get node info from cluster manager
func (c *Client) NodeInfo(ctx context.Context, id proto.NodeID) (ret *BlobNodeInfo, err error) {
ret = &BlobNodeInfo{}
err = c.GetWith(ctx, "/node/info?node_id="+id.ToString(), ret)
return
}
// TopoInfo get nodeset and diskset topo info from cluster manager
func (c *Client) TopoInfo(ctx context.Context) (ret *TopoInfo, err error) {
ret = &TopoInfo{}
err = c.GetWith(ctx, "/topo/info", ret)
return
}
// AddShardNode add a new shardnode into cluster manager and return allocated nodeID
func (c *Client) AddShardNode(ctx context.Context, info *ShardNodeInfo) (proto.NodeID, error) {
ret := &NodeIDAllocRet{}
err := c.PostWith(ctx, "/shardnode/add", ret, info)
if err != nil {
return 0, err
}
return ret.NodeID, nil
}
// ShardNodeInfo get shardnode info from cluster manager
func (c *Client) ShardNodeInfo(ctx context.Context, id proto.NodeID) (ret *ShardNodeInfo, err error) {
ret = &ShardNodeInfo{}
err = c.GetWith(ctx, "/shardnode/info?node_id="+id.ToString(), ret)
return
}
// ShardNodeTopoInfo get shardnode nodeset and diskset topo info from cluster manager
func (c *Client) ShardNodeTopoInfo(ctx context.Context) (ret *TopoInfo, err error) {
ret = &TopoInfo{}
err = c.GetWith(ctx, "/shardnode/topo/info", ret)
return
}

View File

@ -18,7 +18,9 @@ import (
"context"
"fmt"
"github.com/cubefs/cubefs/blobstore/api/blobnode"
"github.com/cubefs/cubefs/blobstore/common/proto"
"github.com/cubefs/cubefs/blobstore/common/raftserver"
)
const (
@ -37,12 +39,11 @@ type ClusterInfo struct {
}
type StatInfo struct {
LeaderHost string `json:"leader_host"`
ReadOnly bool `json:"read_only"`
RaftStatus interface{} `json:"raft_status"`
BlobNodeSpaceStat SpaceStatInfo `json:"space_stat"`
ShardNodeSpaceStat SpaceStatInfo `json:"shard_node_space_stat"`
VolumeStat VolumeStatInfo `json:"volume_stat"`
LeaderHost string `json:"leader_host"`
ReadOnly bool `json:"read_only"`
RaftStatus raftserver.Status `json:"raft_status"`
SpaceStat SpaceStatInfo `json:"space_stat"`
VolumeStat VolumeStatInfo `json:"volume_stat"`
}
func GetConsulClusterPath(region string) string {
@ -53,7 +54,6 @@ func GetConsulClusterPath(region string) string {
type ClientAPI interface {
APIAccess
APIProxy
APIBlobnode
}
// APIAccess sub of cluster manager api for access
@ -61,19 +61,13 @@ type APIAccess interface {
GetConfig(ctx context.Context, key string) (string, error)
GetService(ctx context.Context, args GetServiceArgs) (ServiceInfo, error)
ListDisk(ctx context.Context, options *ListOptionArgs) (ListDiskRet, error)
AuthSpace(ctx context.Context, args *AuthSpaceArgs) (err error)
GetSpaceByName(ctx context.Context, args *GetSpaceByNameArgs) (ret *Space, err error)
GetCatalogChanges(ctx context.Context, args *GetCatalogChangesArgs) (ret *GetCatalogChangesRet, err error)
ShardNodeDiskInfo(ctx context.Context, id proto.DiskID) (ret *ShardNodeDiskInfo, err error)
ListShardNodeDisk(ctx context.Context, options *ListOptionArgs) (ret ListShardNodeDiskRet, err error)
}
// APIProxy sub of cluster manager api for allocator
type APIProxy interface {
GetConfig(ctx context.Context, key string) (string, error)
GetVolumeInfo(ctx context.Context, args *GetVolumeArgs) (*VolumeInfo, error)
GetVolumeRoutes(ctx context.Context, args *GetVolumeRoutesArgs) (*GetVolumeRoutesRet, error)
DiskInfo(ctx context.Context, id proto.DiskID) (*BlobNodeDiskInfo, error)
DiskInfo(ctx context.Context, id proto.DiskID) (*blobnode.DiskInfo, error)
AllocVolume(ctx context.Context, args *AllocVolumeArgs) (AllocatedVolumeInfos, error)
AllocBid(ctx context.Context, args *BidScopeArgs) (*BidScopeRet, error)
RetainVolume(ctx context.Context, args *RetainVolumeArgs) (RetainVolumes, error)
@ -83,23 +77,4 @@ type APIProxy interface {
// APIService sub of cluster manager api for service
type APIService interface {
GetService(ctx context.Context, args GetServiceArgs) (ServiceInfo, error)
RegisterService(ctx context.Context, node ServiceNode, tickInterval, heartbeatTicks, expiresTicks uint32) (err error)
}
type APIBlobnode interface {
APIService
GetConfig(ctx context.Context, key string) (value string, err error)
SetConfig(ctx context.Context, key, value string) error
AddNode(ctx context.Context, info *BlobNodeInfo) (proto.NodeID, error)
ListHostDisk(ctx context.Context, host string) (ret []*BlobNodeDiskInfo, err error)
ListDisk(ctx context.Context, options *ListOptionArgs) (ret ListDiskRet, err error)
AddDisk(ctx context.Context, info *BlobNodeDiskInfo) (err error)
DiskInfo(ctx context.Context, id proto.DiskID) (ret *BlobNodeDiskInfo, err error)
SetDisk(ctx context.Context, id proto.DiskID, status proto.DiskStatus) (err error)
AllocDiskID(ctx context.Context) (proto.DiskID, error)
SetCompactChunk(ctx context.Context, args *SetCompactChunkArgs) (err error)
ListVolumeUnit(ctx context.Context, args *ListVolumeUnitArgs) ([]*VolumeUnitInfo, error)
GetVolumeInfo(ctx context.Context, args *GetVolumeArgs) (ret *VolumeInfo, err error)
ReportChunk(ctx context.Context, args *ReportChunkArgs) (err error)
HeartbeatDisk(ctx context.Context, infos []*DiskHeartBeatInfo) (ret []*DiskHeartbeatRet, err error)
}

View File

@ -1,64 +0,0 @@
// Copyright 2024 The CubeFS Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package clustermgr
import (
"context"
"fmt"
)
func (c *Client) AllocShardUnit(ctx context.Context, args *AllocShardUnitArgs) (ret *AllocShardUnitRet, err error) {
ret = &AllocShardUnitRet{}
err = c.PostWith(ctx, "/shard/unit/alloc", ret, args)
return
}
func (c *Client) UpdateShard(ctx context.Context, args *UpdateShardArgs) (err error) {
err = c.PostWith(ctx, "/shard/update", nil, args)
return
}
func (c *Client) ReportShard(ctx context.Context, args *ShardReportArgs) (ret []ShardTask, err error) {
result := &ShardReportRet{}
err = c.PostWith(ctx, "/shard/report", result, args)
return result.ShardTasks, err
}
func (c *Client) GetShardInfo(ctx context.Context, args *GetShardArgs) (ret *Shard, err error) {
ret = &Shard{}
err = c.GetWith(ctx, "/shard/get?shard_id="+args.ShardID.ToString(), ret)
return
}
func (c *Client) ListShardUnit(ctx context.Context, args *ListShardUnitArgs) ([]ShardUnitInfo, error) {
ret := &ListShardUnitRet{}
err := c.GetWith(ctx, "/shard/unit/list?disk_id="+args.DiskID.ToString(), ret)
return ret.ShardUnitInfos, err
}
func (c *Client) ListShard(ctx context.Context, args *ListShardArgs) (ret ListShardRet, err error) {
err = c.GetWith(ctx, fmt.Sprintf("/shard/list?marker=%d&count=%d", args.Marker, args.Count), &ret)
return
}
func (c *Client) AdminUpdateShard(ctx context.Context, args *Shard) (err error) {
err = c.PostWith(ctx, "/admin/update/shard", nil, args)
return
}
func (c *Client) AdminUpdateShardUnit(ctx context.Context, args *AdminUpdateShardUnitArgs) (err error) {
err = c.PostWith(ctx, "/admin/update/shard/unit", nil, args)
return
}

File diff suppressed because it is too large Load Diff

View File

@ -1,108 +0,0 @@
syntax = "proto3";
package cubefs.blobstore.api.clustermgr;
option go_package = "./;clustermgr";
option (gogoproto.sizer_all) = true;
option (gogoproto.marshaler_all) = true;
option (gogoproto.unmarshaler_all) = true;
option (gogoproto.goproto_unkeyed_all) = true;
option (gogoproto.goproto_unrecognized_all) = true;
option (gogoproto.goproto_sizecache_all) = true;
option (gogoproto.goproto_stringer_all) = false;
option (gogoproto.stringer_all) = true;
option (gogoproto.gostring_all) = true;
import "gogoproto/gogo.proto";
import "cubefs/blobstore/common/sharding/range.proto";
message Shard {
uint32 shard_id = 1 [(gogoproto.customname) = "ShardID", (gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.ShardID"];
uint64 applied_index = 2;
uint32 leader_disk_id = 3 [(gogoproto.customname) = "LeaderDiskID", (gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.DiskID"];
cubefs.blobstore.common.sharding.Range range = 4 [(gogoproto.nullable) = false];
repeated ShardUnit units = 5 [(gogoproto.nullable) = false];
uint64 route_version = 6 [(gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.RouteVersion"];
}
message ShardUnit {
uint64 suid = 1 [(gogoproto.customname) = "Suid", (gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.Suid"];
uint32 disk_id = 2 [(gogoproto.customname) = "DiskID", (gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.DiskID"];
bool learner = 3;
string host = 4;
uint32 status = 5 [(gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.ShardUnitStatus"];
}
message ShardUnitInfo {
uint64 suid = 1 [(gogoproto.customname) = "Suid", (gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.Suid"];
uint32 disk_id = 2 [(gogoproto.customname) = "DiskID", (gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.DiskID"];
uint64 applied_index = 3;
uint32 leader_disk_id = 4 [(gogoproto.customname) = "LeaderDiskID", (gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.DiskID"];
cubefs.blobstore.common.sharding.Range range = 5 [(gogoproto.nullable) = false];
uint64 route_version = 6 [(gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.RouteVersion"];
string host = 7;
bool learner = 8;
}
message ShardTask {
uint32 task_type = 1 [(gogoproto.customname) = "TaskType", (gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.ShardTaskType"];
uint32 disk_id = 2 [(gogoproto.customname) = "DiskID", (gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.DiskID"];
uint32 suid = 3 [(gogoproto.customname) = "Suid", (gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.Suid"];
uint64 old_route_version = 4 [(gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.RouteVersion"];
uint64 route_version = 5 [(gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.RouteVersion"];
}
message ShardReportArgs {
repeated ShardUnitInfo shards = 1 [(gogoproto.nullable) = false];
}
message ShardReportRet {
repeated ShardTask shard_tasks = 1 [(gogoproto.nullable) = false];
}
message AllocShardUnitArgs{
uint64 suid = 1 [(gogoproto.customname) = "Suid", (gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.Suid"];
repeated uint32 exclude_disk_ids = 2 [(gogoproto.customname) = "ExcludeDiskIDs", (gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.DiskID"];
}
message AllocShardUnitRet {
uint64 suid = 1 [(gogoproto.customname) = "Suid", (gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.Suid"];
uint32 disk_id = 2 [(gogoproto.customname) = "DiskID", (gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.DiskID"];
string host = 3;
}
message UpdateShardArgs {
uint64 new_suid = 1 [(gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.Suid"];
uint32 new_disk_id = 2 [(gogoproto.customname) = "NewDiskID", (gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.DiskID"];
bool new_is_leaner = 3;
uint64 old_suid = 4 [(gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.Suid"];
bool old_is_leaner = 5;
}
message GetShardArgs {
uint32 shard_id = 1 [(gogoproto.customname) = "ShardID", (gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.ShardID"];
}
message ListShardUnitArgs {
uint32 disk_id = 1 [(gogoproto.customname) = "DiskID", (gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.DiskID"];
}
message ListShardUnitRet {
repeated ShardUnitInfo shard_unit_infos = 1 [(gogoproto.nullable) = false];
}
message ListShardArgs {
uint32 marker = 1 [(gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.ShardID"];
uint32 count = 2;
}
message ListShardRet {
repeated Shard shards = 1 [(gogoproto.nullable) = false];
uint32 marker = 2 [(gogoproto.casttype) = "github.com/cubefs/cubefs/blobstore/common/proto.ShardID"];
}
message AdminUpdateShardUnitArgs {
uint32 epoch = 1;
uint32 next_epoch = 2;
ShardUnit unit = 3 [(gogoproto.embed) = true, (gogoproto.nullable) = false];
}

Some files were not shown because too many files have changed in this diff Show More