Skip to content

Commit 6a8ba48

Browse files
authored
Merge pull request #519 from proost/feat-counting-bloom-filter
feat: add counting bloom filter
2 parents 95ec07c + f959dc3 commit 6a8ba48

File tree

4 files changed

+2547
-27
lines changed

4 files changed

+2547
-27
lines changed

rueidisprob/README.md

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,80 @@ func main() {
5757
fmt.Println(exists) // true
5858
}
5959
```
60+
61+
### Counting Bloom Filter
62+
63+
It is a variation of the standard Bloom filter that adds a counting mechanism to each element.
64+
This allows for the filter to count the number of times an element has been added to the filter.
65+
And it allows for the removal of elements from the filter.
66+
67+
Example:
68+
69+
```go
70+
71+
package main
72+
73+
import (
74+
"context"
75+
"fmt"
76+
77+
"github.com/redis/rueidis"
78+
"github.com/redis/rueidis/rueidisprob"
79+
)
80+
81+
func main() {
82+
client, err := rueidis.NewClient(rueidis.ClientOption{
83+
InitAddress: []string{"localhost:6379"},
84+
})
85+
if err != nil {
86+
panic(err)
87+
}
88+
89+
cbf, err := rueidisprob.NewCountingBloomFilter(client, "counting_bloom_filter", 1000, 0.01)
90+
91+
err = cbf.Add(context.Background(), "hello")
92+
if err != nil {
93+
panic(err)
94+
}
95+
96+
err = cbf.Add(context.Background(), "world")
97+
if err != nil {
98+
panic(err)
99+
}
100+
101+
exists, err := cbf.Exists(context.Background(), "hello")
102+
if err != nil {
103+
panic(err)
104+
}
105+
fmt.Println(exists) // true
106+
107+
exists, err = cbf.Exists(context.Background(), "world")
108+
if err != nil {
109+
panic(err)
110+
}
111+
fmt.Println(exists) // true
112+
113+
count, err := cbf.Count(context.Background())
114+
if err != nil {
115+
panic(err)
116+
}
117+
fmt.Println(count) // 2
118+
119+
err = cbf.Remove(context.Background(), "hello")
120+
if err != nil {
121+
panic(err)
122+
}
123+
124+
exists, err = cbf.Exists(context.Background(), "hello")
125+
if err != nil {
126+
panic(err)
127+
}
128+
fmt.Println(exists) // false
129+
130+
count, err = cbf.Count(context.Background())
131+
if err != nil {
132+
panic(err)
133+
}
134+
fmt.Println(count) // 1
135+
}
136+
```

rueidisprob/bloomfilter.go

Lines changed: 15 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const (
1515
)
1616

1717
const (
18-
addMultiScript = `
18+
bloomFilterAddMultiScript = `
1919
local hashIterations = tonumber(ARGV[1])
2020
local numElements = tonumber(#ARGV) - 1
2121
local filterKey = KEYS[1]
@@ -48,7 +48,7 @@ end
4848
return redis.call('INCRBY', counterKey, counter)
4949
`
5050

51-
existsMultiScript = `
51+
bloomFilterExistsMultiScript = `
5252
local hashIterations = tonumber(ARGV[1])
5353
local numElements = tonumber(#ARGV) - 1
5454
local filterKey = KEYS[1]
@@ -79,7 +79,7 @@ end
7979
return result
8080
`
8181

82-
resetScript = `
82+
bloomFilterResetScript = `
8383
local filterKey = KEYS[1]
8484
local counterKey = KEYS[2]
8585
@@ -89,7 +89,7 @@ redis.call('SET', counterKey, 0)
8989
return 1
9090
`
9191

92-
deleteScript = `
92+
bloomFilterDeleteScript = `
9393
local filterKey = KEYS[1]
9494
local counterKey = KEYS[2]
9595
@@ -179,14 +179,14 @@ func NewBloomFilter(
179179
return nil, ErrFalsePositiveRateGreaterThanOne
180180
}
181181

182-
size := numberOfBits(expectedNumberOfItems, falsePositiveRate)
182+
size := numberOfBloomFilterBits(expectedNumberOfItems, falsePositiveRate)
183183
if size == 0 {
184184
return nil, ErrBitsSizeZero
185185
}
186186
if size > maxSize {
187187
return nil, ErrBitsSizeTooLarge
188188
}
189-
hashIterations := numberOfHashFunctions(size, expectedNumberOfItems)
189+
hashIterations := numberOfBloomFilterHashFunctions(size, expectedNumberOfItems)
190190

191191
// NOTE: https://redis.io/docs/reference/cluster-spec/#hash-tags
192192
bfName := "{" + name + "}"
@@ -198,18 +198,18 @@ func NewBloomFilter(
198198
hashIterations: hashIterations,
199199
hashIterationString: strconv.FormatUint(uint64(hashIterations), 10),
200200
size: size,
201-
addMultiScript: rueidis.NewLuaScript(addMultiScript),
201+
addMultiScript: rueidis.NewLuaScript(bloomFilterAddMultiScript),
202202
addMultiKeys: []string{bfName, counterName},
203-
existsMultiScript: rueidis.NewLuaScript(existsMultiScript),
203+
existsMultiScript: rueidis.NewLuaScript(bloomFilterExistsMultiScript),
204204
existsMultiKeys: []string{bfName},
205205
}, nil
206206
}
207207

208-
func numberOfBits(n uint, r float64) uint {
208+
func numberOfBloomFilterBits(n uint, r float64) uint {
209209
return uint(math.Ceil(-float64(n) * math.Log(r) / math.Pow(math.Log(2), 2)))
210210
}
211211

212-
func numberOfHashFunctions(s uint, n uint) uint {
212+
func numberOfBloomFilterHashFunctions(s uint, n uint) uint {
213213
return uint(math.Round(float64(s) / float64(n) * math.Log(2)))
214214
}
215215

@@ -229,11 +229,7 @@ func (c *bloomFilter) AddMulti(ctx context.Context, keys []string) error {
229229
args = append(args, indexes...)
230230

231231
resp := c.addMultiScript.Exec(ctx, c.client, c.addMultiKeys, args)
232-
if resp.Error() != nil {
233-
return resp.Error()
234-
}
235-
236-
return nil
232+
return resp.Error()
237233
}
238234

239235
func (c *bloomFilter) indexes(keys []string) []string {
@@ -300,33 +296,25 @@ func (c *bloomFilter) Reset(ctx context.Context) error {
300296
ctx,
301297
c.client.B().
302298
Eval().
303-
Script(resetScript).
299+
Script(bloomFilterResetScript).
304300
Numkeys(2).
305301
Key(c.name, c.counter).
306302
Build(),
307303
)
308-
if resp.Error() != nil {
309-
return resp.Error()
310-
}
311-
312-
return nil
304+
return resp.Error()
313305
}
314306

315307
func (c *bloomFilter) Delete(ctx context.Context) error {
316308
resp := c.client.Do(
317309
ctx,
318310
c.client.B().
319311
Eval().
320-
Script(deleteScript).
312+
Script(bloomFilterDeleteScript).
321313
Numkeys(2).
322314
Key(c.name, c.counter).
323315
Build(),
324316
)
325-
if resp.Error() != nil {
326-
return resp.Error()
327-
}
328-
329-
return nil
317+
return resp.Error()
330318
}
331319

332320
func (c *bloomFilter) Count(ctx context.Context) (uint, error) {

0 commit comments

Comments
 (0)