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

通过openapi接口增加新配置,存在插入多条情况 #1317

Closed
fredxiangdong opened this issue Aug 2, 2018 · 18 comments
Closed

通过openapi接口增加新配置,存在插入多条情况 #1317

fredxiangdong opened this issue Aug 2, 2018 · 18 comments

Comments

@fredxiangdong
Copy link

背景描述:
1、数据库Item表无关于该环境的key唯一索引。
2、apollo-portal项目的openapi接口使用RetryableRestTemplate待重试的工具类请求apollo-adminservice相应ItemController接口。
3、apollo-adminservice相应ItemController接口,使用@PreAcquireNamespaceLock注解通过数据库获取相应锁。
4、NamespaceAcquireLockAspect 切面中关于锁逻辑
//check lock owner is current user
checkLock(namespace, namespaceLock, currentUser);
如果前后一个user,则获取锁成功

造成结果:不含唯一索引的数据库约束,在非幂等的重试策略下,多台服务器并发请求,由已由自己获取相应锁的情况下,造成插入多条配置。

@nobodyiam
Copy link
Member

这个目前确实是可能发生的,尤其是在open api的情况下。由于使用了软删机制,所以没法创建一个唯一索引,不知是否有啥好的建议?

@fredxiangdong
Copy link
Author

我们的本意是在保证数据安全的情况下,最大程度上的提高系统的并发,所以才有了这个namespace用户维度的可重入锁,但是我们未来提高并发,但却未能保证数据安全的初衷。
关于改进可采用的几种方式,我的想法是这样的:
1、直接去掉用户级别的可重入,针对配置中心的业务场景及对这个接口的使用上来说,用户级别可重入没有多大意义。
2、如果要保留用户的可重入,可以考虑namespaceLock中的粒度从namespace维度细化为key维度,namespaceId这个改变逻辑及数据库字段,通过namespaceId加key来细化锁粒度。
3、保持用户可重入另外一个方式,可以考虑去掉item的软删除,直接物理删除加上唯一索引限制,虽然物理删除有违互联网系统一惯做法,但是我们这边有commit表可以记录变更,软删除无实际意义。
4、可以考虑redis、zk这种外部分布式锁来实现方案2,但是引入额外外部依赖,有违apollo初衷尽量少的外部依赖。
关于这几个方案,或者你想如何修复这个场景呢,希望能共同探讨下。

@nobodyiam
Copy link
Member

用户级别的可重入倒不是为了防止插入多条的,主要还是为了实现某些环境(如生产环境)的配置,每次只能一个人修改,另一个人发布这样的规范。

目前会插入多条的问题主要还是由于item表中没有唯一键索引,虽然程序中每次插入前都会检查是否已经存在,不过在open api的情况下,很有可能出现同一时刻有两个一样的item插入,在这时每个工作线程都会发现数据库中并没有这个key,然后就插入了。

或许可以考虑在portal层加一个分布式锁的扩展点,对有这样场景或需求的公司,可以自己实现一个分布式锁的接入?

@fredxiangdong
Copy link
Author

了解了,感谢耐心解答,周末愉快。

@hezhaoye
Copy link

最终这个有解决方案吗

@hezhaoye
Copy link

我这边验证测试了下,通过增加唯一索引,可以解决这个问题。语句为:ALTER TABLE Item ADD UNIQUE ( NamespaceId,Key ,DataChange_CreatedTime)

@nobodyiam
Copy link
Member

#3866 增加了数据表的唯一键索引,不过 Item 表由于要支持文本模式的 comment(key 为空),所以这张表的唯一键索引后来又去除了,需要单独看下是否有合适的方案。

@nisiyong
Copy link
Member

Item表重复确实这个bug,看能否跑下单元测试复现下

@hezhaoye
Copy link

Item表重复确实这个bug,看能否跑下单元测试复现下

必现的,客户端使用多线程并发访问即可重现。

@hezhaoye
Copy link

Item表重复确实这个bug,看能否跑下单元测试复现下

