@@ -17,20 +17,20 @@ import (
17
17
type Bloom struct {
18
18
p * Pool
19
19
c * BloomConfig
20
- bitnum uint64
21
20
expire int64
22
21
}
22
+
23
23
type BloomConfig struct {
24
24
// make sure don't use duplicate BloomName between different blooms
25
25
BloomName string
26
26
//Groupnum decide there are how many keys will be used in redis for this bloom
27
27
//this is useful to balance all redis nodes' request
28
28
//every special key will have a name like BloomName_[0,Groupnum)
29
29
Groupnum uint64 //default 10
30
- //unit is byte
30
+ //unit is bit
31
31
//single hash list's capacity in every group
32
- //so this bloom will use memory: Capacity * hash_func_num(6) * Groupnum
33
- Capacity uint64 //default 1024*1024(1M)
32
+ //so this bloom will use memory: Capacity / 8 * hash_func_num(6) * Groupnum
33
+ Capacity uint64 //default 1024*1024*8 (1M)
34
34
Expire time.Duration //default 30day,min 1hour
35
35
}
36
36
@@ -45,7 +45,7 @@ func (c *BloomConfig) validate() error {
45
45
c .Groupnum = 10
46
46
}
47
47
if c .Capacity == 0 {
48
- c .Capacity = 1024 * 1024
48
+ c .Capacity = 1024 * 1024 * 8
49
49
}
50
50
if c .Expire == 0 {
51
51
c .Expire = 30 * 24 * time .Hour
71
71
end
72
72
redis.call("SET",KEYS[1],ARGV[1],"EX",ARGV[2])`
73
73
74
- var initlua = `local e,k1,k2,k3,k4,k5,k6="{"..KEYS[1].."}_e","{"..KEYS[1].."}_bkdr","{"..KEYS[1].."}_djb","{"..KEYS[1].."}_fnv","{"..KEYS[1].."}_dek","{"..KEYS[1].."}_rs","{"..KEYS[1].."}_sdbm"
75
- if(redis.call("EXISTS",e)==0)
74
+ var initlua = `if(redis.call("EXISTS",KEYS[7])==0)
76
75
then
77
- local r1=redis.call("SETBIT",k1 ,ARGV[1],1)
78
- local r2=redis.call("SETBIT",k2 ,ARGV[1],1)
79
- local r3=redis.call("SETBIT",k3 ,ARGV[1],1)
80
- local r4=redis.call("SETBIT",k4 ,ARGV[1],1)
81
- local r5=redis.call("SETBIT",k5 ,ARGV[1],1)
82
- local r6=redis.call("SETBIT",k6 ,ARGV[1],1)
83
- redis.call("SET",e ,1,"EX",ARGV[2])
84
- redis.call("EXPIRE",k1 ,ARGV[2])
85
- redis.call("EXPIRE",k2 ,ARGV[2])
86
- redis.call("EXPIRE",k3 ,ARGV[2])
87
- redis.call("EXPIRE",k4 ,ARGV[2])
88
- redis.call("EXPIRE",k5 ,ARGV[2])
89
- redis.call("EXPIRE",k6 ,ARGV[2])
76
+ local r1=redis.call("SETBIT",KEYS[1] ,ARGV[1],1)
77
+ local r2=redis.call("SETBIT",KEYS[2] ,ARGV[1],1)
78
+ local r3=redis.call("SETBIT",KEYS[3] ,ARGV[1],1)
79
+ local r4=redis.call("SETBIT",KEYS[4] ,ARGV[1],1)
80
+ local r5=redis.call("SETBIT",KEYS[5] ,ARGV[1],1)
81
+ local r6=redis.call("SETBIT",KEYS[6] ,ARGV[1],1)
82
+ redis.call("SET",KEYS[7] ,1,"EX",ARGV[2])
83
+ redis.call("EXPIRE",KEYS[1] ,ARGV[2])
84
+ redis.call("EXPIRE",KEYS[2] ,ARGV[2])
85
+ redis.call("EXPIRE",KEYS[3] ,ARGV[2])
86
+ redis.call("EXPIRE",KEYS[4] ,ARGV[2])
87
+ redis.call("EXPIRE",KEYS[5] ,ARGV[2])
88
+ redis.call("EXPIRE",KEYS[6] ,ARGV[2])
90
89
end`
91
90
91
+ var ErrBloomConflict = errors .New ("bloom conflict" )
92
+ var ErrBloomExpired = errors .New ("bloom expired" )
93
+
92
94
// NewRedisBitFilter -
93
95
func (p * Pool ) NewBloom (ctx context.Context , c * BloomConfig ) (* Bloom , error ) {
94
- if p .p == nil {
95
- return nil , errors .New ("redis client not inited" )
96
- }
97
96
if e := c .validate (); e != nil {
98
97
return nil , e
99
98
}
@@ -114,20 +113,19 @@ func (p *Pool) NewBloom(ctx context.Context, c *BloomConfig) (*Bloom, error) {
114
113
}
115
114
}
116
115
conn .Close ()
117
- //if exist,use exist's config to replace this config
118
- c = & BloomConfig {}
119
116
if exist {
120
- if e = json .Unmarshal ([]byte (cstr ), c ); e != nil {
121
- return nil , errors .New ("BloomName conflict" )
117
+ //if exist,use exist's config to replace this config
118
+ c = & BloomConfig {}
119
+ if e = json .Unmarshal (common .Str2byte (cstr ), c ); e != nil {
120
+ return nil , ErrBloomConflict
122
121
}
123
122
if c .Groupnum == 0 || c .Capacity == 0 || c .Expire < time .Hour {
124
- return nil , errors . New ( "BloomName conflict" )
123
+ return nil , ErrBloomConflict
125
124
}
126
125
}
127
126
instance := & Bloom {
128
127
p : p ,
129
128
c : c ,
130
- bitnum : c .Capacity * 8 ,
131
129
expire : int64 (c .Expire .Seconds ()),
132
130
}
133
131
//init memory in redis
@@ -136,14 +134,20 @@ func (p *Pool) NewBloom(ctx context.Context, c *BloomConfig) (*Bloom, error) {
136
134
tempindex := i
137
135
go func () {
138
136
key := c .BloomName + "_" + strconv .FormatUint (tempindex , 10 )
139
- bit := c .Capacity * 8 //capacity's unit is byte,transform to bit
137
+ keybkdr := "{" + key + "}_bkdr"
138
+ keydjb := "{" + key + "}_djb"
139
+ keyfnv := "{" + key + "}_fnv"
140
+ keydek := "{" + key + "}_dek"
141
+ keyrs := "{" + key + "}_rs"
142
+ keysdbm := "{" + key + "}_sdbm"
143
+ keyexist := "{" + key + "}_exist"
140
144
conn , e := p .GetContext (ctx )
141
145
if e != nil {
142
146
ch <- e
143
147
return
144
148
}
145
149
defer conn .Close ()
146
- _ , e = conn .DoContext (ctx , "EVAL" , initlua , 1 , key , bit , int64 ( c . Expire . Seconds ()) )
150
+ _ , e = conn .DoContext (ctx , "EVAL" , initlua , 7 , keybkdr , keydjb , keyfnv , keydek , keyrs , keysdbm , keyexist , c . Capacity , instance . expire )
147
151
if e != nil && e != redis .ErrNil {
148
152
ch <- e
149
153
return
@@ -160,30 +164,29 @@ func (p *Pool) NewBloom(ctx context.Context, c *BloomConfig) (*Bloom, error) {
160
164
if string (d ) == cstr {
161
165
return instance , nil
162
166
}
163
- return nil , errors . New ( "BloomName conflict" )
167
+ return nil , ErrBloomConflict
164
168
}
165
169
return instance , nil
166
170
}
167
171
168
- const setlua = `local e,k1,k2,k3,k4,k5,k6="{"..KEYS[1].."}_e","{"..KEYS[1].."}_bkdr","{"..KEYS[1].."}_djb","{"..KEYS[1].."}_fnv","{"..KEYS[1].."}_dek","{"..KEYS[1].."}_rs","{"..KEYS[1].."}_sdbm"
169
- if(redis.call("EXISTS",e)==0)
172
+ const setlua = `if(redis.call("EXISTS",KEYS[7])==0)
170
173
then
171
174
return -1
172
175
end
173
- local r1=redis.call("SETBIT",k1 ,ARGV[1],1)
174
- local r2=redis.call("SETBIT",k2 ,ARGV[2],1)
175
- local r3=redis.call("SETBIT",k3 ,ARGV[3],1)
176
- local r4=redis.call("SETBIT",k4 ,ARGV[4],1)
177
- local r5=redis.call("SETBIT",k5 ,ARGV[5],1)
178
- local r6=redis.call("SETBIT",k6 ,ARGV[6],1)
179
- if(redis.call("EXISTS",e )==0)
176
+ local r1=redis.call("SETBIT",KEYS[1] ,ARGV[1],1)
177
+ local r2=redis.call("SETBIT",KEYS[2] ,ARGV[2],1)
178
+ local r3=redis.call("SETBIT",KEYS[3] ,ARGV[3],1)
179
+ local r4=redis.call("SETBIT",KEYS[4] ,ARGV[4],1)
180
+ local r5=redis.call("SETBIT",KEYS[5] ,ARGV[5],1)
181
+ local r6=redis.call("SETBIT",KEYS[6] ,ARGV[6],1)
182
+ if(redis.call("EXISTS",KEYS[7] )==0)
180
183
then
181
- redis.call("DEL",k1 )
182
- redis.call("DEL",k2 )
183
- redis.call("DEL",k3 )
184
- redis.call("DEL",k4 )
185
- redis.call("DEL",k5 )
186
- redis.call("DEL",k6 )
184
+ redis.call("DEL",KEYS[1] )
185
+ redis.call("DEL",kEYS[2] )
186
+ redis.call("DEL",KEYS[3] )
187
+ redis.call("DEL",KEYS[4] )
188
+ redis.call("DEL",KEYS[5] )
189
+ redis.call("DEL",KEYS[6] )
187
190
return -1
188
191
end
189
192
if(r1==0 or r2==0 or r3==0 or r4==0 or r5==0 or r6==0)
@@ -197,51 +200,57 @@ var hsetlua string
197
200
// Set add key into the bloom
198
201
//true,this key is not in this bloom and add success
199
202
//false,this key maybe already in this bloom,can't 100% confirm
200
- func (b * Bloom ) Set (ctx context.Context , key string ) (bool , error ) {
201
- filterkey := b .getbloomkey (key )
202
- bit1 := common .BkdrhashString (key , b .bitnum )
203
- bit2 := common .DjbhashString (key , b .bitnum )
204
- bit3 := common .FnvhashString (key , b .bitnum )
205
- bit4 := common .DekhashString (key , b .bitnum )
206
- bit5 := common .RshashString (key , b .bitnum )
207
- bit6 := common .SdbmhashString (key , b .bitnum )
203
+ func (b * Bloom ) Set (ctx context.Context , userkey string ) (bool , error ) {
204
+ key := b .c .BloomName + "_" + strconv .FormatUint (common .BkdrhashString (userkey , uint64 (b .c .Groupnum )), 10 )
205
+ keybkdr := "{" + key + "}_bkdr"
206
+ keydjb := "{" + key + "}_djb"
207
+ keyfnv := "{" + key + "}_fnv"
208
+ keydek := "{" + key + "}_dek"
209
+ keyrs := "{" + key + "}_rs"
210
+ keysdbm := "{" + key + "}_sdbm"
211
+ keyexist := "{" + key + "}_exist"
212
+ bit1 := common .BkdrhashString (key , b .c .Capacity )
213
+ bit2 := common .DjbhashString (key , b .c .Capacity )
214
+ bit3 := common .FnvhashString (key , b .c .Capacity )
215
+ bit4 := common .DekhashString (key , b .c .Capacity )
216
+ bit5 := common .RshashString (key , b .c .Capacity )
217
+ bit6 := common .SdbmhashString (key , b .c .Capacity )
208
218
c , e := b .p .GetContext (ctx )
209
219
if e != nil {
210
220
return false , e
211
221
}
212
222
defer c .Close ()
213
- r , e := redis .Int (c .DoContext (ctx , "EVALSHA" , hsetlua , 1 , filterkey , bit1 , bit2 , bit3 , bit4 , bit5 , bit6 , b . expire ))
223
+ r , e := redis .Int (c .DoContext (ctx , "EVALSHA" , hsetlua , 7 , keybkdr , keydjb , keyfnv , keydek , keyrs , keysdbm , keyexist , bit1 , bit2 , bit3 , bit4 , bit5 , bit6 ))
214
224
if e != nil && strings .Contains (e .Error (), "NOSCRIPT" ) {
215
- r , e = redis .Int (c .DoContext (ctx , "EVAL" , setlua , 1 , filterkey , bit1 , bit2 , bit3 , bit4 , bit5 , bit6 , b . expire ))
225
+ r , e = redis .Int (c .DoContext (ctx , "EVAL" , setlua , 7 , keybkdr , keydjb , keyfnv , keydek , keyrs , keysdbm , keyexist , bit1 , bit2 , bit3 , bit4 , bit5 , bit6 ))
216
226
}
217
227
if e != nil {
218
228
return false , e
219
229
}
220
230
if r == - 1 {
221
- return false , errors . New ( "bloom expired" )
231
+ return false , ErrBloomExpired
222
232
}
223
233
return r == 0 , nil
224
234
}
225
235
226
- var checklua = `local e,k1,k2,k3,k4,k5,k6="{"..KEYS[1].."}_e","{"..KEYS[1].."}_bkdr","{"..KEYS[1].."}_djb","{"..KEYS[1].."}_fnv","{"..KEYS[1].."}_dek","{"..KEYS[1].."}_rs","{"..KEYS[1].."}_sdbm"
227
- if(redis.call("EXISTS",e)==0)
236
+ var checklua = `if(redis.call("EXISTS",KEYS[7])==0)
228
237
then
229
238
return -1
230
239
end
231
- local r1=redis.call("GETBIT",k1 ,ARGV[1])
232
- local r2=redis.call("GETBIT",k2 ,ARGV[2])
233
- local r3=redis.call("GETBIT",k3 ,ARGV[3])
234
- local r4=redis.call("GETBIT",k4 ,ARGV[4])
235
- local r5=redis.call("GETBIT",k5 ,ARGV[5])
236
- local r6=redis.call("GETBIT",k6 ,ARGV[6])
237
- if(redis.call("EXISTS",e )==0)
240
+ local r1=redis.call("GETBIT",KEYS[1] ,ARGV[1])
241
+ local r2=redis.call("GETBIT",KEYS[2] ,ARGV[2])
242
+ local r3=redis.call("GETBIT",KEYS[3] ,ARGV[3])
243
+ local r4=redis.call("GETBIT",KEYS[4] ,ARGV[4])
244
+ local r5=redis.call("GETBIT",KEYS[5] ,ARGV[5])
245
+ local r6=redis.call("GETBIT",KEYS[6] ,ARGV[6])
246
+ if(redis.call("EXISTS",KEYS[7] )==0)
238
247
then
239
- redis.call("DEL",k1 )
240
- redis.call("DEL",k2 )
241
- redis.call("DEL",k3 )
242
- redis.call("DEL",k4 )
243
- redis.call("DEL",k5 )
244
- redis.call("DEL",k6 )
248
+ redis.call("DEL",KEYS[1] )
249
+ redis.call("DEL",KEYS[2] )
250
+ redis.call("DEL",KEYS[3] )
251
+ redis.call("DEL",KEYS[4] )
252
+ redis.call("DEL",KEYS[5] )
253
+ redis.call("DEL",KEYS[6] )
245
254
return -1
246
255
end
247
256
if(r1==0 or r2==0 or r3==0 or r4==0 or r5==0 or r6==0)
@@ -255,32 +264,35 @@ var hchecklua string
255
264
// Check -
256
265
//true,this key 100% not in this bloom
257
266
//false,this key maybe in this bloom,can't 100% confirm
258
- func (b * Bloom ) Check (ctx context.Context , key string ) (bool , error ) {
259
- filterkey := b .getbloomkey (key )
260
- bit1 := common .BkdrhashString (key , b .bitnum )
261
- bit2 := common .DjbhashString (key , b .bitnum )
262
- bit3 := common .FnvhashString (key , b .bitnum )
263
- bit4 := common .DekhashString (key , b .bitnum )
264
- bit5 := common .RshashString (key , b .bitnum )
265
- bit6 := common .SdbmhashString (key , b .bitnum )
267
+ func (b * Bloom ) Check (ctx context.Context , userkey string ) (bool , error ) {
268
+ key := b .c .BloomName + "_" + strconv .FormatUint (common .BkdrhashString (userkey , uint64 (b .c .Groupnum )), 10 )
269
+ keybkdr := "{" + key + "}_bkdr"
270
+ keydjb := "{" + key + "}_djb"
271
+ keyfnv := "{" + key + "}_fnv"
272
+ keydek := "{" + key + "}_dek"
273
+ keyrs := "{" + key + "}_rs"
274
+ keysdbm := "{" + key + "}_sdbm"
275
+ keyexist := "{" + key + "}_exist"
276
+ bit1 := common .BkdrhashString (key , b .c .Capacity )
277
+ bit2 := common .DjbhashString (key , b .c .Capacity )
278
+ bit3 := common .FnvhashString (key , b .c .Capacity )
279
+ bit4 := common .DekhashString (key , b .c .Capacity )
280
+ bit5 := common .RshashString (key , b .c .Capacity )
281
+ bit6 := common .SdbmhashString (key , b .c .Capacity )
266
282
c , e := b .p .GetContext (ctx )
267
283
if e != nil {
268
284
return false , e
269
285
}
270
286
defer c .Close ()
271
- r , e := redis .Int (c .DoContext (ctx , "EVALSHA" , hchecklua , 1 , filterkey , bit1 , bit2 , bit3 , bit4 , bit5 , bit6 , b . expire ))
287
+ r , e := redis .Int (c .DoContext (ctx , "EVALSHA" , hchecklua , 7 , keybkdr , keydjb , keyfnv , keydek , keyrs , keysdbm , keyexist , bit1 , bit2 , bit3 , bit4 , bit5 , bit6 ))
272
288
if e != nil && strings .HasPrefix (e .Error (), "NOSCRIPT" ) {
273
- r , e = redis .Int (c .DoContext (ctx , "EVAL" , checklua , 1 , filterkey , bit1 , bit2 , bit3 , bit4 , bit5 , bit6 , b . expire ))
289
+ r , e = redis .Int (c .DoContext (ctx , "EVAL" , checklua , 7 , keybkdr , keydjb , keyfnv , keydek , keyrs , keysdbm , keyexist , bit1 , bit2 , bit3 , bit4 , bit5 , bit6 ))
274
290
}
275
291
if e != nil {
276
292
return false , e
277
293
}
278
294
if r == - 1 {
279
- return false , errors . New ( "bloom expired" )
295
+ return false , ErrBloomExpired
280
296
}
281
297
return r == 0 , nil
282
298
}
283
-
284
- func (b * Bloom ) getbloomkey (userkey string ) string {
285
- return b .c .BloomName + "_" + strconv .FormatUint (common .BkdrhashString (userkey , uint64 (b .c .Groupnum )), 10 )
286
- }
0 commit comments