chore: import upstream snapshot with attribution
Build and test / Build and test AMD64 Ubuntu 22.04 (push) Failing after 0s
Publish Builder / amazonlinux2023 (push) Failing after 1s
Build and test / UT for Go (push) Has been skipped
Publish KRTE Images / KRTE (push) Failing after 1s
Build and test / Integration Test (push) Has been skipped
Build and test / Upload Code Coverage (push) Has been skipped
Publish Builder / rockylinux9 (push) Failing after 1s
Publish Builder / ubuntu22.04 (push) Failing after 0s
Publish Builder / ubuntu24.04 (push) Failing after 0s
Publish Gpu Builder / publish-gpu-builder (push) Failing after 1s
Publish Test Images / PyTest (push) Failing after 0s
Build and test / UT for Cpp (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 12:31:17 +08:00
commit 498b235461
5446 changed files with 2748612 additions and 0 deletions
+59
View File
@@ -0,0 +1,59 @@
## Overview
To test deployment by docker-compose(Both standalone and cluster)
* re-install milvus to check data persistence
1. Deploy Milvus
2. Insert data
3. Build index
4. Search
5. Stop Milvus
6. Repeat from step #1
* upgrade milvus to check data compatibility
1. Deploy Milvus (Previous Release)
2. Insert data
3. Search
4. Stop Milvus
5. Deploy Milvus (Latest Release/Build)
6. Build index
7. Search
## Project structure
```
.
├── README.md
├── cluster # dir to deploy cluster
│ ├── logs # dir to save logs
│ └──docker-compose.yml
├── standalone # dir to deploy standalone
│ ├── logs # dir to save logs
│ └──docker-compose.yml
├── scripts
│ ├── action_after_upgrade.py
│ ├── action_before_upgrade.py
│ ├── action_reinstall.py
│ └── utils.py
├── cluster-values.yaml # config for helm deployment
├── test.sh # script to run a single task
└── run.sh # script to run all tasks
```
## Usage
Make sure you have installed `docker`,`docker-compose` and `pymilvus`!
For different version, you should modify the value of `latest_tag`, `latest_rc_tag` and `Release`. Password of root is needed for deleting volumes dir.
single test task
```bash
$ bash test.sh -m ${Mode} -t ${Task} -p ${Password}
# Mode, the mode of milvus deploy. standalone or cluster"
# Task, the task type of test. reinstall or upgrade
# Password, the password of root"
```
run all tasks
```bash
$ bash run.sh -p ${Password}
# Password, the password of root"
```
+10
View File
@@ -0,0 +1,10 @@
from base.client_base import TestcaseBase
from utils.util_log import test_log as log
class TestDeployBase(TestcaseBase):
def teardown_method(self, method):
log.info(("*" * 35) + " teardown " + ("*" * 35))
log.info("[teardown_method] Start teardown test case %s..." % method.__name__)
log.info("skip drop collection")
@@ -0,0 +1,30 @@
#!/bin/bash
#to check containers all running and minio is healthy
function check_healthy {
Expect=$(yq '.services | length' 'docker-compose.yml')
Expect_health=$(yq '.services' 'docker-compose.yml' |grep 'healthcheck'|wc -l)
cnt=$(docker compose ps | grep -E "running|Running|Up|up" | wc -l)
healthy=$(docker compose ps | grep "healthy" | wc -l)
time_cnt=0
echo "running num $cnt expect num $Expect"
echo "healthy num $healthy expect num $Expect_health"
while [[ $cnt -ne $Expect || $healthy -ne 1 ]];
do
printf "waiting all containers getting running\n"
sleep 5
let time_cnt+=5
# if time is greater than 300s, the condition still not satisfied, we regard it as a failure
if [ $time_cnt -gt 300 ];
then
printf "timeout,there are some issues with deployment!"
exit 1
fi
cnt=$(docker compose ps | grep -E "running|Running|Up|up" | wc -l)
healthy=$(docker compose ps | grep "healthy" | wc -l)
echo "running num $cnt expect num $Expect"
echo "healthy num $healthy expect num $Expect_health"
done
}
check_healthy
@@ -0,0 +1,136 @@
cluster:
enabled: true
log:
level: debug
image:
all:
repository: milvusdb/milvus
tag: master-latest
pullPolicy: IfNotPresent
etcd:
replicaCount: 3
image:
repository: milvusdb/etcd
tag: 3.5.5-r2
minio:
resources:
requests:
memory: 256Mi
kafka:
enabled: false
replicaCount: 3
pulsar:
enabled: true
extra:
bastion: no
wsproxy: no
autorecovery:
resources:
requests:
cpu: 0.1
memory: 256Mi
proxy:
replicaCount: 1
resources:
requests:
cpu: 0.1
memory: 256Mi
wsResources:
requests:
memory: 256Mi
cpu: 0.1
configData:
PULSAR_MEM: >
-Xms256m -Xmx256m
PULSAR_GC: >
-XX:MaxDirectMemorySize=512m
httpNumThreads: "50"
bookkeeper:
replicaCount: 2
resources:
requests:
cpu: 0.1
memory: 512Mi
configData:
PULSAR_MEM: >
-Xms512m
-Xmx512m
-XX:MaxDirectMemorySize=1024m
PULSAR_GC: >
-Dio.netty.leakDetectionLevel=disabled
-Dio.netty.recycler.linkCapacity=1024
-XX:+UseG1GC -XX:MaxGCPauseMillis=10
-XX:+ParallelRefProcEnabled
-XX:+UnlockExperimentalVMOptions
-XX:+DoEscapeAnalysis
-XX:ParallelGCThreads=32
-XX:ConcGCThreads=32
-XX:G1NewSizePercent=50
-XX:+DisableExplicitGC
-XX:-ResizePLAB
-XX:+ExitOnOutOfMemoryError
-XX:+PerfDisableSharedMem
-XX:+PrintGCDetails
nettyMaxFrameSizeBytes: "104867840"
zookeeper:
replicaCount: 1
resources:
requests:
cpu: 0.1
memory: 256Mi
configData:
PULSAR_MEM: >
-Xms512m
-Xmx512m
PULSAR_GC: >
-Dcom.sun.management.jmxremote
-Djute.maxbuffer=10485760
-XX:+ParallelRefProcEnabled
-XX:+UnlockExperimentalVMOptions
-XX:+DoEscapeAnalysis
-XX:+DisableExplicitGC
-XX:+PerfDisableSharedMem
-Dzookeeper.forceSync=no
broker:
replicaCount: 1
resources:
requests:
cpu: 0.1
memory: 512Mi
configData:
PULSAR_MEM: >
-Xms512m
-Xmx512m
-XX:MaxDirectMemorySize=1024m
PULSAR_GC: >
-Dio.netty.leakDetectionLevel=disabled
-Dio.netty.recycler.linkCapacity=1024
-XX:+ParallelRefProcEnabled
-XX:+UnlockExperimentalVMOptions
-XX:+DoEscapeAnalysis
-XX:ParallelGCThreads=32
-XX:ConcGCThreads=32
-XX:G1NewSizePercent=50
-XX:+DisableExplicitGC
-XX:-ResizePLAB
-XX:+ExitOnOutOfMemoryError
maxMessageSize: "104857600"
defaultRetentionTimeInMinutes: "10080"
defaultRetentionSizeInMB: "8192"
backlogQuotaDefaultLimitGB: "8"
backlogQuotaDefaultRetentionPolicy: producer_exception
extraConfigFiles:
user.yaml: |+
dataCoord:
compaction:
indexBasedCompaction: false
indexCoord:
scheduler:
interval: 100
+65
View File
@@ -0,0 +1,65 @@
import json
from utils.util_log import test_log as log
all_index_types = ["FLAT", "IVF_FLAT", "IVF_SQ8", "IVF_PQ", "HNSW", "BIN_FLAT", "BIN_IVF_FLAT"]
default_index_params = [{"nlist": 128}, {"nlist": 128}, {"nlist": 128}, {"nlist": 128, "m": 16, "nbits": 8},
{"M": 48, "efConstruction": 500}, {"nlist": 128}, {"nlist": 128}]
index_params_map = dict(zip(all_index_types, default_index_params))
def gen_index_param(index_type):
metric_type = "L2"
if "BIN" in index_type:
metric_type = "HAMMING"
index_param = {
"index_type": index_type,
"params": index_params_map[index_type],
"metric_type": metric_type
}
return index_param
def gen_search_param(index_type, metric_type="L2"):
search_params = []
if index_type in ["FLAT", "IVF_FLAT", "IVF_SQ8", "IVF_PQ"]:
for nprobe in [10]:
ivf_search_params = {"metric_type": metric_type, "params": {"nprobe": nprobe}}
search_params.append(ivf_search_params)
elif index_type in ["BIN_FLAT", "BIN_IVF_FLAT"]:
for nprobe in [10]:
bin_search_params = {"metric_type": "HAMMING", "params": {"nprobe": nprobe}}
search_params.append(bin_search_params)
elif index_type in ["HNSW"]:
for ef in [64]:
hnsw_search_param = {"metric_type": metric_type, "params": {"ef": ef}}
search_params.append(hnsw_search_param)
elif index_type == "ANNOY":
for search_k in [1000]:
annoy_search_param = {"metric_type": metric_type, "params": {"search_k": search_k}}
search_params.append(annoy_search_param)
else:
print("Invalid index_type.")
raise Exception("Invalid index_type.")
return search_params
def get_deploy_test_collections():
try:
with open("/tmp/ci_logs/deploy_test_all_collections.json", "r") as f:
data = json.load(f)
collections = data["all"]
except Exception as e:
log.error(f"get_all_collections error: {e}")
return []
return collections
def get_chaos_test_collections():
try:
with open("/tmp/ci_logs/chaos_test_all_collections.json", "r") as f:
data = json.load(f)
collections = data["all"]
except Exception as e:
log.error(f"get_all_collections error: {e}")
return []
return collections
+22
View File
@@ -0,0 +1,22 @@
import logging
import pytest
import functools
import socket
import common.common_type as ct
import common.common_func as cf
from utils.util_log import test_log as log
from common.common_func import param_info
from check.param_check import ip_check, number_check
from config.log_config import log_config
from utils.util_pymilvus import get_milvus, gen_unique_str, gen_default_fields, gen_binary_default_fields
from pymilvus.orm.types import CONSISTENCY_STRONG
timeout = 60
dimension = 128
delete_timeout = 60
# add a fixture for all index?
+160
View File
@@ -0,0 +1,160 @@
# This is a sample to deploy a milvus cluster using pulsar with minimum cost of resources.
apiVersion: milvus.io/v1beta1
kind: Milvus
metadata:
name: operator-demo
namespace: chaos-testing
labels:
app: milvus
spec:
mode: cluster
config:
dataNode:
memory:
forceSyncEnable: false
rootCoord:
enableActiveStandby: true
dataCoord:
enableActiveStandby: true
queryCoord:
enableActiveStandby: true
indexCoord:
enableActiveStandby: true
# mixCoord:
# enableActiveStandby: true
quotaAndLimits:
enable: false
log:
level: debug
components:
enableRollingUpdate: true
imageUpdateMode: rollingUpgrade
image: harbor.milvus.io/milvus/milvus:master-20240426-4fb8044a-amd64
disableMetric: false
dataNode:
replicas: 3
indexNode:
replicas: 3
queryNode:
replicas: 3
dependencies:
msgStreamType: kafka
etcd:
inCluster:
deletionPolicy: Retain
pvcDeletion: false
values:
replicaCount: 3
kafka:
inCluster:
deletionPolicy: Retain
pvcDeletion: false
values:
replicaCount: 3
defaultReplicationFactor: 2
metrics:
kafka:
enabled: true
serviceMonitor:
enabled: true
jmx:
enabled: true
pulsar:
inCluster:
deletionPolicy: Retain
pvcDeletion: false
values:
components:
autorecovery: false
functions: false
toolset: false
pulsar_manager: false
monitoring:
prometheus: false
grafana: false
node_exporter: false
alert_manager: false
proxy:
replicaCount: 1
resources:
requests:
cpu: 0.01
memory: 256Mi
configData:
PULSAR_MEM: >
-Xms256m -Xmx256m
PULSAR_GC: >
-XX:MaxDirectMemorySize=256m
bookkeeper:
replicaCount: 2
resources:
requests:
cpu: 0.01
memory: 256Mi
configData:
PULSAR_MEM: >
-Xms256m
-Xmx256m
-XX:MaxDirectMemorySize=256m
PULSAR_GC: >
-Dio.netty.leakDetectionLevel=disabled
-Dio.netty.recycler.linkCapacity=1024
-XX:+UseG1GC -XX:MaxGCPauseMillis=10
-XX:+ParallelRefProcEnabled
-XX:+UnlockExperimentalVMOptions
-XX:+DoEscapeAnalysis
-XX:ParallelGCThreads=32
-XX:ConcGCThreads=32
-XX:G1NewSizePercent=50
-XX:+DisableExplicitGC
-XX:-ResizePLAB
-XX:+ExitOnOutOfMemoryError
-XX:+PerfDisableSharedMem
-XX:+PrintGCDetails
zookeeper:
replicaCount: 1
resources:
requests:
cpu: 0.01
memory: 256Mi
configData:
PULSAR_MEM: >
-Xms256m
-Xmx256m
PULSAR_GC: >
-Dcom.sun.management.jmxremote
-Djute.maxbuffer=10485760
-XX:+ParallelRefProcEnabled
-XX:+UnlockExperimentalVMOptions
-XX:+DoEscapeAnalysis -XX:+DisableExplicitGC
-XX:+PerfDisableSharedMem
-Dzookeeper.forceSync=no
broker:
replicaCount: 1
resources:
requests:
cpu: 0.01
memory: 256Mi
configData:
PULSAR_MEM: >
-Xms256m
-Xmx256m
PULSAR_GC: >
-XX:MaxDirectMemorySize=256m
-Dio.netty.leakDetectionLevel=disabled
-Dio.netty.recycler.linkCapacity=1024
-XX:+ParallelRefProcEnabled
-XX:+UnlockExperimentalVMOptions
-XX:+DoEscapeAnalysis
-XX:ParallelGCThreads=32
-XX:ConcGCThreads=32
-XX:G1NewSizePercent=50
-XX:+DisableExplicitGC
-XX:-ResizePLAB
-XX:+ExitOnOutOfMemoryError
storage:
inCluster:
deletionPolicy: Retain
pvcDeletion: false
values:
mode: distributed
@@ -0,0 +1,27 @@
import argparse
import subprocess
import time
from loguru import logger as log
def run_kubectl_get_pod(duration, interval, release_name):
end_time = time.time() + duration
while time.time() < end_time:
cmd = f"kubectl get pod |grep {release_name}"
res = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = res.communicate()
output = stdout.decode("utf-8")
log.info(f"{cmd}\n{output}\n")
time.sleep(interval)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Script to run "kubectl get pod" command at regular intervals')
parser.add_argument('-d', '--duration', type=int, default=600, help='Duration in seconds (default: 600)')
parser.add_argument('-i', '--interval', type=int, default=5, help='Interval in seconds (default: 30)')
parser.add_argument('-n', '--release_name', type=str, default="", help='release name (default: "None")')
args = parser.parse_args()
run_kubectl_get_pod(args.duration, args.interval, args.release_name)
@@ -0,0 +1,10 @@
--extra-index-url https://test.pypi.org/simple/
docker==5.0.0
grpcio==1.53.2
grpcio-tools==1.37.1
pymilvus==2.0.0rc8
# for test result anaylszer
prettytable==3.8.0
pyarrow==14.0.1
fastparquet==2023.7.0
+29
View File
@@ -0,0 +1,29 @@
#!/bin/bash
set -x
func() {
echo "Usage:"
echo "run.sh [-p Password]"
echo "Password, the password of root"
exit -1
}
while getopts "hp:" OPT;
do
case $OPT in
p) Password="$OPTARG";;
h) func;;
?) func;;
esac
done
pw=$Password
# start test standalone reinstall
bash test.sh -m standalone -t reinstall -p $pw
# start test standalone upgrade
bash test.sh -m standalone -t upgrade -p $pw
# start test cluster reinstall
bash test.sh -m cluster -t reinstall -p $pw
# start test cluster upgrade
bash test.sh -m cluster -t upgrade -p $pw
@@ -0,0 +1,47 @@
from pymilvus import connections
from utils import *
def task_1(data_size, host):
"""
task_1:
before reinstall: create collection, insert data, create index and insert data, load and search
after reinstall: get collection, load, search, release, insert data, create index, load, and search
"""
prefix = "task_1_"
connections.connect(host=host, port=19530, timeout=60)
get_collections(prefix)
load_and_search(prefix)
release_collection(prefix)
create_collections_and_insert_data(prefix,count=data_size)
load_and_search(prefix)
def task_2(data_zise, host):
"""
task_2:
before reinstall: create collection, insert data and create index, load and search
after reinstall: get collection, load, search, insert data, release, create index, load, and search
"""
prefix = "task_2_"
connections.connect(host=host, port=19530, timeout=60)
get_collections(prefix)
load_and_search(prefix)
create_collections_and_insert_data(prefix, count=data_size)
release_collection(prefix)
create_index(prefix)
load_and_search(prefix)
if __name__ == '__main__':
import argparse
import threading
parser = argparse.ArgumentParser(description='config for deploy test')
parser.add_argument('--host', type=str, default="127.0.0.1", help='milvus server ip')
parser.add_argument('--data_size', type=int, default=3000, help='data size')
args = parser.parse_args()
host = args.host
data_size = args.data_size
logger.info(f"data size: {data_size}")
task_1(data_size, host)
task_2(data_size, host)
@@ -0,0 +1,114 @@
from pymilvus import connections
import sys
sys.path.append("..")
sys.path.append("../..")
from common.milvus_sys import MilvusSys
from utils import *
def task_1(data_size, host):
"""
task_1:
before upgrade: create collection and insert data with flush, create index, load and search
after upgrade: get collection, load, search, insert data with flush, release, create index, load, and search
"""
prefix = "task_1_"
connections.connect(host=host, port=19530, timeout=60)
col_list = get_collections(prefix, check=True)
assert len(col_list) > 0
create_index(prefix)
load_and_search(prefix)
create_collections_and_insert_data(prefix, count=data_size)
release_collection(prefix)
create_index(prefix)
load_and_search(prefix)
def task_2(data_size, host):
"""
task_2:
before upgrade: create collection, insert data and create index, load and search
after upgrade: get collection, load, search, insert data, release, create index, load, and search
"""
prefix = "task_2_"
connections.connect(host=host, port=19530, timeout=60)
col_list = get_collections(prefix, check=True)
assert len(col_list) > 0
load_and_search(prefix)
create_collections_and_insert_data(prefix, count=data_size)
release_collection(prefix)
create_index(prefix)
load_and_search(prefix)
def task_3(data_size, host):
"""
task_3:
before upgrade: create collection, insert data, flush, create index, load with one replicas and search
after upgrade: get collection, load, search, insert data, release, create index, load with multi replicas, and search
"""
prefix = "task_3_"
connections.connect(host=host, port=19530, timeout=60)
col_list = get_collections(prefix, check=True)
assert len(col_list) > 0
load_and_search(prefix)
create_collections_and_insert_data(prefix, count=data_size)
release_collection(prefix)
create_index(prefix)
load_and_search(prefix, replicas=NUM_REPLICAS)
def task_4(data_size, host):
"""
task_4:
before upgrade: create collection, insert data, flush, and create index
after upgrade: get collection, load with multi replicas, search, insert data, load with multi replicas and search
"""
prefix = "task_4_"
connections.connect(host=host, port=19530, timeout=60)
col_list = get_collections(prefix, check=True)
assert len(col_list) > 0
load_and_search(prefix, replicas=NUM_REPLICAS)
create_collections_and_insert_data(prefix, flush=False, count=data_size)
load_and_search(prefix, replicas=NUM_REPLICAS)
def task_5(data_size, host):
"""
task_5_:
before upgrade: create collection and insert data without flush
after upgrade: get collection, create index, load with multi replicas, search, insert data with flush, load with multi replicas and search
"""
prefix = "task_5_"
connections.connect(host=host, port=19530, timeout=60)
col_list = get_collections(prefix, check=True)
assert len(col_list) > 0
create_index(prefix)
load_and_search(prefix, replicas=NUM_REPLICAS)
create_collections_and_insert_data(prefix, flush=True, count=data_size)
load_and_search(prefix, replicas=NUM_REPLICAS)
if __name__ == '__main__':
import argparse
import threading
parser = argparse.ArgumentParser(description='config for deploy test')
parser.add_argument('--host', type=str, default="127.0.0.1", help='milvus server ip')
parser.add_argument('--data_size', type=int, default=3000, help='data size')
args = parser.parse_args()
data_size = args.data_size
host = args.host
logger.info(f"data size: {data_size}")
connections.connect(host=host, port=19530, timeout=60)
ms = MilvusSys()
# create index for flat
logger.info("create index for flat start")
create_index_flat()
logger.info("create index for flat done")
task_1(data_size, host)
task_2(data_size, host)
if len(ms.query_nodes) >= NUM_REPLICAS:
task_3(data_size, host)
task_4(data_size, host)
task_5(data_size, host)
@@ -0,0 +1,48 @@
from pymilvus import connections
from utils import *
def task_1(data_size, host):
"""
task_1:
before reinstall: create collection, insert data, create index and insert data, load and search
after reinstall: get collection, load, search, release, insert data, create index, load, and search
"""
prefix = "task_1_"
connections.connect(host=host, port=19530, timeout=60)
get_collections(prefix)
create_collections_and_insert_data(prefix,count=data_size)
create_index(prefix)
load_and_search(prefix)
create_collections_and_insert_data(prefix,count=data_size)
load_and_search(prefix)
def task_2(data_size, host):
"""
task_2:
before reinstall: create collection, insert data, create index, insert data, create index,load and search
after reinstall: get collection, load, search, insert data, create index, load, and search
"""
prefix = "task_2_"
connections.connect(host=host, port=19530, timeout=60)
get_collections(prefix)
create_collections_and_insert_data(prefix, count=data_size)
create_index(prefix)
create_collections_and_insert_data(prefix, count=data_size)
create_index(prefix)
load_and_search(prefix)
if __name__ == '__main__':
import argparse
import threading
parser = argparse.ArgumentParser(description='config for deploy test')
parser.add_argument('--host', type=str, default="127.0.0.1", help='milvus server ip')
parser.add_argument('--data_size', type=int, default=3000, help='data size')
args = parser.parse_args()
data_size = args.data_size
host = args.host
logger.info(f"data_size: {data_size}")
task_1(data_size, host)
task_2(data_size, host)
@@ -0,0 +1,91 @@
from pymilvus import connections
import sys
sys.path.append("..")
sys.path.append("../..")
from common.milvus_sys import MilvusSys
from utils import *
def task_1(data_size, host):
"""
task_1:
before upgrade: create collection and insert data with flush, create index, load and search
after upgrade: get collection, load, search, insert data with flush, release, create index, load, and search
"""
prefix = "task_1_"
connections.connect(host=host, port=19530, timeout=60)
get_collections(prefix)
create_collections_and_insert_data(prefix, count=data_size)
create_index(prefix)
load_and_search(prefix)
def task_2(data_size, host):
"""
task_2:
before upgrade: create collection, insert data and create index, load , search, and insert data without flush
after upgrade: get collection, load, search, insert data, release, create index, load, and search
"""
prefix = "task_2_"
connections.connect(host=host, port=19530, timeout=60)
get_collections(prefix)
create_collections_and_insert_data(prefix, count=data_size)
create_index(prefix)
load_and_search(prefix)
create_collections_and_insert_data(prefix, flush=False, count=data_size)
def task_3(data_size, host):
"""
task_3:
before upgrade: create collection, insert data, flush, create index, load with one replicas and search
after upgrade: get collection, load, search, insert data, create index, release, load with multi replicas, and search
"""
prefix = "task_3_"
connections.connect(host=host, port=19530, timeout=60)
get_collections(prefix)
create_collections_and_insert_data(prefix, count=data_size)
create_index(prefix)
load_and_search(prefix)
def task_4(data_size, host):
"""
task_4_:
before upgrade: create collection, insert data, flush, and create index
after upgrade: get collection, load with multi replicas, search, insert data, load with multi replicas and search
"""
prefix = "task_4_"
connections.connect(host=host, port=19530, timeout=60)
get_collections(prefix)
create_collections_and_insert_data(prefix, flush=True, count=data_size)
create_index(prefix)
def task_5(data_size, host):
"""
task_5_:
before upgrade: create collection and insert data without flush
after upgrade: get collection, create index, load with multi replicas, search, insert data with flush, load with multi replicas and search
"""
prefix = "task_5_"
connections.connect(host=host, port=19530, timeout=60)
get_collections(prefix)
create_collections_and_insert_data(prefix, flush=False, count=data_size)
if __name__ == '__main__':
import argparse
import threading
parser = argparse.ArgumentParser(description='config for deploy test')
parser.add_argument('--host', type=str, default="127.0.0.1", help='milvus server ip')
parser.add_argument('--data_size', type=int, default=3000, help='data size')
args = parser.parse_args()
data_size = args.data_size
host = args.host
logger.info(f"data size: {data_size}")
connections.connect(host=host, port=19530, timeout=60)
ms = MilvusSys()
task_1(data_size, host)
task_2(data_size, host)
if len(ms.query_nodes) >= NUM_REPLICAS:
task_3(data_size, host)
task_4(data_size, host)
task_5(data_size, host)
@@ -0,0 +1,53 @@
import psutil
import time
from loguru import logger
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser(description='config for rolling update process')
parser.add_argument('--wait_time', type=int, default=60, help='wait time after rolling update started')
args = parser.parse_args()
wait_time = args.wait_time
logger.info("start to watch rolling update process")
start_time = time.time()
end_time = time.time()
flag = False
while not flag and end_time - start_time < 360:
process_list = [p.info for p in psutil.process_iter(attrs=['pid', 'name','cmdline'])]
for process in process_list:
logger.debug(process)
logger.debug("##"*30)
for process in process_list:
if isinstance(process.get("cmdline", []), list):
cmdline_list = process.get("cmdline", [])
for cmdline in cmdline_list:
if "rollingUpdate.sh" in cmdline:
logger.info(f"rolling update process: {process} started")
flag = True
break
if flag:
break
time.sleep(0.5)
end_time = time.time()
if not flag:
logger.info(f"rolling update process not found, wait for {end_time - start_time} seconds")
else:
logger.info(f"rolling update process {process} found, wait for {end_time - start_time} seconds")
if flag:
logger.info(f"wait {wait_time}s to kill rolling update process")
time.sleep(wait_time)
logger.info("start to kill rolling update process")
try:
p = psutil.Process(process["pid"])
p.terminate()
logger.info(f"rolling update process: {process} killed")
except Exception as e:
logger.error(f"rolling update process: {process} kill failed, {e}")
else:
logger.info("all process info")
for process in process_list:
logger.info(process)
@@ -0,0 +1,201 @@
import threading
import h5py
import numpy as np
import time
import sys
import copy
from pathlib import Path
from loguru import logger
import pymilvus
from pymilvus import (
connections,
FieldSchema, CollectionSchema, DataType,
Collection, utility
)
pymilvus_version = pymilvus.__version__
all_index_types = ["IVF_FLAT", "IVF_SQ8", "HNSW"]
default_index_params = [{"nlist": 128}, {"nlist": 128}, {"M": 48, "efConstruction": 200}]
index_params_map = dict(zip(all_index_types, default_index_params))
def gen_index_params(index_type, metric_type="L2"):
default_index = {"index_type": "IVF_FLAT", "params": {"nlist": 128}, "metric_type": metric_type}
index = copy.deepcopy(default_index)
index["index_type"] = index_type
index["params"] = index_params_map[index_type]
if index_type in ["BIN_FLAT", "BIN_IVF_FLAT"]:
index["metric_type"] = "HAMMING"
return index
def gen_search_param(index_type, metric_type="L2"):
search_params = []
if index_type in ["FLAT", "IVF_FLAT", "IVF_SQ8", "IVF_PQ"]:
for nprobe in [10]:
ivf_search_params = {"metric_type": metric_type, "params": {"nprobe": nprobe}}
search_params.append(ivf_search_params)
elif index_type in ["BIN_FLAT", "BIN_IVF_FLAT"]:
for nprobe in [10]:
bin_search_params = {"metric_type": "HAMMING", "params": {"nprobe": nprobe}}
search_params.append(bin_search_params)
elif index_type in ["HNSW"]:
for ef in [150]:
hnsw_search_param = {"metric_type": metric_type, "params": {"ef": ef}}
search_params.append(hnsw_search_param)
elif index_type == "ANNOY":
for search_k in [1000]:
annoy_search_param = {"metric_type": metric_type, "params": {"search_k": search_k}}
search_params.append(annoy_search_param)
else:
logger.info("Invalid index_type.")
raise Exception("Invalid index_type.")
return search_params[0]
def read_benchmark_hdf5(file_path):
f = h5py.File(file_path, 'r')
train = np.array(f["train"])
test = np.array(f["test"])
neighbors = np.array(f["neighbors"])
f.close()
return train, test, neighbors
dim = 128
TIMEOUT = 200
def milvus_recall_test(host='127.0.0.1', index_type="HNSW"):
logger.info(f"recall test for index type {index_type}")
file_path = f"{str(Path(__file__).absolute().parent.parent.parent)}/assets/ann_hdf5/sift-128-euclidean.hdf5"
train, test, neighbors = read_benchmark_hdf5(file_path)
connections.connect(host=host, port="19530")
default_fields = [
FieldSchema(name="int64", dtype=DataType.INT64, is_primary=True),
FieldSchema(name="float", dtype=DataType.FLOAT),
FieldSchema(name="varchar", dtype=DataType.VARCHAR, max_length=65535),
FieldSchema(name="float_vector", dtype=DataType.FLOAT_VECTOR, dim=dim)
]
default_schema = CollectionSchema(
fields=default_fields, description="test collection")
name = f"sift_128_euclidean_{index_type}"
logger.info(f"Create collection {name}")
collection = Collection(name=name, schema=default_schema)
nb = len(train)
batch_size = 50000
epoch = int(nb / batch_size)
t0 = time.time()
for i in range(epoch):
logger.info(f"epoch: {i}")
start = i * batch_size
end = (i + 1) * batch_size
if end > nb:
end = nb
data = [
[i for i in range(start, end)],
[np.float32(i) for i in range(start, end)],
[str(i) for i in range(start, end)],
train[start:end]
]
collection.insert(data)
t1 = time.time()
logger.info(f"Insert {nb} vectors cost {t1 - t0:.4f} seconds")
t0 = time.time()
logger.info(f"Get collection entities...")
if pymilvus_version >= "2.2.0":
collection.flush()
else:
collection.num_entities
logger.info(collection.num_entities)
t1 = time.time()
logger.info(f"Get collection entities cost {t1 - t0:.4f} seconds")
# create index
default_index = gen_index_params(index_type)
logger.info(f"Create index...")
t0 = time.time()
collection.create_index(field_name="float_vector",
index_params=default_index)
t1 = time.time()
logger.info(f"Create index cost {t1 - t0:.4f} seconds")
# load collection
replica_number = 1
logger.info(f"load collection...")
t0 = time.time()
collection.load(replica_number=replica_number)
t1 = time.time()
logger.info(f"load collection cost {t1 - t0:.4f} seconds")
res = utility.get_query_segment_info(name)
cnt = 0
logger.info(f"segments info: {res}")
for segment in res:
cnt += segment.num_rows
assert cnt == collection.num_entities
logger.info(f"wait for loading complete...")
time.sleep(30)
res = utility.get_query_segment_info(name)
logger.info(f"segments info: {res}")
# search
topK = 100
nq = 10000
current_search_params = gen_search_param(index_type)
# define output_fields of search result
for i in range(3):
t0 = time.time()
logger.info(f"Search...")
res = collection.search(
test[:nq], "float_vector", current_search_params, topK, output_fields=["int64"], timeout=TIMEOUT
)
t1 = time.time()
logger.info(f"search cost {t1 - t0:.4f} seconds")
result_ids = []
for hits in res:
result_id = []
for hit in hits:
result_id.append(hit.entity.get("int64"))
result_ids.append(result_id)
# calculate recall
true_ids = neighbors[:nq, :topK]
sum_radio = 0.0
logger.info(f"Calculate recall...")
for index, item in enumerate(result_ids):
# tmp = set(item).intersection(set(flat_id_list[index]))
assert len(item) == len(true_ids[index])
tmp = set(true_ids[index]).intersection(set(item))
sum_radio = sum_radio + len(tmp) / len(item)
recall = round(sum_radio / len(result_ids), 6)
logger.info(f"recall={recall}")
if index_type in ["IVF_PQ", "ANNOY"]:
assert recall >= 0.6, f"recall={recall} < 0.6"
else:
assert 0.95 <= recall < 1.0, f"recall is {recall}, less than 0.95, greater than or equal to 1.0"
# query
expr = "int64 in [2,4,6,8]"
output_fields = ["int64", "float"]
res = collection.query(expr, output_fields, timeout=TIMEOUT)
sorted_res = sorted(res, key=lambda k: k['int64'])
for r in sorted_res:
logger.info(r)
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description='config for recall test')
parser.add_argument('--host', type=str,
default="127.0.0.1", help='milvus server ip')
args = parser.parse_args()
host = args.host
tasks = []
for index_type in ["HNSW"]:
milvus_recall_test(host, index_type)
@@ -0,0 +1,33 @@
import requests
import json
milvus_dev = "https://registry.hub.docker.com/v2/repositories/milvusdb/milvus/tags?ordering=last_updated"
milvus = "https://registry.hub.docker.com/v2/repositories/milvusdb/milvus/tags?ordering=last_updated"
def get_tag(url):
payload = {}
headers = {}
response = requests.request("GET", url, headers=headers, data=payload)
res = response.json()["results"]
sorted_r = sorted(res, key=lambda k: k['last_updated'])
tags = [r["name"] for r in sorted_r]
return tags
latest_tag = [tag for tag in get_tag(milvus_dev) if "latest" not in tag][-1]
latest_rc_tag = [tag for tag in get_tag(milvus) if "v" in tag][-1]
# release_version = "-".join(latest_rc_tag.split("-")[:-2])
# print(release_version)
print(latest_tag, latest_rc_tag)
data = {
"latest_tag": latest_tag,
"latest_rc_tag": latest_rc_tag,
# "release_version": release_version
}
print(data)
with open("tag_info.json", "w") as f:
f.write(json.dumps(data))
@@ -0,0 +1,103 @@
import h5py
import numpy as np
import time
import sys
import threading
from pathlib import Path
from loguru import logger
from pymilvus import connections, Collection
all_index_types = ["IVF_FLAT", "IVF_SQ8", "HNSW"]
def read_benchmark_hdf5(file_path):
f = h5py.File(file_path, 'r')
train = np.array(f["train"])
test = np.array(f["test"])
neighbors = np.array(f["neighbors"])
f.close()
return train, test, neighbors
def gen_search_param(index_type, metric_type="L2"):
search_params = []
if index_type in ["FLAT", "IVF_FLAT", "IVF_SQ8", "IVF_PQ"]:
for nprobe in [10]:
ivf_search_params = {"metric_type": metric_type, "params": {"nprobe": nprobe}}
search_params.append(ivf_search_params)
elif index_type in ["BIN_FLAT", "BIN_IVF_FLAT"]:
for nprobe in [10]:
bin_search_params = {"metric_type": "HAMMING", "params": {"nprobe": nprobe}}
search_params.append(bin_search_params)
elif index_type in ["HNSW"]:
for ef in [150]:
hnsw_search_param = {"metric_type": metric_type, "params": {"ef": ef}}
search_params.append(hnsw_search_param)
elif index_type == "ANNOY":
for search_k in [1000]:
annoy_search_param = {"metric_type": metric_type, "params": {"search_k": search_k}}
search_params.append(annoy_search_param)
else:
logger.info("Invalid index_type.")
raise Exception("Invalid index_type.")
return search_params[0]
dim = 128
TIMEOUT = 200
def search_test(host="127.0.0.1", index_type="HNSW"):
logger.info(f"recall test for index type {index_type}")
file_path = f"{str(Path(__file__).absolute().parent.parent.parent)}/assets/ann_hdf5/sift-128-euclidean.hdf5"
train, test, neighbors = read_benchmark_hdf5(file_path)
connections.connect(host=host, port="19530")
collection = Collection(name=f"sift_128_euclidean_{index_type}")
nq = 10000
topK = 100
search_params = gen_search_param(index_type)
for i in range(3):
t0 = time.time()
logger.info(f"\nSearch...")
# define output_fields of search result
res = collection.search(
test[:nq], "float_vector", search_params, topK, output_fields=["int64"], timeout=TIMEOUT
)
t1 = time.time()
logger.info(f"search cost {t1 - t0:.4f} seconds")
result_ids = []
for hits in res:
result_id = []
for hit in hits:
result_id.append(hit.entity.get("int64"))
result_ids.append(result_id)
# calculate recall
true_ids = neighbors[:nq, :topK]
sum_radio = 0.0
for index, item in enumerate(result_ids):
# tmp = set(item).intersection(set(flat_id_list[index]))
assert len(item) == len(true_ids[index]), f"get {len(item)} but expect {len(true_ids[index])}"
tmp = set(true_ids[index]).intersection(set(item))
sum_radio = sum_radio + len(tmp) / len(item)
recall = round(sum_radio / len(result_ids), 6)
logger.info(f"recall={recall}")
if index_type in ["IVF_PQ", "ANNOY"]:
assert recall >= 0.6, f"recall={recall} < 0.6"
else:
assert 0.95 <= recall < 1.0, f"recall is {recall}, less than 0.95, greater than or equal to 1.0"
if __name__ == "__main__":
import argparse
import threading
parser = argparse.ArgumentParser(description='config for recall test')
parser.add_argument('--host', type=str, default="127.0.0.1", help='milvus server ip')
args = parser.parse_args()
host = args.host
tasks = []
for index_type in ["HNSW"]:
search_test(host, index_type)
+268
View File
@@ -0,0 +1,268 @@
import sys
import copy
import time
from loguru import logger
import pymilvus
from pymilvus import (
FieldSchema, CollectionSchema, DataType,
Collection, list_collections,
)
logger.remove()
logger.add(sys.stderr, format= "<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | "
"<level>{level: <8}</level> | "
"<cyan>{thread.name}</cyan> |"
"<cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>",
level="INFO")
pymilvus_version = pymilvus.__version__
all_index_types = ["FLAT", "IVF_FLAT", "IVF_SQ8", "IVF_PQ", "HNSW"]
default_index_params = [{}, {"nlist": 128}, {"nlist": 128}, {"nlist": 128, "m": 16, "nbits": 8},
{"M": 48, "efConstruction": 500}]
index_params_map = dict(zip(all_index_types, default_index_params))
NUM_REPLICAS = 2
def filter_collections_by_prefix(prefix):
col_list = list_collections()
logger.info(f"all collections: {col_list}")
res = []
for col in col_list:
if col.startswith(prefix):
if any(index_name in col for index_name in all_index_types):
res.append(col)
else:
logger.warning(f"collection {col} has no supported index, skip")
logger.info(f"filtered collections with prefix {prefix}: {res}")
return res
def gen_search_param(index_type, metric_type="L2"):
search_params = []
if index_type in ["FLAT", "IVF_FLAT", "IVF_SQ8", "IVF_PQ"]:
for nprobe in [10]:
ivf_search_params = {"metric_type": metric_type, "params": {"nprobe": nprobe}}
search_params.append(ivf_search_params)
elif index_type in ["BIN_FLAT", "BIN_IVF_FLAT"]:
for nprobe in [10]:
bin_search_params = {"metric_type": "HAMMING", "params": {"nprobe": nprobe}}
search_params.append(bin_search_params)
elif index_type in ["HNSW"]:
for ef in [64]:
hnsw_search_param = {"metric_type": metric_type, "params": {"ef": ef}}
search_params.append(hnsw_search_param)
elif index_type == "ANNOY":
for search_k in [1000]:
annoy_search_param = {"metric_type": metric_type, "params": {"search_k": search_k}}
search_params.append(annoy_search_param)
else:
logger.info("Invalid index_type.")
raise Exception("Invalid index_type.")
return search_params
def get_collections(prefix, check=False):
logger.info("\nList collections...")
col_list = filter_collections_by_prefix(prefix)
logger.info(f"collections_nums: {len(col_list)}")
# list entities if collections
for name in col_list:
c = Collection(name=name)
if pymilvus_version >= "2.2.0":
c.flush()
else:
c.num_entities
num_entities = c.num_entities
logger.info(f"{name}: {num_entities}")
if check:
assert num_entities >= 3000
return col_list
def create_collections_and_insert_data(prefix, flush=True, count=3000, collection_cnt=11):
import random
dim = 128
nb = count // 10
default_fields = [
FieldSchema(name="count", dtype=DataType.INT64, is_primary=True),
FieldSchema(name="random_value", dtype=DataType.DOUBLE),
FieldSchema(name="float_vector", dtype=DataType.FLOAT_VECTOR, dim=dim)
]
default_schema = CollectionSchema(fields=default_fields, description="test collection")
for index_name in all_index_types[:collection_cnt]:
logger.info("\nCreate collection...")
col_name = prefix + index_name
collection = Collection(name=col_name, schema=default_schema)
logger.info(f"collection name: {col_name}")
logger.info(f"begin insert, count: {count} nb: {nb}")
times = int(count // nb)
total_time = 0.0
vectors = [[random.random() for _ in range(dim)] for _ in range(count)]
for j in range(times):
start_time = time.time()
collection.insert(
[
[i for i in range(nb * j, nb * j + nb)],
[float(random.randrange(-20, -10)) for _ in range(nb)],
vectors[nb*j:nb*j+nb]
]
)
end_time = time.time()
logger.info(f"[{j+1}/{times}] insert {nb} data, time: {end_time - start_time:.4f}")
total_time += end_time - start_time
if j <= times - 3:
collection.flush()
collection.num_entities
if j == times - 3:
collection.compact()
logger.info(f"end insert, time: {total_time:.4f}")
if flush:
logger.info("Get collection entities")
start_time = time.time()
if pymilvus_version >= "2.2.0":
collection.flush()
else:
collection.num_entities
logger.info(f"collection entities: {collection.num_entities}")
end_time = time.time()
logger.info("Get collection entities time = %.4fs" % (end_time - start_time))
logger.info("\nList collections...")
logger.info(get_collections(prefix))
def create_index_flat():
# create index
default_flat_index = {"index_type": "FLAT", "params": {}, "metric_type": "L2"}
all_col_list = list_collections()
col_list = []
for col_name in all_col_list:
if "FLAT" in col_name and "task" in col_name and "IVF" not in col_name:
col_list.append(col_name)
logger.info("\nCreate index for FLAT...")
for col_name in col_list:
c = Collection(name=col_name)
logger.info(c)
try:
replicas = c.get_replicas()
replica_number = len(replicas.groups)
c.release()
except Exception as e:
replica_number = 0
logger.info(e)
t0 = time.time()
c.create_index(field_name="float_vector", index_params=default_flat_index)
logger.info(f"create index time: {time.time() - t0:.4f}")
if replica_number > 0:
c.load(replica_number=replica_number)
def create_index(prefix):
# create index
default_index = {"index_type": "IVF_FLAT", "params": {"nlist": 128}, "metric_type": "L2"}
col_list = get_collections(prefix)
logger.info("\nCreate index...")
for col_name in col_list:
c = Collection(name=col_name)
try:
replicas = c.get_replicas()
replica_number = len(replicas.groups)
c.release()
except Exception as e:
replica_number = 0
logger.info(e)
index_name = col_name.replace(prefix, "")
logger.info(index_name)
logger.info(c)
index = copy.deepcopy(default_index)
index["index_type"] = index_name
index["params"] = index_params_map[index_name]
if index_name in ["BIN_FLAT", "BIN_IVF_FLAT"]:
index["metric_type"] = "HAMMING"
index_info_list = [x.to_dict() for x in c.indexes]
logger.info(index_info_list)
is_indexed = False
for index_info in index_info_list:
if "metric_type" in index_info.keys() or "metric_type" in index_info["index_param"]:
is_indexed = True
logger.info(f"collection {col_name} has been indexed with {index_info}")
if not is_indexed:
t0 = time.time()
c.create_index(field_name="float_vector", index_params=index)
logger.info(f"create index time: {time.time() - t0:.4f}")
if replica_number > 0:
c.load(replica_number=replica_number)
def release_collection(prefix):
col_list = get_collections(prefix)
logger.info("release collection")
for col_name in col_list:
c = Collection(name=col_name)
c.release()
def load_and_search(prefix, replicas=1):
logger.info("search data starts")
col_list = get_collections(prefix)
for col_name in col_list:
c = Collection(name=col_name)
logger.info(f"collection name: {col_name}")
logger.info("load collection")
if replicas == 1:
t0 = time.time()
c.load()
logger.info(f"load time: {time.time() - t0:.4f}")
if replicas > 1:
logger.info("release collection before load if replicas > 1")
t0 = time.time()
c.release()
logger.info(f"release time: {time.time() - t0:.4f}")
t0 = time.time()
c.load(replica_number=replicas)
logger.info(f"load time: {time.time() - t0:.4f}")
logger.info(c.get_replicas())
topK = 5
vectors = [[1.0 for _ in range(128)] for _ in range(3000)]
index_name = col_name.replace(prefix, "")
search_params = gen_search_param(index_name)[0]
logger.info(search_params)
# search_params = {"metric_type": "L2", "params": {"nprobe": 10}}
start_time = time.time()
logger.info(f"\nSearch...")
# define output_fields of search result
v_search = vectors[:1]
res = c.search(
v_search, "float_vector", search_params, topK,
"count > 500", output_fields=["count", "random_value"], timeout=120
)
end_time = time.time()
# show result
for hits in res:
for hit in hits:
logger.info(f"hit: {hit}")
ids = hits.ids
assert len(ids) == topK, f"get {len(ids)} results, but topK is {topK}"
logger.info(ids)
assert len(res) == len(v_search), f"get {len(res)} results, but search num is {len(v_search)}"
logger.info("search latency: %.4fs" % (end_time - start_time))
t0 = time.time()
expr = "count in [2,4,6,8]"
if "SQ" in col_name or "PQ" in col_name:
output_fields = ["count", "random_value"]
else:
output_fields = ["count", "random_value", "float_vector"]
res = c.query(expr, output_fields, timeout=120)
sorted_res = sorted(res, key=lambda k: k['count'])
for r in sorted_res:
logger.info(r)
t1 = time.time()
assert len(res) == 4
logger.info("query latency: %.4fs" % (t1 - t0))
# c.release()
logger.info("###########")
logger.info("search data ends")
@@ -0,0 +1,43 @@
cluster:
enabled: false
log:
level: debug
image:
all:
repository: milvusdb/milvus
tag: master-latest
pullPolicy: IfNotPresent
standalone:
resources:
limits:
cpu: 8
memory: 16Gi
requests:
cpu: 4
memory: 8Gi
kafka:
enabled: false
name: kafka
replicaCount: 3
defaultReplicationFactor: 2
etcd:
replicaCount: 3
image:
repository: milvusdb/etcd
tag: 3.5.5-r2
minio:
mode: standalone
pulsar:
enabled: false
extraConfigFiles:
user.yaml: |+
dataCoord:
compaction:
indexBasedCompaction: false
indexCoord:
scheduler:
interval: 100
+242
View File
@@ -0,0 +1,242 @@
#!/bin/bash
set -e
# set -x
func() {
echo "Usage:"
echo "test.sh [-t Task] [-m Mode] [-r Release] [-p Password]"
echo "Description"
echo "Task, the task type of test. reinstall or upgrade"
echo "Mode, the mode of milvus deploy. standalone or cluster"
echo "Release, the release of milvus. e.g. 2.0.0-rc8"
echo "Password, the password of root"
exit -1
}
echo "check os env"
platform='unknown'
unamestr=$(uname)
if [[ "$unamestr" == 'Linux' ]]; then
platform='Linux'
elif [[ "$unamestr" == 'Darwin' ]]; then
platform='Mac'
fi
echo "platform: $platform"
Task="reinstall"
Mode="standalone"
Release="v2.0.0"
while getopts "hm:t:p:" OPT;
do
case $OPT in
m) Mode="$OPTARG";;
t) Task="$OPTARG";;
p) Password="$OPTARG";;
h) func;;
?) func;;
esac
done
ROOT_FOLDER=$(cd "$(dirname "$0")";pwd)
# to export docker compose logs before exit
function error_exit {
pushd ${ROOT_FOLDER}/${Deploy_Dir}
echo "test failed"
current=$(date "+%Y-%m-%d-%H-%M-%S")
if [ ! -d logs ];
then
mkdir logs
fi
docker compose ps
docker compose logs > ./logs/${Deploy_Dir}-${Task}-${current}.log 2>&1
echo "log saved to $(pwd)/logs/${Deploy_Dir}-${Task}-${current}.log"
popd
exit 1
}
function replace_image_tag {
image_repo=$1
image_tag=$2
if [ "$platform" == "Mac" ];
then
# for mac os
sed -i "" "s/milvusdb\/milvus.*/${image_repo}\:${image_tag}/g" docker-compose.yml
else
#for linux os
sed -i "s/milvusdb\/milvus.*/${image_repo}\:${image_tag}/g" docker-compose.yml
fi
}
#to check containers all running and minio is healthy
function check_healthy {
cnt=$(docker compose ps | grep -E "running|Running|Up|up" | wc -l)
healthy=$(docker compose ps | grep "healthy" | wc -l)
time_cnt=0
echo "running num $cnt expect num $Expect"
echo "healthy num $healthy expect num $Expect_health"
while [[ $cnt -ne $Expect || $healthy -ne 1 ]];
do
printf "waiting all containers get running\n"
sleep 5
let time_cnt+=5
# if time is greater than 300s, the condition still not satisfied, we regard it as a failure
if [ $time_cnt -gt 300 ];
then
printf "timeout,there are some issue with deployment!"
error_exit
fi
cnt=$(docker compose ps | grep -E "running|Running|Up|up" | wc -l)
healthy=$(docker compose ps | grep "healthy" | wc -l)
echo "running num $cnt expect num $Expect"
echo "healthy num $healthy expect num $Expect_health"
done
}
Deploy_Dir=$Mode
Task=$Task
Release=$Release
pw=$Password
echo "mode: $Mode"
echo "task: $Task"
echo "password: $pw"
## if needed, install dependency
#echo "install dependency"
#pip install -r scripts/requirements.txt
if [ ! -d ${Deploy_Dir} ];
then
mkdir ${Deploy_Dir}
fi
echo "get tag info"
python scripts/get_tag.py
latest_tag=$(jq -r ".latest_tag" tag_info.json)
latest_rc_tag=$(jq -r ".latest_rc_tag" tag_info.json)
# release_version="v${Release}"
echo $latest_rc_tag
pushd ${Deploy_Dir}
# download docker-compose.yml
wget https://github.com/milvus-io/milvus/releases/download/${latest_rc_tag}/milvus-${Mode}-docker-compose.yml -O docker-compose.yml
ls
# clean env to deploy a fresh milvus
docker compose down
docker compose ps
echo "$pw"| sudo -S rm -rf ./volumes
# first deployment
if [ "$Task" == "reinstall" ];
then
printf "download latest milvus docker compose yaml file from github\n"
wget https://raw.githubusercontent.com/milvus-io/milvus/master/deployments/docker/${Mode}/docker-compose.yml -O docker-compose.yml
printf "start to deploy latest rc tag milvus\n"
replace_image_tag "milvusdb\/milvus" $latest_tag
fi
if [ "$Task" == "upgrade" ];
then
printf "start to deploy previous rc tag milvus\n"
replace_image_tag "milvusdb\/milvus" $latest_rc_tag # replace previous rc tag
fi
cat docker-compose.yml|grep milvusdb
Expect=$(grep "container_name" docker-compose.yml | wc -l)
Expect_health=$(grep "healthcheck" docker-compose.yml | wc -l)
docker compose up -d
check_healthy
docker compose ps
popd
# test for first deployment
printf "test for first deployment\n"
if [ "$Task" == "reinstall" ];
then
python scripts/action_before_reinstall.py || error_exit
fi
if [ "$Task" == "upgrade" ];
then
python scripts/action_before_upgrade.py || error_exit
fi
pushd ${Deploy_Dir}
# uninstall milvus
printf "start to uninstall milvus\n"
docker compose down
sleep 10
printf "check all containers removed\n"
docker compose ps
# second deployment
if [ "$Task" == "reinstall" ];
then
printf "start to reinstall milvus\n"
#because the task is reinstall, so don't change images tag
fi
if [ "$Task" == "upgrade" ];
then
printf "start to upgrade milvus\n"
# because the task is upgrade, so replace image tag to latest, like rc4-->rc5
printf "download latest milvus docker compose yaml file from github\n"
wget https://raw.githubusercontent.com/milvus-io/milvus/master/deployments/docker/${Mode}/docker-compose.yml -O docker-compose.yml
printf "start to deploy latest rc tag milvus\n"
replace_image_tag "milvusdb\/milvus" $latest_tag
fi
cat docker-compose.yml|grep milvusdb
docker compose up -d
check_healthy
docker compose ps
popd
# wait for milvus ready
sleep 120
# test for second deployment
printf "test for second deployment\n"
if [ "$Task" == "reinstall" ];
then
python scripts/action_after_reinstall.py || error_exit
fi
if [ "$Task" == "upgrade" ];
then
python scripts/action_after_upgrade.py || error_exit
fi
# test for third deployment(after docker compose restart)
pushd ${Deploy_Dir}
printf "start to restart milvus\n"
docker compose restart
check_healthy
docker compose ps
popd
# wait for milvus ready
sleep 120
printf "test for third deployment\n"
if [ "$Task" == "reinstall" ];
then
python scripts/action_after_reinstall.py || error_exit
fi
if [ "$Task" == "upgrade" ];
then
python scripts/action_after_upgrade.py || error_exit
fi
pushd ${Deploy_Dir}
# clean env
docker compose ps
docker compose down
sleep 10
docker compose ps
echo "$pw"|sudo -S rm -rf ./volumes
popd
@@ -0,0 +1,202 @@
import pytest
import random
from common import common_func as cf
from common import common_type as ct
from common.common_type import CaseLabel, CheckTasks
from common.milvus_sys import MilvusSys
from utils.util_pymilvus import *
from deploy.base import TestDeployBase
from deploy import common as dc
from deploy.common import gen_index_param, gen_search_param
default_nb = ct.default_nb
default_nq = ct.default_nq
default_dim = ct.default_dim
default_limit = ct.default_limit
default_search_field = ct.default_float_vec_field_name
default_search_params = ct.default_search_params
default_int64_field_name = ct.default_int64_field_name
default_float_field_name = ct.default_float_field_name
default_bool_field_name = ct.default_bool_field_name
default_string_field_name = ct.default_string_field_name
binary_field_name = default_binary_vec_field_name
default_search_exp = "int64 >= 0"
default_term_expr = f'{ct.default_int64_field_name} in [0, 1]'
class TestActionBeforeReinstall(TestDeployBase):
""" Test case of action before reinstall """
def teardown_method(self, method):
log.info(("*" * 35) + " teardown " + ("*" * 35))
log.info("[teardown_method] Start teardown test case %s..." %
method.__name__)
log.info("skip drop collection")
@pytest.mark.skip()
@pytest.mark.tags(CaseLabel.L3)
@pytest.mark.parametrize("index_type", dc.all_index_types) # , "BIN_FLAT"
def test_task_1(self, index_type, data_size):
"""
before reinstall: create collection and insert data, load and search
after reinstall: get collection, search, create index, load, and search
"""
name = "task_1_" + index_type
insert_data = False
is_binary = True if "BIN" in index_type else False
is_flush = False
# init collection
collection_w = self.init_collection_general(insert_data=insert_data, is_binary=is_binary, nb=data_size,
is_flush=is_flush, name=name)[0]
if is_binary:
_, vectors_to_search = cf.gen_binary_vectors(
default_nb, default_dim)
default_search_field = ct.default_binary_vec_field_name
else:
vectors_to_search = cf.gen_vectors(default_nb, default_dim)
default_search_field = ct.default_float_vec_field_name
search_params = gen_search_param(index_type)[0]
# search
collection_w.search(vectors_to_search[:default_nq], default_search_field,
search_params, default_limit,
default_search_exp,
check_task=CheckTasks.check_search_results,
check_items={"nq": default_nq,
"limit": default_limit})
# query
output_fields = [ct.default_int64_field_name]
collection_w.query(default_term_expr, output_fields=output_fields,
check_task=CheckTasks.check_query_not_empty)
# create index
default_index = gen_index_param(index_type)
collection_w.create_index(default_search_field, default_index)
# release and load after creating index
collection_w.release()
collection_w.load()
# search
collection_w.search(vectors_to_search[:default_nq], default_search_field,
search_params, default_limit,
default_search_exp,
check_task=CheckTasks.check_search_results,
check_items={"nq": default_nq,
"limit": default_limit})
# query
output_fields = [ct.default_int64_field_name]
collection_w.query(default_term_expr, output_fields=output_fields,
check_task=CheckTasks.check_query_not_empty)
@pytest.mark.tags(CaseLabel.L3)
@pytest.mark.parametrize("index_type", dc.all_index_types) # , "BIN_FLAT"
def test_task_2(self, index_type, data_size):
"""
before reinstall: create collection, insert data and create index,load and search
after reinstall: get collection, search, insert data, create index, load, and search
"""
name = "task_2_" + index_type
is_binary = True if "BIN" in index_type else False
# init collection
collection_w = self.init_collection_general(insert_data=False, is_binary=is_binary, nb=data_size,
is_flush=False, name=name, active_trace=True)[0]
vectors_to_search = cf.gen_vectors(default_nb, default_dim)
default_search_field = ct.default_float_vec_field_name
if is_binary:
_, vectors_to_search = cf.gen_binary_vectors(
default_nb, default_dim)
default_search_field = ct.default_binary_vec_field_name
search_params = gen_search_param(index_type)[0]
output_fields = [ct.default_int64_field_name]
# search
collection_w.search(vectors_to_search[:default_nq], default_search_field,
search_params, default_limit,
default_search_exp,
output_fields=output_fields,
check_task=CheckTasks.check_search_results,
check_items={"nq": default_nq,
"limit": default_limit})
# query
collection_w.query(default_term_expr, output_fields=output_fields,
check_task=CheckTasks.check_query_not_empty)
# insert data
self.init_collection_general(insert_data=True, is_binary=is_binary, nb=data_size,
is_flush=False, name=name, active_trace=True)
# create index
default_index = gen_index_param(index_type)
collection_w.create_index(default_search_field, default_index)
# release and load after
collection_w.release()
collection_w.load()
# search
collection_w.search(vectors_to_search[:default_nq], default_search_field,
search_params, default_limit,
default_search_exp,
output_fields=output_fields,
check_task=CheckTasks.check_search_results,
check_items={"nq": default_nq,
"limit": default_limit})
# query
collection_w.query(default_term_expr, output_fields=output_fields,
check_task=CheckTasks.check_query_not_empty)
@pytest.mark.tags(CaseLabel.L3)
@pytest.mark.parametrize("replica_number", [0, 1, 2])
@pytest.mark.parametrize("is_compacted", [True, False])
@pytest.mark.parametrize("is_deleted", [True, False])
@pytest.mark.parametrize("is_string_indexed", [True, False])
@pytest.mark.parametrize("is_vector_indexed", [True, False]) # , "BIN_FLAT"
@pytest.mark.parametrize("segment_status", ["only_growing", "sealed", "all"]) # , "BIN_FLAT"
# @pytest.mark.parametrize("is_empty", [True, False]) # , "BIN_FLAT" (keep one is enough)
@pytest.mark.parametrize("index_type", random.sample(dc.all_index_types, 3)) # , "BIN_FLAT"
def test_task_all(self, index_type, is_compacted,
segment_status, is_vector_indexed, is_string_indexed, replica_number, is_deleted, data_size):
"""
before reinstall: create collection and insert data, load and search
after reinstall: get collection, search, create index, load, and search
"""
name = f"index_type_{index_type}_segment_status_{segment_status}_is_vector_indexed_{is_vector_indexed}_is_string_indexed_{is_string_indexed}_is_compacted_{is_compacted}_is_deleted_{is_deleted}_replica_number_{replica_number}_data_size_{data_size}"
ms = MilvusSys()
is_binary = True if "BIN" in index_type else False
# insert with small size data without flush to get growing segment
collection_w = self.init_collection_general(insert_data=True, is_binary=is_binary, nb=3000,
is_flush=False, name=name)[0]
# load for growing segment
if replica_number > 0:
collection_w.load(replica_number=replica_number)
delete_expr = f"{ct.default_int64_field_name} in [0,1,2,3,4,5,6,7,8,9]"
# delete data for growing segment
if is_deleted:
collection_w.delete(expr=delete_expr)
if segment_status == "only_growing":
pytest.skip("already get growing segment, skip testcase")
# insert with flush multiple times to generate multiple sealed segment
for i in range(5):
self.init_collection_general(insert_data=True, is_binary=is_binary, nb=data_size,
is_flush=False, name=name)
if is_binary:
default_index_field = ct.default_binary_vec_field_name
else:
default_index_field = ct.default_float_vec_field_name
if is_vector_indexed:
# create index
default_index_param = gen_index_param(index_type)
collection_w.create_index(default_index_field, default_index_param)
if is_string_indexed:
# create index
default_string_index_params = {}
collection_w.create_index(default_string_field_name, default_string_index_params)
# delete data for sealed segment
delete_expr = f"{ct.default_int64_field_name} in [10,11,12,13,14,15,16,17,18,19]"
if is_deleted:
collection_w.delete(expr=delete_expr)
if is_compacted:
collection_w.compact()
# reload after flush and create index
if replica_number > 0:
collection_w.release()
collection_w.load(replica_number=replica_number)
@@ -0,0 +1,125 @@
import pytest
from common import common_func as cf
from common import common_type as ct
from common.common_type import CaseLabel, CheckTasks
from utils.util_pymilvus import *
from deploy.base import TestDeployBase
from deploy import common as dc
from deploy.common import gen_index_param, gen_search_param
default_nb = ct.default_nb
default_nq = ct.default_nq
default_dim = ct.default_dim
default_limit = ct.default_limit
default_search_field = ct.default_float_vec_field_name
default_search_params = ct.default_search_params
default_int64_field_name = ct.default_int64_field_name
default_float_field_name = ct.default_float_field_name
default_bool_field_name = ct.default_bool_field_name
default_string_field_name = ct.default_string_field_name
binary_field_name = default_binary_vec_field_name
default_search_exp = "int64 >= 0"
default_term_expr = f'{ct.default_int64_field_name} in [0, 1]'
class TestActionBeforeReinstall(TestDeployBase):
""" Test case of action before reinstall """
def teardown_method(self, method):
log.info(("*" * 35) + " teardown " + ("*" * 35))
log.info("[teardown_method] Start teardown test case %s..." % method.__name__)
log.info("skip drop collection")
@pytest.mark.skip()
@pytest.mark.tags(CaseLabel.L3)
@pytest.mark.parametrize("index_type", dc.all_index_types) #, "BIN_FLAT"
def test_task_1(self, index_type, data_size):
"""
before reinstall: create collection and insert data, load and search
after reinstall: get collection, load, search, create index, load, and search
"""
name = "task_1_" + index_type
insert_data = True
is_binary = True if "BIN" in index_type else False
is_flush = False
collection_w = self.init_collection_general(insert_data=insert_data, is_binary=is_binary, nb=data_size,
is_flush=is_flush, name=name)[0]
collection_w.load()
if is_binary:
_, vectors_to_search = cf.gen_binary_vectors(default_nb, default_dim)
default_search_field = ct.default_binary_vec_field_name
else:
vectors_to_search = cf.gen_vectors(default_nb, default_dim)
default_search_field = ct.default_float_vec_field_name
search_params = gen_search_param(index_type)[0]
collection_w.search(vectors_to_search[:default_nq], default_search_field,
search_params, default_limit,
default_search_exp,
check_task=CheckTasks.check_search_results,
check_items={"nq": default_nq,
"limit": default_limit})
output_fields = [ct.default_int64_field_name]
collection_w.query(default_term_expr, output_fields=output_fields,
check_task=CheckTasks.check_query_not_empty)
@pytest.mark.tags(CaseLabel.L3)
@pytest.mark.parametrize("index_type", dc.all_index_types) # , "BIN_FLAT"
def test_task_2(self, index_type, data_size):
"""
before reinstall: create collection, insert data and create index,load and search
after reinstall: get collection, load, search, insert data, create index, load, and search
"""
name = "task_2_" + index_type
insert_data = True
is_binary = True if "BIN" in index_type else False
is_flush = False
# create collection and insert data
collection_w = self.init_collection_general(insert_data=insert_data, is_binary=is_binary, nb=data_size,
is_flush=is_flush, name=name, active_trace=True)[0]
vectors_to_search = cf.gen_vectors(default_nb, default_dim)
default_search_field = ct.default_float_vec_field_name
if is_binary:
_, vectors_to_search = cf.gen_binary_vectors(default_nb, default_dim)
default_search_field = ct.default_binary_vec_field_name
# create index
default_index = gen_index_param(index_type)
collection_w.create_index(default_search_field, default_index)
# load
collection_w.load()
# search
search_params = gen_search_param(index_type)[0]
collection_w.search(vectors_to_search[:default_nq], default_search_field,
search_params, default_limit,
default_search_exp,
check_task=CheckTasks.check_search_results,
check_items={"nq": default_nq,
"limit": default_limit})
# query
output_fields = [ct.default_int64_field_name]
collection_w.query(default_term_expr, output_fields=output_fields,
check_task=CheckTasks.check_query_not_empty)
@@ -0,0 +1,220 @@
import pytest
import pymilvus
from common import common_func as cf
from common import common_type as ct
from common.common_type import CaseLabel, CheckTasks
from common.milvus_sys import MilvusSys
from utils.util_pymilvus import *
from deploy.base import TestDeployBase
from deploy.common import gen_index_param, gen_search_param
from utils.util_log import test_log as log
pymilvus_version = pymilvus.__version__
default_nb = ct.default_nb
default_nq = ct.default_nq
default_dim = ct.default_dim
default_limit = ct.default_limit
default_search_field = ct.default_float_vec_field_name
default_search_params = ct.default_search_params
default_int64_field_name = ct.default_int64_field_name
default_float_field_name = ct.default_float_field_name
default_bool_field_name = ct.default_bool_field_name
default_string_field_name = ct.default_string_field_name
binary_field_name = ct.default_binary_vec_field_name
default_search_exp = "int64 >= 0"
default_term_expr = f'{ct.default_int64_field_name} in [0, 1]'
prefix = "deploy_test"
TIMEOUT = 120
class TestActionFirstDeployment(TestDeployBase):
""" Test case of action before reinstall """
def teardown_method(self, method):
log.info(("*" * 35) + " teardown " + ("*" * 35))
log.info("[teardown_method] Start teardown test case %s..." %
method.__name__)
log.info("skip drop collection")
@pytest.mark.tags(CaseLabel.L3)
@pytest.mark.parametrize("replica_number", [0])
@pytest.mark.parametrize("index_type", ["HNSW", "BIN_IVF_FLAT"])
def test_task_all_empty(self, index_type, replica_number):
"""
before reinstall: create collection
"""
name = ""
for k, v in locals().items():
if k in ["self", "name"]:
continue
name += f"_{k}_{v}"
name = prefix + name + "_" + "empty"
is_binary = False
if "BIN" in name:
is_binary = True
collection_w = \
self.init_collection_general(insert_data=False, is_binary=is_binary, name=name, enable_dynamic_field=False,
with_json=False, is_index=False)[0]
if collection_w.has_index():
index_names = [index.index_name for index in collection_w.indexes]
for index_name in index_names:
collection_w.drop_index(index_name=index_name)
@pytest.mark.tags(CaseLabel.L3)
@pytest.mark.parametrize("replica_number", [0, 1, 2])
@pytest.mark.parametrize("is_compacted", ["is_compacted", "not_compacted"])
@pytest.mark.parametrize("is_deleted", ["is_deleted"])
@pytest.mark.parametrize("is_scalar_indexed", ["is_scalar_indexed", "not_scalar_indexed"])
@pytest.mark.parametrize("segment_status", ["only_growing", "all"])
@pytest.mark.parametrize("index_type", ["HNSW", "BIN_IVF_FLAT", "IVF_FLAT", "IVF_SQ8", "IVF_PQ"])
def test_task_all(self, index_type, is_compacted,
segment_status, is_scalar_indexed, replica_number, is_deleted, data_size):
"""
before reinstall: create collection and insert data, load and search
"""
name = ""
for k, v in locals().items():
if k in ["self", "name"]:
continue
name += f"_{k}_{v}"
name = prefix + name
log.info(f"collection name: {name}")
self._connect()
ms = MilvusSys()
if len(ms.query_nodes) < replica_number:
# this step is to make sure this testcase can run on standalone mode
# or cluster mode which has only one querynode
pytest.skip("skip test, not enough nodes")
log.info(f"collection name: {name}, replica_number: {replica_number}, is_compacted: {is_compacted},"
f"is_deleted: {is_deleted}, is_scalar_indexed: {is_scalar_indexed},"
f"segment_status: {segment_status}, index_type: {index_type}")
is_binary = True if "BIN" in index_type else False
# params for search and query
if is_binary:
_, vectors_to_search = cf.gen_binary_vectors(
default_nb, default_dim)
default_search_field = ct.default_binary_vec_field_name
else:
vectors_to_search = cf.gen_vectors(default_nb, default_dim)
default_search_field = ct.default_float_vec_field_name
search_params = gen_search_param(index_type)[0]
# init collection and insert with small size data without flush to get growing segment
collection_w = self.init_collection_general(insert_data=True, is_binary=is_binary, nb=3000,
is_flush=False, is_index=False, name=name,
enable_dynamic_field=False,
with_json=False)[0]
# params for creating index
if is_binary:
default_index_field = ct.default_binary_vec_field_name
else:
default_index_field = ct.default_float_vec_field_name
# create index for vector
default_index_param = gen_index_param(index_type)
collection_w.create_index(default_index_field, default_index_param)
# create index for scalar
if is_scalar_indexed == "is_scalar_indexed":
int_field_name = cf.get_int64_field_name(schema=collection_w.schema)
# create stl sort index for int field
collection_w.create_index(int_field_name, {"index_type": "STL_SORT"})
varchar_field_name = cf.get_varchar_field_name(schema=collection_w.schema)
# 50% chance to create trie index for varchar field
if random.randint(0, 1) == 1:
collection_w.create_index(varchar_field_name, {"index_type": "TRIE"})
scalar_field_names = cf.get_scalar_field_name_list(schema=collection_w.schema)
indexes = [index.to_dict() for index in collection_w.indexes]
indexed_fields = [index['field'] for index in indexes]
# create inverted index for other scalar field
for f in scalar_field_names:
if f in indexed_fields:
continue
collection_w.create_index(f, {"index_type": "INVERTED"},)
# load for growing segment
if replica_number >= 1:
try:
collection_w.release()
except Exception as e:
log.error(
f"release collection failed: {e} maybe the collection is not loaded")
collection_w.load(replica_number=replica_number, timeout=TIMEOUT)
self.utility_wrap.wait_for_loading_complete(name)
# delete data for growing segment
delete_expr = f"{ct.default_int64_field_name} in {[i for i in range(0, 10)]}"
if is_deleted == "is_deleted":
collection_w.delete(expr=delete_expr)
# search and query for growing segment
if replica_number >= 1:
collection_w.search(vectors_to_search[:default_nq], default_search_field,
search_params, default_limit,
default_search_exp,
check_task=CheckTasks.check_search_results,
check_items={"nq": default_nq,
"limit": default_limit})
output_fields = [ct.default_int64_field_name]
collection_w.query(default_term_expr, output_fields=output_fields,
check_task=CheckTasks.check_query_not_empty)
# skip subsequent operations when segment_status is set to only_growing
if segment_status == "only_growing":
pytest.skip(
"already get growing segment, skip subsequent operations")
# insert with flush multiple times to generate multiple sealed segment
for i in range(5):
self.init_collection_general(insert_data=True, is_binary=is_binary, nb=data_size,
is_flush=False, is_index=False, name=name, enable_dynamic_field=False,
with_json=False)
# at this step, all segment are sealed
if pymilvus_version >= "2.2.0":
collection_w.flush()
else:
collection_w.collection.num_entities
# delete data for sealed segment and before index
delete_expr = f"{ct.default_int64_field_name} in {[i for i in range(10, 20)]}"
if is_deleted == "is_deleted":
collection_w.delete(expr=delete_expr)
# delete data for sealed segment and after index
delete_expr = f"{ct.default_int64_field_name} in {[i for i in range(20, 30)]}"
if is_deleted == "is_deleted":
collection_w.delete(expr=delete_expr)
if is_compacted == "is_compacted":
collection_w.compact()
# get growing segment before reload
if segment_status == "all":
self.init_collection_general(insert_data=True, is_binary=is_binary, nb=3000,
is_flush=False, is_index=False, name=name, enable_dynamic_field=False,
with_json=False)
# reload after flush and creating index
if replica_number > 0:
collection_w.release()
collection_w.load(replica_number=replica_number, timeout=TIMEOUT)
self.utility_wrap.wait_for_loading_complete(name)
# insert data to get growing segment after reload
if segment_status == "all":
self.init_collection_general(insert_data=True, is_binary=is_binary, nb=3000,
is_flush=False, is_index=False, name=name, enable_dynamic_field=False,
with_json=False)
# search and query for sealed and growing segment
if replica_number > 0:
collection_w.search(vectors_to_search[:default_nq], default_search_field,
search_params, default_limit,
default_search_exp,
check_task=CheckTasks.check_search_results,
check_items={"nq": default_nq,
"limit": default_limit})
output_fields = [ct.default_int64_field_name]
collection_w.query(default_term_expr, output_fields=output_fields,
check_task=CheckTasks.check_query_not_empty)
@@ -0,0 +1,231 @@
import pytest
import re
import time
import pymilvus
from common import common_func as cf
from common import common_type as ct
from common.common_type import CaseLabel, CheckTasks
from common.milvus_sys import MilvusSys
from utils.util_pymilvus import *
from deploy.base import TestDeployBase
from deploy.common import gen_index_param, gen_search_param, get_deploy_test_collections
from utils.util_log import test_log as log
default_nb = ct.default_nb
default_nq = ct.default_nq
default_dim = ct.default_dim
default_limit = ct.default_limit
default_search_field = ct.default_float_vec_field_name
default_search_params = ct.default_search_params
default_int64_field_name = ct.default_int64_field_name
default_float_field_name = ct.default_float_field_name
default_bool_field_name = ct.default_bool_field_name
default_string_field_name = ct.default_string_field_name
binary_field_name = ct.default_binary_vec_field_name
default_search_exp = "int64 >= 0"
default_term_expr = f'{ct.default_int64_field_name} in [0, 1]'
pymilvus_version = pymilvus.__version__
class TestActionSecondDeployment(TestDeployBase):
""" Test case of action before reinstall """
@pytest.fixture(scope="function", params=get_deploy_test_collections())
def all_collection_name(self, request):
if request.param == [] or request.param == "":
pytest.skip("The collection name is invalid")
yield request.param
def teardown_method(self, method):
log.info(("*" * 35) + " teardown " + ("*" * 35))
log.info("[teardown_method] Start teardown test case %s..." %
method.__name__)
log.info("show collection info")
log.info(f"collection {self.collection_w.name} has entities: {self.collection_w.num_entities}")
res, _ = self.utility_wrap.get_query_segment_info(self.collection_w.name)
log.info(f"The segment info of collection {self.collection_w.name} is {res}")
index_infos = [index.to_dict() for index in self.collection_w.indexes]
log.info(f"collection {self.collection_w.name} index infos {index_infos}")
log.info("skip drop collection")
def create_index(self, collection_w, default_index_field, default_index_param):
index_field_map = dict([(index.field_name, index.index_name) for index in collection_w.indexes])
index_infos = [index.to_dict() for index in collection_w.indexes]
log.info(f"index info: {index_infos}")
# log.info(f"{default_index_field:} {default_index_param:}")
if len(index_infos) > 0:
log.info(
f"current index param is {index_infos[0]['index_param']}, passed in param is {default_index_param}")
log.info(
f"current index name is {index_infos[0]['index_name']}, passed in param is {index_field_map.get(default_index_field)}")
collection_w.create_index(default_index_field, default_index_param,
index_name=index_field_map.get(default_index_field, gen_unique_str("test")))
collection_w.create_index(default_string_field_name, {},
index_name=index_field_map.get(default_string_field_name, gen_unique_str("test")))
@pytest.mark.tags(CaseLabel.L3)
def test_check(self, all_collection_name, data_size):
"""
before reinstall: create collection
"""
self._connect()
ms = MilvusSys()
name = all_collection_name
is_binary = False
if "BIN" in name:
is_binary = True
collection_w, _ = self.collection_wrap.init_collection(name=name)
self.collection_w = collection_w
schema = collection_w.schema
data_type = [field.dtype for field in schema.fields]
field_name = [field.name for field in schema.fields]
type_field_map = dict(zip(data_type, field_name))
if is_binary:
default_index_field = ct.default_binary_vec_field_name
vector_index_type = "BIN_IVF_FLAT"
else:
default_index_field = ct.default_float_vec_field_name
vector_index_type = "IVF_FLAT"
binary_vector_index_types = [index.params["index_type"] for index in collection_w.indexes if
index.field_name == type_field_map.get(100, "")]
float_vector_index_types = [index.params["index_type"] for index in collection_w.indexes if
index.field_name == type_field_map.get(101, "")]
index_field_map = dict([(index.field_name, index.index_name) for index in collection_w.indexes])
index_names = [index.index_name for index in collection_w.indexes] # used to drop index
vector_index_types = binary_vector_index_types + float_vector_index_types
if len(vector_index_types) > 0:
vector_index_type = vector_index_types[0]
try:
t0 = time.time()
self.utility_wrap.wait_for_loading_complete(name)
log.info(f"wait for {name} loading complete cost {time.time() - t0}")
except Exception as e:
log.error(e)
# get replicas loaded
try:
replicas = collection_w.get_replicas(enable_traceback=False)
replicas_loaded = len(replicas.groups)
except Exception as e:
log.error(e)
replicas_loaded = 0
log.info(f"collection {name} has {replicas_loaded} replicas")
actual_replicas = re.search(r'replica_number_(.*?)_', name).group(1)
assert replicas_loaded == int(actual_replicas)
# params for search and query
if is_binary:
_, vectors_to_search = cf.gen_binary_vectors(
default_nb, default_dim)
default_search_field = ct.default_binary_vec_field_name
else:
vectors_to_search = cf.gen_vectors(default_nb, default_dim)
default_search_field = ct.default_float_vec_field_name
search_params = gen_search_param(vector_index_type)[0]
# load if not loaded
if replicas_loaded == 0:
# create index for vector if not exist before load
is_vector_indexed = False
index_infos = [index.to_dict() for index in collection_w.indexes]
for index_info in index_infos:
if "metric_type" in index_info.keys() or "metric_type" in index_info["index_param"]:
is_vector_indexed = True
break
if is_vector_indexed is False:
default_index_param = gen_index_param(vector_index_type)
self.create_index(collection_w, default_index_field, default_index_param)
collection_w.load()
# search and query
if "empty" in name:
# if the collection is empty, the search result should be empty, so no need to check
check_task = None
else:
check_task = CheckTasks.check_search_results
collection_w.search(vectors_to_search[:default_nq], default_search_field,
search_params, default_limit,
default_search_exp,
output_fields=[ct.default_int64_field_name],
check_task=check_task,
check_items={"nq": default_nq,
"limit": default_limit})
if "empty" in name:
check_task = None
else:
check_task = CheckTasks.check_query_not_empty
collection_w.query(default_term_expr, output_fields=[ct.default_int64_field_name],
check_task=check_task)
# flush
if pymilvus_version >= "2.2.0":
collection_w.flush()
else:
collection_w.collection.num_entities
# search and query
if "empty" in name:
check_task = None
else:
check_task = CheckTasks.check_search_results
collection_w.search(vectors_to_search[:default_nq], default_search_field,
search_params, default_limit,
default_search_exp,
output_fields=[ct.default_int64_field_name],
check_task=check_task,
check_items={"nq": default_nq,
"limit": default_limit})
if "empty" in name:
check_task = None
else:
check_task = CheckTasks.check_query_not_empty
collection_w.query(default_term_expr, output_fields=[ct.default_int64_field_name],
check_task=check_task)
# insert data and flush
for i in range(2):
self.insert_data_general(insert_data=True, is_binary=is_binary, nb=data_size,
is_flush=False, is_index=True, name=name,
enable_dynamic_field=False, with_json=False)
if pymilvus_version >= "2.2.0":
collection_w.flush()
else:
collection_w.collection.num_entities
# delete data
delete_expr = f"{ct.default_int64_field_name} in [0,1,2,3,4,5,6,7,8,9]"
collection_w.delete(expr=delete_expr)
# search and query
collection_w.search(vectors_to_search[:default_nq], default_search_field,
search_params, default_limit,
default_search_exp,
output_fields=[ct.default_int64_field_name],
check_task=CheckTasks.check_search_results,
check_items={"nq": default_nq,
"limit": default_limit})
collection_w.query(default_term_expr, output_fields=[ct.default_int64_field_name],
check_task=CheckTasks.check_query_not_empty)
# release and reload with changed replicas
collection_w.release()
replica_number = 1
if replicas_loaded in [0, 1] and len(ms.query_nodes) >= 2:
replica_number = 2
collection_w.load(replica_number=replica_number)
# search and query
collection_w.search(vectors_to_search[:default_nq], default_search_field,
search_params, default_limit,
default_search_exp,
output_fields=[ct.default_int64_field_name],
check_task=CheckTasks.check_search_results,
check_items={"nq": default_nq,
"limit": default_limit})
collection_w.query(default_term_expr, output_fields=[ct.default_int64_field_name],
check_task=CheckTasks.check_query_not_empty)
@@ -0,0 +1,27 @@
import json
import pytest
from base.client_base import TestcaseBase
from deploy.common import get_deploy_test_collections
from common.common_type import CaseLabel
from utils.util_log import test_log as log
class TestGetCollections(TestcaseBase):
""" Test case of getting all collections """
@pytest.mark.tags(CaseLabel.L3)
def test_get_collections_by_prefix(self,):
self._connect()
all_collections = self.utility_wrap.list_collections()[0]
all_collections = [c_name for c_name in all_collections if "deploy_test" in c_name]
log.info(f"find {len(all_collections)} collections:")
log.info(all_collections)
data = {
"all": all_collections,
}
with open("/tmp/ci_logs/deploy_test_all_collections.json", "w") as f:
f.write(json.dumps(data))
log.info(f"write {len(all_collections)} collections to /tmp/ci_logs/deploy_test_all_collections.json")
collections_in_json = get_deploy_test_collections()
assert len(all_collections) == len(collections_in_json)
+56
View File
@@ -0,0 +1,56 @@
#!/bin/bash
function replace_image_tag {
image_repo=$1
image_tag=$2
image_repo=${image_repo//\//\\\/}
platform='unknown'
unamestr=$(uname)
if [[ "$unamestr" == 'Linux' ]]; then
platform='Linux'
elif [[ "$unamestr" == 'Darwin' ]]; then
platform='Mac'
fi
echo "before replace: "
cat docker-compose.yml | grep milvusdb
if [[ "$platform" == "Mac" ]];
then
# for mac os
echo "replace image tag for mac start"
sed -i "" "s/milvusdb.*/${image_repo}\:${image_tag}/g" docker-compose.yml
echo "replace image tag for mac done"
else
#for linux os
sed -i "s/milvusdb.*/${image_repo}\:${image_tag}/g" docker-compose.yml
fi
echo "after replace: "
cat docker-compose.yml | grep milvusdb
}
#to check containers all running and minio is healthy
function check_healthy {
Expect=$(yq '.services | length' 'docker-compose.yml')
Expect_health=$(yq '.services' 'docker-compose.yml' |grep 'healthcheck'|wc -l)
cnt=$(docker compose ps | grep -E "running|Running|Up|up" | wc -l)
healthy=$(docker compose ps | grep "healthy" | wc -l)
time_cnt=0
echo "running num $cnt expect num $Expect"
echo "healthy num $healthy expect num $Expect_health"
while [[ $cnt -ne $Expect || $healthy -ne 1 ]];
do
printf "waiting all containers getting running\n"
sleep 5
let time_cnt+=5
# if time is greater than 300s, the condition still not satisfied, we regard it as a failure
if [ $time_cnt -gt 300 ];
then
printf "timeout,there are some issues with deployment!"
exit 1
fi
cnt=$(docker compose ps | grep -E "running|Running|Up|up" | wc -l)
healthy=$(docker compose ps | grep "healthy" | wc -l)
echo "running num $cnt expect num $Expect"
echo "healthy num $healthy expect num $Expect_health"
done
}