From d1511c5eb0db110a491c8b840a160eb75685e7fe Mon Sep 17 00:00:00 2001
From: Alex <33497058+bexsoft@users.noreply.github.com>
Date: Fri, 23 Sep 2022 12:35:55 -0500
Subject: [PATCH] Limit concurrent downloads & uploads (#2313)
---
models/environment_constants.go | 70 +++++++++++
models/session_response.go | 46 +++++++
.../Objects/ListObjects/ListObjects.tsx | 18 ++-
.../Objects/ListObjects/ObjectDetailPanel.tsx | 7 +-
.../ObjectDetails/VersionsNavigator.tsx | 7 +-
.../Buckets/ListBuckets/Objects/utils.ts | 5 +-
.../Common/ObjectManager/ObjectHandled.tsx | 1 -
.../Common/ObjectManager/TrafficMonitor.tsx | 112 ++++++++++++++++++
portal-ui/src/screens/Console/ConsoleKBar.tsx | 2 +
.../ObjectBrowser/RenameLongFilename.tsx | 7 +-
.../ObjectBrowser/objectBrowserSlice.ts | 53 +++++++++
.../Console/ObjectBrowser/transferManager.ts | 14 +++
.../screens/Console/ObjectBrowser/types.ts | 7 +-
portal-ui/src/screens/Console/consoleSlice.ts | 1 +
portal-ui/src/screens/Console/types.ts | 6 +
restapi/config.go | 18 +++
restapi/consts.go | 2 +
restapi/embedded_spec.go | 34 ++++++
restapi/user_session.go | 8 ++
swagger-console.yml | 14 +++
20 files changed, 412 insertions(+), 20 deletions(-)
create mode 100644 models/environment_constants.go
create mode 100644 portal-ui/src/screens/Console/Common/ObjectManager/TrafficMonitor.tsx
diff --git a/models/environment_constants.go b/models/environment_constants.go
new file mode 100644
index 000000000..f07e6d9c5
--- /dev/null
+++ b/models/environment_constants.go
@@ -0,0 +1,70 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// This file is part of MinIO Console Server
+// Copyright (c) 2022 MinIO, Inc.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+//
+
+package models
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "context"
+
+ "github.com/go-openapi/strfmt"
+ "github.com/go-openapi/swag"
+)
+
+// EnvironmentConstants environment constants
+//
+// swagger:model environmentConstants
+type EnvironmentConstants struct {
+
+ // max concurrent downloads
+ MaxConcurrentDownloads int64 `json:"maxConcurrentDownloads,omitempty"`
+
+ // max concurrent uploads
+ MaxConcurrentUploads int64 `json:"maxConcurrentUploads,omitempty"`
+}
+
+// Validate validates this environment constants
+func (m *EnvironmentConstants) Validate(formats strfmt.Registry) error {
+ return nil
+}
+
+// ContextValidate validates this environment constants based on context it is used
+func (m *EnvironmentConstants) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
+ return nil
+}
+
+// MarshalBinary interface implementation
+func (m *EnvironmentConstants) MarshalBinary() ([]byte, error) {
+ if m == nil {
+ return nil, nil
+ }
+ return swag.WriteJSON(m)
+}
+
+// UnmarshalBinary interface implementation
+func (m *EnvironmentConstants) UnmarshalBinary(b []byte) error {
+ var res EnvironmentConstants
+ if err := swag.ReadJSON(b, &res); err != nil {
+ return err
+ }
+ *m = res
+ return nil
+}
diff --git a/models/session_response.go b/models/session_response.go
index 8ca83198d..d68eb9911 100644
--- a/models/session_response.go
+++ b/models/session_response.go
@@ -47,6 +47,9 @@ type SessionResponse struct {
// distributed mode
DistributedMode bool `json:"distributedMode,omitempty"`
+ // env constants
+ EnvConstants *EnvironmentConstants `json:"envConstants,omitempty"`
+
// features
Features []string `json:"features"`
@@ -69,6 +72,10 @@ func (m *SessionResponse) Validate(formats strfmt.Registry) error {
res = append(res, err)
}
+ if err := m.validateEnvConstants(formats); err != nil {
+ res = append(res, err)
+ }
+
if err := m.validateStatus(formats); err != nil {
res = append(res, err)
}
@@ -105,6 +112,25 @@ func (m *SessionResponse) validateAllowResources(formats strfmt.Registry) error
return nil
}
+func (m *SessionResponse) validateEnvConstants(formats strfmt.Registry) error {
+ if swag.IsZero(m.EnvConstants) { // not required
+ return nil
+ }
+
+ if m.EnvConstants != nil {
+ if err := m.EnvConstants.Validate(formats); err != nil {
+ if ve, ok := err.(*errors.Validation); ok {
+ return ve.ValidateName("envConstants")
+ } else if ce, ok := err.(*errors.CompositeError); ok {
+ return ce.ValidateName("envConstants")
+ }
+ return err
+ }
+ }
+
+ return nil
+}
+
var sessionResponseTypeStatusPropEnum []interface{}
func init() {
@@ -152,6 +178,10 @@ func (m *SessionResponse) ContextValidate(ctx context.Context, formats strfmt.Re
res = append(res, err)
}
+ if err := m.contextValidateEnvConstants(ctx, formats); err != nil {
+ res = append(res, err)
+ }
+
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
@@ -178,6 +208,22 @@ func (m *SessionResponse) contextValidateAllowResources(ctx context.Context, for
return nil
}
+func (m *SessionResponse) contextValidateEnvConstants(ctx context.Context, formats strfmt.Registry) error {
+
+ if m.EnvConstants != nil {
+ if err := m.EnvConstants.ContextValidate(ctx, formats); err != nil {
+ if ve, ok := err.(*errors.Validation); ok {
+ return ve.ValidateName("envConstants")
+ } else if ce, ok := err.(*errors.CompositeError); ok {
+ return ce.ValidateName("envConstants")
+ }
+ return err
+ }
+ }
+
+ return nil
+}
+
// MarshalBinary interface implementation
func (m *SessionResponse) MarshalBinary() ([]byte, error) {
if m == nil {
diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjects.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjects.tsx
index e8729e324..f919ecb19 100644
--- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjects.tsx
+++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjects.tsx
@@ -99,7 +99,9 @@ import {
import {
makeid,
+ removeTrace,
storeCallForObjectWithID,
+ storeFormDataWithID,
} from "../../../../ObjectBrowser/transferManager";
import {
cancelObjectInList,
@@ -777,12 +779,15 @@ const ListObjects = () => {
`${bucketName}-${object.name}-${new Date().getTime()}-${Math.random()}`
);
+ const ID = makeid(8);
+
const downloadCall = download(
bucketName,
encodeURLString(object.name),
object.version_id,
object.size,
null,
+ ID,
(progress) => {
dispatch(
updateProgress({
@@ -801,7 +806,6 @@ const ListObjects = () => {
dispatch(cancelObjectInList(identityDownload));
}
);
- const ID = makeid(8);
storeCallForObjectWithID(ID, downloadCall);
dispatch(
setNewObject({
@@ -818,8 +822,6 @@ const ListObjects = () => {
errorMessage: "",
})
);
-
- downloadCall.send();
};
const openPath = (idElement: string) => {
@@ -865,6 +867,7 @@ const ListObjects = () => {
const fileWebkitRelativePath = get(file, "webkitRelativePath", "");
let relativeFolderPath = folderPath;
+ const ID = makeid(8);
// File was uploaded via drag & drop
if (filePath !== "") {
@@ -924,6 +927,8 @@ const ListObjects = () => {
if (xhr.status >= 200 && xhr.status < 300) {
dispatch(completeObject(identity));
resolve({ status: xhr.status });
+
+ removeTrace(ID);
} else {
// reject promise if there was a server error
if (errorMessages[xhr.status]) {
@@ -936,6 +941,7 @@ const ListObjects = () => {
errorMessage = "something went wrong";
}
}
+
dispatch(
failObject({
instanceID: identity,
@@ -943,6 +949,8 @@ const ListObjects = () => {
})
);
reject({ status: xhr.status, message: errorMessage });
+
+ removeTrace(ID);
}
};
@@ -990,7 +998,6 @@ const ListObjects = () => {
const formData = new FormData();
if (file.size !== undefined) {
formData.append(file.size.toString(), blobFile, fileName);
- const ID = makeid(8);
storeCallForObjectWithID(ID, xhr);
dispatch(
setNewObject({
@@ -1008,7 +1015,8 @@ const ListObjects = () => {
})
);
- xhr.send(formData);
+ storeFormDataWithID(ID, formData);
+ storeCallForObjectWithID(ID, xhr);
}
});
};
diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ObjectDetailPanel.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ObjectDetailPanel.tsx
index c65a0f359..5097f30f8 100644
--- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ObjectDetailPanel.tsx
+++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ObjectDetailPanel.tsx
@@ -302,12 +302,15 @@ const ObjectDetailPanel = ({
return;
}
+ const ID = makeid(8);
+
const downloadCall = download(
bucketName,
internalPaths,
object.version_id,
parseInt(object.size || "0"),
null,
+ ID,
(progress) => {
dispatch(
updateProgress({
@@ -326,7 +329,7 @@ const ObjectDetailPanel = ({
dispatch(cancelObjectInList(identityDownload));
}
);
- const ID = makeid(8);
+
storeCallForObjectWithID(ID, downloadCall);
dispatch(
setNewObject({
@@ -343,8 +346,6 @@ const ObjectDetailPanel = ({
errorMessage: "",
})
);
-
- downloadCall.send();
};
const closeDeleteModal = (closeAndReload: boolean) => {
diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/VersionsNavigator.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/VersionsNavigator.tsx
index 0ee45b7e1..8a20d401a 100644
--- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/VersionsNavigator.tsx
+++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/VersionsNavigator.tsx
@@ -256,12 +256,15 @@ const VersionsNavigator = ({
`${bucketName}-${object.name}-${new Date().getTime()}-${Math.random()}`
);
+ const ID = makeid(8);
+
const downloadCall = download(
bucketName,
internalPaths,
object.version_id,
parseInt(object.size || "0"),
null,
+ ID,
(progress) => {
dispatch(
updateProgress({
@@ -280,7 +283,7 @@ const VersionsNavigator = ({
dispatch(cancelObjectInList(identityDownload));
}
);
- const ID = makeid(8);
+
storeCallForObjectWithID(ID, downloadCall);
dispatch(
setNewObject({
@@ -297,8 +300,6 @@ const VersionsNavigator = ({
errorMessage: "",
})
);
-
- downloadCall.send();
};
const onShareItem = (item: IFileInfo) => {
diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/utils.ts b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/utils.ts
index 1920d683a..b7aa8ff68 100644
--- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/utils.ts
+++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/utils.ts
@@ -17,6 +17,7 @@
import { BucketObjectItem } from "./ListObjects/types";
import { IAllowResources } from "../../../types";
import { encodeURLString } from "../../../../../common/utils";
+import { removeTrace } from "../../../ObjectBrowser/transferManager";
export const download = (
bucketName: string,
@@ -24,6 +25,7 @@ export const download = (
versionID: any,
fileSize: number,
overrideFileName: string | null = null,
+ id: string,
progressCallback: (progress: number) => void,
completeCallback: () => void,
errorCallback: (msg: string) => void,
@@ -74,6 +76,8 @@ export const download = (
completeCallback();
}
+ removeTrace(id);
+
var link = document.createElement("a");
link.href = window.URL.createObjectURL(req.response);
link.download = filename;
@@ -104,7 +108,6 @@ export const download = (
abortCallback();
}
};
- //req.send();
return req;
};
diff --git a/portal-ui/src/screens/Console/Common/ObjectManager/ObjectHandled.tsx b/portal-ui/src/screens/Console/Common/ObjectManager/ObjectHandled.tsx
index be757fc9d..59e80d133 100644
--- a/portal-ui/src/screens/Console/Common/ObjectManager/ObjectHandled.tsx
+++ b/portal-ui/src/screens/Console/Common/ObjectManager/ObjectHandled.tsx
@@ -170,7 +170,6 @@ const ObjectHandled = ({