Skip to content

Commit

Permalink
initial import
Browse files Browse the repository at this point in the history
  • Loading branch information
kevwan committed Jul 26, 2020
0 parents commit 7e3a369
Show file tree
Hide file tree
Showing 647 changed files with 54,754 additions and 0 deletions.
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
**/.git
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* text=auto eol=lf
46 changes: 46 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Ignore all
*

# Unignore all with extensions
!*.*
!**/Dockerfile

# Unignore all dirs
!*/
!api

.idea
**/.DS_Store
**/logs
!vendor/github.com/songtianyi/rrframework/logs
**/*.pem
**/*.prof
**/*.p12
!Makefile

# gitlab ci
.cache

# chatbot
**/production.json
**/*.corpus.json
**/*.txt
**/*.gob

# example
example/**/*.csv

# hera
service/hera/cli/readdata/intergrationtest/data
service/hera/devkit/ch331/data
service/hera/devkit/ch331/ck
service/hera/cli/replaybeat/etc
# goctl
tools/goctl/api/autogen

vendor/*
/service/hera/cli/dboperation/etc/hera.json

# vim auto backup file
*~
!OWNERS
16 changes: 16 additions & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
stages:
- analysis

variables:
GOPATH: '/runner-cache/zero'
GOCACHE: '/runner-cache/zero'
GOPROXY: 'https://goproxy.cn,direct'

analysis:
stage: analysis
image: golang
script:
- go version && go env
- go test -short $(go list ./...) | grep -v "no test"
only:
- merge_requests
43 changes: 43 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
run:
# concurrency: 6
timeout: 5m
skip-dirs:
- core
- diq
- doc
- dq
- example
- kmq
- kq
- ngin
- rq
- rpcx
# - service
- stash
- tools


linters:
disable-all: true
enable:
- bodyclose
- deadcode
- errcheck
- gosimple
- govet
- ineffassign
- staticcheck
- structcheck
- typecheck
- unused
- varcheck
# - dupl


linters-settings:

issues:
exclude-rules:
- linters:
- staticcheck
text: 'SA1019: (baseresponse.BoolResponse|oldresponse.FormatBadRequestResponse|oldresponse.FormatResponse)|SA5008: unknown JSON option ("optional"|"default=|"range=|"options=)'
161 changes: 161 additions & 0 deletions core/bloom/bloom.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
package bloom

import (
"errors"
"strconv"

"zero/core/hash"
"zero/core/stores/redis"
)

const (
// for detailed error rate table, see http://pages.cs.wisc.edu/~cao/papers/summary-cache/node8.html
// maps as k in the error rate table
maps = 14
setScript = `
local key = KEYS[1]
for _, offset in ipairs(ARGV) do
redis.call("setbit", key, offset, 1)
end
`
testScript = `
local key = KEYS[1]
for _, offset in ipairs(ARGV) do
if tonumber(redis.call("getbit", key, offset)) == 0 then
return false
end
end
return true
`
)

var ErrTooLargeOffset = errors.New("too large offset")

type (
BitSetProvider interface {
check([]uint) (bool, error)
set([]uint) error
}

BloomFilter struct {
bits uint
maps uint
bitSet BitSetProvider
}
)

// New create a BloomFilter, store is the backed redis, key is the key for the bloom filter,
// bits is how many bits will be used, maps is how many hashes for each addition.
// best practices:
// elements - means how many actual elements
// when maps = 14, formula: 0.7*(bits/maps), bits = 20*elements, the error rate is 0.000067 < 1e-4
// for detailed error rate table, see http://pages.cs.wisc.edu/~cao/papers/summary-cache/node8.html
func New(store *redis.Redis, key string, bits uint) *BloomFilter {
return &BloomFilter{
bits: bits,
bitSet: newRedisBitSet(store, key, bits),
}
}

func (f *BloomFilter) Add(data []byte) error {
locations := f.getLocations(data)
err := f.bitSet.set(locations)
if err != nil {
return err
}
return nil
}

func (f *BloomFilter) Exists(data []byte) (bool, error) {
locations := f.getLocations(data)
isSet, err := f.bitSet.check(locations)
if err != nil {
return false, err
}
if !isSet {
return false, nil
}

return true, nil
}

func (f *BloomFilter) getLocations(data []byte) []uint {
locations := make([]uint, maps)
for i := uint(0); i < maps; i++ {
hashValue := hash.Hash(append(data, byte(i)))
locations[i] = uint(hashValue % uint64(f.bits))
}

return locations
}

type redisBitSet struct {
store *redis.Redis
key string
bits uint
}

func newRedisBitSet(store *redis.Redis, key string, bits uint) *redisBitSet {
return &redisBitSet{
store: store,
key: key,
bits: bits,
}
}

func (r *redisBitSet) buildOffsetArgs(offsets []uint) ([]string, error) {
var args []string

for _, offset := range offsets {
if offset >= r.bits {
return nil, ErrTooLargeOffset
}

args = append(args, strconv.FormatUint(uint64(offset), 10))
}

return args, nil
}

func (r *redisBitSet) check(offsets []uint) (bool, error) {
args, err := r.buildOffsetArgs(offsets)
if err != nil {
return false, err
}

resp, err := r.store.Eval(testScript, []string{r.key}, args)
if err == redis.Nil {
return false, nil
} else if err != nil {
return false, err
}

if exists, ok := resp.(int64); !ok {
return false, nil
} else {
return exists == 1, nil
}
}

func (r *redisBitSet) del() error {
_, err := r.store.Del(r.key)
return err
}

func (r *redisBitSet) expire(seconds int) error {
return r.store.Expire(r.key, seconds)
}

func (r *redisBitSet) set(offsets []uint) error {
args, err := r.buildOffsetArgs(offsets)
if err != nil {
return err
}

_, err = r.store.Eval(setScript, []string{r.key}, args)
if err == redis.Nil {
return nil
} else {
return err
}
}
63 changes: 63 additions & 0 deletions core/bloom/bloom_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package bloom

import (
"testing"

"zero/core/stores/redis"

"github.com/alicebob/miniredis"
"github.com/stretchr/testify/assert"
)

func TestRedisBitSet_New_Set_Test(t *testing.T) {
s, err := miniredis.Run()
if err != nil {
t.Error("Miniredis could not start")
}
defer s.Close()

store := redis.NewRedis(s.Addr(), redis.NodeType)
bitSet := newRedisBitSet(store, "test_key", 1024)
isSetBefore, err := bitSet.check([]uint{0})
if err != nil {
t.Fatal(err)
}
if isSetBefore {
t.Fatal("Bit should not be set")
}
err = bitSet.set([]uint{512})
if err != nil {
t.Fatal(err)
}
isSetAfter, err := bitSet.check([]uint{512})
if err != nil {
t.Fatal(err)
}
if !isSetAfter {
t.Fatal("Bit should be set")
}
err = bitSet.expire(3600)
if err != nil {
t.Fatal(err)
}
err = bitSet.del()
if err != nil {
t.Fatal(err)
}
}

func TestRedisBitSet_Add(t *testing.T) {
s, err := miniredis.Run()
if err != nil {
t.Error("Miniredis could not start")
}
defer s.Close()

store := redis.NewRedis(s.Addr(), redis.NodeType)
filter := New(store, "test_key", 64)
assert.Nil(t, filter.Add([]byte("hello")))
assert.Nil(t, filter.Add([]byte("world")))
ok, err := filter.Exists([]byte("hello"))
assert.Nil(t, err)
assert.True(t, ok)
}
Loading

0 comments on commit 7e3a369

Please sign in to comment.