一个排序引发的BUG (3)

return a1.order > a2.order ? 1:-1

那么这一块的代码,整体就会变成这样:

一个排序引发的BUG

这样仔细一看:咦,好像还能再优化一下。78 行和 80 行是一样的,所以可以去掉 78 行。

一个排序引发的BUG

好的,经过这样的一番改造。

恭喜你,获得了一个老版本的代码:

左边是之前版本的代码,右边是现在 Master 分支的代码:

一个排序引发的BUG

为什么会发生变化,必然是有原因的。

看一眼提交记录:

一个排序引发的BUG

这次提交指向了编号为 7778 的提交:

https://github.com/apache/dubbo/pull/7778

而这次提交指向了编号为 7757 的 issue:

一个排序引发的BUG

https://github.com/apache/dubbo/issues/7757

而这个 issue 在前面提到的编号为 8055 的 issue 里也提到了:

一个排序引发的BUG

这个 issue 主要就是两张图。

第一张图是这样的:

在没有任何自定义 Filter,仅有官方原有的 Filter 的情况下,构建出来的 Filter 链,ExecuteLimitFilter 在 MonitorFilter 之前。

一个排序引发的BUG

第二张图是这样的:

一个排序引发的BUG

在加入了一系列自定义的 Filter(没有指定 Order)之后,ExecuteLimitFilter 就排在了 MonitorFilter 之后了。

至于这两个 Filter 排前排后的影响是什么,和文本关系不大,就不扩展了,你有兴趣的可以去看看对应的链接。

总之,只有这样的判断逻辑是不稳当的:

return a1.order > a2.order ? 1:-1

来个例子演示一下:

一个排序引发的BUG

左边是测试用例,右边是排序规则,下面是输出结果。

从输出结果可以看到,最终的 Filter 链取决于 list 的添加顺序。

这也就是 7757 这个 issues 说的:

list 的遍历顺序会影响到排序的顺序。

一个排序引发的BUG

因此,才会有了这样的一次提交:

一个排序引发的BUG

好,现在我们把排序顺序改回来,同样的测试用例再跑一次,就稳定了:

一个排序引发的BUG

眼睛尖的朋友可能还发现了一个问题。

这个地方还有一次提交:

一个排序引发的BUG

第一种判断:return o1.getSimpleName().compareTo(o2.getSimpleName())

第二种判断:return o1.getSimpleName().compareTo(o2.getSimpleName()) > 0 ? 1 : -1;

你说这是在干啥?

第一种判断还疏忽了这样的一种情况,包名不同但是类名相同的情况:

com.why.a.whyFilter

com.why.b.whyFilter

这个时候 o1.getSimpleName().compareTo(o2.getSimpleName()) 返回的是 0。

返回 0 会发生啥?

直接吞掉一个 Filter 你信不信?

比如你的集合是 HashSet,或者是 TreeMap。

这就巧了,Dubbo 用的就是 TreeMap。

来个测试用例演示一下。

如果采用第一种判断,最后 TreeMap 里面只有一个 Filter 了:

一个排序引发的BUG

如果采用第二种判断,最后 TreeMap 里面会有两个 Filter :

一个排序引发的BUG

细节,魔鬼都在细节里面。

哎呀,真的是防不胜防啊。

好了,比较器我就说完了,但是你发现没有,我到现在都还没给你说排序过程不稳定这个 BUG 到底是啥,只是给你引申了一个其他的 BUG 出来。

莫慌,这不是我还没想好怎么给你描述嘛。

这个过程其实比较复杂,涉及到 Timsort 排序方法,就这方法就得另起一篇文章才能说清楚。

所以,我换了一个思路,主要给你看比较的过程,至于这个过程背后的原因。

就是 Timsort 在搞鬼,欢迎你自己去探索一番。

那过程是啥呢?

我在比较方法的入口处加上这样的输出语句:

一个排序引发的BUG

五个 Filter 是这样的:

一个排序引发的BUG

测试用例是这样的:

@Test
public void whyTest(){
    List<Class> filters = new ArrayList<>();
    filters.add(Filter4.class);
    filters.add(Filter3.class);
    filters.add(Filter2.class);
    filters.add(Filter1.class);
    filters.add(Filter5.class);
    Collections.sort(filters, ActivateComparator.COMPARATOR);
    StringBuilder builder = new StringBuilder();
    for (int i = 0; i < filters.size(); i++) {
        builder.append(filters.get(i).getSimpleName()).append("->");
    }
    System.out.println(builder.toString());
}

输出的日志是这样的:

一个排序引发的BUG

发现问题了没?

首先我很心机的控制了一下 list 的添加顺序:

一个排序引发的BUG

这样前三次比较就能构建这样的 Filter 链:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zypsgd.html