Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
taosdata
TDengine
提交
87c1656f
T
TDengine
项目概览
taosdata
/
TDengine
1 年多 前同步成功
通知
1185
Star
22016
Fork
4786
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
1
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
T
TDengine
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
1
Issue
1
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
未验证
提交
87c1656f
编写于
8月 08, 2022
作者:
L
Liu Jicong
提交者:
GitHub
8月 08, 2022
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #15859 from taosdata/feature/stream
fix(stream): memory leak
上级
cb2a9eea
48449e80
变更
11
隐藏空白更改
内联
并排
Showing
11 changed file
with
155 addition
and
41 deletion
+155
-41
examples/rust
examples/rust
+1
-0
include/libs/stream/tstream.h
include/libs/stream/tstream.h
+32
-3
source/dnode/mgmt/node_mgmt/src/dmTransport.c
source/dnode/mgmt/node_mgmt/src/dmTransport.c
+2
-1
source/dnode/vnode/src/tq/tq.c
source/dnode/vnode/src/tq/tq.c
+2
-0
source/dnode/vnode/src/vnd/vnodeSync.c
source/dnode/vnode/src/vnd/vnodeSync.c
+9
-1
source/libs/stream/src/stream.c
source/libs/stream/src/stream.c
+1
-0
source/libs/stream/src/streamDispatch.c
source/libs/stream/src/streamDispatch.c
+6
-1
source/libs/stream/src/streamExec.c
source/libs/stream/src/streamExec.c
+6
-2
source/libs/stream/src/streamRecover.c
source/libs/stream/src/streamRecover.c
+93
-31
source/libs/stream/src/streamTask.c
source/libs/stream/src/streamTask.c
+2
-2
tools/taos-tools
tools/taos-tools
+1
-0
未找到文件。
rust
@
7ed7a977
Subproject commit 7ed7a97715388fa144718764d6bf20f9bfc29a12
include/libs/stream/tstream.h
浏览文件 @
87c1656f
...
...
@@ -226,11 +226,36 @@ typedef struct {
int32_t
nodeId
;
int32_t
childId
;
int32_t
taskId
;
int64_t
checkpointVer
;
int64_t
processedVer
;
SEpSet
epSet
;
//
int64_t checkpointVer;
//
int64_t processedVer;
SEpSet
epSet
;
}
SStreamChildEpInfo
;
typedef
struct
{
int32_t
nodeId
;
int32_t
childId
;
int64_t
stateSaveVer
;
int64_t
stateProcessedVer
;
}
SStreamCheckpointInfo
;
typedef
struct
{
int64_t
streamId
;
int64_t
checkTs
;
int32_t
checkpointId
;
// incremental
int32_t
taskId
;
SArray
*
checkpointVer
;
// SArray<SStreamCheckpointInfo>
}
SStreamMultiVgCheckpointInfo
;
typedef
struct
{
int32_t
taskId
;
int32_t
checkpointId
;
// incremental
}
SStreamCheckpointKey
;
typedef
struct
{
int32_t
taskId
;
SArray
*
checkpointVer
;
}
SStreamRecoveringState
;
typedef
struct
SStreamTask
{
int64_t
streamId
;
int32_t
taskId
;
...
...
@@ -256,6 +281,8 @@ typedef struct SStreamTask {
// children info
SArray
*
childEpInfo
;
// SArray<SStreamChildEpInfo*>
int32_t
nextCheckId
;
SArray
*
checkpointInfo
;
// SArray<SStreamCheckpointInfo>
// exec
STaskExec
exec
;
...
...
@@ -445,6 +472,7 @@ typedef struct {
int32_t
tDecodeStreamDispatchReq
(
SDecoder
*
pDecoder
,
SStreamDispatchReq
*
pReq
);
int32_t
tDecodeStreamRetrieveReq
(
SDecoder
*
pDecoder
,
SStreamRetrieveReq
*
pReq
);
void
tFreeStreamDispatchReq
(
SStreamDispatchReq
*
pReq
);
int32_t
streamSetupTrigger
(
SStreamTask
*
pTask
);
...
...
@@ -468,6 +496,7 @@ typedef struct SStreamMeta {
TTB
*
pTaskDb
;
TTB
*
pStateDb
;
SHashObj
*
pTasks
;
SHashObj
*
pRecoveringState
;
void
*
ahandle
;
TXN
txn
;
FTaskExpand
*
expandFunc
;
...
...
source/dnode/mgmt/node_mgmt/src/dmTransport.c
浏览文件 @
87c1656f
...
...
@@ -255,7 +255,8 @@ static inline void dmReleaseHandle(SRpcHandleInfo *pHandle, int8_t type) {
static
bool
rpcRfp
(
int32_t
code
,
tmsg_t
msgType
)
{
if
(
code
==
TSDB_CODE_RPC_REDIRECT
||
code
==
TSDB_CODE_RPC_NETWORK_UNAVAIL
||
code
==
TSDB_CODE_NODE_NOT_DEPLOYED
||
code
==
TSDB_CODE_SYN_NOT_LEADER
||
code
==
TSDB_CODE_APP_NOT_READY
||
code
==
TSDB_CODE_RPC_BROKEN_LINK
)
{
if
(
msgType
==
TDMT_SCH_QUERY
||
msgType
==
TDMT_SCH_MERGE_QUERY
||
msgType
==
TDMT_SCH_FETCH
||
msgType
==
TDMT_SCH_MERGE_FETCH
)
{
if
(
msgType
==
TDMT_SCH_QUERY
||
msgType
==
TDMT_SCH_MERGE_QUERY
||
msgType
==
TDMT_SCH_FETCH
||
msgType
==
TDMT_SCH_MERGE_FETCH
)
{
return
false
;
}
return
true
;
...
...
source/dnode/vnode/src/tq/tq.c
浏览文件 @
87c1656f
...
...
@@ -859,8 +859,10 @@ void vnodeEnqueueStreamMsg(SVnode* pVnode, SRpcMsg* pMsg) {
tDecoderInit
(
&
decoder
,
msgBody
,
msgLen
);
if
(
tDecodeStreamDispatchReq
(
&
decoder
,
&
req
)
<
0
)
{
code
=
TSDB_CODE_MSG_DECODE_ERROR
;
tDecoderClear
(
&
decoder
);
goto
FAIL
;
}
tDecoderClear
(
&
decoder
);
int32_t
taskId
=
req
.
taskId
;
...
...
source/dnode/vnode/src/vnd/vnodeSync.c
浏览文件 @
87c1656f
...
...
@@ -141,6 +141,10 @@ static void inline vnodeHandleWriteMsg(SVnode *pVnode, SRpcMsg *pMsg) {
}
if
(
rsp
.
info
.
handle
!=
NULL
)
{
tmsgSendRsp
(
&
rsp
);
}
else
{
if
(
rsp
.
pCont
)
{
rpcFreeCont
(
rsp
.
pCont
);
}
}
}
...
...
@@ -299,6 +303,10 @@ void vnodeApplyWriteMsg(SQueueInfo *pInfo, STaosQall *qall, int32_t numOfMsgs) {
vnodePostBlockMsg
(
pVnode
,
pMsg
);
if
(
rsp
.
info
.
handle
!=
NULL
)
{
tmsgSendRsp
(
&
rsp
);
}
else
{
if
(
rsp
.
pCont
)
{
rpcFreeCont
(
rsp
.
pCont
);
}
}
vGTrace
(
"vgId:%d, msg:%p is freed, code:0x%x index:%"
PRId64
,
vgId
,
pMsg
,
rsp
.
code
,
pMsg
->
info
.
conn
.
applyIndex
);
...
...
@@ -733,4 +741,4 @@ bool vnodeIsLeader(SVnode *pVnode) {
}
return
true
;
}
\ No newline at end of file
}
source/libs/stream/src/stream.c
浏览文件 @
87c1656f
...
...
@@ -136,6 +136,7 @@ int32_t streamTaskEnqueue(SStreamTask* pTask, SStreamDispatchReq* pReq, SRpcMsg*
pRsp
->
pCont
=
buf
;
pRsp
->
contLen
=
sizeof
(
SMsgHead
)
+
sizeof
(
SStreamDispatchRsp
);
tmsgSendRsp
(
pRsp
);
tFreeStreamDispatchReq
(
pReq
);
return
status
==
TASK_INPUT_STATUS__NORMAL
?
0
:
-
1
;
}
...
...
source/libs/stream/src/streamDispatch.c
浏览文件 @
87c1656f
...
...
@@ -62,6 +62,11 @@ int32_t tDecodeStreamDispatchReq(SDecoder* pDecoder, SStreamDispatchReq* pReq) {
return
0
;
}
void
tFreeStreamDispatchReq
(
SStreamDispatchReq
*
pReq
)
{
taosArrayDestroyP
(
pReq
->
data
,
taosMemoryFree
);
taosArrayDestroy
(
pReq
->
dataLen
);
}
int32_t
tEncodeStreamRetrieveReq
(
SEncoder
*
pEncoder
,
const
SStreamRetrieveReq
*
pReq
)
{
if
(
tStartEncode
(
pEncoder
)
<
0
)
return
-
1
;
if
(
tEncodeI64
(
pEncoder
,
pReq
->
streamId
)
<
0
)
return
-
1
;
...
...
@@ -279,7 +284,7 @@ int32_t streamDispatchAllBlocks(SStreamTask* pTask, const SStreamDataBlock* pDat
}
code
=
0
;
FAIL_FIXED_DISPATCH:
taosArrayDestroy
(
req
.
data
);
taosArrayDestroy
P
(
req
.
data
,
taosMemoryFree
);
taosArrayDestroy
(
req
.
dataLen
);
return
code
;
...
...
source/libs/stream/src/streamExec.c
浏览文件 @
87c1656f
...
...
@@ -15,7 +15,7 @@
#include "streamInc.h"
static
int32_t
streamTaskExecImpl
(
SStreamTask
*
pTask
,
void
*
data
,
SArray
*
pRes
)
{
static
int32_t
streamTaskExecImpl
(
SStreamTask
*
pTask
,
const
void
*
data
,
SArray
*
pRes
)
{
void
*
exec
=
pTask
->
exec
.
executor
;
// set input
...
...
@@ -82,14 +82,16 @@ static int32_t streamTaskExecImpl(SStreamTask* pTask, void* data, SArray* pRes)
return
0
;
}
#if 0
static FORCE_INLINE int32_t streamUpdateVer(SStreamTask* pTask, SStreamDataBlock* pBlock) {
ASSERT(pBlock->type == STREAM_INPUT__DATA_BLOCK);
int32_t childId = pBlock->childId;
int64_t ver = pBlock->sourceVer;
SStreamChildEpInfo* pChildInfo = taosArrayGetP(pTask->childEpInfo, childId);
pChildInfo
->
processedVer
=
ver
;
/*pChildInfo-> = ver;*/
return 0;
}
#endif
int32_t
streamPipelineExec
(
SStreamTask
*
pTask
,
int32_t
batchNum
)
{
ASSERT
(
pTask
->
taskLevel
!=
TASK_LEVEL__SINK
);
...
...
@@ -198,6 +200,8 @@ int32_t streamExecForAll(SStreamTask* pTask) {
streamTaskExecImpl
(
pTask
,
data
,
pRes
);
qDebug
(
"stream task %d exec end"
,
pTask
->
taskId
);
streamFreeQitem
(
data
);
if
(
taosArrayGetSize
(
pRes
)
!=
0
)
{
SStreamDataBlock
*
qRes
=
taosAllocateQitem
(
sizeof
(
SStreamDataBlock
),
DEF_QITEM
);
if
(
qRes
==
NULL
)
{
...
...
source/libs/stream/src/streamRecover.c
浏览文件 @
87c1656f
...
...
@@ -87,63 +87,95 @@ int32_t tDecodeSMStreamTaskRecoverRsp(SDecoder* pDecoder, SMStreamTaskRecoverRsp
return
0
;
}
typedef
struct
{
int32_t
vgId
;
int32_t
childId
;
int64_t
ver
;
}
SStreamVgVerCheckpoint
;
int32_t
tEncodeSStreamVgVerCheckpoint
(
SEncoder
*
pEncoder
,
const
SStreamVgVerCheckpoint
*
pCheckpoint
)
{
if
(
tEncodeI32
(
pEncoder
,
pCheckpoint
->
vgId
)
<
0
)
return
-
1
;
int32_t
tEncodeSStreamCheckpointInfo
(
SEncoder
*
pEncoder
,
const
SStreamCheckpointInfo
*
pCheckpoint
)
{
if
(
tEncodeI32
(
pEncoder
,
pCheckpoint
->
nodeId
)
<
0
)
return
-
1
;
if
(
tEncodeI32
(
pEncoder
,
pCheckpoint
->
childId
)
<
0
)
return
-
1
;
if
(
tEncodeI64
(
pEncoder
,
pCheckpoint
->
v
er
)
<
0
)
return
-
1
;
if
(
tEncodeI64
(
pEncoder
,
pCheckpoint
->
stateProcessedV
er
)
<
0
)
return
-
1
;
return
0
;
}
int32_t
tDecodeSStream
VgVerCheckpoint
(
SDecoder
*
pDecoder
,
SStreamVgVerCheckpoint
*
pCheckpoint
)
{
if
(
tDecodeI32
(
pDecoder
,
&
pCheckpoint
->
vg
Id
)
<
0
)
return
-
1
;
int32_t
tDecodeSStream
CheckpointInfo
(
SDecoder
*
pDecoder
,
SStreamCheckpointInfo
*
pCheckpoint
)
{
if
(
tDecodeI32
(
pDecoder
,
&
pCheckpoint
->
node
Id
)
<
0
)
return
-
1
;
if
(
tDecodeI32
(
pDecoder
,
&
pCheckpoint
->
childId
)
<
0
)
return
-
1
;
if
(
tDecodeI64
(
pDecoder
,
&
pCheckpoint
->
v
er
)
<
0
)
return
-
1
;
if
(
tDecodeI64
(
pDecoder
,
&
pCheckpoint
->
stateProcessedV
er
)
<
0
)
return
-
1
;
return
0
;
}
typedef
struct
{
int64_t
streamId
;
int64_t
checkTs
;
int64_t
checkpointId
;
int32_t
taskId
;
SArray
*
checkpointVer
;
// SArray<SStreamVgCheckpointVer>
}
SStreamAggVerCheckpoint
;
int32_t
tEncodeSStreamAggVerCheckpoint
(
SEncoder
*
pEncoder
,
const
SStreamAggVerCheckpoint
*
pCheckpoint
)
{
int32_t
tEncodeSStreamMultiVgCheckpointInfo
(
SEncoder
*
pEncoder
,
const
SStreamMultiVgCheckpointInfo
*
pCheckpoint
)
{
if
(
tEncodeI64
(
pEncoder
,
pCheckpoint
->
streamId
)
<
0
)
return
-
1
;
if
(
tEncodeI64
(
pEncoder
,
pCheckpoint
->
checkTs
)
<
0
)
return
-
1
;
if
(
tEncodeI
64
(
pEncoder
,
pCheckpoint
->
checkpointId
)
<
0
)
return
-
1
;
if
(
tEncodeI
32
(
pEncoder
,
pCheckpoint
->
checkpointId
)
<
0
)
return
-
1
;
if
(
tEncodeI32
(
pEncoder
,
pCheckpoint
->
taskId
)
<
0
)
return
-
1
;
int32_t
sz
=
taosArrayGetSize
(
pCheckpoint
->
checkpointVer
);
if
(
tEncodeI32
(
pEncoder
,
sz
)
<
0
)
return
-
1
;
for
(
int32_t
i
=
0
;
i
<
sz
;
i
++
)
{
SStream
VgVerCheckpoint
*
pOneVgCkpoint
=
taosArrayGet
(
pCheckpoint
->
checkpointVer
,
i
);
if
(
tEncodeSStream
VgVerCheckpoint
(
pEncoder
,
pOneVgCkpoint
)
<
0
)
return
-
1
;
SStream
CheckpointInfo
*
pOneVgCkpoint
=
taosArrayGet
(
pCheckpoint
->
checkpointVer
,
i
);
if
(
tEncodeSStream
CheckpointInfo
(
pEncoder
,
pOneVgCkpoint
)
<
0
)
return
-
1
;
}
return
0
;
}
int32_t
tDecodeSStream
AggVerCheckpoint
(
SDecoder
*
pDecoder
,
SStreamAggVerCheckpoint
*
pCheckpoint
)
{
int32_t
tDecodeSStream
MultiVgCheckpointInfo
(
SDecoder
*
pDecoder
,
SStreamMultiVgCheckpointInfo
*
pCheckpoint
)
{
if
(
tDecodeI64
(
pDecoder
,
&
pCheckpoint
->
streamId
)
<
0
)
return
-
1
;
if
(
tDecodeI64
(
pDecoder
,
&
pCheckpoint
->
checkTs
)
<
0
)
return
-
1
;
if
(
tDecodeI
64
(
pDecoder
,
&
pCheckpoint
->
checkpointId
)
<
0
)
return
-
1
;
if
(
tDecodeI
32
(
pDecoder
,
&
pCheckpoint
->
checkpointId
)
<
0
)
return
-
1
;
if
(
tDecodeI32
(
pDecoder
,
&
pCheckpoint
->
taskId
)
<
0
)
return
-
1
;
int32_t
sz
;
if
(
tDecodeI32
(
pDecoder
,
&
sz
)
<
0
)
return
-
1
;
for
(
int32_t
i
=
0
;
i
<
sz
;
i
++
)
{
SStream
VgVerCheckpoint
oneVgCheckpoint
;
if
(
tDecodeSStream
VgVerCheckpoint
(
pDecoder
,
&
oneVgCheckpoint
)
<
0
)
return
-
1
;
SStream
CheckpointInfo
oneVgCheckpoint
;
if
(
tDecodeSStream
CheckpointInfo
(
pDecoder
,
&
oneVgCheckpoint
)
<
0
)
return
-
1
;
taosArrayPush
(
pCheckpoint
->
checkpointVer
,
&
oneVgCheckpoint
);
}
return
0
;
}
int32_t
streamCheckSinkLevel
(
SStreamMeta
*
pMeta
,
SStreamTask
*
pTask
)
{
void
*
buf
=
NULL
;
ASSERT
(
pTask
->
taskLevel
==
TASK_LEVEL__SINK
);
int32_t
sz
=
taosArrayGetSize
(
pTask
->
checkpointInfo
);
SStreamMultiVgCheckpointInfo
checkpoint
;
checkpoint
.
checkpointId
=
0
;
checkpoint
.
checkTs
=
taosGetTimestampMs
();
checkpoint
.
streamId
=
pTask
->
streamId
;
checkpoint
.
taskId
=
pTask
->
taskId
;
checkpoint
.
checkpointVer
=
pTask
->
checkpointInfo
;
int32_t
len
;
int32_t
code
;
tEncodeSize
(
tEncodeSStreamMultiVgCheckpointInfo
,
&
checkpoint
,
len
,
code
);
if
(
code
<
0
)
{
return
-
1
;
}
buf
=
taosMemoryCalloc
(
1
,
len
);
if
(
buf
==
NULL
)
{
return
-
1
;
}
SEncoder
encoder
;
tEncoderInit
(
&
encoder
,
buf
,
len
);
tEncodeSStreamMultiVgCheckpointInfo
(
&
encoder
,
&
checkpoint
);
tEncoderClear
(
&
encoder
);
SStreamCheckpointKey
key
=
{
.
taskId
=
pTask
->
taskId
,
.
checkpointId
=
checkpoint
.
checkpointId
,
};
if
(
tdbTbUpsert
(
pMeta
->
pStateDb
,
&
key
,
sizeof
(
SStreamCheckpointKey
),
buf
,
len
,
&
pMeta
->
txn
)
<
0
)
{
ASSERT
(
0
);
goto
FAIL
;
}
taosMemoryFree
(
buf
);
return
0
;
FAIL:
if
(
buf
)
taosMemoryFree
(
buf
);
return
-
1
;
}
int32_t
streamRecoverSinkLevel
(
SStreamMeta
*
pMeta
,
SStreamTask
*
pTask
)
{
ASSERT
(
pTask
->
taskLevel
==
TASK_LEVEL__SINK
);
// load status
...
...
@@ -154,9 +186,39 @@ int32_t streamRecoverSinkLevel(SStreamMeta* pMeta, SStreamTask* pTask) {
}
SDecoder
decoder
;
tDecoderInit
(
&
decoder
,
pVal
,
vLen
);
SStreamAggVerCheckpoint
aggCheckpoint
;
tDecodeSStreamAggVerCheckpoint
(
&
decoder
,
&
aggCheckpoint
);
/*pTask->*/
SStreamMultiVgCheckpointInfo
aggCheckpoint
;
tDecodeSStreamMultiVgCheckpointInfo
(
&
decoder
,
&
aggCheckpoint
);
tDecoderClear
(
&
decoder
);
pTask
->
nextCheckId
=
aggCheckpoint
.
checkpointId
+
1
;
pTask
->
checkpointInfo
=
aggCheckpoint
.
checkpointVer
;
return
0
;
}
int32_t
streamCheckAggLevel
(
SStreamMeta
*
pMeta
,
SStreamTask
*
pTask
)
{
ASSERT
(
pTask
->
taskLevel
==
TASK_LEVEL__AGG
);
// save and copy state
// save state info
return
0
;
}
int32_t
streamRecoverAggLevel
(
SStreamMeta
*
pMeta
,
SStreamTask
*
pTask
)
{
ASSERT
(
pTask
->
taskLevel
==
TASK_LEVEL__AGG
);
// try recover sink level
// after all sink level recovered, choose current state backend to recover
return
0
;
}
int32_t
streamCheckSourceLevel
(
SStreamMeta
*
pMeta
,
SStreamTask
*
pTask
)
{
ASSERT
(
pTask
->
taskLevel
==
TASK_LEVEL__SOURCE
);
// try recover agg level
//
return
0
;
}
int32_t
streamRecoverSourceLevel
(
SStreamMeta
*
pMeta
,
SStreamTask
*
pTask
)
{
ASSERT
(
pTask
->
taskLevel
==
TASK_LEVEL__SOURCE
);
return
0
;
}
...
...
source/libs/stream/src/streamTask.c
浏览文件 @
87c1656f
...
...
@@ -34,7 +34,7 @@ int32_t tEncodeStreamEpInfo(SEncoder* pEncoder, const SStreamChildEpInfo* pInfo)
if
(
tEncodeI32
(
pEncoder
,
pInfo
->
taskId
)
<
0
)
return
-
1
;
if
(
tEncodeI32
(
pEncoder
,
pInfo
->
nodeId
)
<
0
)
return
-
1
;
if
(
tEncodeI32
(
pEncoder
,
pInfo
->
childId
)
<
0
)
return
-
1
;
if
(
tEncodeI64
(
pEncoder
,
pInfo
->
processedVer
)
<
0
)
return
-
1
;
/*if (tEncodeI64(pEncoder, pInfo->processedVer) < 0) return -1;*/
if
(
tEncodeSEpSet
(
pEncoder
,
&
pInfo
->
epSet
)
<
0
)
return
-
1
;
return
0
;
}
...
...
@@ -43,7 +43,7 @@ int32_t tDecodeStreamEpInfo(SDecoder* pDecoder, SStreamChildEpInfo* pInfo) {
if
(
tDecodeI32
(
pDecoder
,
&
pInfo
->
taskId
)
<
0
)
return
-
1
;
if
(
tDecodeI32
(
pDecoder
,
&
pInfo
->
nodeId
)
<
0
)
return
-
1
;
if
(
tDecodeI32
(
pDecoder
,
&
pInfo
->
childId
)
<
0
)
return
-
1
;
if
(
tDecodeI64
(
pDecoder
,
&
pInfo
->
processedVer
)
<
0
)
return
-
1
;
/*if (tDecodeI64(pDecoder, &pInfo->processedVer) < 0) return -1;*/
if
(
tDecodeSEpSet
(
pDecoder
,
&
pInfo
->
epSet
)
<
0
)
return
-
1
;
return
0
;
}
...
...
taos-tools
@
3c7dafee
Subproject commit 3c7dafeea3e558968165b73bee0f51024898e3da
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录