-
Notifications
You must be signed in to change notification settings - Fork 64
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
分库分表:merger 排序实现 #166
分库分表:merger 排序实现 #166
Conversation
Codecov Report
@@ Coverage Diff @@
## dev #166 +/- ##
==========================================
+ Coverage 79.69% 79.82% +0.13%
==========================================
Files 30 32 +2
Lines 2354 2563 +209
==========================================
+ Hits 1876 2046 +170
- Misses 394 423 +29
- Partials 84 94 +10
📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more |
这个思路是没问题,不过有几个点:
数据类型的转换,在 sql 包里面是 convertAssignRows 这个东西。所以基本上就是抄一遍这个方法。实在无力吐槽这个关键的方法它居然是私有的。 这里面是有隐患的,就是你在 Next 的时候就需要取出来数据,然后临时存着。理论上来说用 []byte 不会有什么问题,但是我觉得这里面可能有不少的坑。 |
你考虑这样一个场景,就是分页的场景,比如说排序之后取(LIMIT 100,10),那么你完全不需要全部排序一遍,而是就只排序一部分。 |
那么 Scan 里面,就调用你抄过来的那个 ConvertAndAssign 就可以。 |
internal/merger/sortmerger/merger.go
Outdated
DESC Order = false | ||
) | ||
|
||
type Merger[T any] struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这个 Merger 肯定不可能是泛型的。注意,在 merger 这个包,它完全没有任何将数据转化为结构体的概念。
迭代过程中的错误处理我是这样搞的,无论是排序过程中遇到的,还是sql.rows的Next方法遇到的错误。都会结束迭代,调用close方法然后返回false。 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@longyue0521 @Stone-afk 麻烦看看。@Stone-afk 你会是主要用户,所以你看看是否符合你的预期
}{ | ||
|
||
{ | ||
name: "排序列id ASC_多个sqlRows_每个sqlRows返回行数相同_交叉读取各个sqlRows", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
你对交叉读的定义是什么? sqlRows[1~3]每个有两行数据,读取顺序为1-2->3, 1-2-3这样读你给它一个名字叫什么?
你可以不用我给的名字,你自己起名字但是要对应好场景并始终保持一致,你现在的“交叉读”的意义是不一致的.
请给出 “轮训”“顺序”“交叉”的实例, 假设有3个sqlRows,每个sqlRows返回2行数据.
1.1 1.2 2.1 2.2 3.1 3.2 叫什么?
1.1 2.1 3.1 1.2 2.2 3.2 叫什么?
1.1 2.1 2.2 3.1 1.2 3.2 叫什么?
明确定义后,自行校对一下测试用例是否符合你的定义.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
就两种,顺序一个sql.rows读完再读另外一个直至全部读完,交叉就是一个读了部分再读其他
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
可以,顺序读定义明确;交叉要不要分为部分交叉和全交叉?为什么不要?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
我改改
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
那我再加一个部分交叉的例子:有一行一次性全部读完。两行的交叉着读
}, | ||
}, | ||
{ | ||
name: "排序列id ASC_多个sqlRows_部分sqlRows返回行数相同_顺序读取各个sqlRows", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
既然你不想重新设计这个测试用例(#166 (comment)) ,那就补充缺少的测试用例以覆盖 —— 各个sqlRows返回行数都不同的场景
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
我下面我记得写了一个呀
}, | ||
}, | ||
{ | ||
name: "排序列id ASC_部分sqlRows返回行数相同_顺序读取各个sqlRows", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#166 (comment) 这个问题仍然没有解决,把你觉得写了的那个用例的行数发一下。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
我觉得 longyue 的想法是说,在这个测试用例设计上,有几个因素会影响 Next 在不同的 sql.Rows 里面切换:
- 完全交叉、部分交叉和完全不交叉
- 行数,部分数量比较少的行会提前结束,那么其它 Rows 依旧能够正确被读取完毕。那么极端的情况就是大家行数都不同,行数都相同;再结合行数可以为0,那么可以是头几个为行数为0,后几个行数为0,中间几个行数为0
有一些你在这个测试用例里面可以不体现的,就是排序列是正序还是倒序,以及多少个排序列。这部分是你的 heap 实现来保证的,这边有一个简单的例子就差不多——主要是确保 Heap 和 Next 运作正常。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- 完全交叉,行数相同
- 完全交叉,行数部分不同
- 完全交叉,行数完全不同
- 部分交叉,行数相同
- 部分交叉, 行数部分不同
- 部分交叉,行数完全不同
- 顺序读,行数相同
- 顺序读,行数部分不同
- 顺序读,行数完全不同
// 这边就不分交叉读什么的了,上面已经讨论过了,以完全交叉读为例 - 返回的行数为0,在前面
- 返回的行数为0,在中间
- 返回的行数为0,在后面
- 返回的行数全部为0
- 排序列返回的顺序和数据库里的字段顺序不一致
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@longyue0521 @flycash 这样搞你们觉得还行不
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
有一些你在这个测试用例里面可以不体现的,就是排序列是正序还是倒序,以及多少个排序列。这部分是你的 heap 实现来保证的,这边有一个简单的例子就差不多——主要是确保 Heap 和 Next 运作正常。
我赞同 @flycash的建议,但这是有前提的——你抽取出去的heap有对应的测试覆盖来保证。现在的情况是抽取出的heap没有对应用的测试测试,所以这里只能用类似于”集成测试“或粒度更大的单元测试的概念来保证正确性。
返回的行数为0,在前面
返回的行数为0,在中间
返回的行数为0,在后面
返回的行数全部为0
这几种情况都可以通过精心设计测试数据,融入到 1~9 测试用例中,比如返回的行数全部为0
就是XXXX,行数相同
的一种特例。中间/前面返回的行数为0
可以体现在xxx,行数完全不同
或xxx,行数部分不同
中。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
我觉得可以的。这边我比较建议你把和排序本身相关的挪过去 heap 那边。然后这边集中测试不同行数,以及交叉情况的用例
}, | ||
}, | ||
{ | ||
name: "排序列id ASC,name ASC_每个sqlRows行数不同_全部交叉读取各个sqlRows", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
每个slqRows返回行数都不同
}, | ||
}, | ||
{ | ||
name: "排序列id ASC,name DESC_每个sqlRows行数相同_全部交叉读取各个sqlRows", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
每个slqRows返回行数都相同
}, | ||
}, | ||
{ | ||
name: "排序列name ASC,id DESC(排序列出现的顺序和sqlRows的顺序不同)_返回的行数全部相同_全部交叉读取各个sqlRows", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
你把各个用例的name都抽取出来,放在一起比较一下,name的命名规则/模式要保持一致,请仔细校验后续全部name,将其改为统一命名形式,保证测试的可读性。
通过仔细阅读这些name看看你还缺哪些用例,比如:id DESC, name DESC
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
名字可以取得比较简洁,然后如果觉得名字不够细节,那么在前面加上一个注释也可以。
}, | ||
}, | ||
{ | ||
name: "排序列id ASC,name ASC_其中有一个sqlRows的数据为空的在中间_部分sqlRows返回行数相同_全部交叉读取各个sqlRows", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
其中有一个sqlRows的数据为空的在中间
去掉
}, | ||
}, | ||
{ | ||
name: "部分sqlRows返回的行数为0,在前面", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
按照我上一个留言,将”返回行为0“出现的位置,融入到之前的测试用例中。再次检查name的命名,使其保持一致、可读性。
assert.Equal(ms.T(), cols, columns) | ||
} | ||
|
||
func (ms *MergerSuite) TestRows_Close() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
设计测试用例,覆盖Close返回mulitierror的情况。
当前实现缺少的测试:
@juniaoshaonian 读源码或者实际测试一下sql.Rows在上面各种情况的表现,尽可能做到与sql.Rows语义一致。 |
这边测试的话,拆成不同的方法,比如说 Err 一个方法, Err 和 Scan 交叉的再拆出来一个方法。这样我们的 test case 的名字就不至于非常长。这种组织方式,也更加容易理解。 另外有一个测试,就是 Err 不会 nil 之后再次尝试 Scan 会发生什么?或者说在 Scan 中间发现了 Err,继续 Scan 会怎么办。 |
关于并发测试应该怎么测,我的理解我们保证的是单个Next或者Scan,他们两和起来不是并发安全的,sql.rows里面的Next,Scan方法都加了读写锁。但是这个咋测单个Next和Scan是并发安全的,大佬们有没有想法聊聊呀。 |
并发测试这边不需要花太大精力,靠 review 来。其实大部分情况下 review 能够保证基本正确,尤其是在这里加读写锁,并发逻辑并不复杂。而奇诡的并发,我只能说你测也测不出来,看也看不出来。 并发这种就有一点,尽人事看天命的感觉 |
合并一下最新代码 |
scan方法我没有实现