mirror of https://github.com/milvus-io/milvus.git
Add README of scale test (#6800)
* add get_svc_ip func add scale case tag Signed-off-by: ThreadDao <yufen.zong@zilliz.com> * add scale test README Signed-off-by: ThreadDao <yufen.zong@zilliz.com>pull/6816/head
parent
e43b43e7d8
commit
386954d9c3
|
@ -137,6 +137,7 @@ def gen_binary_vectors(num, dim):
|
||||||
for _ in range(num):
|
for _ in range(num):
|
||||||
raw_vector = [random.randint(0, 1) for _ in range(dim)]
|
raw_vector = [random.randint(0, 1) for _ in range(dim)]
|
||||||
raw_vectors.append(raw_vector)
|
raw_vectors.append(raw_vector)
|
||||||
|
# packs a binary-valued array into bits in a unit8 array, and bytes array_of_ints
|
||||||
binary_vectors.append(bytes(np.packbits(raw_vector, axis=-1).tolist()))
|
binary_vectors.append(bytes(np.packbits(raw_vector, axis=-1).tolist()))
|
||||||
return raw_vectors, binary_vectors
|
return raw_vectors, binary_vectors
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
# Scale Tests
|
||||||
|
## Goal
|
||||||
|
Scale tests are designed to check the scalability of Milvus.
|
||||||
|
|
||||||
|
For instance, if the dataNode pod expands from one to two:
|
||||||
|
- verify the consistency of existing data
|
||||||
|
|
||||||
|
- verify that the DDL and DML operation is working
|
||||||
|
|
||||||
|
## Prerequisite
|
||||||
|
- Milvus Helm Chart ( refer to [Milvus Helm Chart](https://github.com/milvus-io/milvus-helm/blob/master/charts/milvus/README.md) )
|
||||||
|
|
||||||
|
## Test Scenarios
|
||||||
|
### Milvus in cluster mode
|
||||||
|
- expand / shrink dataNode pod
|
||||||
|
|
||||||
|
- expand / shrink indexNode pod
|
||||||
|
|
||||||
|
- expand / shrink queryNode pod
|
||||||
|
|
||||||
|
- expand / shrink proxy pod
|
||||||
|
|
||||||
|
## How it works
|
||||||
|
|
||||||
|
- Milvus scales the number of pods in a deployment based on the helm upgrade
|
||||||
|
|
||||||
|
- Scale test decouple the milvus deployment from the test code
|
||||||
|
|
||||||
|
- Each test scenario is carried out along the process:
|
||||||
|
<br> deploy milvus -> operate milvus -> scale milvus -> verify milvus
|
||||||
|
|
||||||
|
- Milvus deployment and milvus scaling are designed in `helm_env.py`
|
||||||
|
|
||||||
|
## Run
|
||||||
|
### Manually
|
||||||
|
Run a single test scenario manually(take scale dataNode as instance):
|
||||||
|
|
||||||
|
- update milvus helm chart path (choose one of the following)
|
||||||
|
- `export MILVUS_CHART_ENV=/your/milvus-helm/charts/milvus`
|
||||||
|
|
||||||
|
- update <code>MILVUS_CHART = '/home/zong/milvus-helm/charts/milvus'</code> in <code>scale/constants.py</code>
|
||||||
|
|
||||||
|
- run the commands below:
|
||||||
|
```bash
|
||||||
|
cd /milvus/tests20/python_client/scale
|
||||||
|
|
||||||
|
pytest test_data_node_scale.py::TestDataNodeScale::test_expand_data_node -v -s
|
||||||
|
```
|
||||||
|
|
||||||
|
### Nightly
|
||||||
|
still in planning
|
|
@ -1,6 +1,7 @@
|
||||||
# scale object
|
# scale object
|
||||||
IMAGE_REPOSITORY = "milvusdb/milvus-dev"
|
IMAGE_REPOSITORY = "milvusdb/milvus-dev"
|
||||||
IMAGE_TAG = "master-latest"
|
IMAGE_TAG = "master-latest"
|
||||||
|
NAMESPACE = "chaos-testing"
|
||||||
IF_NOT_PRESENT = "IfNotPresent"
|
IF_NOT_PRESENT = "IfNotPresent"
|
||||||
ALWAYS = "Always"
|
ALWAYS = "Always"
|
||||||
PROXY = "proxy"
|
PROXY = "proxy"
|
||||||
|
@ -9,4 +10,5 @@ INDEX_NODE = "indexNode"
|
||||||
QUERY_NODE = "queryNode"
|
QUERY_NODE = "queryNode"
|
||||||
|
|
||||||
# my values.yaml path
|
# my values.yaml path
|
||||||
HELM_VALUES_PATH = '/home/zong/milvus-helm/charts/milvus'
|
MILVUS_CHART_ENV = 'MILVUS_CHART_ENV'
|
||||||
|
MILVUS_CHART = '/home/zong/milvus-helm/charts/milvus'
|
||||||
|
|
|
@ -4,6 +4,7 @@ from time import sleep
|
||||||
from scale import constants
|
from scale import constants
|
||||||
from utils.util_log import test_log as log
|
from utils.util_log import test_log as log
|
||||||
from common import common_func as cf
|
from common import common_func as cf
|
||||||
|
from scale import scale_common as sc
|
||||||
|
|
||||||
|
|
||||||
class HelmEnv:
|
class HelmEnv:
|
||||||
|
@ -35,8 +36,9 @@ class HelmEnv:
|
||||||
f'--set indexNode.replicas={self.index_node} ' \
|
f'--set indexNode.replicas={self.index_node} ' \
|
||||||
f'--set queryNode.replicas={self.query_node} ' \
|
f'--set queryNode.replicas={self.query_node} ' \
|
||||||
f'{self.release_name} . '
|
f'{self.release_name} . '
|
||||||
log.info(install_cmd)
|
log.debug(f'install_cmd: {install_cmd}')
|
||||||
os.system(f'cd {constants.HELM_VALUES_PATH} && {install_cmd}')
|
log.debug(f'MILVUS CHART: {sc.get_milvus_chart_env_var()}')
|
||||||
|
os.system(f'cd {sc.get_milvus_chart_env_var()} && {install_cmd}')
|
||||||
# raise Exception("Failed to deploy cluster milvus")
|
# raise Exception("Failed to deploy cluster milvus")
|
||||||
# todo
|
# todo
|
||||||
# return svc ip
|
# return svc ip
|
||||||
|
@ -62,8 +64,9 @@ class HelmEnv:
|
||||||
f'--set indexNode.replicas={index_node} ' \
|
f'--set indexNode.replicas={index_node} ' \
|
||||||
f'--set queryNode.replicas={query_node} ' \
|
f'--set queryNode.replicas={query_node} ' \
|
||||||
f'{self.release_name} . '
|
f'{self.release_name} . '
|
||||||
log.info(upgrade_cmd)
|
log.debug(f'upgrade_cmd: {upgrade_cmd}')
|
||||||
if os.system(f'cd {constants.HELM_VALUES_PATH} && {upgrade_cmd}'):
|
log.debug(f'MILVUS CHART: {sc.get_milvus_chart_env_var()}')
|
||||||
|
if os.system(f'cd {sc.get_milvus_chart_env_var()} && {upgrade_cmd}'):
|
||||||
raise Exception(f'Failed to upgrade cluster milvus with {kwargs}')
|
raise Exception(f'Failed to upgrade cluster milvus with {kwargs}')
|
||||||
|
|
||||||
def helm_uninstall_cluster_milvus(self):
|
def helm_uninstall_cluster_milvus(self):
|
||||||
|
@ -81,11 +84,21 @@ class HelmEnv:
|
||||||
# delete plusar
|
# delete plusar
|
||||||
# delete_pvc_plusar_cmd = "kubectl delete pvc scale-test-milvus-pulsar"
|
# delete_pvc_plusar_cmd = "kubectl delete pvc scale-test-milvus-pulsar"
|
||||||
|
|
||||||
|
def get_svc_external_ip(self):
|
||||||
|
from kubernetes import client, config
|
||||||
|
# from kubernetes.client.rest import ApiException
|
||||||
|
config.load_kube_config()
|
||||||
|
v1 = client.CoreV1Api()
|
||||||
|
service = v1.read_namespaced_service(f'{self.release_name}-milvus', constants.NAMESPACE)
|
||||||
|
return service.status.load_balancer.ingress[0].ip
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# default deploy q replicas
|
# default deploy q replicas
|
||||||
release_name = "scale-test"
|
release_name = "scale-test"
|
||||||
env = HelmEnv(release_name=release_name)
|
env = HelmEnv(release_name=release_name)
|
||||||
|
# host = env.get_svc_external_ip()
|
||||||
|
# log.debug(host)
|
||||||
# env.helm_install_cluster_milvus()
|
# env.helm_install_cluster_milvus()
|
||||||
# env.helm_upgrade_cluster_milvus(queryNode=2)
|
# env.helm_upgrade_cluster_milvus(queryNode=2)
|
||||||
env.helm_uninstall_cluster_milvus()
|
env.helm_uninstall_cluster_milvus()
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
from scale import constants
|
||||||
|
from utils.util_log import test_log as log
|
||||||
|
|
||||||
|
|
||||||
|
def get_milvus_chart_env_var(var=constants.MILVUS_CHART_ENV):
|
||||||
|
""" get log path for testing """
|
||||||
|
try:
|
||||||
|
milvus_helm_chart = os.environ[var]
|
||||||
|
return str(milvus_helm_chart)
|
||||||
|
except Exception as e:
|
||||||
|
milvus_helm_chart = constants.MILVUS_CHART
|
||||||
|
log.warning(f"Failed to get environment variables: {var}, use default: {milvus_helm_chart}, error: {str(e)}")
|
||||||
|
return milvus_helm_chart
|
|
@ -1,8 +1,7 @@
|
||||||
import random
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from base.collection_wrapper import ApiCollectionWrapper
|
from base.collection_wrapper import ApiCollectionWrapper
|
||||||
|
from common.common_type import CaseLabel
|
||||||
from utils.util_log import test_log as log
|
from utils.util_log import test_log as log
|
||||||
from common import common_func as cf
|
from common import common_func as cf
|
||||||
from common import common_type as ct
|
from common import common_type as ct
|
||||||
|
@ -18,6 +17,7 @@ default_index_params = {"index_type": "IVF_SQ8", "metric_type": "L2", "params":
|
||||||
|
|
||||||
class TestDataNodeScale:
|
class TestDataNodeScale:
|
||||||
|
|
||||||
|
@pytest.mark.tags(CaseLabel.L3)
|
||||||
def test_expand_data_node(self):
|
def test_expand_data_node(self):
|
||||||
"""
|
"""
|
||||||
target: test create and insert api after expand dataNode pod
|
target: test create and insert api after expand dataNode pod
|
||||||
|
@ -30,9 +30,10 @@ class TestDataNodeScale:
|
||||||
release_name = "scale-test"
|
release_name = "scale-test"
|
||||||
env = HelmEnv(release_name=release_name)
|
env = HelmEnv(release_name=release_name)
|
||||||
env.helm_install_cluster_milvus()
|
env.helm_install_cluster_milvus()
|
||||||
|
host = env.get_svc_external_ip()
|
||||||
|
|
||||||
# connect
|
# connect
|
||||||
connections.add_connection(default={"host": '10.98.0.8', "port": 19530})
|
connections.add_connection(default={"host": host, "port": 19530})
|
||||||
connections.connect(alias='default')
|
connections.connect(alias='default')
|
||||||
# create
|
# create
|
||||||
c_name = cf.gen_unique_str(prefix)
|
c_name = cf.gen_unique_str(prefix)
|
||||||
|
@ -63,6 +64,7 @@ class TestDataNodeScale:
|
||||||
new_collection_w.drop()
|
new_collection_w.drop()
|
||||||
# env.helm_uninstall_cluster_milvus()
|
# env.helm_uninstall_cluster_milvus()
|
||||||
|
|
||||||
|
@pytest.mark.tags(CaseLabel.L3)
|
||||||
def test_shrink_data_node(self):
|
def test_shrink_data_node(self):
|
||||||
"""
|
"""
|
||||||
target: test shrink dataNode from 2 to 1
|
target: test shrink dataNode from 2 to 1
|
||||||
|
@ -101,5 +103,3 @@ class TestDataNodeScale:
|
||||||
collection_w2.drop()
|
collection_w2.drop()
|
||||||
|
|
||||||
# env.helm_uninstall_cluster_milvus()
|
# env.helm_uninstall_cluster_milvus()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import datetime
|
import datetime
|
||||||
# import pdb
|
# import pdb
|
||||||
|
import pytest
|
||||||
from pymilvus_orm import connections
|
from pymilvus_orm import connections
|
||||||
|
|
||||||
from base.collection_wrapper import ApiCollectionWrapper
|
from base.collection_wrapper import ApiCollectionWrapper
|
||||||
|
from common.common_type import CaseLabel
|
||||||
from scale.helm_env import HelmEnv
|
from scale.helm_env import HelmEnv
|
||||||
from common import common_func as cf
|
from common import common_func as cf
|
||||||
from common import common_type as ct
|
from common import common_type as ct
|
||||||
|
@ -15,6 +16,7 @@ default_index_params = {"index_type": "IVF_SQ8", "metric_type": "L2", "params":
|
||||||
|
|
||||||
class TestIndexNodeScale:
|
class TestIndexNodeScale:
|
||||||
|
|
||||||
|
@pytest.mark.tags(CaseLabel.L3)
|
||||||
def test_expand_index_node(self):
|
def test_expand_index_node(self):
|
||||||
"""
|
"""
|
||||||
target: test expand indexNode from 1 to 2
|
target: test expand indexNode from 1 to 2
|
||||||
|
@ -67,6 +69,7 @@ class TestIndexNodeScale:
|
||||||
log.debug(f't1: {t1}')
|
log.debug(f't1: {t1}')
|
||||||
assert round(t0 / t1) == 2
|
assert round(t0 / t1) == 2
|
||||||
|
|
||||||
|
@pytest.mark.tags(CaseLabel.L3)
|
||||||
def test_shrink_index_node(self):
|
def test_shrink_index_node(self):
|
||||||
"""
|
"""
|
||||||
target: test shrink indexNode from 2 to 1
|
target: test shrink indexNode from 2 to 1
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
import pdb
|
import pdb
|
||||||
import random
|
import random
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from base.collection_wrapper import ApiCollectionWrapper
|
from base.collection_wrapper import ApiCollectionWrapper
|
||||||
|
from common.common_type import CaseLabel
|
||||||
from scale.helm_env import HelmEnv
|
from scale.helm_env import HelmEnv
|
||||||
from utils.util_log import test_log as log
|
from utils.util_log import test_log as log
|
||||||
from common import common_func as cf
|
from common import common_func as cf
|
||||||
|
@ -19,6 +22,7 @@ default_index_params = {"index_type": "IVF_SQ8", "metric_type": "L2", "params":
|
||||||
|
|
||||||
class TestQueryNodeScale:
|
class TestQueryNodeScale:
|
||||||
|
|
||||||
|
@pytest.mark.tags(CaseLabel.L3)
|
||||||
def test_expand_query_node(self):
|
def test_expand_query_node(self):
|
||||||
release_name = "scale-query"
|
release_name = "scale-query"
|
||||||
env = HelmEnv(release_name=release_name)
|
env = HelmEnv(release_name=release_name)
|
||||||
|
@ -60,6 +64,7 @@ class TestQueryNodeScale:
|
||||||
|
|
||||||
assert res1[0].ids == res2[0].ids
|
assert res1[0].ids == res2[0].ids
|
||||||
|
|
||||||
|
@pytest.mark.tags(CaseLabel.L3)
|
||||||
def test_shrink_query_node(self):
|
def test_shrink_query_node(self):
|
||||||
"""
|
"""
|
||||||
target: test shrink queryNode from 2 to 1
|
target: test shrink queryNode from 2 to 1
|
||||||
|
|
Loading…
Reference in New Issue