String portalUrl = "http://127.0.0.1:8070/";
String nacosToken = "e383b7c099c0031ec1e0b2e3d9c6b5dd75f74532";
ApolloOpenApiClient client = ApolloOpenApiClient.newBuilder().withPortalUrl(portalUrl).withToken(nacosToken).build();

    OpenItemDTO itemDTO = new OpenItemDTO();
    itemDTO.setKey("test_05");
    itemDTO.setValue("aaaaaa");
    itemDTO.setExtension("xxxxx");
    itemDTO.setComment("bbbbbb");
    itemDTO.setDataChangeCreatedBy("hedi");


    for (int i = 0; i < 20; i++) {
        new Thread(()->{
          try {
              OpenItemDTO item = client.createItem("demo-application", "TEST", "default", "application",
                      itemDTO);
              System.out.println(">>>>>>>>>>>>处理成功");
          }catch (Exception e) {
              System.out.println(e.getMessage());
          }
        }).start();
    }

@hezhaoye
Copy link

hezhaoye commented Jul 1, 2022

#3866 增加了数据表的唯一键索引,不过 Item 表由于要支持文本模式的 comment(key 为空),所以这张表的唯一键索引后来又去除了,需要单独看下是否有合适的方案。

最近发现这2个表【AppNamespace、Namespace】也存在并发插入问题,导致select时会发生错误。请教下,这2个表是否可以增加唯一索引来解决呢

@hezhaoye
Copy link

hezhaoye commented Jul 1, 2022

改进可采用的几种方式,我的想法是这样的:

你好,你们公司最终使用了哪种方案来解决此问题,可以分享下吗

@nobodyiam
Copy link
Member

#3866 增加了数据表的唯一键索引,不过 Item 表由于要支持文本模式的 comment(key 为空),所以这张表的唯一键索引后来又去除了,需要单独看下是否有合适的方案。

最近发现这2个表【AppNamespace、Namespace】也存在并发插入问题,导致select时会发生错误。请教下,这2个表是否可以增加唯一索引来解决呢

这两个表在新版本已经加上唯一索引

@hezhaoye
Copy link

hezhaoye commented Jul 1, 2022

我看表都是通过新建一个删除时间字段作为唯一索引,感觉略显麻烦,是否可以使用本身的创建时间字段作为索引字段呢,例如App表中的UNIQUE KEY UK_AppId_DeletedAt (AppId,DeletedAt), 改为 UNIQUE KEY UK_AppId_DataChange_CreatedTime (AppId,DataChange_CreatedTime)呢

@nobodyiam
Copy link
Member

是否可以使用本身的创建时间字段作为索引字段呢,例如App表中的UNIQUE KEY UK_AppId_DeletedAt (AppId,DeletedAt), 改为 UNIQUE KEY UK_AppId_DataChange_CreatedTime (AppId,DataChange_CreatedTime)呢

这个只能解决在同一秒(DataChange_CreatedTime 目前配置的精度是到秒)插入两个 appid 数据的问题,不同时间插入同样 appid 还是无法解决的

@hezhaoye
Copy link

hezhaoye commented Jul 4, 2022

是否可以使用本身的创建时间字段作为索引字段呢,例如App表中的UNIQUE KEY UK_AppId_DeletedAt (AppId,DeletedAt), 改为 UNIQUE KEY UK_AppId_DataChange_CreatedTime (AppId,DataChange_CreatedTime)呢

这个只能解决在同一秒(DataChange_CreatedTime 目前配置的精度是到秒)插入两个 appid 数据的问题,不同时间插入同样 appid 还是无法解决的

那item的重复问题,目前是不是还没有比较好的解决方案呢

@liutao5121
Copy link

liutao5121 commented Sep 7, 2023

我用的是apollo2.0.1版本的,正好注册中心是zookeeper,最近也发现了这个问题,打算用zk分布式锁来解决

@elvislou
Copy link

我用的是apollo2.0.1版本的,正好注册中心是zookeeper,最近也发现了这个问题,打算用zk分布式锁来解决

具体怎么解决啊,自己修改portal的代码?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants