【重要】这个布谷鸟的库有一个坑,请尽快更新!

有没有,存不存在?这是一个很经典的场景,几乎每一位互联网开发者都遇到过。其对应的解决方案也比较多。布谷鸟过滤器就是其中一种十分流行的方案。

当然,本篇并不是来介绍布谷鸟过滤器的原理,而是记录一个老许在使用一个布谷鸟过滤器开源库时遇到的坑。如果有读者和老许使用了相同的开源库,请及时更新最新的代码以避免线上panic。当然,如果是自实现的布谷鸟过滤器,老许愿称你为:

【重要】这个布谷鸟的库有一个坑,请尽快更新!

其他废话不多说,我们来看一下这个开源库和坑分别是什么。

开源库

https://github.com/seiflotfy/cuckoofilter

func TestService_getInstalledApps(t *testing.T) {
    // 从缓存或者其他地方取出布谷鸟过滤器的数据,解析得到布谷鸟过滤器实例
 c, err := cuckoo.Decode([]byte(""))
 assert.Nil(t, err)
 // 查询 test 是否存在
 assert.False(t, c.Lookup([]byte("test")))
}

上面这个单元测试是无法正常运行的,其结果如下:

--- FAIL: TestService_getInstalledApps (0.00s)
panic: runtime error: index out of range [3532051776] with length 0 [recovered]
 panic: runtime error: index out of range [3532051776] with length 0

goroutine 19 [running]:
testing.tRunner.func1.2({0x102e36060, 0x140000e4240})
 /usr/local/go/src/testing/testing.go:1209 +0x258
testing.tRunner.func1(0x140000fe680)
 /usr/local/go/src/testing/testing.go:1212 +0x284
panic({0x102e36060, 0x140000e4240})
 /usr/local/go/src/runtime/panic.go:1038 +0x21c

根据上面的单元测试,我们下面分别看一看DecodeLookup函数。

Decode函数

// Decode returns a Cuckoofilter from a byte slice
func Decode(bytes []byte) (*Filter, error) {
 var count uint
 if len(bytes)%bucketSize != 0 {
  return nil, fmt.Errorf("expected bytes to be multiple of %d, got %d", bucketSize, len(bytes))
 }
 buckets := make([]bucket, len(bytes)/4)
 for i, b := range buckets {
  for j := range b {
   index := (i * len(b)) + j
   if bytes[index] != 0 {
    buckets[i][j] = fingerprint(bytes[index])
    count++
   }
  }
 }
 return &Filter{
  buckets:   buckets,
  count:     count,
  bucketPow: uint(bits.TrailingZeros(uint(len(buckets)))),
 }, nil
}

首先,检查输入是否为bucketSize的倍数,如果不是则输入不合法,如果是则构建布谷鸟实例。然而,这里有一个隐含条件是空字符串的长度一定是bucketSize的倍数,这也就导致构建的布谷鸟实例中buckets的长度为0,同时也为后续的panic埋下了伏笔。

这里额外提一句,bits.TrailingZeros函数在惊!Go里面居然有这样精妙的小函数!中提到过,其作用是返回输入值最低位为0的个数。所以,输入值为0时,则返回值为32或者64

Lookup函数

// Lookup returns true if data is in the counter
func (cf *Filter) Lookup(data []byte) bool {
 i1, fp := getIndexAndFingerprint(data, cf.bucketPow)
 if cf.buckets[i1].getFingerprintIndex(fp) > -1 {
  return true
 }
 i2 := getAltIndex(fp, i1, cf.bucketPow)
 return cf.buckets[i2].getFingerprintIndex(fp) > -1
}

上面代码中getIndexAndFingerprint函数返回需要使用的bucket索引和指纹。而根据前文知buckets长度为0,所以你的i1值无论是什么必定会因为cf.buckets[i1]的调用而造成panic。

以上,就是这个布谷鸟开源库的一个坑!要解决这个坑也很简单,只需要在检测到输入为空时返回不合法的错误即可。万万没想到,这个坑如此简单,那么这个pr老许我要定了!

https://github.com/seiflotfy/cuckoofilter/pull/46

【重要】这个布谷鸟的库有一个坑,请尽快更新!

该开源库最新的代码已合入老许的改动,请使用该开源库的同学及时更新代码。

最后,衷心希望本文能够对各位读者有一定的帮助。


原文始发于微信公众号(Gopher指北):【重要】这个布谷鸟的库有一个坑,请尽快更新!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/29504.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!