Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add determine single namespace item num limit logic #5227

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Apollo 2.4.0
* [Fix: Resolve issues with duplicate comments and blank lines in configuration management](https://github.com/apolloconfig/apollo/pull/5232)
* [Fix link namespace published items show missing some items](https://github.com/apolloconfig/apollo/pull/5240)
* [Feature: Add limit and whitelist for namespace count per appid+cluster](https://github.com/apolloconfig/apollo/pull/5228)
* [Feature added limit the number of items under a single namespace](https://github.com/apolloconfig/apollo/pull/5228)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* [Feature added limit the number of items under a single namespace](https://github.com/apolloconfig/apollo/pull/5228)
* [Feature add limit for items count per namespace](https://github.com/apolloconfig/apollo/pull/5227)


------------------
All issues and pull requests are [here](https://github.com/apolloconfig/apollo/milestone/15?closed=1)
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.ctrip.framework.apollo.adminservice.controller;

import com.ctrip.framework.apollo.adminservice.aop.PreAcquireNamespaceLock;
import com.ctrip.framework.apollo.biz.config.BizConfig;
import com.ctrip.framework.apollo.biz.entity.Commit;
import com.ctrip.framework.apollo.biz.entity.Item;
import com.ctrip.framework.apollo.biz.entity.Namespace;
Expand Down Expand Up @@ -58,13 +59,14 @@ public class ItemController {
private final NamespaceService namespaceService;
private final CommitService commitService;
private final ReleaseService releaseService;
private final BizConfig bizConfig;


public ItemController(final ItemService itemService, final NamespaceService namespaceService, final CommitService commitService, final ReleaseService releaseService) {
public ItemController(final ItemService itemService, final NamespaceService namespaceService, final CommitService commitService, final ReleaseService releaseService, final BizConfig bizConfig) {
this.itemService = itemService;
this.namespaceService = namespaceService;
this.commitService = commitService;
this.releaseService = releaseService;
this.bizConfig = bizConfig;
}

@PreAcquireNamespaceLock
Expand All @@ -78,6 +80,14 @@ public ItemDTO create(@PathVariable("appId") String appId,
if (managedEntity != null) {
throw BadRequestException.itemAlreadyExists(entity.getKey());
}

if (bizConfig.isItemNumLimitEnabled()) {
int itemCount = itemService.findNonEmptyItemCount(entity.getNamespaceId());
if (itemCount >= bizConfig.itemNumLimit()) {
throw new BadRequestException("current namespace item count=[" + itemCount + "], single namespace max allow count=[ " + bizConfig.itemNumLimit() + "]");
Copy link
Contributor

@coderabbitai coderabbitai bot Sep 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance exception message for better user understanding

The exception message could be more user-friendly and clearer to end-users.

Apply this diff to improve the exception message:

-throw new BadRequestException("current namespace item count=[" + itemCount + "], single namespace max allow count=[ " + bizConfig.itemNumLimit() + "]");
+throw new BadRequestException("The maximum number of items (" + bizConfig.itemNumLimit() + ") for this namespace has been reached. Current item count is " + itemCount + ".");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
throw new BadRequestException("current namespace item count=[" + itemCount + "], single namespace max allow count=[ " + bizConfig.itemNumLimit() + "]");
throw new BadRequestException("The maximum number of items (" + bizConfig.itemNumLimit() + ") for this namespace has been reached. Current item count is " + itemCount + ".");

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a valid suggestion.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nobodyiam, I'm glad you found the suggestion helpful!

(_/)
(^_^)
/ \

}
}

nobodyiam marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Encapsulate item limit logic within ItemService

To maintain separation of concerns, consider moving the item count limit check into the ItemService. This ensures that all item creation operations adhere to the limit, promoting consistency across the application.

entity = itemService.save(entity);
dto = BeanUtils.transform(ItemDTO.class, entity);
commitService.createCommit(appId, clusterName, namespaceName, new ConfigChangeContentBuilder().createItem(entity).build(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ public class BizConfig extends RefreshableConfig {

private static final int DEFAULT_MAX_NAMESPACE_NUM = 200;

private static final int DEFAULT_MAX_ITEM_NUM = 1000;

private static final int DEFAULT_APPNAMESPACE_CACHE_REBUILD_INTERVAL = 60; //60s
private static final int DEFAULT_GRAY_RELEASE_RULE_SCAN_INTERVAL = 60; //60s
private static final int DEFAULT_APPNAMESPACE_CACHE_SCAN_INTERVAL = 1; //1s
Expand Down Expand Up @@ -117,6 +119,15 @@ public Set<String> namespaceNumLimitWhite() {
return Sets.newHashSet(getArrayProperty("namespace.num.limit.white", new String[0]));
}

public boolean isItemNumLimitEnabled() {
return getBooleanProperty("item.num.limit.enabled", false);
}

public int itemNumLimit() {
int limit = getIntProperty("item.num.limit", DEFAULT_MAX_ITEM_NUM);
return checkInt(limit, 5, Integer.MAX_VALUE, DEFAULT_MAX_ITEM_NUM);
nobodyiam marked this conversation as resolved.
Show resolved Hide resolved
}

public Map<Long, Integer> namespaceValueLengthLimitOverride() {
String namespaceValueLengthOverrideString = getValue("namespace.value.length.limit.override");
Map<Long, Integer> namespaceValueLengthOverride = Maps.newHashMap();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,7 @@ public interface ItemRepository extends PagingAndSortingRepository<Item, Long> {
@Query("update Item set IsDeleted = true, DeletedAt = ROUND(UNIX_TIMESTAMP(NOW(4))*1000), DataChange_LastModifiedBy = ?2 where NamespaceId = ?1 and IsDeleted = false")
int deleteByNamespaceId(long namespaceId, String operator);

@Query("select count(*) from Item where namespaceId = :namespaceId and key <>''")
int countByNamespaceIdAndFilterKeyEmpty(@Param("namespaceId") long namespaceId);
nobodyiam marked this conversation as resolved.
Show resolved Hide resolved

}
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ public List<Item> findItemsModifiedAfterDate(long namespaceId, Date date) {
return itemRepository.findByNamespaceIdAndDataChangeLastModifiedTimeGreaterThan(namespaceId, date);
}

public int findNonEmptyItemCount(long namespaceId) {
return itemRepository.countByNamespaceIdAndFilterKeyEmpty(namespaceId);
}
Comment on lines +138 to +140
Copy link
Contributor

@coderabbitai coderabbitai bot Oct 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Approve new method, but item.num.limit implementation is incomplete.

The new findNonEmptyItemCount method is correctly implemented to count non-empty items in a namespace. However, the item.num.limit feature mentioned in the PR objectives is not fully implemented yet.

To complete the item.num.limit feature:

  1. Add a method in BizConfig to retrieve the item.num.limit value.
  2. Implement logic in methods like save and update to check against this limit before adding or modifying items.
  3. Update relevant controllers to handle cases where the item limit is reached.

Example implementation in the save method:

@Transactional
public Item save(Item entity) {
    // Existing checks...
    
    long namespaceId = entity.getNamespaceId();
    int currentItemCount = findNonEmptyItemCount(namespaceId);
    int itemNumLimit = bizConfig.getItemNumLimit(namespaceId);
    
    if (currentItemCount >= itemNumLimit) {
        throw new BadRequestException("Item limit reached for this namespace. Limit: " + itemNumLimit);
    }
    
    // Existing save logic...
}

Similar checks should be added to other relevant methods.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Batch modification will result in a large number of queries to the DB

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@youngzil, I understand your concern about the potential performance impact when batch modifications result in numerous database queries. Thank you for bringing this to my attention.


✏️ Learnings added
Learnt from: youngzil
PR: apolloconfig/apollo#5227
File: apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/service/ItemService.java:138-140
Timestamp: 2024-10-07T09:49:21.175Z
Learning: When implementing item count limits, batch modifications can result in a large number of database queries, leading to performance issues.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


public Page<Item> findItemsByKey(String key, Pageable pageable) {
return itemRepository.findByKey(key, pageable);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/
package com.ctrip.framework.apollo.biz.service;

import com.ctrip.framework.apollo.biz.config.BizConfig;
import com.ctrip.framework.apollo.biz.entity.Audit;
import com.ctrip.framework.apollo.biz.entity.Item;
import com.ctrip.framework.apollo.biz.entity.Namespace;
Expand All @@ -25,6 +26,7 @@
import com.ctrip.framework.apollo.common.exception.BadRequestException;
import com.ctrip.framework.apollo.common.exception.NotFoundException;
import com.ctrip.framework.apollo.common.utils.BeanUtils;
import com.ctrip.framework.apollo.core.utils.StringUtils;
import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -38,20 +40,23 @@ public class ItemSetService {
private final CommitService commitService;
private final ItemService itemService;
private final NamespaceService namespaceService;
private final BizConfig bizConfig;

public ItemSetService(
final AuditService auditService,
final CommitService commitService,
final ItemService itemService,
final NamespaceService namespaceService) {
final NamespaceService namespaceService,
final BizConfig bizConfig) {
this.auditService = auditService;
this.commitService = commitService;
this.itemService = itemService;
this.namespaceService = namespaceService;
this.bizConfig = bizConfig;
}

@Transactional
public ItemChangeSets updateSet(Namespace namespace, ItemChangeSets changeSets){
public ItemChangeSets updateSet(Namespace namespace, ItemChangeSets changeSets) {
return updateSet(namespace.getAppId(), namespace.getClusterName(), namespace.getNamespaceName(), changeSets);
}

Expand All @@ -64,6 +69,16 @@ public ItemChangeSets updateSet(String appId, String clusterName,
throw NotFoundException.namespaceNotFound(appId, clusterName, namespaceName);
}

if (bizConfig.isItemNumLimitEnabled()) {
int itemCount = itemService.findNonEmptyItemCount(namespace.getId());
int createItemCount = (int) changeSet.getCreateItems().stream().filter(item -> !StringUtils.isEmpty(item.getKey())).count();
int deleteItemCount = (int) changeSet.getDeleteItems().stream().filter(item -> !StringUtils.isEmpty(item.getKey())).count();
Comment on lines +74 to +75
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Clarify variable names for better readability

The variables createItemCount and deleteItemCount represent the number of items to be added and removed. For improved code readability, consider renaming them to newItemCount and removedItemCount, respectively.

itemCount = itemCount + createItemCount - deleteItemCount;
if (itemCount > bizConfig.itemNumLimit()) {
throw new BadRequestException("current namespace item count=[" + itemCount + "], single namespace max allow count=[ " + bizConfig.itemNumLimit() + "]");
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Address potential concurrency issues in item count validation

The current logic for enforcing the item number limit may encounter concurrency issues. If multiple requests modify the namespace items simultaneously, the item count retrieved might be outdated by the time the validation occurs, potentially leading to inconsistent states or exceeding the limit unintentionally.

To ensure consistency and prevent race conditions, consider implementing synchronization mechanisms such as database-level locks, optimistic locking, or wrapping this operation within a transaction with an appropriate isolation level.


String operator = changeSet.getDataChangeLastModifiedBy();
ConfigChangeContentBuilder configChangeContentBuilder = new ConfigChangeContentBuilder();

Expand All @@ -84,7 +99,7 @@ public ItemChangeSets updateSet(String appId, String clusterName,

if (configChangeContentBuilder.hasContent()) {
commitService.createCommit(appId, clusterName, namespaceName, configChangeContentBuilder.build(),
changeSet.getDataChangeLastModifiedBy());
changeSet.getDataChangeLastModifiedBy());
}

return changeSet;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*
* Copyright 2024 Apollo Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.ctrip.framework.apollo.biz.service;

import static org.mockito.Mockito.when;

import com.ctrip.framework.apollo.biz.AbstractIntegrationTest;
import com.ctrip.framework.apollo.biz.config.BizConfig;
import com.ctrip.framework.apollo.biz.entity.Item;
import com.ctrip.framework.apollo.biz.entity.Namespace;
import com.ctrip.framework.apollo.common.dto.ItemChangeSets;
import com.ctrip.framework.apollo.common.dto.ItemDTO;
import com.ctrip.framework.apollo.common.exception.BadRequestException;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.util.ReflectionTestUtils;

public class ItemSetServiceTest extends AbstractIntegrationTest {

@Mock
private BizConfig bizConfig;

@Autowired
private ItemService itemService;
@Autowired
private NamespaceService namespaceService;

@Autowired
private ItemSetService itemSetService;

@Test
@Sql(scripts = "/sql/itemset-test.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public void testUpdateSetWithoutItemNumLimit() {

ReflectionTestUtils.setField(itemSetService, "bizConfig", bizConfig);
when(bizConfig.isItemNumLimitEnabled()).thenReturn(false);
when(bizConfig.itemNumLimit()).thenReturn(5);

Namespace namespace = namespaceService.findOne(1L);

ItemChangeSets changeSets = new ItemChangeSets();
changeSets.addCreateItem(buildNormalItem(0L, namespace.getId(), "k6", "v6", "test item num limit", 6));
changeSets.addCreateItem(buildNormalItem(0L, namespace.getId(), "k7", "v7", "test item num limit", 7));

try {
itemSetService.updateSet(namespace, changeSets);
} catch (Exception e) {
Assert.fail();
}

int size = itemService.findNonEmptyItemCount(namespace.getId());
Assert.assertEquals(7, size);

}

@Test
@Sql(scripts = "/sql/itemset-test.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public void testUpdateSetWithItemNumLimit() {

ReflectionTestUtils.setField(itemSetService, "bizConfig", bizConfig);
when(bizConfig.isItemNumLimitEnabled()).thenReturn(true);
when(bizConfig.itemNumLimit()).thenReturn(5);

Namespace namespace = namespaceService.findOne(1L);
Item item9901 = itemService.findOne(9901);
Item item9902 = itemService.findOne(9902);

ItemChangeSets changeSets = new ItemChangeSets();
changeSets.addUpdateItem(buildNormalItem(item9901.getId(), item9901.getNamespaceId(), item9901.getKey(), item9901.getValue() + " update", item9901.getComment(), item9901.getLineNum()));
changeSets.addDeleteItem(buildNormalItem(item9902.getId(), item9902.getNamespaceId(), item9902.getKey(), item9902.getValue() + " update", item9902.getComment(), item9902.getLineNum()));
changeSets.addCreateItem(buildNormalItem(0L, item9901.getNamespaceId(), "k6", "v6", "test item num limit", 6));
changeSets.addCreateItem(buildNormalItem(0L, item9901.getNamespaceId(), "k7", "v7", "test item num limit", 7));

try {
itemSetService.updateSet(namespace, changeSets);
Assert.fail();
} catch (Exception e) {
Assert.assertTrue(e instanceof BadRequestException);
}

int size = itemService.findNonEmptyItemCount(namespace.getId());
Assert.assertEquals(5, size);

}

@Test
@Sql(scripts = "/sql/itemset-test.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public void testUpdateSetWithItemNumLimit2() {

ReflectionTestUtils.setField(itemSetService, "bizConfig", bizConfig);
when(bizConfig.isItemNumLimitEnabled()).thenReturn(true);
when(bizConfig.itemNumLimit()).thenReturn(5);

Namespace namespace = namespaceService.findOne(1L);
Item item9901 = itemService.findOne(9901);
Item item9902 = itemService.findOne(9902);

ItemChangeSets changeSets = new ItemChangeSets();
changeSets.addUpdateItem(buildNormalItem(item9901.getId(), item9901.getNamespaceId(), item9901.getKey(), item9901.getValue() + " update", item9901.getComment(), item9901.getLineNum()));
changeSets.addDeleteItem(buildNormalItem(item9902.getId(), item9902.getNamespaceId(), item9902.getKey(), item9902.getValue() + " update", item9902.getComment(), item9902.getLineNum()));
changeSets.addCreateItem(buildNormalItem(0L, item9901.getNamespaceId(), "k6", "v6", "test item num limit", 6));

try {
itemSetService.updateSet(namespace, changeSets);
} catch (Exception e) {
Assert.fail();
}

int size = itemService.findNonEmptyItemCount(namespace.getId());
Assert.assertEquals(5, size);

}


private ItemDTO buildNormalItem(Long id, Long namespaceId, String key, String value, String comment, int lineNum) {
ItemDTO item = new ItemDTO(key, value, comment, lineNum);
item.setId(id);
item.setNamespaceId(namespaceId);
return item;
}

}
25 changes: 25 additions & 0 deletions apollo-biz/src/test/resources/sql/itemset-test.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
--
-- Copyright 2024 Apollo Authors
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--

INSERT INTO "Namespace" (`Id`, `AppId`, `ClusterName`, `NamespaceName`, `IsDeleted`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(1,'testApp', 'default', 'application', 0, 'apollo', 'apollo');

INSERT INTO "Item" (`Id`, `NamespaceId`, "Key", "Type", "Value", `Comment`, `LineNum`)
VALUES
(9901, 1, 'k1', 0, 'v1', '', 1),
(9902, 1, 'k2', 2, 'v2', '', 2),
(9903, 1, 'k3', 0, 'v3', '', 3),
(9904, 1, 'k4', 0, 'v4', '', 4),
(9905, 1, 'k5', 0, 'v5', '', 5);
Binary file added doc/images/item-num-limit-enabled.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/images/item-num-limit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 16 additions & 0 deletions docs/en/portal/apollo-user-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,22 @@ Starting from version 2.4.0, apollo-portal provides the function of checking the



## 6.4 单个命名空间下的配置项数量限制
Starting from version 2.4.0, apollo-portal provides the function of limiting the number of configuration items in a single namespace. This function is disabled by default and needs to be enabled by configuring the system `item.num.limit.enabled`. At the same time, the system parameter `item.num.limit` is provided to dynamically configure the upper limit of the number of items in a single Namespace.

**Setting method:**
1. Log in to the Apollo Configuration Center interface with a super administrator account
2. Go to the `Administrator Tools - System Parameters - ConfigDB Configuration Management` page and add or modify the `item.num.limit.enabled` configuration item to true/false to enable/disable this function.

[//]: # ( ![item-num-limit-enabled]&#40;https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/item-num-limit-enabled.png&#41;)
![item-num-limit-enabled](../../../doc/images/item-num-limit-enabled.png)
3. Go to the `Admin Tools - System Parameters - ConfigDB Configuration Management` page and add or modify the `item.num.limit` configuration item to configure the upper limit of the number of items under a single Namespace.

[//]: # ( ![item-num-limit]&#40;https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/item-num-limit.png&#41;)
![item-num-limit](../../../doc/images/item-num-limit.png)



# VII. Best practices

## 7.1 Security Related
Expand Down
17 changes: 17 additions & 0 deletions docs/zh/portal/apollo-user-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,23 @@ Apollo从1.6.0版本开始增加访问密钥机制,从而只有经过身份验



## 6.5 单个命名空间下的配置项数量限制
从2.4.0版本开始,apollo-portal提供了限制单个命名空间下的配置项数量的功能,此功能默认关闭,需要配置系统 `item.num.limit.enabled` 开启,同时提供了系统参数`item.num.limit`来动态配置单个Namespace下的item数量上限值

**设置方法:**
1. 用超级管理员账号登录到Apollo配置中心的界面
2. 进入`管理员工具 - 系统参数 - ConfigDB 配置管理`页面新增或修改`item.num.limit.enabled`配置项为true/false 即可开启/关闭此功能

[//]: # ( ![item-num-limit-enabled]&#40;https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/item-num-limit-enabled.png&#41;)
![item-num-limit-enabled](../../../doc/images/item-num-limit-enabled.png)
3. 进入`管理员工具 - 系统参数 - ConfigDB 配置管理`页面新增或修改`item.num.limit`配置项来配置单个Namespace下的item数量上限值

[//]: # ( ![item-num-limit]&#40;https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/item-num-limit.png&#41;)
![item-num-limit](../../../doc/images/item-num-limit.png)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Inconsistent image references need attention.

There are inconsistencies in the image references:

  1. Line 512-513: Uses a CDN link (commented out) and a relative path.
  2. Line 516-517: Same issue as above.

Please standardize the image references. If using relative paths, ensure they are correct and consistent throughout the document.





Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider reorganizing the document structure.

The new section 6.5 is added at the end of section 6. Consider:

  1. Reviewing the ordering of subsections in section 6 to ensure logical flow.
  2. Adding a brief introduction at the beginning of section 6 to overview all the "other function configurations".
  3. Updating the table of contents (if exists) to include this new section.

# 七、最佳实践

## 7.1 安全相关
Expand Down
Loading