Skip to content

Commit 10c01c2

Browse files
committed
feat(datastore): support backup and restore
1 parent cd671cb commit 10c01c2

13 files changed

Lines changed: 853 additions & 6 deletions

File tree

client/starwhale/base/client/models/models.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,19 @@ class OrderByDesc(SwBaseModel):
410410
descending: Optional[bool] = None
411411

412412

413+
class BackupVo(SwBaseModel):
414+
id: Optional[str] = None
415+
created_at: Optional[int] = Field(None, alias='createdAt')
416+
done_at: Optional[int] = Field(None, alias='doneAt')
417+
approximate_size_bytes: Optional[int] = Field(None, alias='approximateSizeBytes')
418+
419+
420+
class ResponseMessageBackupVo(SwBaseModel):
421+
code: str
422+
message: str
423+
data: BackupVo
424+
425+
413426
class ListTablesRequest(SwBaseModel):
414427
prefix: Optional[str] = None
415428
prefixes: Optional[List[str]] = Field(None)
@@ -1127,6 +1140,12 @@ class RuntimeSuggestionVo(SwBaseModel):
11271140
runtimes: Optional[List[RuntimeVersionVo]] = None
11281141

11291142

1143+
class ResponseMessageListBackupVo(SwBaseModel):
1144+
code: str
1145+
message: str
1146+
data: List[BackupVo]
1147+
1148+
11301149
class UserRoleDeleteRequest(UserCheckPasswordRequest):
11311150
pass
11321151

