Skip to content

Commit 7e3a369

Browse files
committed
initial import
0 parents  commit 7e3a369

File tree

647 files changed

+54754
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

647 files changed

+54754
-0
lines changed

.dockerignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
**/.git

.gitattributes

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* text=auto eol=lf

.gitignore

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Ignore all
2+
*
3+
4+
# Unignore all with extensions
5+
!*.*
6+
!**/Dockerfile
7+
8+
# Unignore all dirs
9+
!*/
10+
!api
11+
12+
.idea
13+
**/.DS_Store
14+
**/logs
15+
!vendor/github.com/songtianyi/rrframework/logs
16+
**/*.pem
17+
**/*.prof
18+
**/*.p12
19+
!Makefile
20+
21+
# gitlab ci
22+
.cache
23+
24+
# chatbot
25+
**/production.json
26+
**/*.corpus.json
27+
**/*.txt
28+
**/*.gob
29+
30+
# example
31+
example/**/*.csv
32+
33+
# hera
34+
service/hera/cli/readdata/intergrationtest/data
35+
service/hera/devkit/ch331/data
36+
service/hera/devkit/ch331/ck
37+
service/hera/cli/replaybeat/etc
38+
# goctl
39+
tools/goctl/api/autogen
40+
41+
vendor/*
42+
/service/hera/cli/dboperation/etc/hera.json
43+
44+
# vim auto backup file
45+
*~
46+
!OWNERS

.gitlab-ci.yml

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
stages:
2+
- analysis
3+
4+
variables:
5+
GOPATH: '/runner-cache/zero'
6+
GOCACHE: '/runner-cache/zero'
7+
GOPROXY: 'https://goproxy.cn,direct'
8+
9+
analysis:
10+
stage: analysis
11+
image: golang
12+
script:
13+
- go version && go env
14+
- go test -short $(go list ./...) | grep -v "no test"
15+
only:
16+
- merge_requests

.golangci.yml

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
run:
2+
# concurrency: 6
3+
timeout: 5m
4+
skip-dirs:
5+
- core
6+
- diq
7+
- doc
8+
- dq
9+
- example
10+
- kmq
11+
- kq
12+
- ngin
13+
- rq
14+
- rpcx
15+
# - service
16+
- stash
17+
- tools
18+
19+
20+
linters:
21+
disable-all: true
22+
enable:
23+
- bodyclose
24+
- deadcode
25+
- errcheck
26+
- gosimple
27+
- govet
28+
- ineffassign
29+
- staticcheck
30+
- structcheck
31+
- typecheck
32+
- unused
33+
- varcheck
34+
# - dupl
35+
36+
37+
linters-settings:
38+
39+
issues:
40+
exclude-rules:
41+
- linters:
42+
- staticcheck
43+
text: 'SA1019: (baseresponse.BoolResponse|oldresponse.FormatBadRequestResponse|oldresponse.FormatResponse)|SA5008: unknown JSON option ("optional"|"default=|"range=|"options=)'

core/bloom/bloom.go

+161
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
package bloom
2+
3+
import (
4+
"errors"
5+
"strconv"
6+
7+
"zero/core/hash"
8+
"zero/core/stores/redis"
9+
)
10+
11+
const (
12+
// for detailed error rate table, see http://pages.cs.wisc.edu/~cao/papers/summary-cache/node8.html
13+
// maps as k in the error rate table
14+
maps = 14
15+
setScript = `
16+
local key = KEYS[1]
17+
for _, offset in ipairs(ARGV) do
18+
redis.call("setbit", key, offset, 1)
19+
end
20+
`
21+
testScript = `
22+
local key = KEYS[1]
23+
for _, offset in ipairs(ARGV) do
24+
if tonumber(redis.call("getbit", key, offset)) == 0 then
25+
return false
26+
end
27+
end
28+
return true
29+
`
30+
)
31+
32+
var ErrTooLargeOffset = errors.New("too large offset")
33+
34+
type (
35+
BitSetProvider interface {
36+
check([]uint) (bool, error)
37+
set([]uint) error
38+
}
39+
40+
BloomFilter struct {
41+
bits uint
42+
maps uint
43+
bitSet BitSetProvider
44+
}
45+
)
46+
47+
// New create a BloomFilter, store is the backed redis, key is the key for the bloom filter,
48+
// bits is how many bits will be used, maps is how many hashes for each addition.
49+
// best practices:
50+
// elements - means how many actual elements
51+
// when maps = 14, formula: 0.7*(bits/maps), bits = 20*elements, the error rate is 0.000067 < 1e-4
52+
// for detailed error rate table, see http://pages.cs.wisc.edu/~cao/papers/summary-cache/node8.html
53+
func New(store *redis.Redis, key string, bits uint) *BloomFilter {
54+
return &BloomFilter{
55+
bits: bits,
56+
bitSet: newRedisBitSet(store, key, bits),
57+
}
58+
}
59+
60+
func (f *BloomFilter) Add(data []byte) error {
61+
locations := f.getLocations(data)
62+
err := f.bitSet.set(locations)
63+
if err != nil {
64+
return err
65+
}
66+
return nil
67+
}
68+
69+
func (f *BloomFilter) Exists(data []byte) (bool, error) {
70+
locations := f.getLocations(data)
71+
isSet, err := f.bitSet.check(locations)
72+
if err != nil {
73+
return false, err
74+
}
75+
if !isSet {
76+
return false, nil
77+
}
78+
79+
return true, nil
80+
}
81+
82+
func (f *BloomFilter) getLocations(data []byte) []uint {
83+
locations := make([]uint, maps)
84+
for i := uint(0); i < maps; i++ {
85+
hashValue := hash.Hash(append(data, byte(i)))
86+
locations[i] = uint(hashValue % uint64(f.bits))
87+
}
88+
89+
return locations
90+
}
91+
92+
type redisBitSet struct {
93+
store *redis.Redis
94+
key string
95+
bits uint
96+
}
97+
98+
func newRedisBitSet(store *redis.Redis, key string, bits uint) *redisBitSet {
99+
return &redisBitSet{
100+
store: store,
101+
key: key,
102+
bits: bits,
103+
}
104+
}
105+
106+
func (r *redisBitSet) buildOffsetArgs(offsets []uint) ([]string, error) {
107+
var args []string
108+
109+
for _, offset := range offsets {
110+
if offset >= r.bits {
111+
return nil, ErrTooLargeOffset
112+
}
113+
114+
args = append(args, strconv.FormatUint(uint64(offset), 10))
115+
}
116+
117+
return args, nil
118+
}
119+
120+
func (r *redisBitSet) check(offsets []uint) (bool, error) {
121+
args, err := r.buildOffsetArgs(offsets)
122+
if err != nil {
123+
return false, err
124+
}
125+
126+
resp, err := r.store.Eval(testScript, []string{r.key}, args)
127+
if err == redis.Nil {
128+
return false, nil
129+
} else if err != nil {
130+
return false, err
131+
}
132+
133+
if exists, ok := resp.(int64); !ok {
134+
return false, nil
135+
} else {
136+
return exists == 1, nil
137+
}
138+
}
139+
140+
func (r *redisBitSet) del() error {
141+
_, err := r.store.Del(r.key)
142+
return err
143+
}
144+
145+
func (r *redisBitSet) expire(seconds int) error {
146+
return r.store.Expire(r.key, seconds)
147+
}
148+
149+
func (r *redisBitSet) set(offsets []uint) error {
150+
args, err := r.buildOffsetArgs(offsets)
151+
if err != nil {
152+
return err
153+
}
154+
155+
_, err = r.store.Eval(setScript, []string{r.key}, args)
156+
if err == redis.Nil {
157+
return nil
158+
} else {
159+
return err
160+
}
161+
}

core/bloom/bloom_test.go

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package bloom
2+
3+
import (
4+
"testing"
5+
6+
"zero/core/stores/redis"
7+
8+
"github.com/alicebob/miniredis"
9+
"github.com/stretchr/testify/assert"
10+
)
11+
12+
func TestRedisBitSet_New_Set_Test(t *testing.T) {
13+
s, err := miniredis.Run()
14+
if err != nil {
15+
t.Error("Miniredis could not start")
16+
}
17+
defer s.Close()
18+
19+
store := redis.NewRedis(s.Addr(), redis.NodeType)
20+
bitSet := newRedisBitSet(store, "test_key", 1024)
21+
isSetBefore, err := bitSet.check([]uint{0})
22+
if err != nil {
23+
t.Fatal(err)
24+
}
25+
if isSetBefore {
26+
t.Fatal("Bit should not be set")
27+
}
28+
err = bitSet.set([]uint{512})
29+
if err != nil {
30+
t.Fatal(err)
31+
}
32+
isSetAfter, err := bitSet.check([]uint{512})
33+
if err != nil {
34+
t.Fatal(err)
35+
}
36+
if !isSetAfter {
37+
t.Fatal("Bit should be set")
38+
}
39+
err = bitSet.expire(3600)
40+
if err != nil {
41+
t.Fatal(err)
42+
}
43+
err = bitSet.del()
44+
if err != nil {
45+
t.Fatal(err)
46+
}
47+
}
48+
49+
func TestRedisBitSet_Add(t *testing.T) {
50+
s, err := miniredis.Run()
51+
if err != nil {
52+
t.Error("Miniredis could not start")
53+
}
54+
defer s.Close()
55+
56+
store := redis.NewRedis(s.Addr(), redis.NodeType)
57+
filter := New(store, "test_key", 64)
58+
assert.Nil(t, filter.Add([]byte("hello")))
59+
assert.Nil(t, filter.Add([]byte("world")))
60+
ok, err := filter.Exists([]byte("hello"))
61+
assert.Nil(t, err)
62+
assert.True(t, ok)
63+
}

0 commit comments

Comments
 (0)