Browse Source

support modify replica count (#701)

Signed-off-by: ryjiang <jiangruiyi@gmail.com>
ryjiang 7 months ago
parent
commit
affda651f6

+ 21 - 5
client/src/components/icons/Icons.tsx

@@ -3,7 +3,6 @@ import { IconsType } from './Types';
 import { SvgIcon } from '@mui/material';
 import AppsIcon from '@mui/icons-material/Apps';
 import CancelIcon from '@mui/icons-material/Cancel';
-import AttuIcon from '@/assets/icons/attu.svg?react';
 import ConsoleIcon from '@/assets/icons/console.svg?react';
 import KeyIcon from '@/assets/icons/key.svg?react';
 import SearchEmptyIcon from '@/assets/icons/search.svg?react';
@@ -50,8 +49,8 @@ const icons: { [x in IconsType]: (props?: any) => React.ReactElement } = {
       height="15"
       viewBox="0 0 15 15"
       fill="none"
-      xmlns="http://www.w3.org/2000/svg"
       {...props}
+      xmlns="http://www.w3.org/2000/svg"
     >
       <path
         d="M7.49991 0.876892C3.84222 0.876892 0.877075 3.84204 0.877075 7.49972C0.877075 11.1574 3.84222 14.1226 7.49991 14.1226C11.1576 14.1226 14.1227 11.1574 14.1227 7.49972C14.1227 3.84204 11.1576 0.876892 7.49991 0.876892ZM1.82707 7.49972C1.82707 4.36671 4.36689 1.82689 7.49991 1.82689C10.6329 1.82689 13.1727 4.36671 13.1727 7.49972C13.1727 10.6327 10.6329 13.1726 7.49991 13.1726C4.36689 13.1726 1.82707 10.6327 1.82707 7.49972ZM7.50003 4C7.77617 4 8.00003 4.22386 8.00003 4.5V7H10.5C10.7762 7 11 7.22386 11 7.5C11 7.77614 10.7762 8 10.5 8H8.00003V10.5C8.00003 10.7761 7.77617 11 7.50003 11C7.22389 11 7.00003 10.7761 7.00003 10.5V8H4.50003C4.22389 8 4.00003 7.77614 4.00003 7.5C4.00003 7.22386 4.22389 7 4.50003 7H7.00003V4.5C7.00003 4.22386 7.22389 4 7.50003 4Z"
@@ -61,6 +60,23 @@ const icons: { [x in IconsType]: (props?: any) => React.ReactElement } = {
       ></path>
     </SvgIcon>
   ),
+  fileplus: props => (
+    <SvgIcon
+      width="15"
+      height="15"
+      viewBox="0 0 15 15"
+      fill="none"
+      {...props}
+      xmlns="http://www.w3.org/2000/svg"
+    >
+      <path
+        d="M3.5 2C3.22386 2 3 2.22386 3 2.5V12.5C3 12.7761 3.22386 13 3.5 13H11.5C11.7761 13 12 12.7761 12 12.5V4.70711L9.29289 2H3.5ZM2 2.5C2 1.67157 2.67157 1 3.5 1H9.5C9.63261 1 9.75979 1.05268 9.85355 1.14645L12.7803 4.07322C12.921 4.21388 13 4.40464 13 4.60355V12.5C13 13.3284 12.3284 14 11.5 14H3.5C2.67157 14 2 13.3284 2 12.5V2.5ZM4.75 7.5C4.75 7.22386 4.97386 7 5.25 7H7V5.25C7 4.97386 7.22386 4.75 7.5 4.75C7.77614 4.75 8 4.97386 8 5.25V7H9.75C10.0261 7 10.25 7.22386 10.25 7.5C10.25 7.77614 10.0261 8 9.75 8H8V9.75C8 10.0261 7.77614 10.25 7.5 10.25C7.22386 10.25 7 10.0261 7 9.75V8H5.25C4.97386 8 4.75 7.77614 4.75 7.5Z"
+        fill="currentColor"
+        fillRule="evenodd"
+        clipRule="evenodd"
+      ></path>
+    </SvgIcon>
+  ),
   delete: (props = {}) => (
     <SvgIcon
       width="15"
@@ -237,8 +253,8 @@ const icons: { [x in IconsType]: (props?: any) => React.ReactElement } = {
       height="15"
       viewBox="0 0 15 15"
       fill="none"
-      xmlns="http://www.w3.org/2000/svg"
       {...props}
+      xmlns="http://www.w3.org/2000/svg"
     >
       <path
         d="M7.49991 0.876892C3.84222 0.876892 0.877075 3.84204 0.877075 7.49972C0.877075 11.1574 3.84222 14.1226 7.49991 14.1226C11.1576 14.1226 14.1227 11.1574 14.1227 7.49972C14.1227 3.84204 11.1576 0.876892 7.49991 0.876892ZM1.82707 7.49972C1.82707 4.36671 4.36689 1.82689 7.49991 1.82689C10.6329 1.82689 13.1727 4.36671 13.1727 7.49972C13.1727 10.6327 10.6329 13.1726 7.49991 13.1726C4.36689 13.1726 1.82707 10.6327 1.82707 7.49972ZM4.50003 7C4.22389 7 4.00003 7.22386 4.00003 7.5C4.00003 7.77614 4.22389 8 4.50003 8H10.5C10.7762 8 11 7.77614 11 7.5C11 7.22386 10.7762 7 10.5 7H4.50003Z"
@@ -938,8 +954,8 @@ const icons: { [x in IconsType]: (props?: any) => React.ReactElement } = {
       <path
         d="M2.5 1H12.5C13.3284 1 14 1.67157 14 2.5V12.5C14 13.3284 13.3284 14 12.5 14H2.5C1.67157 14 1 13.3284 1 12.5V2.5C1 1.67157 1.67157 1 2.5 1ZM2.5 2C2.22386 2 2 2.22386 2 2.5V8.3636L3.6818 6.6818C3.76809 6.59551 3.88572 6.54797 4.00774 6.55007C4.12975 6.55216 4.24568 6.60372 4.32895 6.69293L7.87355 10.4901L10.6818 7.6818C10.8575 7.50607 11.1425 7.50607 11.3182 7.6818L13 9.3636V2.5C13 2.22386 12.7761 2 12.5 2H2.5ZM2 12.5V9.6364L3.98887 7.64753L7.5311 11.4421L8.94113 13H2.5C2.22386 13 2 12.7761 2 12.5ZM12.5 13H10.155L8.48336 11.153L11 8.6364L13 10.6364V12.5C13 12.7761 12.7761 13 12.5 13ZM6.64922 5.5C6.64922 5.03013 7.03013 4.64922 7.5 4.64922C7.96987 4.64922 8.35078 5.03013 8.35078 5.5C8.35078 5.96987 7.96987 6.35078 7.5 6.35078C7.03013 6.35078 6.64922 5.96987 6.64922 5.5ZM7.5 3.74922C6.53307 3.74922 5.74922 4.53307 5.74922 5.5C5.74922 6.46693 6.53307 7.25078 7.5 7.25078C8.46693 7.25078 9.25078 6.46693 9.25078 5.5C9.25078 4.53307 8.46693 3.74922 7.5 3.74922Z"
         fill="currentColor"
-        fill-rule="evenodd"
-        clip-rule="evenodd"
+        fillRule="evenodd"
+        clipRule="evenodd"
       ></path>
     </svg>
   ),

+ 2 - 1
client/src/components/icons/Types.ts

@@ -58,4 +58,5 @@ export type IconsType =
   | 'cross'
   | 'day'
   | 'night'
-  | 'img';
+  | 'img'
+  | 'fileplus';

+ 1 - 0
client/src/i18n/cn/button.ts

@@ -39,6 +39,7 @@ const btnTrans = {
   edit: '编辑',
   explore: '探索',
   close: '关闭',
+  modify: '修改',
 
   // tips
   loadColTooltip: '加载Collection',

+ 2 - 0
client/src/i18n/cn/collection.ts

@@ -29,6 +29,8 @@ const collectionTrans = {
     '一致性是指确保每个节点或副本在给定时间写入或读取数据时具有相同数据视图的属性。',
   entityCountInfo:
     '这个计数是一个近似值,并可能因为Milvus的独特机制而稍有延迟。实际的计数可能会有所变化,并会定期更新。请注意,这个数字应该被用作参考,而不是精确的计数。',
+  replicaTooltip: 'Collection的副本数量, 不能超过查询节点的数量。',
+  modifyReplicaTooltip: '调整副本数量',
 
   // create dialog
   createTitle: '创建Collection',

+ 1 - 0
client/src/i18n/cn/dialog.ts

@@ -11,6 +11,7 @@ const dialogTrans = {
   flush: `为 {{type}} 的数据落盘`,
   loadTitle: `加载 {{type}}`,
   editEntityTitle: `编辑 Entity`,
+  modifyReplicaTitle: `修改 {{type}} 的副本`,
 
   loadContent: `您正在尝试加载带有数据的 {{type}}。只有已加载的 {{type}} 可以被搜索。`,
   releaseContent: `您正在尝试发布带有数据的 {{type}}。请注意,数据将不再可用于搜索。`,

+ 1 - 0
client/src/i18n/cn/success.ts

@@ -9,6 +9,7 @@ const successTrans = {
   duplicate: `{{name}}复制成功.`,
   empty: `{{name}}清空已经开始.`,
   reset: `{{name}}重置成功。`,
+  modifyReplica: `{{name}}修改副本数量成功。`,
 };
 
 export default successTrans;

+ 1 - 0
client/src/i18n/en/button.ts

@@ -39,6 +39,7 @@ const btnTrans = {
   edit: 'Edit',
   explore: 'Explore',
   close: 'Close',
+  modify: 'Modify',
 
   // tips
   loadColTooltip: 'Load Collection',

+ 2 - 0
client/src/i18n/en/collection.ts

@@ -29,6 +29,8 @@ const collectionTrans = {
     'Consistency refers to the property that ensures every node or replica has the same view of data when writing or reading data at a given time.',
   entityCountInfo:
     'This count is an approximation and may be slightly delayed due to the unique mechanisms of Milvus. The actual count may vary and is updated periodically. Please note that this number should be used as a reference and not as an exact count.',
+  replicaTooltip: 'The number of replicas for the collection, it can not exceed the number of query nodes.',
+  modifyReplicaTooltip: 'Modify Replica Number',
 
   // create dialog
   createTitle: 'Create Collection',

+ 1 - 0
client/src/i18n/en/dialog.ts

@@ -11,6 +11,7 @@ const dialogTrans = {
   flush: `Flush data for {{type}}`,
   loadTitle: `Load {{type}}`,
   editEntityTitle: `Edit Entity(JSON)`,
+  modifyReplicaTitle: `Modify replica for {{type}}`,
 
   loadContent: `You are trying to load a {{type}} with data. Only loaded {{type}} can be searched.`,
   releaseContent: `You are trying to release {{type}} with data. Please be aware that the data will no longer be available for search.`,

+ 1 - 0
client/src/i18n/en/success.ts

@@ -9,6 +9,7 @@ const successTrans = {
   duplicate: `{{name}} has been duplicated.`,
   empty: `Emptying data for {{name}} has started.`,
   reset: `{{name}} has been reset.`,
+  modifyReplica: `Replica number for {{name}} has been modified.`,
 };
 
 export default successTrans;

+ 1 - 1
client/src/pages/databases/collections/CreateFields.tsx

@@ -189,7 +189,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
         wrapperClass={classes.select}
         options={_options}
         size="small"
-        onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
+        onChange={e => {
           onChange(e.target.value as DataTypeEnum);
         }}
         value={value}

+ 44 - 11
client/src/pages/databases/collections/schema/Schema.tsx

@@ -6,16 +6,21 @@ import { ColDefinitionsType } from '@/components/grid/Types';
 import { useTranslation } from 'react-i18next';
 import Icons from '@/components/icons/Icons';
 import { formatFieldType, formatNumber } from '@/utils';
-import { dataContext } from '@/context';
+import { dataContext, rootContext, systemContext } from '@/context';
 import IndexTypeElement from './IndexTypeElement';
 import { getLabelDisplayedRows } from '@/pages/search/Utils';
 import StatusAction from '@/pages/databases/collections/StatusAction';
 import CustomToolTip from '@/components/customToolTip/CustomToolTip';
 import { FieldObject } from '@server/types';
 import { useStyles } from './Styles';
+import CustomIconButton from '@/components/customButton/CustomIconButton';
+import LoadCollectionDialog from '@/pages/dialogs/LoadCollectionDialog';
 
 const Overview = () => {
   const { fetchCollection, collections, loading } = useContext(dataContext);
+  const { data } = useContext(systemContext);
+  const { setDialog } = useContext(rootContext);
+
   const { collectionName = '' } = useParams<{ collectionName: string }>();
   const classes = useStyles();
   const { t: collectionTrans } = useTranslation('collection');
@@ -171,6 +176,10 @@ const Overview = () => {
     );
   }
 
+  // enable modify replica if there are more than one query node
+  const enableModifyReplica =
+    data && data.queryNodes && data.queryNodes.length > 1;
+
   // get loading state label
   return (
     <section className={classes.wrapper}>
@@ -196,18 +205,42 @@ const Overview = () => {
 
             <div className={classes.card}>
               <Typography variant="h5">{collectionTrans('status')}</Typography>
-              <Typography variant="h6">
-                <StatusAction
-                  status={collection.status}
-                  percentage={collection.loadedPercentage}
-                  collection={collection}
-                  showExtraAction={true}
-                  createIndexElement={CreateIndexElement}
-                />
+              <StatusAction
+                status={collection.status}
+                percentage={collection.loadedPercentage}
+                collection={collection}
+                showExtraAction={true}
+                createIndexElement={CreateIndexElement}
+              />
+              <Typography variant="h5">
+                {collectionTrans('replica')}
+                <CustomToolTip title={collectionTrans('replicaTooltip')}>
+                  <Icons.question classes={{ root: classes.questionIcon }} />
+                </CustomToolTip>
               </Typography>
-              <Typography variant="h5">{collectionTrans('replica')}</Typography>
               <Typography variant="h6">
-                {collection.replicas?.length}
+                {collection.loaded ? collection.replicas?.length : '...'}
+                {collection.loaded && enableModifyReplica && (
+                  <CustomIconButton
+                    tooltip={collectionTrans('modifyReplicaTooltip')}
+                    onClick={() => {
+                      setDialog({
+                        open: true,
+                        type: 'custom',
+                        params: {
+                          component: (
+                            <LoadCollectionDialog
+                              collection={collection}
+                              isModifyReplica={true}
+                            />
+                          ),
+                        },
+                      });
+                    }}
+                  >
+                    <Icons.settings className={classes.addReplicaBtn} />
+                  </CustomIconButton>
+                )}
               </Typography>
 
               <Typography variant="h5">

+ 5 - 1
client/src/pages/databases/collections/schema/Styles.tsx

@@ -15,6 +15,7 @@ export const useStyles = makeStyles((theme: Theme) => ({
       fontWeight: 400,
     },
     '& h6': {
+      fontSize: 14,
       color: theme.palette.text.primary,
       marginBottom: theme.spacing(1),
     },
@@ -47,8 +48,11 @@ export const useStyles = makeStyles((theme: Theme) => ({
   extraBtn: {
     height: 24,
   },
+  addReplicaBtn: {
+    width: 15,
+  },
   questionIcon: {
-    width: 14,
+    width: 12,
     position: 'relative',
     top: '6px',
     right: '-2px',

+ 10 - 7
client/src/pages/dialogs/LoadCollectionDialog.tsx

@@ -35,10 +35,11 @@ const useStyles = makeStyles((theme: Theme) => ({
 const LoadCollectionDialog = (props: {
   collection: CollectionObject;
   onLoad?: (collection: CollectionObject) => void;
+  isModifyReplica?: boolean;
 }) => {
   const { loadCollection } = useContext(dataContext);
   const classes = useStyles();
-  const { collection, onLoad } = props;
+  const { collection, onLoad, isModifyReplica } = props;
   const { t: dialogTrans } = useTranslation('dialog');
   const { t: collectionTrans } = useTranslation('collection');
   const { t: successTrans } = useTranslation('success');
@@ -46,12 +47,14 @@ const LoadCollectionDialog = (props: {
   const { t: warningTrans } = useTranslation('warning');
   const { handleCloseDialog, openSnackBar } = useContext(rootContext);
   const [form, setForm] = useState({
-    replica: 1,
+    replica: collection.replicas?.length || 1,
   });
   const { isManaged } = useContext(authContext);
 
   const [enableRelica, setEnableRelica] = useState(false);
-  const [replicaToggle, setReplicaToggle] = useState(false);
+  const [replicaToggle, setReplicaToggle] = useState(
+    collection.replicas!.length > 1
+  );
   const [maxQueryNode, setMaxQueryNode] = useState(1);
   const [btnDisabled, setBtnDisabled] = useState(false);
 
@@ -108,7 +111,7 @@ const LoadCollectionDialog = (props: {
 
       // show success message
       openSnackBar(
-        successTrans('load', {
+        successTrans(isModifyReplica ? 'modifyReplica' : 'load', {
           name: collectionTrans('collection'),
         })
       );
@@ -180,14 +183,14 @@ const LoadCollectionDialog = (props: {
 
   return (
     <DialogTemplate
-      title={dialogTrans('loadTitle', {
+      title={dialogTrans(isModifyReplica ? 'modifyReplicaTitle' : 'loadTitle', {
         type: collection.collection_name,
       })}
       handleClose={handleCloseDialog}
       children={
         <>
           <Typography variant="body1" component="p" className={classes.desc}>
-            {collectionTrans('loadContent')}
+            {collectionTrans(isModifyReplica ? 'replicaDes' : 'loadContent')}
           </Typography>
           {enableRelica ? (
             <>
@@ -219,7 +222,7 @@ const LoadCollectionDialog = (props: {
           ) : null}
         </>
       }
-      confirmLabel={btnTrans('load')}
+      confirmLabel={btnTrans(isModifyReplica ? 'confirm' : 'load')}
       handleConfirm={handleConfirm}
       confirmDisabled={replicaToggle ? disabled || btnDisabled : btnDisabled}
     />