console/src/api/server/Api.ts

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import {
3333
ICompleteUploadBlobData,
3434
IConfigRequest,
3535
IConsumeNextDataData,
36+
ICreateBackupData,
3637
ICreateJobData,
3738
ICreateJobTemplateRequest,
3839
ICreateModelServingData,
@@ -49,6 +50,7 @@ import {
4950
IDatasetBuildRequest,
5051
IDatasetTagRequest,
5152
IDatasetUploadRequest,
53+
IDeleteBackupData,
5254
IDeleteDatasetData,
5355
IDeleteDatasetVersionTagData,
5456
IDeleteModelData,
@@ -113,6 +115,7 @@ import {
113115
IJobModifyPinRequest,
114116
IJobModifyRequest,
115117
IJobRequest,
118+
IListBackupsData,
116119
IListBuildRecordsData,
117120
IListDatasetData,
118121
IListDatasetTreeData,
@@ -179,6 +182,7 @@ import {
179182
IReleaseFtData,
180183
IRemoveJobData,
181184
IResourcePool,
185+
IRestoreBackupData,
182186
IRevertDatasetRequest,
183187
IRevertDatasetVersionData,
184188
IRevertModelVersionData,
@@ -2776,6 +2780,90 @@ export class Api<SecurityDataType = unknown> {
27762780
...params,
27772781
})
27782782

2783+
/**
2784+
* No description
2785+
*
2786+
* @tags data-store-controller
2787+
* @name ListBackups
2788+
* @request GET:/api/v1/datastore/maintain/backup
2789+
* @secure
2790+
* @response `200` `IListBackupsData` OK
2791+
*/
2792+
listBackups = (params: RequestParams = {}) =>
2793+
this.http.request<IListBackupsData, any>({
2794+
path: `/api/v1/datastore/maintain/backup`,
2795+
method: 'GET',
2796+
secure: true,
2797+
...params,
2798+
})
2799+
2800+
useListBackups = (params: RequestParams = {}) =>
2801+
useQuery(qs.stringify(['listBackups', params]), () => this.listBackups(params), {
2802+
enabled: [].every(Boolean),
2803+
})
2804+
/**
2805+
* No description
2806+
*
2807+
* @tags data-store-controller
2808+
* @name CreateBackup
2809+
* @request POST:/api/v1/datastore/maintain/backup
2810+
* @secure
2811+
* @response `200` `ICreateBackupData` OK
2812+
*/
2813+
createBackup = (params: RequestParams = {}) =>
2814+
this.http.request<ICreateBackupData, any>({
2815+
path: `/api/v1/datastore/maintain/backup`,
2816+
method: 'POST',
2817+
secure: true,
2818+
...params,
2819+
})
2820+
2821+
/**
2822+
* No description
2823+
*
2824+
* @tags data-store-controller
2825+
* @name DeleteBackup
2826+
* @request DELETE:/api/v1/datastore/maintain/backup
2827+
* @secure
2828+
* @response `200` `IDeleteBackupData` OK
2829+
*/
2830+
deleteBackup = (
2831+
query: {
2832+
id: string
2833+
},
2834+
params: RequestParams = {}
2835+
) =>
2836+
this.http.request<IDeleteBackupData, any>({
2837+
path: `/api/v1/datastore/maintain/backup`,
2838+
method: 'DELETE',
2839+
query: query,
2840+
secure: true,
2841+
...params,
2842+
})
2843+
2844+
/**
2845+
* No description
2846+
*
2847+
* @tags data-store-controller
2848+
* @name RestoreBackup
2849+
* @request POST:/api/v1/datastore/maintain/backup/restore
2850+
* @secure
2851+
* @response `200` `IRestoreBackupData` OK
2852+
*/
2853+
restoreBackup = (
2854+
query: {
2855+
id: string
2856+
},
2857+
params: RequestParams = {}
2858+
) =>
2859+
this.http.request<IRestoreBackupData, any>({
2860+
path: `/api/v1/datastore/maintain/backup/restore`,
2861+
method: 'POST',
2862+
query: query,
2863+
secure: true,
2864+
...params,
2865+
})
2866+
27792867
/**
27802868
* No description
27812869
*

console/src/api/server/data-contracts.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,22 @@ export interface ITableQueryOperandDesc {
548548
bytesValue?: string
549549
}
550550

551+
export interface IBackupVo {
552+
id?: string
553+
/** @format int64 */
554+
createdAt?: number
555+
/** @format int64 */
556+
doneAt?: number
557+
/** @format int64 */
558+
approximateSizeBytes?: number
559+
}
560+
561+
export interface IResponseMessageBackupVo {
562+
code: string
563+
message: string
564+
data: IBackupVo
565+
}
566+
551567
export interface IListTablesRequest {
552568
prefix?: string
553569
/** @uniqueItems true */
@@ -2145,6 +2161,12 @@ export interface IRuntimeSuggestionVo {
21452161
runtimes?: IRuntimeVersionVo[]
21462162
}
21472163

2164+
export interface IResponseMessageListBackupVo {
2165+
code: string
2166+
message: string
2167+
data: IBackupVo[]
2168+
}
2169+
21482170
export interface IUserRoleDeleteRequest {
21492171
currentUserPwd: string
21502172
}
@@ -2359,6 +2381,14 @@ export type IQueryTableData = IResponseMessageRecordListVo['data']
23592381

23602382
export type IQueryAndExportData = any
23612383

2384+
export type IListBackupsData = IResponseMessageListBackupVo['data']
2385+
2386+
export type ICreateBackupData = IResponseMessageBackupVo['data']
2387+
2388+
export type IDeleteBackupData = IResponseMessageString['data']
2389+
2390+
export type IRestoreBackupData = IResponseMessageString['data']
2391+
23622392
export type IListTablesData = IResponseMessageTableNameListVo['data']
23632393

23642394
export type IFlushData = IResponseMessageString['data']

server/controller/src/main/java/ai/starwhale/mlops/api/DataStoreController.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import ai.starwhale.mlops.api.protocol.Code;
2020
import ai.starwhale.mlops.api.protocol.ResponseMessage;
21+
import ai.starwhale.mlops.api.protocol.datastore.BackupVo;
2122
import ai.starwhale.mlops.api.protocol.datastore.ColumnDesc;
2223
import ai.starwhale.mlops.api.protocol.datastore.FlushRequest;
2324
import ai.starwhale.mlops.api.protocol.datastore.ListTablesRequest;
@@ -35,6 +36,7 @@
3536
import ai.starwhale.mlops.datastore.DataStoreScanRequest;
3637
import ai.starwhale.mlops.datastore.RecordList;
3738
import ai.starwhale.mlops.datastore.TableQueryFilter;
39+
import ai.starwhale.mlops.datastore.backup.Meta;
3840
import ai.starwhale.mlops.datastore.exporter.RecordsStreamingExporter;
3941
import ai.starwhale.mlops.datastore.impl.RecordDecoder;
4042
import ai.starwhale.mlops.exception.SwProcessException;
@@ -60,9 +62,12 @@
6062
import org.springframework.util.CollectionUtils;
6163
import org.springframework.util.StringUtils;
6264
import org.springframework.validation.annotation.Validated;
65+
import org.springframework.web.bind.annotation.DeleteMapping;
66+
import org.springframework.web.bind.annotation.GetMapping;
6367
import org.springframework.web.bind.annotation.PostMapping;
6468
import org.springframework.web.bind.annotation.RequestBody;
6569
import org.springframework.web.bind.annotation.RequestMapping;
70+
import org.springframework.web.bind.annotation.RequestParam;
6671
import org.springframework.web.bind.annotation.RestController;
6772

6873
@RestController
@@ -293,6 +298,49 @@ void scanAndExport(
293298
}
294299
}
295300

301+
@PostMapping(value = "/datastore/maintain/backup")
302+
@PreAuthorize("hasAnyRole('OWNER')")
303+
ResponseEntity<ResponseMessage<BackupVo>> createBackup() {
304+
try {
305+
var meta = this.dataStore.createBackup();
306+
return ResponseEntity.ok(Code.success.asResponse(meta.toVo()));
307+
} catch (IOException e) {
308+
log.error("create backup failed", e);
309+
throw new SwProcessException(ErrorType.SYSTEM, "create backup failed", e);
310+
}
311+
}
312+
313+
@GetMapping(value = "/datastore/maintain/backup")
314+
@PreAuthorize("hasAnyRole('OWNER')")
315+
ResponseEntity<ResponseMessage<List<BackupVo>>> listBackups() {
316+
var backups = this.dataStore.listBackups().stream().map(Meta::toVo).collect(Collectors.toList());
317+
return ResponseEntity.ok(Code.success.asResponse(backups));
318+
}
319+
320+
@PostMapping(value = "/datastore/maintain/backup/restore")
321+
@PreAuthorize("hasAnyRole('OWNER')")
322+
ResponseEntity<ResponseMessage<String>> restoreBackup(@Valid @RequestParam String id) {
323+
try {
324+
this.dataStore.restoreWithBackup(id);
325+
} catch (IOException e) {
326+
log.error("restore backup failed", e);
327+
throw new SwProcessException(ErrorType.SYSTEM, "restore backup failed", e);
328+
}
329+
return ResponseEntity.ok(Code.success.asResponse("success"));
330+
}
331+
332+
@DeleteMapping(value = "/datastore/maintain/backup")
333+
@PreAuthorize("hasAnyRole('OWNER')")
334+
ResponseEntity<ResponseMessage<String>> deleteBackup(@Valid @RequestParam String id) {
335+
try {
336+
this.dataStore.deleteBackup(id);
337+
} catch (IOException e) {
338+
log.error("delete backup failed", e);
339+
throw new SwProcessException(ErrorType.SYSTEM, "delete backup failed", e);
340+
}
341+
return ResponseEntity.ok(Code.success.asResponse("success"));
342+
}
343+
296344
private RecordList queryRecordList(QueryTableRequest request) {
297345
if (request.getTableName() == null) {
298346
throw new SwValidationException(
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright 2022 Starwhale, Inc. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package ai.starwhale.mlops.api.protocol.datastore;
18+
19+
import lombok.AllArgsConstructor;
20+
import lombok.Builder;
21+
import lombok.Data;
22+
import lombok.NoArgsConstructor;
23+
24+
@Data
25+
@Builder
26+
@NoArgsConstructor
27+
@AllArgsConstructor
28+
public class BackupVo {
29+
String id;
30+
31+
/**
32+
* The time when the backup is created
33+
*/
34+
private Long createdAt;
35+
36+
/**
37+
* The time when the backup is done
38+
*/
39+
private Long doneAt;
40+
41+
/**
42+
* The approximate size of the backup in bytes
43+
*/
44+
private Long approximateSizeBytes;
45+
}

0 commit comments

Comments
 (0)