def test_shrink_proxy(self): """ target: test shrink proxy pod from 2 to 1 method: 1.deploy two proxy node 2.e2e test 3.shrink proxy pods 4.e2e test expected: """ # deploy all nodes one pod cluster milvus with helm release_name = "scale-proxy" env = HelmEnv(release_name=release_name, proxy=2) host = env.helm_install_cluster_milvus() c_name = cf.gen_unique_str(prefix) sc.e2e_milvus(host, c_name) # scale proxy env.helm_upgrade_cluster_milvus(proxy=1) # c_name_2 = cf.gen_unique_str(prefix) sc.e2e_milvus(host, c_name, collection_exist=True)
def test_partition_release_dropped_collection(self): """ target: verify release an dropped collection method: 1.create a collection and partition 2. drop the collection 2. release the partition expected: raise exception """ # create collection collection_w = self.init_collection_wrap() # create partition partition_name = cf.gen_unique_str(prefix) partition_w = self.init_partition_wrap(collection_w, partition_name) assert collection_w.has_partition(partition_name)[0] # drop collection collection_w.drop() # release the partition and check err response partition_w.release(check_task=CheckTasks.err_res, check_items={ct.err_code: 1, ct.err_msg: "can't find collection"})
def test_partition_insert_dropped_collection(self): """ target: verify insert data into dropped collection method: 1.create a collection 2. insert some data into dropped collection expected: raise exception """ # create collection collection_w = self.init_collection_wrap() # create partition partition_name = cf.gen_unique_str(prefix) partition_w = self.init_partition_wrap(collection_w, partition_name) assert collection_w.has_partition(partition_name)[0] # drop collection collection_w.drop() # insert data to partition partition_w.insert(cf.gen_default_dataframe_data(), check_task=CheckTasks.err_res, check_items={ct.err_code: 1, ct.err_msg: "None Type"})
def test_delete_sealed_segment_without_flush(self): """ target: test delete without flush method: 1.insert and flush data 2.delete ids from collection and no flush 3.load and query with id expected: No query result """ # create collection, insert data without flush collection_w = self.init_collection_wrap( name=cf.gen_unique_str(prefix)) df = cf.gen_default_dataframe_data(tmp_nb) collection_w.insert(df) assert collection_w.num_entities == tmp_nb # delete del_res, _ = collection_w.delete(tmp_expr) assert del_res.delete_count == 1 # load and query with id collection_w.load() collection_w.query(tmp_expr, check_task=CheckTasks.check_query_empty)
def init_collection_wrap(self, name=None, schema=None, shards_num=2, check_task=None, check_items=None, **kwargs): name = cf.gen_unique_str('coll_') if name is None else name schema = cf.gen_default_collection_schema( ) if schema is None else schema if self.connection_wrap.get_connection( alias=DefaultConfig.DEFAULT_USING)[0] is None: self._connect() collection_w = ApiCollectionWrapper() collection_w.init_collection(name=name, schema=schema, shards_num=shards_num, check_task=check_task, check_items=check_items, **kwargs) self.collection_object_list.append(collection_w) return collection_w
def test_delete_sealed_data_channel_delete(self): """ target: test delete sealed data and get deleteMsg from insertChannel method: 1.create, insert and flush data 2.load collection 3.delete id without flush 4.query deleted ids (queryNode get deleted ids from channel not persistence) expected: Delete successfully and no query result """ # create collection and insert flush data collection_w = self.init_collection_wrap( name=cf.gen_unique_str(prefix)) df = cf.gen_default_dataframe_data(tmp_nb) collection_w.insert(df) assert collection_w.num_entities == tmp_nb # load collection and queryNode subscribe channel collection_w.load() # delete ids and query collection_w.delete(tmp_expr) collection_w.query(tmp_expr, check_task=CheckTasks.check_query_empty)
def test_delete_growing_data_channel_delete(self): """ target: test delete entities from growing segment, and channel deleteMsg method: 1.create collection 2.load collection 3.insert data and delete ids 4.query deleted ids expected: No query result """ # create collection collection_w = self.init_collection_wrap( name=cf.gen_unique_str(prefix)) # load collection and the queryNode watch the insertChannel collection_w.load() # insert data df = cf.gen_default_dataframe_data(tmp_nb) collection_w.insert(df) # delete id 0 del_res = collection_w.delete(tmp_expr)[0] assert del_res.delete_count == 1 # query id 0 collection_w.query(tmp_expr, check_task=CheckTasks.check_query_empty)
def test_partition_drop_non_empty_partition(self): """ target: verify drop a partition which has data inserted method: 1.create a partition with default schema 2. insert some data 3. drop the partition expected: drop successfully """ # create collection collection_w = self.init_collection_wrap() # create partition partition_name = cf.gen_unique_str(prefix) partition_w = self.init_partition_wrap(collection_w, partition_name) assert collection_w.has_partition(partition_name)[0] # insert data to partition partition_w.insert(cf.gen_default_dataframe_data()) # drop partition partition_w.drop() assert not collection_w.has_partition(partition_name)[0]
def test_partition_create_and_drop_multi_times(self): """ target: verify create and drop for times method: 1.create a partition with default schema 2. drop the partition 3. loop #1 and #2 for times expected: create and drop successfully """ # create collection collection_w = self.init_collection_wrap() # range for 5 times partition_name = cf.gen_unique_str(prefix) for i in range(5): # create partition and check that the partition exists partition_w = self.init_partition_wrap(collection_w, partition_name) assert collection_w.has_partition(partition_name)[0] # drop partition and check that the partition not exists partition_w.drop() assert not collection_w.has_partition(partition_name)[0]
def test_compact_cross_shards(self): """ target: test compact cross shards method: 1.create with shard_num=2 2.insert once and flush (two segments, belonging to two shards) 3.compact and completed expected: Verify no compact """ # insert into two segments with two shard collection_w = self.init_collection_wrap( name=cf.gen_unique_str(prefix), shards_num=2) df = cf.gen_default_dataframe_data(tmp_nb) collection_w.insert(df) assert collection_w.num_entities == tmp_nb # compact collection_w.compact() collection_w.wait_for_compaction_completed(timeout=1) c_plans = collection_w.get_compaction_plans()[0] # Actually no merged assert len(c_plans.plans) == 0
def test_compact_search_after_delete_channel(self): """ target: test search after compact, and queryNode get delete request from channel, rather than compacted delta log method: 1.insert, flush and load 2.delete half 3.compact 4.search expected: No compact, compact get delta log from storage """ collection_w = self.init_collection_wrap(cf.gen_unique_str(prefix), shards_num=1) df = cf.gen_default_dataframe_data() insert_res, _ = collection_w.insert(df) assert collection_w.num_entities == ct.default_nb collection_w.load() expr = f'{ct.default_int64_field_name} in {insert_res.primary_keys[:ct.default_nb // 2]}' collection_w.delete(expr) collection_w.compact() c_plans = collection_w.get_compaction_plans()[0] assert len(c_plans.plans) == 0 # search collection_w.load() search_res, _ = collection_w.search( cf.gen_vectors(ct.default_nq, ct.default_dim), ct.default_float_vec_field_name, ct.default_search_params, ct.default_limit, check_task=CheckTasks.check_search_results, check_items={ "nq": ct.default_nq, "ids": insert_res.primary_keys[ct.default_nb // 2:], "limit": ct.default_limit })
def test_partition_drop(self): """ target: verify drop a partition in one collection method: 1. create a partition in one collection 2. drop the partition expected: 1. drop successfully """ # create collection collection_w = self.init_collection_wrap() # create partition partition_name = cf.gen_unique_str(prefix) partition_w = self.init_partition_wrap(collection_w, partition_name) # check that the partition exists assert collection_w.has_partition(partition_name)[0] # drop partition partition_w.drop() # check that the partition not exists assert not collection_w.has_partition(partition_name)[0]
def test_query_output_fields_part_vector_wildcard(self): """ target: test query output_fields with part wildcard method: specify output_fields as wildcard and part field expected: verify query result """ # init collection with fields: int64, float, float_vec, float_vector1 collection_w, df = self.init_multi_fields_collection_wrap(cf.gen_unique_str(prefix)) collection_w.load() # query with output_fields=["%", float), expected: all fields res = df.iloc[:2].to_dict('records') collection_w.query(default_term_expr, output_fields=["%", ct.default_float_field_name], check_task=CheckTasks.check_query_results, check_items={exp_res: res, "with_vec": True}) # query with output_fields=["%", float_vector), expected: int64, float_vector, float_vector1 output_fields = [ct.default_int64_field_name, ct.default_float_vec_field_name, ct.another_float_vec_field_name] res2 = df.loc[:1, output_fields].to_dict('records') collection_w.query(default_term_expr, output_fields=["%", ct.default_float_vec_field_name], check_task=CheckTasks.check_query_results, check_items={exp_res: res2, "with_vec": True})
def insert_entities_into_two_partitions_in_half(self, half, prefix='query'): """ insert default entities into two partitions(partition_w and _default) in half(int64 and float fields values) :param half: half of nb :return: collection wrap and partition wrap """ self._connect() collection_w = self.init_collection_wrap( name=cf.gen_unique_str(prefix)) partition_w = self.init_partition_wrap(collection_wrap=collection_w) # insert [0, half) into partition_w df_partition = cf.gen_default_dataframe_data(nb=half, start=0) partition_w.insert(df_partition) # insert [half, nb) into _default df_default = cf.gen_default_dataframe_data(nb=half, start=half) collection_w.insert(df_default) # flush collection_w.num_entities collection_w.load(partition_names=[partition_w.name, "_default"]) return collection_w, partition_w, df_partition, df_default
def test_index_type_invalid(self, get_invalid_index_type): """ target: test index with error index type method: input invalid index type expected: raise exception """ c_name = cf.gen_unique_str(prefix) collection_w = self.init_collection_wrap(name=c_name) index_params = copy.deepcopy(default_index_params) index_params["index_type"] = get_invalid_index_type if not isinstance(index_params["index_type"], str): msg = "must be str" else: msg = "Invalid index_type" self.index_wrap.init_index(collection_w.collection, default_field_name, index_params, check_task=CheckTasks.err_res, check_items={ "err_code": 1, "err_msg": msg })
def test_compact_only_growing_segment(self): """ target: test compact growing data method: 1.insert into multi segments without flush 2.compact expected: No compaction (compact just for sealed data) """ # create and insert without flush collection_w = self.init_collection_wrap(name=cf.gen_unique_str(prefix)) df = cf.gen_default_dataframe_data(tmp_nb) collection_w.insert(df) # compact when only growing segment collection_w.compact() collection_w.wait_for_compaction_completed() c_plans = collection_w.get_compaction_plans()[0] assert len(c_plans.plans) == 0 collection_w.load() segments_info = self.utility_wrap.get_query_segment_info(collection_w.name)[0] for segment_info in segments_info: assert segment_info.state == SegmentState.Growing
def collection_insert_multi_segments_one_shard(self, collection_prefix, num_of_segment=2, nb_of_segment=1, is_dup=True): """ init collection with one shard, insert data into two segments on one shard (they can be merged) :param collection_prefix: collection name prefix :param num_of_segment: number of segments :param nb_of_segment: number of entities per segment :param is_dup: whether the primary keys of each segment is duplicated :return: collection wrap and partition wrap """ collection_w = self.init_collection_wrap( name=cf.gen_unique_str(collection_prefix), shards_num=1) for i in range(num_of_segment): start = 0 if is_dup else i * nb_of_segment df = cf.gen_default_dataframe_data(nb_of_segment, start=start) collection_w.insert(df) assert collection_w.num_entities == nb_of_segment * (i + 1) return collection_w
def test_expand_proxy(self): """ target: test milvus operation after proxy expand method: 1.deploy two proxy pods 2.milvus e2e test 3.expand proxy pod from 1 to 2 4.milvus e2e test expected: 1.verify data consistent and func work """ # deploy all nodes one pod cluster milvus with helm release_name = "scale-proxy" env = HelmEnv(release_name=release_name) host = env.helm_install_cluster_milvus() c_name = cf.gen_unique_str(prefix) sc.e2e_milvus(host, c_name) # scale proxy env.helm_upgrade_cluster_milvus(proxy=2) # c_name_2 = cf.gen_unique_str(prefix) sc.e2e_milvus(host, c_name, collection_exist=True)
def test_compact_delete_outside_time_travel(self): """ target: test compact outside time_travel range method: 1.create and insert 2.get time stamp 3.delete 4.compact after compact_retention_duration 5.load and search with travel time tt expected: Empty search result But no way to verify, because travel time does not support travel back to retentionDuration ago so far """ from pymilvus import utility collection_w = self.init_collection_wrap(cf.gen_unique_str(prefix), shards_num=1) # insert df = cf.gen_default_dataframe_data(tmp_nb) insert_res, _ = collection_w.insert(df) tt = utility.mkts_from_hybridts(insert_res.timestamp, milliseconds=0.) expr = f'{ct.default_int64_field_name} in {insert_res.primary_keys}' delete_res, _ = collection_w.delete(expr) log.debug(collection_w.num_entities) # ensure compact remove delta data that delete outside retention range sleep(ct.compact_retention_duration + 1) collection_w.compact() collection_w.wait_for_compaction_completed() collection_w.get_compaction_plans(check_task=CheckTasks.check_delete_compact) collection_w.load() # search with travel_time tt collection_w.search(df[ct.default_float_vec_field_name][:1].to_list(), ct.default_float_vec_field_name, ct.default_search_params, ct.default_limit, travel_timestamp=tt, check_task=CheckTasks.err_res, check_items={ct.err_code: 1, ct.err_msg: "only support to travel back to"})
def test_query_output_multi_float_vec_field(self, vec_fields): """ target: test query and output multi float vec fields method: a.specify multi vec field as output b.specify output_fields with wildcard % expected: verify query result """ # init collection with two float vector fields schema = cf.gen_schema_multi_vector_fields(vec_fields) collection_w = self.init_collection_wrap(name=cf.gen_unique_str(prefix), schema=schema) df = cf.gen_dataframe_multi_vec_fields(vec_fields=vec_fields) collection_w.insert(df) assert collection_w.num_entities == ct.default_nb # query with two vec output_fields output_fields = [ct.default_int64_field_name, ct.default_float_vec_field_name] for vec_field in vec_fields: output_fields.append(vec_field.name) res = df.loc[:1, output_fields].to_dict('records') collection_w.load() collection_w.query(default_term_expr, output_fields=output_fields, check_task=CheckTasks.check_query_results, check_items={exp_res: res, "with_vec": True})
def test_partition_dropped_collection(self): """ target: verify create partition against a dropped collection method: 1. create collection1 2. drop collection1 3. create partition in collection1 expected: 1. raise exception """ # create collection collection_w = self.init_collection_wrap() # drop collection collection_w.drop() # create partition failed self.partition_wrap.init_partition(collection_w.collection, cf.gen_unique_str(prefix), check_task=CheckTasks.err_res, check_items={ ct.err_code: 1, ct.err_msg: "can't find collection" })
def test_compact_no_merge(self): """ target: test compact when no segments merge method: 1.create with shard_num=1 2.insert and flush 3.compact and search expected: No exception """ # create collection collection_w = self.init_collection_wrap( name=cf.gen_unique_str(prefix), shards_num=1) df = cf.gen_default_dataframe_data(tmp_nb) collection_w.insert(df) assert collection_w.num_entities == tmp_nb collection_w.compact() while True: c_state = collection_w.get_compaction_state() log.debug(c_state) if c_state.state == State.Completed and c_state.in_timeout == 0: break c_plans, _ = collection_w.get_compaction_plans() assert len(c_plans.plans) == 0
def test_partition_drop_partition_twice(self): """ target: verify drop the same partition twice method: 1.create a partition with default schema 2. drop the partition 3. drop the same partition again expected: raise exception when 2nd time """ # create collection collection_w = self.init_collection_wrap() # create partition partition_name = cf.gen_unique_str(prefix) partition_w = self.init_partition_wrap(collection_w, partition_name) collection_w.has_partition(partition_name) # drop partition partition_w.drop() assert not collection_w.has_partition(partition_name)[0] # verify that drop the partition again with exception partition_w.drop(check_task=CheckTasks.err_res, check_items={ct.err_code: 1, ct.err_msg: PartitionErrorMessage.PartitionNotExist})
def keep_running(self): while True: t0 = time.time() _, result = self.c_wrap.create_index( ct.default_float_vec_field_name, constants.DEFAULT_INDEX_PARAM, name=cf.gen_unique_str('index_'), timeout=timeout, enable_traceback=enable_traceback, check_task=CheckTasks.check_nothing) t1 = time.time() if result: self.rsp_times.append(t1 - t0) self.average_time = ( (t1 - t0) + self.average_time * self._succ) / (self._succ + 1) self._succ += 1 log.debug( f"index success, time: {t1 - t0:.4f}, average_time: {self.average_time:.4f}" ) self.c_wrap.drop_index(timeout=timeout) else: self._fail += 1
def test_alias_create_alias_with_invalid_name(self, alias_name): """ target: test alias inserting data method: create a collection with invalid alias name expected: create alias failed """ self._connect() c_name = cf.gen_unique_str("collection") collection_w = self.init_collection_wrap( name=c_name, schema=default_schema, check_task=CheckTasks.check_collection_property, check_items={ exp_name: c_name, exp_schema: default_schema }) error = { ct.err_code: 1, ct.err_msg: f"Invalid collection alias: {alias_name}" } collection_w.create_alias(alias_name, check_task=CheckTasks.err_res, check_items=error)
def test_drop_partition_repeatedly(self): """ target: test drop partition twice, check status and partition if existed method: create partitions first, then call function: drop_partition expected: status not ok, no partitions in db """ collection_w = self.init_collection_wrap() partition_name = cf.gen_unique_str(prefix) partition_w = self.init_partition_wrap(collection_w, partition_name) # drop partition collection_w.drop_partition(partition_w.name) # check that the partition not exists assert not collection_w.has_partition(partition_name)[0] collection_w.drop_partition(partition_w.name, check_task=CheckTasks.err_res, check_items={ ct.err_code: 1, 'err_msg': "Partition not exist" })
def test_partition_special_chars_description(self, description): """ target: verify create a partition with special characters in description method: 1. create a partition with special characters in description expected: 1. create successfully """ # create collection collection_w = self.init_collection_wrap() # create partition partition_name = cf.gen_unique_str(prefix) self.init_partition_wrap( collection_w, partition_name, description=description, check_task=CheckTasks.check_partition_property, check_items={ "name": partition_name, "description": description, "is_empty": True, "num_entities": 0 }) assert collection_w.has_partition(partition_name)[0]
def test_insert_auto_id_create_index(self): """ target: test create index in auto_id=True collection method: 1.create auto_id=True collection and insert 2.create index expected: index correct """ schema = cf.gen_default_collection_schema(auto_id=True) collection_w = self.init_collection_wrap( name=cf.gen_unique_str(prefix), schema=schema) df = cf.gen_default_dataframe_data() df.drop(ct.default_int64_field_name, axis=1, inplace=True) mutation_res, _ = collection_w.insert(data=df) assert cf._check_primary_keys(mutation_res.primary_keys, ct.default_nb) assert collection_w.num_entities == ct.default_nb # create index collection_w.create_index(ct.default_float_vec_field_name, default_index_params) assert collection_w.has_index()[0] index, _ = collection_w.index() assert index == Index(collection_w.collection, ct.default_float_vec_field_name, default_index_params) assert collection_w.indexes[0] == index
def test_delete_sealed_data_sealed_delete(self): """ target: test delete with sealed data and sealed delete request method: 1.create, insert 2.delete and flush (will flush data and delete) 3.load and query expected: Empty query result """ # create collection collection_w = self.init_collection_wrap( name=cf.gen_unique_str(prefix)) # insert without flush df = cf.gen_default_dataframe_data(tmp_nb) collection_w.insert(df) # delete id 0 and flush del_res = collection_w.delete(tmp_expr)[0] assert del_res.delete_count == 1 assert collection_w.num_entities == tmp_nb # load and query id 0 collection_w.load() collection_w.query(tmp_expr, check_task=CheckTasks.check_query_empty)
def test_index_field_name_invalid(self, field_name): """ target: test index with error field name method: input field name expected: raise exception """ collection_name = cf.gen_unique_str(prefix) collection_w = self.init_collection_wrap(name=collection_name) log.error(iem.WrongFieldName % (str(field_name), type(field_name))) self.index_wrap.init_index( collection_w.collection, field_name, default_index_params, check_task=CheckTasks.err_res, check_items={ ct.err_code: 1, ct.err_msg: iem.WrongFieldName % (str(field_name), type(field_name).__name__) })